diff --git a/gomspace/gomspace.mk b/gomspace/gomspace.mk index 675fa426..57d38da7 100644 --- a/gomspace/gomspace.mk +++ b/gomspace/gomspace.mk @@ -5,7 +5,15 @@ CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/rtable/csp_rtable_cidr.c) CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/crypto/*.c) CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/arch/posix/*.c) CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/transport/*.c) +CSRC += $(wildcard $(CURRENTPATH)/p60-dock_client/src/*.c) +CSRC += $(wildcard $(CURRENTPATH)/libparam_client/src/*.c) +CSRC += $(wildcard $(CURRENTPATH)/libparam_client/src/rparam/*.c) INCLUDES += $(CURRENTPATH)/libcsp/include INCLUDES += $(CURRENTPATH)/libcsp/include/csp/crypto -INCLUDES += $(CURRENTPATH)/libcsp \ No newline at end of file +INCLUDES += $(CURRENTPATH)/libcsp +INCLUDES += $(CURRENTPATH)/p60-dock_client/include/gs/p60-dock/param +INCLUDES += $(CURRENTPATH)/libparam_client/include +INCLUDES += $(CURRENTPATH)/libparam_client/include/deprecated +INCLUDES += $(CURRENTPATH)/libp60_client/include +INCLUDES += $(CURRENTPATH)/libutil/include \ No newline at end of file diff --git a/gomspace/libp60_client/include/p60.h b/gomspace/libp60_client/include/p60.h new file mode 100644 index 00000000..186d43f8 --- /dev/null +++ b/gomspace/libp60_client/include/p60.h @@ -0,0 +1,48 @@ +#ifndef _P60_H_ +#define _P60_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#define P60_PORT_RPARAM 7 +#define P60_PORT_GNDWDT_RESET 9 +#define P60_PORT_CMDCONTROL 10 +#define P60_PORT_GSSB_SERVICE 15 +#define P60_PORT_GSCRIPT 22 + +/** FRAM MEMORY MAP */ +#define P60_FRAM_BOARD 0x0000 + +/** FRAM FILENAMES */ +#define P60_FNO_BOARD 0 +#define P60_FNO_BOARD_DFL 4 + +#define P60_FRAM_WP_BEGIN (0x1000) +#define P60_FRAM_WP_END (0x1C00 - 1) + +/** GND WD FRAM ADDR **/ +#define P60_FRAM_GNDWDT 0x1F00 + +/** PARAM INDEX MAP */ +#define P60_BOARD_PARAM 0 + +#define DEVICE_FM24CL64B 0 + +typedef enum { + UNKNOWN_RST = 0, + GND_WDT_RST, + I2C_WDT_RST, + CAN_WDT_RST, + EXT_HARD_RST, + EXT_SOFT_RST, +} p60_reset_cause_t; + +extern const uint8_t board_fallback_type; +extern const uint8_t csp_fallback_addr; +extern const uint8_t board_rs422_mode; + +extern void module_init_early(void); +extern void module_init(void); +extern void wdt_gnd_clear(void); +extern uint16_t command_control(uint8_t *packet, uint16_t length); +extern void module_task(void * pvParameters); + +#endif /* _P60_H_ */ diff --git a/gomspace/libp60_client/include/p60_board.h b/gomspace/libp60_client/include/p60_board.h new file mode 100644 index 00000000..2abd906d --- /dev/null +++ b/gomspace/libp60_client/include/p60_board.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + * NanoCom firmware + * + */ + +#ifndef P60_PARAM_H_ +#define P60_PARAM_H_ + +#include +#include + +/** + * Define memory space + */ +#define P60_BOARD_UID 0x00 +#define P60_BOARD_TYPE 0x10 +#define P60_BOARD_REV 0x11 +#define P60_BOARD_CSP_ADDR 0x12 +#define P60_BOARD_I2C_ADDR 0x13 +#define P60_BOARD_I2C_SPEED_KHZ 0x14 +#define P60_BOARD_CAN_SPEED_KHZ 0x16 +#define P60_BOARD_KISS_ENABLE 0x18 +#define P60_BOARD_RS422_MODE 0x19 +#define P60_BOARD_RS422_SPEED_KHZ 0x1C +#define P60_BOARD_RTABLE_STR 0x20 //! This one is 0x60 = 96 bytes +#define P60_BOARD_RTABLE_STR_SIZE 0x60 + +/** Define the memory size */ +#define P60_BOARD_PARAM_SIZE 0x80 + +extern const param_table_t p60_config[]; +extern const int p60_config_count; + +#endif /* P60_PARAM_H_ */ diff --git a/gomspace/libp60_client/include/power_if.h b/gomspace/libp60_client/include/power_if.h new file mode 100644 index 00000000..6762d1ec --- /dev/null +++ b/gomspace/libp60_client/include/power_if.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + * NanoPower firmware + * + */ + +#ifndef POWER_IF_H_ +#define POWER_IF_H_ + +#include + +#define POWER_IF_SET 1 +#define POWER_IF_GET 2 +#define POWER_IF_LIST 3 + +#define POWER_IF_STATUS_OK 0 +#define POWER_IF_STATUS_ERROR 1 + +#define POWER_IF_NAME_LEN 8 + +typedef struct __attribute__((packed)) { + uint8_t ch_idx; + uint8_t mode; + uint16_t on_cnt; + uint16_t off_cnt; + uint16_t cur_lu_lim; + uint16_t cur_lim; + uint16_t voltage; + int16_t current; + uint16_t latchup; + char name[POWER_IF_NAME_LEN]; +} power_if_ch_status_t; + +typedef struct __attribute__((packed)) { + uint8_t ch_idx; + uint8_t mode; + char name[8]; +} power_if_list_t; + +typedef struct __attribute__((packed)) { + uint8_t cmd; + uint8_t status; + power_if_ch_status_t ch_status; +} power_if_cmd_request_t; + +typedef struct __attribute__((packed)) { + uint8_t cmd; + uint8_t status; + power_if_ch_status_t ch_status; +} power_if_cmd_response_t; + +typedef struct __attribute__((packed)) { + uint8_t cmd; + uint8_t status; + uint8_t count; + power_if_list_t list[16]; +} power_if_cmd_list_response_t; + +int p60_power_if_cmd(uint8_t node, uint8_t port, uint32_t timeout, uint8_t cmd, void * ch_status_p); +uint8_t p60_power_if_get_ch_idx(char * ch_name, uint8_t * ch_no, uint8_t ch_no_max); + +#endif /* POWER_IF_H_ */ diff --git a/gomspace/libp60_client/src/cmd/power_if_cmd.c b/gomspace/libp60_client/src/cmd/power_if_cmd.c new file mode 100644 index 00000000..9851f912 --- /dev/null +++ b/gomspace/libp60_client/src/cmd/power_if_cmd.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +static uint8_t power_if_port = 10; +static uint32_t power_if_timeout = 5000; + +static int cmd_power_if_port(struct command_context * ctx) { + if (ctx->argc < 2) { + printf("Current port is %d\n", power_if_port); + return CMD_ERROR_NONE; + } + power_if_port = atoi(ctx->argv[1]); + return CMD_ERROR_NONE; +} + +static int cmd_power_if_timeout(struct command_context *ctx) { + if (ctx->argc < 2) { + printf("Current timeout is %"PRIu32"\n", power_if_timeout); + return CMD_ERROR_NONE; + } + if (sscanf(command_args(ctx), "%"SCNu32, &power_if_timeout) != 1) + return CMD_ERROR_SYNTAX; + if (power_if_timeout > 30000) { + printf("Timeout set to high, limited to 30000 ms\n"); + power_if_timeout = 30000; + } + printf("Timeout set to %"PRIu32"\n", power_if_timeout); + return CMD_ERROR_NONE; +} + +static int cmd_power_if_set_get(struct command_context * ctx) { + power_if_ch_status_t ch_status; + + if (ctx->argc < 3) { + return CMD_ERROR_SYNTAX; + } + uint8_t node = atoi(ctx->argv[1]); + memset(&ch_status, 0, sizeof(ch_status)); + strncpy(ch_status.name, ctx->argv[2], 7); + ch_status.name[7] = 0; + char * cmd = ctx->argv[0]; + if (!strcmp(cmd, "status") && (ctx->argc == 3)) { + if (!p60_power_if_cmd(node, power_if_port, power_if_timeout, POWER_IF_GET, &ch_status)) { + return CMD_ERROR_FAIL; + } + } else { + if (!strcmp(cmd, "on")) { + ch_status.mode = 1; + } else if (!strcmp(cmd, "off")) { + ch_status.mode = 0; + } + ch_status.on_cnt = (ctx->argc > 3) ? atoi(ctx->argv[3]) : 0; + ch_status.off_cnt = (ctx->argc > 4) ? atoi(ctx->argv[4]) : 0; + if (!p60_power_if_cmd(node, power_if_port, power_if_timeout, POWER_IF_SET, &ch_status)) { + return CMD_ERROR_FAIL; + } + } + printf("Node %u, Output channel '%s' (%u) is %s\r\n", node, ch_status.name, ch_status.ch_idx, (ch_status.mode ? "ON": "OFF")); + printf(" ch_idx: %u\r\n", ch_status.ch_idx); + printf(" mode: %u\r\n", ch_status.mode); + printf(" on_cnt: %u\r\n", ch_status.on_cnt); + printf(" off_cnt: %u\r\n", ch_status.off_cnt); + printf(" cur_lu_lim: %u\r\n", ch_status.cur_lu_lim); + printf(" cur_lim: %u\r\n", ch_status.cur_lim); + printf(" voltage: %u\r\n", ch_status.voltage); + printf(" current: %d\r\n", ch_status.current); + printf(" latchup: %u\r\n", ch_status.latchup); + + return CMD_ERROR_NONE; +} + +static int cmd_power_if_list(struct command_context * ctx) { + if (ctx->argc < 2) { + return CMD_ERROR_SYNTAX; + } + uint8_t node = atoi(ctx->argv[1]); + power_if_cmd_list_response_t ch_list; + if (!p60_power_if_cmd(node, power_if_port, power_if_timeout, POWER_IF_LIST, &ch_list)) { + return CMD_ERROR_FAIL; + } + printf("ch name status\r\n"); + for (uint8_t ch = 0; ch < ch_list.count; ch++) { + printf("%2u %-8s %s\r\n", ch_list.list[ch].ch_idx, ch_list.list[ch].name, ch_list.list[ch].mode ? "ON": "OFF"); + } + return CMD_ERROR_NONE; +} + +command_t power_if_commands[] = { + { + .name = "port", + .help = "Set power interface port (default is 10)", + .usage = "", + .handler = cmd_power_if_port, + }, + { + .name = "timeout", + .help = "Set power interface timeout in milliseconds", + .usage = "", + .handler = cmd_power_if_timeout, + }, + { + .name = "status", + .help = "Get power channel status", + .usage = " ", + .handler = cmd_power_if_set_get, + }, + { + .name = "on", + .help = "Turn power channel on", + .usage = " [ ]", + .handler = cmd_power_if_set_get, + }, + { + .name = "off", + .help = "Turn power channel off", + .usage = " off [ ]", + .handler = cmd_power_if_set_get, + }, + { + .name = "list", + .help = "Get list power channels", + .usage = "", + .handler = cmd_power_if_list, + }, +}; + +command_t __root_command power_if_root_command[] = { + { + .name = "power", + .help = "client: NanoPower P60", + .chain = INIT_CHAIN(power_if_commands), + } +}; + +void cmd_power_if_setup(void) { + command_register(power_if_root_command); +} diff --git a/gomspace/libp60_client/src/p60_client.c b/gomspace/libp60_client/src/p60_client.c new file mode 100644 index 00000000..76e1a905 --- /dev/null +++ b/gomspace/libp60_client/src/p60_client.c @@ -0,0 +1,33 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + * NanoCom firmware + * + */ + +#include +#include +#include +#include + +#include +#include + +/** + * Setup info about board configuration parameters + */ +const param_table_t p60_config[] = { + {.name = "uid", .addr = P60_BOARD_UID, .type = PARAM_STRING, .size = 16}, + {.name = "type", .addr = P60_BOARD_TYPE, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "rev", .addr = P60_BOARD_REV, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "csp_addr", .addr = P60_BOARD_CSP_ADDR, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "i2c_addr", .addr = P60_BOARD_I2C_ADDR, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "i2c_speed", .addr = P60_BOARD_I2C_SPEED_KHZ, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "can_speed", .addr = P60_BOARD_CAN_SPEED_KHZ, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "kiss_en", .addr = P60_BOARD_KISS_ENABLE, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "rs422_mode", .addr = P60_BOARD_RS422_MODE, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "rs422_speed", .addr = P60_BOARD_RS422_SPEED_KHZ, .type = PARAM_UINT32, .size = sizeof(uint32_t)}, + {.name = "csp_rtable", .addr = P60_BOARD_RTABLE_STR, .type = PARAM_STRING, .size = P60_BOARD_RTABLE_STR_SIZE}, + +}; + +const int p60_config_count = sizeof(p60_config) / sizeof(p60_config[0]); diff --git a/gomspace/libp60_client/src/power_if.c b/gomspace/libp60_client/src/power_if.c new file mode 100644 index 00000000..e9266ca0 --- /dev/null +++ b/gomspace/libp60_client/src/power_if.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + * NanoPower firmware + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +uint8_t p60_power_if_get_ch_idx(char * ch_name, uint8_t * ch_no, uint8_t ch_no_max) { + + uint8_t result = 1; + uint8_t len = strlen(ch_name); + for (int i = 0; i < len; i++) { + if (!isdigit(ch_name[i])) { + result = 0; + break; + } + } + if (result) { + *ch_no = atoi(ch_name); + if (*ch_no >= ch_no_max) { + result = 0; + } + } + return result; +} + +int p60_power_if_cmd(uint8_t node, uint8_t port, uint32_t timeout, uint8_t cmd, void * ch_status_p) { + + power_if_cmd_request_t req; + + if ((cmd == POWER_IF_SET) || (cmd == POWER_IF_GET)) { + power_if_cmd_response_t resp; + power_if_ch_status_t * ch_status = (power_if_ch_status_t *)ch_status_p; + if (cmd == POWER_IF_SET) { + req.cmd = POWER_IF_SET; + req.ch_status.mode = ch_status->mode; + req.ch_status.on_cnt = csp_hton16(ch_status->on_cnt); + req.ch_status.off_cnt = csp_hton16(ch_status->off_cnt); + } else { + req.cmd = POWER_IF_GET; + req.ch_status.mode = 0; + req.ch_status.on_cnt = 0; + req.ch_status.off_cnt = 0; + } + ch_status->name[POWER_IF_NAME_LEN - 1] = 0; + strcpy(req.ch_status.name, ch_status->name); + if (csp_transaction(CSP_PRIO_HIGH, node, port, timeout, &req, sizeof(power_if_cmd_request_t), &resp, sizeof(power_if_cmd_response_t))) { + if ((resp.cmd == POWER_IF_SET) || (resp.cmd == POWER_IF_GET)) { + if (resp.status == POWER_IF_STATUS_OK) { + ch_status->ch_idx = resp.ch_status.ch_idx; + ch_status->mode = resp.ch_status.mode; + ch_status->on_cnt = csp_ntoh16(resp.ch_status.on_cnt); + ch_status->off_cnt = csp_ntoh16(resp.ch_status.off_cnt); + ch_status->cur_lu_lim = csp_ntoh16(resp.ch_status.cur_lu_lim); + ch_status->cur_lim = csp_ntoh16(resp.ch_status.cur_lim); + ch_status->voltage = csp_ntoh16(resp.ch_status.voltage); + ch_status->current = csp_ntoh16(resp.ch_status.current); + ch_status->latchup = csp_ntoh16(resp.ch_status.latchup); + strncpy(ch_status->name, resp.ch_status.name, POWER_IF_NAME_LEN - 1); + /* Ensure zero termination*/ + ch_status->name[POWER_IF_NAME_LEN - 1] = 0; + return 1; + } + } + } + } else if (cmd == POWER_IF_LIST) { + power_if_cmd_list_response_t * ch_list = (power_if_cmd_list_response_t *)ch_status_p; + req.cmd = POWER_IF_LIST; + req.ch_status.mode = 0; + req.ch_status.on_cnt = 0; + req.ch_status.off_cnt = 0; + req.ch_status.name[0] = 0; + if (csp_transaction(CSP_PRIO_HIGH, node, port, timeout, &req, sizeof(power_if_cmd_request_t), ch_list, sizeof(power_if_cmd_list_response_t))) { + if ((ch_list->cmd == POWER_IF_LIST) && (ch_list->status == POWER_IF_STATUS_OK)) { + return 1; + } + } + } + + return 0; +} diff --git a/gomspace/libp60_client/wscript b/gomspace/libp60_client/wscript new file mode 100644 index 00000000..dc4dcf8a --- /dev/null +++ b/gomspace/libp60_client/wscript @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. + +APPNAME = 'p60_client' + + +def options(ctx): + gr = ctx.add_option_group('NanoPower-P60 library client options') + gr.add_option('--disable-libp60-cmd', action='store_true', help='Disable client cmd code for NanoPower-P60 library') + + +def configure(ctx): + ctx.env.append_unique('FILES_LIBP60_CLIENT', ['src/*.c']) + if not ctx.options.disable_libp60_cmd: + ctx.env.append_unique('FILES_LIBP60_CLIENT', ['src/cmd/*.c']) + + +def build(ctx): + public_include = APPNAME + '_h' + ctx(export_includes=['include'], name=public_include) + ctx.objects(source=ctx.path.ant_glob(ctx.env.FILES_LIBP60_CLIENT), + target=APPNAME, + use=['csp', 'gosh', 'param', 'param_client', 'util', public_include]) + + +def gs_dist(ctx): + ctx.add_default_files(source_module=True) diff --git a/gomspace/libparam_client/include/deprecated/param/param_client.h b/gomspace/libparam_client/include/deprecated/param/param_client.h new file mode 100644 index 00000000..b1755541 --- /dev/null +++ b/gomspace/libparam_client/include/deprecated/param/param_client.h @@ -0,0 +1,15 @@ +#ifndef PARAM_PARAM_CLIENT_H +#define PARAM_PARAM_CLIENT_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Legacy/deprecated include of header files for libparam client. +*/ + +#include +#include +#include +#include + +#endif diff --git a/gomspace/libparam_client/include/deprecated/param/param_lock.h b/gomspace/libparam_client/include/deprecated/param/param_lock.h new file mode 100644 index 00000000..5fab0b91 --- /dev/null +++ b/gomspace/libparam_client/include/deprecated/param/param_lock.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + * GomSpace Parameter System. + */ +#ifndef PARAM_PARAM_LOCK_H_ +#define PARAM_PARAM_LOCK_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static inline void param_lock(param_index_t * mem) +{ + gs_param_table_lock((gs_param_table_instance_t *) mem); +} + +static inline void param_unlock(param_index_t * mem) +{ + gs_param_table_unlock((gs_param_table_instance_t *) mem); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/deprecated/param/param_serializer.h b/gomspace/libparam_client/include/deprecated/param/param_serializer.h new file mode 100644 index 00000000..c05e782e --- /dev/null +++ b/gomspace/libparam_client/include/deprecated/param/param_serializer.h @@ -0,0 +1,55 @@ +#ifndef PARAM_PARAM_SERIALIZER_H +#define PARAM_PARAM_SERIALIZER_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + * GomSpace Parameter System + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef gs_param_serialize_flags_t param_serializer_flags; + +static inline int param_betoh(param_type_t type, void * item) +{ + return gs_param_betoh(type, item); +} + +static inline int param_htobe(param_type_t type, void * item) +{ + return gs_param_htobe(type, item); +} + +static inline int param_serialize_full_table(param_index_t * mem, unsigned int *start, uint8_t * buf, unsigned int maxbuflen, param_serializer_flags flags) +{ + unsigned int buf_pos = 0; + gs_error_t error = gs_param_serialize_full_table((gs_param_table_instance_t *) mem, start, flags, buf, maxbuflen, &buf_pos); + return (error == GS_OK) ? (int) buf_pos : -1; +} + +static inline gs_error_t param_serialize_item(const param_table_t * param, uint16_t addr, uint8_t * buf, uint16_t * pos, unsigned int maxlen, void * item, param_serializer_flags flags) +{ + unsigned int tmp_pos = *pos; + gs_error_t error = gs_param_serialize_item((const gs_param_table_row_t*) param, addr, item, flags, buf, maxlen, &tmp_pos); + *pos = tmp_pos; + return (error == GS_OK) ? 0 : -1; +} + +static inline gs_error_t param_deserialize(param_index_t * mem, uint8_t * buf, int len, param_serializer_flags flags) +{ + return (gs_param_deserialize((gs_param_table_instance_t *) mem, buf, len, flags) == GS_OK) ? 0 : -1; +} + +static inline gs_error_t param_deserialize_item(const param_table_t * param, uint16_t addr, param_index_t * mem, void * item, param_serializer_flags flags) +{ + return (gs_param_deserialize_item((gs_param_table_instance_t *) mem, (const gs_param_table_row_t*)param, addr, item, flags) == GS_OK) ? 0 : -1; +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/deprecated/param/param_string.h b/gomspace/libparam_client/include/deprecated/param/param_string.h new file mode 100644 index 00000000..35e19a0b --- /dev/null +++ b/gomspace/libparam_client/include/deprecated/param/param_string.h @@ -0,0 +1,74 @@ +#ifndef PARAM_PARAM_STRING_H +#define PARAM_PARAM_STRING_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + * GomSpace Parameter System + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef union { + const param_table_t * row3; + const gs_param_table_row_t * row4; +} gs_param_row_align_t; + +static inline const param_table_t * param_find_name(const param_table_t rows[], size_t row_count, const char * name) +{ + gs_param_row_align_t row_in = {.row3 = rows}; + gs_param_row_align_t row_out = {.row4 = gs_param_row_by_name(name, row_in.row4, row_count)}; + return row_out.row3; +} + +static inline const param_table_t * param_find_addr(const param_table_t rows[], size_t row_count, uint16_t addr) +{ + gs_param_row_align_t row_in = {.row3 = rows}; + gs_param_row_align_t row_out = {.row4 = gs_param_row_by_address(addr, row_in.row4, row_count)}; + return row_out.row3; +} + +static inline void param_list_single(param_table_t * param, param_index_t * mem, int do_read) +{ + gs_param_list_single((gs_param_table_instance_t *) mem, (const gs_param_table_row_t *) param, (do_read != 0)); +} + +static inline void param_list(param_index_t * mem, int do_read) +{ + gs_param_list((gs_param_table_instance_t *) mem, (do_read != 0)); +} + +static inline gs_error_t param_from_string(const param_table_t * param , const char * string, void * value) +{ + return gs_param_from_string((const gs_param_table_row_t *)param , string, value); +} + +static inline int param_to_string(const param_table_t * param, char * buf, int pos, const void * value, int with_type, int max_size) +{ + unsigned int written = 0; + gs_param_to_string((const gs_param_table_row_t *)param, value, with_type, buf, max_size, pos, &written); + return (int) written; +} + +static inline const char * param_type_to_string(param_type_t type) +{ + return gs_param_type_to_string(type); +} + +static inline uint16_t param_index_chksum(param_index_t * mem) +{ + return gs_param_table_checksum((gs_param_table_instance_t*)mem); +} + +static inline uint16_t param_index_chksum2(param_index_t * mem) +{ + return gs_param_table_checksum2((gs_param_table_instance_t*)mem); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/deprecated/param/param_types.h b/gomspace/libparam_client/include/deprecated/param/param_types.h new file mode 100644 index 00000000..61cc8351 --- /dev/null +++ b/gomspace/libparam_client/include/deprecated/param/param_types.h @@ -0,0 +1,92 @@ +#ifndef PARAM_PARAM_TYPES_H +#define PARAM_PARAM_TYPES_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Legacy/deprecated parameter types and definitions - use . +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_PARAM_NAME_LEN GS_PARAM_MAX_NAME + +/* + Legacy parameter type definition - matches gs_param_type_t exactly. + */ +typedef gs_param_type_t param_type_t; + +/* + Legacy table row definition - matches gs_param_table_row_t exactly. +*/ +typedef struct param_table_s { + uint16_t addr; + param_type_t type; + uint8_t size; + uint8_t count; // -> array_size + uint8_t flags; + char name[MAX_PARAM_NAME_LEN]; +} param_table_t; + +#define PARAM_COUNT gs_max(GS_PGM_UINT8((param)->count), 1) +#define PARAM_SIZE GS_PARAM_SIZE(param) +#define PARAM_TYPE GS_PARAM_TYPE(param) +#define PARAM_ADDR GS_PARAM_ADDR(param) +#define PARAM_FLAGS GS_PARAM_FLAGS(param) + +#define RPARAM_QUERY_MAX_LEN GS_RPARAM_MAX_QUERY + +#define PARAM_MAX_FILESIZE 0x400 + +struct param_index_s; + +/* + Legacy callback - matches gs_param_callback_func_t exactly. +*/ +typedef void (*param_callback_func)(uint16_t addr, struct param_index_s * index); + +typedef gs_param_table_id_t param_mem; + +/* + Legacy table instance definition - matches gs_param_table_instance_t exactly. +*/ +typedef struct param_index_s { + const char * name; + param_mem mem_id; + const param_table_t * table; + unsigned int count; + void * physaddr; + unsigned int size; + const gs_param_function_interface_t function_interface; + const uint16_t table_chksum; + const uint16_t chksum2; + const uint16_t checksum_le; + const gs_mutex_t lock; + param_callback_func callback; + const char * const var1; + const char * const var2; + const void * const var3; + const void * const var4; + const void * const var5; + const void * const var6; + const void * const var7; + const uint32_t var8; +} param_index_t; + +/** + Return base size of a parameter type. +*/ +static inline uint8_t param_type_size(param_type_t type) +{ + return gs_param_type_size(type); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/deprecated/param/rparam_client.h b/gomspace/libparam_client/include/deprecated/param/rparam_client.h new file mode 100644 index 00000000..a8c2655b --- /dev/null +++ b/gomspace/libparam_client/include/deprecated/param/rparam_client.h @@ -0,0 +1,208 @@ +#ifndef PARAM_RPARAM_CLIENT_H +#define PARAM_RPARAM_CLIENT_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + * GomSpace Parameter System + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Legacy magic checksum. + @deprecated use #GS_RPARAM_MAGIC_CHECKSUM (if remote products supports it). +*/ +#define GS_RPARAM_LEGACY_MAGIC_CHECKSUM 0xb00b + +static inline int rparam_get_full_table(param_index_t* idx, int node, int port, int index_id, uint32_t timeout_ms) +{ + return (gs_rparam_get_full_table((gs_param_table_instance_t *) idx, + node, + index_id, + GS_RPARAM_LEGACY_MAGIC_CHECKSUM, + timeout_ms) == GS_OK) ? 0 : -1; +} + +static inline int rparam_download_table_spec_from_remote_and_save_to_file2(const char* fname, uint8_t node, uint8_t port, param_index_t* idx, uint16_t* return_checksum, uint32_t timeout_ms) +{ + return (gs_rparam_download_table_spec((gs_param_table_instance_t *) idx, + fname, + node, + idx->mem_id, + timeout_ms, + return_checksum) == GS_OK) ? 0 : -1; +} + +static inline int rparam_download_table_spec_from_remote_and_save_to_file(const char* fname, uint8_t node, uint8_t port, param_index_t* idx, uint16_t* checksum) +{ + return rparam_download_table_spec_from_remote_and_save_to_file2(fname, node, port, idx, checksum, 10000); +} + +static inline int rparam_load_table_spec_from_file(const char* fname, param_index_t* idx, uint16_t* return_checksum) +{ + return (gs_rparam_load_table_spec((gs_param_table_instance_t *) idx, fname, return_checksum) == GS_OK) ? 0 : -1; +} + +static inline int rparam_get_single(void * out, uint16_t addr, param_type_t type, int size, int mem_id, int node, int port, uint32_t timeout_ms) +{ + return (gs_rparam_get(node, mem_id, addr, type, GS_RPARAM_LEGACY_MAGIC_CHECKSUM, timeout_ms, out, size) == GS_OK) ? size : -1; +} + +static inline int rparam_set_single(const void * in, uint16_t addr, param_type_t type, int size, int mem_id, int node, int port, uint32_t timeout_ms) +{ + return (gs_rparam_set(node, mem_id, addr, type, GS_RPARAM_LEGACY_MAGIC_CHECKSUM, timeout_ms, in, size) == GS_OK) ? size : -1; +} + +static inline int rparam_copy(uint8_t node, uint8_t port, uint32_t timeout_ms, uint8_t from, uint8_t to) +{ + return (gs_rparam_copy(node, timeout_ms, from, to) == GS_OK) ? 1 : 0; +} + +static inline int rparam_save(uint8_t node, uint8_t port, uint32_t timeout_ms, uint8_t from, uint8_t to) +{ + return (gs_rparam_save(node, timeout_ms, from, to) == GS_OK) ? 1 : 0; +} + +static inline int rparam_load(uint8_t node, uint8_t port, uint32_t timeout_ms, uint8_t from, uint8_t to) +{ + return (gs_rparam_load(node, timeout_ms, from, to) == GS_OK) ? 1 : 0; +} + +/** + * Remote getters for parameters. + * @param out pointer to the output buffer/parameter + * @param outlen length of the supplied out-buffer, only applies to rparam_get_string() + * @param addr local address of the parameter + * @param node CSP address of the node to query + * @param port CSP port of the parameter server + * @param timeout timeout on remote CSP calls + * @returns <0 on error in which case the contents of out is invalid, >0 on success + */ +static inline int rparam_get_string(char *out, int outlen, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_STRING, outlen, index_id, node, port, timeout_ms); +} + +/** + * Remote setters for parameters. + * @param in pointer to the buffer/parameter value to set + * @param inlen length of the supplied in-buffer, only applies to rparam_set_string() + * @param addr local address of the parameter + * @param node CSP address of the node to query + * @param port CSP port of the parameter server + * @param timeout timeout on remote CSP calls + * @returns <0 on error in which case the contents of out is invalid, >0 on success + */ +static inline int rparam_set_string(const char *in, int inlen, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_STRING, inlen, index_id, node, port, timeout_ms); +} + +static inline int rparam_get_uint8(uint8_t * out, uint16_t addr, int tinst_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_UINT8, sizeof(uint8_t), tinst_id, node, port, timeout_ms); +} + +static inline int rparam_set_uint8(const uint8_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_UINT8, sizeof(uint8_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_uint16(uint16_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_UINT16, sizeof(uint16_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_uint16(const uint16_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_UINT16, sizeof(uint16_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_uint32(uint32_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_UINT32, sizeof(uint32_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_uint32(const uint32_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_UINT32, sizeof(uint32_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_uint64(uint64_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_UINT64, sizeof(uint64_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_uint64(const uint64_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_UINT64, sizeof(uint64_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_int8(int8_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_INT8, sizeof(int8_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_int8(const int8_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_INT8, sizeof(int8_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_int16(int16_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_INT16, sizeof(int16_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_int16(const int16_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_INT16, sizeof(int16_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_int32(int32_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_INT32, sizeof(int32_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_int32(const int32_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_INT32, sizeof(int32_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_int64(int64_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_INT64, sizeof(int64_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_int64(const int64_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_INT64, sizeof(int64_t), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_float(float * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_FLOAT, sizeof(float), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_float(const float * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_FLOAT, sizeof(float), index_id, node, port, timeout_ms); +} + +static inline int rparam_get_double(double * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_get_single(out, addr, PARAM_DOUBLE, sizeof(double), index_id, node, port, timeout_ms); +} + +static inline int rparam_set_double(const double * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms) +{ + return rparam_set_single(in, addr, PARAM_DOUBLE, sizeof(double), index_id, node, port, timeout_ms); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/pp/i2c/i2c.h b/gomspace/libparam_client/include/gs/param/internal/pp/i2c/i2c.h new file mode 100644 index 00000000..484d4959 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/pp/i2c/i2c.h @@ -0,0 +1,42 @@ +#ifndef GS_PARAM_INTERNAL_PP_I2C_I2C_H +#define GS_PARAM_INTERNAL_PP_I2C_I2C_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GS_PARAM_I2C_LENGTH_TABLE(length, table) ((length << 3) | (table & 0x07)) +#define GS_PARAM_I2C_LENGTH_TABLE_TO_LENGTH(lt) ((lt >> 3) & 0x1f) +#define GS_PARAM_I2C_LENGTH_TABLE_TO_TABLE(lt) (lt & 0x07) + +/** + Data structure for setting parameter. +*/ +typedef struct __attribute__ ((packed)) gs_param_i2c_set_request { + gs_i2c_slave_dispatch_header_t header; + uint8_t length_table; + uint8_t addr; + uint8_t data[]; // data (+ checksum) +} gs_param_i2c_set_request_t; + +/** + Data structure for getting parameter. +*/ +typedef struct __attribute__ ((packed)) gs_param_i2c_get_request { + gs_i2c_slave_dispatch_header_t header; + uint8_t length_table; + uint8_t addr; + uint8_t checksum; // optional +} gs_param_i2c_get_request_t; + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/pp/i2c/slave_dispatch.h b/gomspace/libparam_client/include/gs/param/internal/pp/i2c/slave_dispatch.h new file mode 100644 index 00000000..a3779b5d --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/pp/i2c/slave_dispatch.h @@ -0,0 +1,71 @@ +#ifndef GS_PARAM_INTERNAL_PP_I2C_SLAVE_DISPATCH_H +#define GS_PARAM_INTERNAL_PP_I2C_SLAVE_DISPATCH_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Header for I2C slave dispatch protocol - must be first in all protocols. +*/ +typedef struct __attribute__ ((packed)) gs_i2c_slave_dispatch_header { + uint8_t domain_command; +} gs_i2c_slave_dispatch_header_t; + +/** + Make header from domain and command. +*/ +#define GS_I2C_SLAVE_DISPATCH_HEADER(domain, command) ((domain << 5) | (command & 0x1f)) + +/** + Extract domain from header. +*/ +#define GS_I2C_SLAVE_DISPATCH_HEADER_TO_DOMAIN(header) ((header >> 5) & 0x7) + +/** + Extract comman from header. +*/ +#define GS_I2C_SLAVE_DISPATCH_HEADER_TO_COMMAND(header) (header & 0x1f) + +/** + Domain handler. + + Basically same features as normal I2C slave rx callback. The generic I2C dispatch interface will parse the header (first byte) + and call the associated handler, based on the domain. + + @param[in] channel I2C channel (handle). + @param[in] cmd domain specific command. + @param[in] rx_buffer Pointer to start of rx buffer. + @param[in] rx number of bytes received so far. + @param[in] cswitch If called from within an ISR (embedded platform), this will none NULL. + @return total number of bytes to receive before next call back. Return 0 to ignore rest of data - no additional call backs will be done for current I2C transaction. +*/ +typedef void (* gs_i2c_slave_dispatch_handler_t)(uint8_t channel, uint8_t cmd, const uint8_t * rx, size_t rx_length, gs_context_switch_t * cswitch); + +/** + Dispatch domains. + + Standard domains should be assigned from the lowest value. + Application/board/product should be assigned from highest value. +*/ +typedef enum { + GS_I2C_SLAVE_DISPATCH_DOMAIN_RESERVED = 0, //! Avoid use - reserved for GSSB, GSSB broadcasts UID request on domain=0, cmd=13 + GS_I2C_SLAVE_DISPATCH_DOMAIN_USER1, //! Avoid use - overlap with GSSB commands + GS_I2C_SLAVE_DISPATCH_DOMAIN_USER2, //! Avoid use - may overlap with GSSB commands + GS_I2C_SLAVE_DISPATCH_DOMAIN_USER3, //! Avoid use - may overlap with GSSB commands + GS_I2C_SLAVE_DISPATCH_DOMAIN_PARAM, //! Reserved for libparam. + GS_I2C_SLAVE_DISPATCH_DOMAIN_USER5, + GS_I2C_SLAVE_DISPATCH_DOMAIN_USER6, + GS_I2C_SLAVE_DISPATCH_DOMAIN_USER7, +} gs_i2c_slave_dispatch_domain_t; + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/pp/spi/slave_dispatch.h b/gomspace/libparam_client/include/gs/param/internal/pp/spi/slave_dispatch.h new file mode 100644 index 00000000..1a4a3959 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/pp/spi/slave_dispatch.h @@ -0,0 +1,80 @@ +#ifndef GS_PARAM_INTERNAL_PP_SPI_SLAVE_DISPATCH_H +#define GS_PARAM_INTERNAL_PP_SPI_SLAVE_DISPATCH_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Header for SPI slave dispatch protocol - must be first in all protocols. +*/ +typedef struct __attribute__ ((packed)) gs_spi_slave_dispatch_header { + uint8_t domain_command; +} gs_spi_slave_dispatch_header_t; + +/** + Make header from domain and command. +*/ +#define GS_SPI_SLAVE_DISPATCH_HEADER(domain, command) ((domain << 5) | (command & 0x1f)) + +/** + Extract domain from header. +*/ +#define GS_SPI_SLAVE_DISPATCH_HEADER_TO_DOMAIN(header) ((header >> 5) & 0x7) + +/** + Extract comman from header. +*/ +#define GS_SPI_SLAVE_DISPATCH_HEADER_TO_COMMAND(header) (header & 0x1f) + +/** + Domain handler. + + Basically same features as normal SPI slave rx callback. The generic SPI dispatch interface will parse the header (first byte) + and call the associated handler, based on the domain. + + @param[in] channel SPI channel (handle). + @param[in] cmd domain specific command. + @param[in] rx_buffer Pointer to start of rx buffer. + @param[in] rx number of bytes received so far. + @param[in] cswitch If called from within an ISR (embedded platform), this will none NULL. + @return total number of bytes to receive before next call back. Return 0 to ignore rest of data - no additional call backs will be done for current SPI transaction. +*/ +typedef uint8_t (* gs_spi_slave_dispatch_handler_t)(uint8_t channel, uint8_t cmd, const uint8_t * rx_buffer, size_t rx, gs_context_switch_t * cswitch); + +/** + Dispatch domains. + + Standard domains should be assigned form the lowest value. + Application/board/product should be assigned from highest value. +*/ +typedef enum { + GS_SPI_SLAVE_DISPATCH_DOMAIN_RESERVED = 0, //! Avoid using 0, + GS_SPI_SLAVE_DISPATCH_DOMAIN_USER1, + GS_SPI_SLAVE_DISPATCH_DOMAIN_USER2, + GS_SPI_SLAVE_DISPATCH_DOMAIN_USER3, + GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, + GS_SPI_SLAVE_DISPATCH_DOMAIN_USER5, + GS_SPI_SLAVE_DISPATCH_DOMAIN_USER6, + GS_SPI_SLAVE_DISPATCH_DOMAIN_USER7, +} gs_spi_slave_dispatch_domain_t; + +/** + Slave dispatch SPI receiver. + + Must be added on the channel as receiver function, using gs_spi_slave_set_rx(). + + @param[in] cswitch If called from within an ISR (embedded platform), this will be set and allow for triggering context switch. +*/ +uint8_t gs_spi_slave_dispatch_rx(uint8_t channel, const uint8_t * rx_buffer, size_t rx, bool new_request, gs_context_switch_t * cswitch); + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/pp/spi/spi.h b/gomspace/libparam_client/include/gs/param/internal/pp/spi/spi.h new file mode 100644 index 00000000..b9303dcb --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/pp/spi/spi.h @@ -0,0 +1,65 @@ +#ifndef GS_PARAM_INTERNAL_PP_SPI_SPI_H +#define GS_PARAM_INTERNAL_PP_SPI_SPI_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Domain commands. +*/ +typedef enum { + GS_PARAM_SPI_COMMAND_SET = 1, + GS_PARAM_SPI_COMMAND_GET = 2, + GS_PARAM_SPI_COMMAND_SET_WITH_CHECKSUM = 10, + GS_PARAM_SPI_COMMAND_GET_WITH_CHECKSUM = 11, +} gs_param_spi_command_t; + +#define GS_PARAM_SPI_LENGTH_TABLE(length, table) ((length << 3) | (table & 0x07)) +#define GS_PARAM_SPI_LENGTH_TABLE_TO_LENGTH(lt) ((lt >> 3) & 0x1f) +#define GS_PARAM_SPI_LENGTH_TABLE_TO_TABLE(lt) (lt & 0x07) + +/** + Data structure for setting parameter. +*/ +typedef struct __attribute__ ((packed)) gs_param_spi_set { + gs_spi_slave_dispatch_header_t header; + uint8_t length_table; + uint8_t addr; + uint8_t data[]; // data (+ checksum) +} gs_param_spi_set_t; + +/** + Data structure for getting parameter. +*/ +typedef struct __attribute__ ((packed)) gs_param_spi_get { + gs_spi_slave_dispatch_header_t header; + uint8_t length_table; + uint8_t addr; + uint8_t filler; // filler/delay - allow slave to find and prepare data/response + uint8_t data[]; // data +} gs_param_spi_get_t; + +/** + Data structure for getting parameter with checksum +*/ +typedef struct __attribute__ ((packed)) gs_param_spi_get_with_checksum { + gs_spi_slave_dispatch_header_t header; + uint8_t length_table; + uint8_t addr; + uint8_t checksum; + uint8_t filler; // filler/delay - allow slave to find and prepare data/response + uint8_t data[]; // data + checksum +} gs_param_spi_get_with_checksum_t; + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/rparam.h b/gomspace/libparam_client/include/gs/param/internal/rparam.h new file mode 100644 index 00000000..ae70d215 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/rparam.h @@ -0,0 +1,146 @@ +#ifndef GS_PARAM_INTERNAL_RPARAM_H +#define GS_PARAM_INTERNAL_RPARAM_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Max query payload in a single message (bytes). +*/ +#define GS_RPARAM_QUERY_MAX_PAYLOAD 180 + +/** + Macro for calculating total query message size, header + payload. +*/ +#define RPARAM_QUERY_LENGTH(query, payload_size) (sizeof(*query) - sizeof(query->payload) + payload_size) + +/** + R(emote) parameter request codes. +*/ +typedef enum { + /** + Get one or more parameters. + */ + RPARAM_GET = 0x00, + /** + Reply to a request. + */ + RPARAM_REPLY = 0x55, + /** + Set one or more parameters. + */ + RPARAM_SET = 0xFF, + // RPARAM_SET_TO_FILE = 0xEE, + /** + Download table specification. + */ + RPARAM_TABLE = 0x44, + /** + Copy memory slot to memory slot. + @version 4.x: Not supported. + */ + RPARAM_COPY = 0x77, + /** + Load from file (slot) to memory (slot). + @version 4.x: Only load from primary store - file (slot) is ignored. + */ + RPARAM_LOAD = 0x88, + /** + Load from file (slot) to memory (slot). + @version 4.x: load by name(s). + */ + RPARAM_LOAD_FROM_STORE = 0x89, + /** + Save from memory (slot) to file (slot). + @version 4.x: Only save to primary store - file (slot) is ignored. + */ + RPARAM_SAVE = 0x99, + /** + Save from memory (slot) to file (slot). + @version 4.x: save by name(s). + */ + RPARAM_SAVE_TO_STORE = 0x9a, + // RPARAM_CLEAR = 0xAA, - completely removed +} gs_rparam_action_t; + +/** + R(emote) parameter reply/completion codes. +*/ +typedef enum { + RPARAM_SET_OK = 1, + RPARAM_LOAD_OK = 2, + RPARAM_SAVE_OK = 3, + RPARAM_COPY_OK = 4, + // RPARAM_CLEAR_OK = 5, + RPARAM_ERROR = 0xFF, +} gs_rparam_reply_t; + +/** + Payload - save/load to/from stores + @version 4 +*/ +typedef struct __attribute__ ((packed)) { + char table[25 + 1]; + char store[25 + 1]; + char slot[25 + 1]; +} gs_rparam_query_payload_store_t; + +/** + Payload. +*/ +typedef union __attribute__ ((packed)) { + uint16_t addr[0]; //! action = RPARAM_GET + uint8_t packed[0]; //! action = RPARAM_REPLY | RPARAM_SET + struct { //! action = RPARAM_COPY | RPARAM_LOAD | RPARM_SAVE + uint8_t from; + uint8_t to; + } copy; +} gs_rparam_query_payload_t; + +/** + Protocol between client and server. + @version 4.x: layout (size) has not changed - only naming of certain fields. +*/ +typedef struct __attribute__ ((packed)) { + /** + Request (gs_rparam_action_t) or Reply (gs_rparam_reply_t). + */ + uint8_t action; + /** + Table id. + Name changed in 4.0 from \a mem. + */ + uint8_t table_id; + /** + Length/size of \a payload in bytes. + */ + uint16_t length; + /** + Fletcher's checksum. + */ + uint16_t checksum; + /** + Sequence number when split over multiple frames (messages). + */ + uint16_t seq; + /** + Total number of frames. + */ + uint16_t total; + /** + Payload. + */ + gs_rparam_query_payload_t payload; +} gs_rparam_query_t; + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/serialize.h b/gomspace/libparam_client/include/gs/param/internal/serialize.h new file mode 100644 index 00000000..704b67f5 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/serialize.h @@ -0,0 +1,29 @@ +#ifndef GS_PARAM_INTERNAL_SERIALIZE_H +#define GS_PARAM_INTERNAL_SERIALIZE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Serialize data + * @param mem Pointer to indexed parameter table + * @param addr Array of addresses to serialize + * @param addr_count number of items in addr array + * @param[in|out] param_offset table parameter offset + * @param flags Set options using combination of flags + * @param buf Pointer to output + * @param buf_size Size of \a buf. + */ +gs_error_t gs_param_serialize_list(gs_param_table_instance_t * tinst, const uint16_t addr[], unsigned int addr_count, unsigned int * param_pos, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos); + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/table.h b/gomspace/libparam_client/include/gs/param/internal/table.h new file mode 100644 index 00000000..11b123bc --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/table.h @@ -0,0 +1,19 @@ +#ifndef GS_PARAM_INTERNAL_TABLE_H +#define GS_PARAM_INTERNAL_TABLE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +gs_error_t gs_param_parse_name_and_array_index(const char * inp, char * name, size_t size_name, uint8_t * array_index, bool * return_is_array); + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/internal/types.h b/gomspace/libparam_client/include/gs/param/internal/types.h new file mode 100644 index 00000000..5cefc4c0 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/internal/types.h @@ -0,0 +1,129 @@ +#ifndef GS_PARAM_INTERNAL_TYPES_H +#define GS_PARAM_INTERNAL_TYPES_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#if (GS_PARAM_INTERNAL_USE) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Table instance. +*/ +struct gs_param_table_instance { + /** + Name of table. + */ + const char * name; + + /** + Table id. + */ + gs_param_table_id_t id; + + /** + Table elements/rows. + */ + const gs_param_table_row_t * rows; + /** + Table element/row count. + */ + unsigned int row_count; + + /** + Table memory - volatile parameter store. + The allocated size must be at least \a memory_size bytes. + */ + void * memory; + + /** + Size of table data memory in bytes, normally size of memory allocated \a memory. + Size must always be specified, even when using function interface and no memory is allocated. + */ + unsigned int memory_size; + + /** + Function interface, e.g. get/set. + */ + gs_param_function_interface_t function_interface; + + /** + Checksum - based on host order (e.g. le or be). + @see gs_param_table_checksum() + */ + uint16_t checksum; + + /** + Checksum - based on big-endian (address converted to big-endian). + @see gs_param_table_checksum_be() + */ + uint16_t checksum_be; + + /** + Checksum - based on little-endian (address converted to little-endian). + @see gs_param_table_checksum_le() + */ + uint16_t checksum_le; + + /** + Lock. + Internal access/use only, use gs_param_lock() and gs_param_unlock() to lock and un-lock table. + */ + gs_mutex_t lock; + + /** + Callback for table (data) change. + */ + void (*callback)(uint16_t addr, gs_param_table_instance_t * tinst); + + /** + Store location(s). + CSV format, e.g. \"persistent,protected\". + */ + const char * stores; + + /** + Auto-persist. + */ + struct { + /** + Store. + */ + const char * store; + + /** + User context(s) for the \a set function. + */ + void * context1; + void * context2; + + /** + Set/write parameter. + */ + gs_error_t (*set)(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, const void * item, size_t size, uint32_t flags); + } auto_persist; + + /** + Function for initializing table. + */ + gs_error_t (*initializer_function)(gs_param_table_instance_t * tinst); + + /** + Default values for initializing table. + */ + const void * default_values; + + /** + Future flags. + */ + uint32_t flags; +}; + +#ifdef __cplusplus +} +#endif +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/pp/i2c/i2c.h b/gomspace/libparam_client/include/gs/param/pp/i2c/i2c.h new file mode 100644 index 00000000..7ffdfd1b --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/pp/i2c/i2c.h @@ -0,0 +1,60 @@ +#ifndef GS_PARAM_PP_I2C_I2C_H +#define GS_PARAM_PP_I2C_I2C_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + I2C param protocol client interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Commands. +*/ +typedef enum { + /** + Set parameter. + */ + GS_PARAM_I2C_COMMAND_SET = 1, + /** + Get parameter. + */ + GS_PARAM_I2C_COMMAND_GET = 2, + /** + Lock table. + */ + GS_PARAM_I2C_COMMAND_SET_LOCK_WITH_CHECKSUM = 3, + /** + Get table lock state. + */ + GS_PARAM_I2C_COMMAND_GET_LOCK_WITH_CHECKSUM = 4, + /** + Set parameter with checksum. + */ + GS_PARAM_I2C_COMMAND_SET_WITH_CHECKSUM = 10, + /** + Get parameter with checksum. + */ + GS_PARAM_I2C_COMMAND_GET_WITH_CHECKSUM = 11, +} gs_param_i2c_command_t; + +/** + Initialize the param protocol handle for I2C. + + @param[in] pp handle. + @param[in] bus bus to communicate on. + @param[in] addr address of node. + @param[in] big_endian \a true if slave is big endian. Used to convert to host endian. + @return_gs_error_t +*/ +gs_error_t gs_pp_i2c_init(gs_pp_t * pp, uint8_t bus, uint8_t addr, bool big_endian); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/pp/pp.h b/gomspace/libparam_client/include/gs/param/pp/pp.h new file mode 100644 index 00000000..a3bbb539 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/pp/pp.h @@ -0,0 +1,214 @@ +#ifndef GS_PARAM_PP_PP_H +#define GS_PARAM_PP_PP_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Param Protocol (PP) API - generic interface for getting/setting parameters over SPI, I2C, etc. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Flags used in gs_pp_xxx() functions. + @{ +*/ +/** + Use checksum in transfer. + @see gs_pp_checksum8() +*/ +#define GS_PP_FLAG_CHECKSUM 0x0001 +/**@}*/ + +/** + Handle for a protocol connection. +*/ +typedef struct gs_pp gs_pp_t; + +/** + Callback for getting a parameter. +*/ +typedef gs_error_t (*gs_pp_get_t)(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags); + +/** + Callback for setting a parameter. +*/ +typedef gs_error_t (*gs_pp_set_t)(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags); + +/** + Callback for setting table lock. +*/ +typedef gs_error_t (*gs_pp_get_table_lock_t)(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags); + +/** + Callback for setting table lock. +*/ +typedef gs_error_t (*gs_pp_set_table_lock_t)(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags); + +/** + Handle for a protocol connection. +*/ +struct gs_pp { + /** + Endian type of slave. + */ + bool big_endian; + /** + Callback function for \a get. + */ + gs_pp_get_t get; + /** + Callback function for \a set. + */ + gs_pp_set_t set; + /** + Callback function for \a get_table_lock. + */ + gs_pp_get_table_lock_t get_table_lock; + /** + Callback function for \a set_table_lock. + */ + gs_pp_set_table_lock_t set_table_lock; + /** + Protocol specifics. + */ + union { + /** + SPI connection. + */ + struct { + /** + SPI slave id. + */ + uint8_t slave; + } spi; + /** + I2C connection. + */ + struct { + /** + I2C bus. + */ + uint8_t bus; + /** + I2C address. + */ + uint8_t addr; + } i2c; + } pp; +}; + +/** + Calculate very simple 8 bit checksum. + The checksum is calculated by adding all bytes. If the checksum is 0 (zero), the checksum is set to 1 (one). + @param[in] data data to calculate checksum for. + @param[in] length data length. + @return checksum +*/ +uint8_t gs_pp_checksum8(const void * data, size_t length); + +/** + Get lock value + + @param[in] pp Handle for connection + @param[in] table_id Table ID + @param[out] value Lock state (0 = unlocked, 1 = locked) + @param[in] flags + @return_gs_error_t + */ +gs_error_t gs_pp_get_table_lock(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags); + +/** + Set lock value + + @param[in] pp Handle for connection + @param[in] table_id Table ID + @param[in] value Lock state (0 = unlocked, 1 = locked) + @param[in] flags + @return_gs_error_t +*/ +gs_error_t gs_pp_set_table_lock(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags); + +/** + Get int8. +*/ +gs_error_t gs_pp_get_int8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int8_t * value, size_t count, uint32_t flags); + +/** + Set int8. +*/ +gs_error_t gs_pp_set_int8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int8_t * value, size_t count, uint32_t flags); + +/** + Get uint8. +*/ +gs_error_t gs_pp_get_uint8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint8_t * value, size_t count, uint32_t flags); + +/** + Set uint8. +*/ +gs_error_t gs_pp_set_uint8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint8_t * value, size_t count, uint32_t flags); + +/** + Get int16. +*/ +gs_error_t gs_pp_get_int16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int16_t * value, size_t count, uint32_t flags); + +/** + Set int16. +*/ +gs_error_t gs_pp_set_int16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int16_t * value, size_t count, uint32_t flags); + +/** + Get uint16. +*/ +gs_error_t gs_pp_get_uint16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint16_t * value, size_t count, uint32_t flags); + +/** + Set uint16. +*/ +gs_error_t gs_pp_set_uint16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint16_t * value, size_t count, uint32_t flags); + +/** + Get int32. +*/ +gs_error_t gs_pp_get_int32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int32_t * value, size_t count, uint32_t flags); + +/** + Set int32. +*/ +gs_error_t gs_pp_set_int32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int32_t * value, size_t count, uint32_t flags); + +/** + Get uint32. +*/ +gs_error_t gs_pp_get_uint32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint32_t * value, size_t count, uint32_t flags); + +/** + Set uint32. +*/ +gs_error_t gs_pp_set_uint32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint32_t * value, size_t count, uint32_t flags); + +/** + Get float. +*/ +gs_error_t gs_pp_get_float(gs_pp_t * pp, uint8_t table_id, uint8_t addr, float * value, size_t count, uint32_t flags); + +/** + Set float. +*/ +gs_error_t gs_pp_set_float(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const float * value, size_t count, uint32_t flags); + +/** + Register commands. +*/ +gs_error_t gs_pp_register_commands(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/pp/spi/spi.h b/gomspace/libparam_client/include/gs/param/pp/spi/spi.h new file mode 100644 index 00000000..32006c45 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/pp/spi/spi.h @@ -0,0 +1,29 @@ +#ifndef GS_PARAM_PP_SPI_SPI_H +#define GS_PARAM_PP_SPI_SPI_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + SPI Param Protocol (pp) client interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Initialize the param protocol handle for SPI. + + @param[in] pp handle. + @param[in] slave slave to communicate with. + @param[in] big_endian \a true if slave is big endian. Used to convert to host endian. + @return_gs_error_t +*/ +gs_error_t gs_pp_spi_init(gs_pp_t * pp, uint8_t slave, bool big_endian); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/rparam.h b/gomspace/libparam_client/include/gs/param/rparam.h new file mode 100644 index 00000000..950e22cf --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/rparam.h @@ -0,0 +1,395 @@ +#ifndef GS_PARAM_REMOTE_H +#define GS_PARAM_REMOTE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Remote parameter API - pending refactoring. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Magic checksum. + If specifying a magic checksum, the rparam server will ignore/skip checksum validation. +*/ +#define GS_RPARAM_MAGIC_CHECKSUM 0x0bb0 + +/** + Register rparam commands. + @return_gs_error_t +*/ +gs_error_t gs_rparam_register_commands(void); + +/** + Download all (data) values from a remote table to a local instance. + + @param[in] tinst local table instance. + @param[in] node CSP address. + @param[in] table_id remote table id to download. + @param[in] checksum table checksum. + @param[in] timeout_ms timeout. + @return_gs_error_t +*/ +gs_error_t gs_rparam_get_full_table(gs_param_table_instance_t * tinst, + uint8_t node, + gs_param_table_id_t table_id, + uint16_t checksum, + uint32_t timeout_ms); + +/** + Download a table specification from a remote node, store it in memory and save it to local file system. + + @note Will free existing rows - do not use this on table instances with static assigned rows. + + Table memory will be (re)allocated to match specification. + + @param[in] tinst local table instance. + @param[in] fname name of the file to store the table specification in. If NULL, no file will be stored. + @param[in] node CSP address + @param[in] table_id remote table id to download. + @param[in] timeout_ms timeout. + @param[out] return_checksum fletcher16 checksum of downloaded specification "as is" - before network/host swapping. + @return_gs_error_t + @see gs_param_table_free() +*/ +gs_error_t gs_rparam_download_table_spec(gs_param_table_instance_t * tinst, + const char * fname, + uint8_t node, + gs_param_table_id_t table_id, + uint32_t timeout_ms, + uint16_t * return_checksum); + +/** + Load a table specification from a local file and store it in memory. + + @note Will free existing rows - do not use this on table instances with static assigned rows. + + Table memory will be (re)allocated to match specification. + + @param[in] tinst local table instance. + @param[in] fname name of the file to load the table specification from. + @param[out] return_checksum fletcher16 checksum stored in file. + @return_gs_error_t + @see gs_param_table_free() +*/ +gs_error_t gs_rparam_load_table_spec(gs_param_table_instance_t * tinst, const char* fname, uint16_t * return_checksum); + +/** + Copy from one table to another table. + + @deprecated Not supported by a param 4 backend and future versions. + + @param[in] node CSP address + @param[in] timeout_ms timeout on remote CSP calls + @param[in] from from table-id. + @param[in] to to table-id. + @return_gs_error_t +*/ +gs_error_t gs_rparam_copy(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to); + +/** + Save table. + + @note On a param 4 backend, the table will always be saved to it's primary store. + + @param[in] node CSP address + @param[in] timeout_ms timeout on remote CSP calls + @param[in] id table to save. + @param[in] to to file slot - ignored on param 4 backends. + @return_gs_error_t +*/ +gs_error_t gs_rparam_save(uint8_t node, uint32_t timeout_ms, gs_param_table_id_t id, uint8_t to); + +/** + Save table. + + @version 4 + @param[in] node CSP address + @param[in] timeout_ms timeout on remote CSP calls + @param[in] table_id remote table id. + @param[in] store store name. + @param[in] slot slot within store. + @return_gs_error_t +*/ +gs_error_t gs_rparam_save_to_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id, + const char * store, const char * slot); + +/** + Load table from store. + + @note On a param 4 backend, the specified table will be loadded from it's primary store. + + @param[in] node CSP address + @param[in] timeout_ms timeout on remote CSP calls + @param[in] from from file slot - ignored on param 4 backends. + @param[in] id table to load. + @return_gs_error_t +*/ +gs_error_t gs_rparam_load(uint8_t node, uint32_t timeout_ms, uint8_t from, gs_param_table_id_t id); + +/** + Load table from store. + + @version 4 + @param[in] node CSP address + @param[in] timeout_ms timeout on remote CSP calls + @param[in] table_id remote table id. + @param[in] store store name. + @param[in] slot slot within store. + @return_gs_error_t +*/ +gs_error_t gs_rparam_load_from_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id, + const char * store, const char * slot); + +/** + Get parameter. + + @param[in] node CSP address + @param[in] table_id remote table id. + @param[in] addr parameter address (remote table). + @param[in] type parameter type. + @param[in] checksum checksum + @param[in] timeout_ms timeout + @param[out] value returned value (user allocated) + @param[in] value_size size of \a value buffer. + @return_gs_error_t +*/ +gs_error_t gs_rparam_get(uint8_t node, + gs_param_table_id_t table_id, + uint16_t addr, + gs_param_type_t type, + uint16_t checksum, + uint32_t timeout_ms, + void * value, + size_t value_size); + +/** + Set parameter. + + @param[in] node CSP address + @param[in] table_id remote table id. + @param[in] addr parameter address (remote table). + @param[in] type parameter type. + @param[in] checksum checksum + @param[in] timeout_ms timeout + @param[in] value value to set + @param[in] value_size size of \a value. + @return_gs_error_t +*/ +gs_error_t gs_rparam_set(uint8_t node, + gs_param_table_id_t table_id, + uint16_t addr, + gs_param_type_t type, + uint16_t checksum, + uint32_t timeout_ms, + const void * value, + size_t value_size); + +/** + Get string. +*/ +static inline gs_error_t gs_rparam_get_string(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, char * value, size_t value_size) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_STRING, checksum, timeout_ms, value, value_size); +} + +/** + Set string. +*/ +static inline gs_error_t gs_rparam_set_string(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, const char * value, size_t value_size) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_STRING, checksum, timeout_ms, value, (value_size == 0) ? (strlen(value) + 1) : value_size); +} + +/** + Get int8. +*/ +static inline gs_error_t gs_rparam_get_int8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int8_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_INT8, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set int8. +*/ +static inline gs_error_t gs_rparam_set_int8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int8_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_INT8, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get uint8. +*/ +static inline gs_error_t gs_rparam_get_uint8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint8_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT8, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set uint8. +*/ +static inline gs_error_t gs_rparam_set_uint8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint8_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT8, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get int16. +*/ +static inline gs_error_t gs_rparam_get_int16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int16_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_INT16, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set int16. +*/ +static inline gs_error_t gs_rparam_set_int16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int16_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_INT16, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get uint16. +*/ +static inline gs_error_t gs_rparam_get_uint16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint16_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT16, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set uint16. +*/ +static inline gs_error_t gs_rparam_set_uint16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint16_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT16, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get int32. +*/ +static inline gs_error_t gs_rparam_get_int32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int32_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_INT32, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set int32. +*/ +static inline gs_error_t gs_rparam_set_int32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int32_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_INT32, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get uint32. +*/ +static inline gs_error_t gs_rparam_get_uint32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint32_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT32, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set uint32. +*/ +static inline gs_error_t gs_rparam_set_uint32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint32_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT32, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get int64. +*/ +static inline gs_error_t gs_rparam_get_int64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int64_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_INT64, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set int64. +*/ +static inline gs_error_t gs_rparam_set_int64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, int64_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_INT64, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get uint64. +*/ +static inline gs_error_t gs_rparam_get_uint64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint64_t * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT64, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set uint64. +*/ +static inline gs_error_t gs_rparam_set_uint64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, uint64_t value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT64, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get float. +*/ +static inline gs_error_t gs_rparam_get_float(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, float * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_FLOAT, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set float. +*/ +static inline gs_error_t gs_rparam_set_float(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, float value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_FLOAT, checksum, timeout_ms, &value, sizeof(value)); +} + +/** + Get double. +*/ +static inline gs_error_t gs_rparam_get_double(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, double * value) +{ + return gs_rparam_get(node, table_id, addr, GS_PARAM_DOUBLE, checksum, timeout_ms, value, sizeof(*value)); +} + +/** + Set double. +*/ +static inline gs_error_t gs_rparam_set_double(uint8_t node, gs_param_table_id_t table_id, uint16_t addr, + uint16_t checksum, uint32_t timeout_ms, double value) +{ + return gs_rparam_set(node, table_id, addr, GS_PARAM_DOUBLE, checksum, timeout_ms, &value, sizeof(value)); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/serialize.h b/gomspace/libparam_client/include/gs/param/serialize.h new file mode 100644 index 00000000..9b935e3d --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/serialize.h @@ -0,0 +1,101 @@ +#ifndef GS_PARAM_CLIENT_SERIALIZE_H +#define GS_PARAM_CLIENT_SERIALIZE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Serialize API - pending refactoring. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Serialize/deserialize flags + Flags must be in range: bit 8 - 15, to avoid clash with other parts of the parameter system. +*/ +typedef enum { + GS_PARAM_SF_DRY_RUN = (1 << 8), //!< F_DRY_RUN do not write to memory + GS_PARAM_SF_TO_BIG_ENDIAN = (1 << 9), //!< F_TO_BIG_ENDIAN Convert from host to big endian + GS_PARAM_SF_FROM_BIG_ENDIAN = (1 << 10), //!< F_FROM_BIG_ENDIAN Confert from big endian to host order + GS_PARAM_SF_PACKED = (1 << 11), //!< F_PACKED Do not pack addresses + + F_DRY_RUN = GS_PARAM_SF_DRY_RUN, + F_TO_BIG_ENDIAN = GS_PARAM_SF_TO_BIG_ENDIAN, + F_FROM_BIG_ENDIAN = GS_PARAM_SF_FROM_BIG_ENDIAN, + F_PACKED = GS_PARAM_SF_PACKED, +} gs_param_serialize_flags_t; + +/** + * In-place conversion of a single parameter from big endian to host byte order + * @param type param type + * @param item pointer to parameter memory + * @return 1 if memory has been swapped, 0 if not + */ +bool gs_param_betoh(gs_param_type_t type, void * item); + +/** + * In-place conversion of a single parameter from host byte order to big endian + * @param type param type + * @param item porinter to parameter memory + * @return 1 if memory has been swapped, 0 if not + */ +bool gs_param_htobe(gs_param_type_t type, void * item); + +/** + Serialize data + + @param[in] tinst table. + @param[in,out] param_pos parameter iterator. + @param[in] flags flags. + @param[out] buf user supplied buffer of \a buf_size. + @param[in] buf_size of size of \a buf + @param[in,out] buf_pos index into \a buf + @return_gs_error_t +*/ +gs_error_t gs_param_serialize_full_table(gs_param_table_instance_t * tinst, unsigned int * param_pos, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos); + +/** + Serialize single item + + @param[in] param parameter to serialize + @param[in] addr Address of item + @param[in] item item to serialize. + @param[in] flags flags. + @param[out] buf user supplied buffer of \a buf_size. + @param[in] buf_size of size of \a buf + @param[in,out] buf_pos index into \a buf + @return_gs_error_t +*/ +gs_error_t gs_param_serialize_item(const gs_param_table_row_t * param, uint16_t addr, const void * item, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos); + +/** + Deserialize packed parameters into memory + + @param[in] tinst table. + @param[in] buf serialized data. + @param[in] buf_size size \a buf containing serialized data + @param[in] flags flags. + @return_gs_error_t +*/ +gs_error_t gs_param_deserialize(gs_param_table_instance_t * tinst, const uint8_t * buf, unsigned int buf_size, uint32_t flags); + +/** + Deserialize a sginle item from a string into memory + + @param[in] tinst table. + @param param Pointer to specific parameter to deserialize + @param addr Address of parameter + @param item Pointer to memory area where item should be written + @param flags Set options using combination of flags + @return_gs_error_t +*/ +gs_error_t gs_param_deserialize_item(gs_param_table_instance_t * tinst, const gs_param_table_row_t * param, uint16_t addr, const void * item, uint32_t flags); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/table.h b/gomspace/libparam_client/include/gs/param/table.h new file mode 100644 index 00000000..0b5e153d --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/table.h @@ -0,0 +1,428 @@ +#ifndef GS_PARAM_TABLE_H +#define GS_PARAM_TABLE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Client table API. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Allocate table memory when creating a table. + + Flags must be in range: bit 16 - 23, to avoid clash with other parts of the parameter system. +*/ +#define GS_PARAM_TABLE_F_ALLOC_MEMORY 0x0100 +/** + Allocate table rows. + + Flags must be in range: bit 16 - 23, to avoid clash with other parts of the parameter system. +*/ +#define GS_PARAM_TABLE_F_ALLOC_ROWS 0x0200 +/** + Disable table locking. + + Flags must be in range: bit 16 - 23, to avoid clash with other parts of the parameter system. +*/ +#define GS_PARAM_TABLE_F_NO_LOCK 0x0400 + +/** + Calculate memory size based on table rows. + + @param[in] rows rows + @param[in] row_count row count. + @return size of table or 0 in case of invalid arguments. +*/ +size_t gs_param_calc_table_size(const gs_param_table_row_t * rows, size_t row_count); + +/** + Return size of table instance. +*/ +size_t gs_param_table_instance_size(void); + +/** + Clear (and check size) of memory for table instance. + + @param[in] var user allocated space of at least gs_param_table_instance_size() bytes. + @param[in] var_size of \a var. + @return table instance + @see gs_param_table_instance_size() + @see #GS_PARAM_TINST_VAR +*/ +gs_param_table_instance_t * gs_param_table_instance_clear(void * var, size_t var_size); + +/** + Allocates aligned space on the stack for a table instance structure. + @param[in] var name of table instsance variable. +*/ +#define GS_PARAM_TINST_VAR(var) uint8_t var##__data [gs_param_table_instance_size()] __attribute__ ((aligned(4))); gs_param_table_instance_t * var = gs_param_table_instance_clear(var##__data, sizeof(var##__data)) + +/** + Allocate memory for table instance. + + Use gs_param_table_free() to free any internal resources. + + Use standard free() to free allocated memory. + + @return table instance on success, otherwise NULL. +*/ +gs_param_table_instance_t * gs_param_table_instance_alloc(void); + +/** + Find row by name. + + @param[in] name parameter name. + @param[in] rows rows + @param[in] row_count row count. + @return row or NULL if not found. +*/ +const gs_param_table_row_t * gs_param_row_by_name(const char * name, const gs_param_table_row_t * rows, size_t row_count); + +/** + Find row by address. + + @param[in] addr parameter address. + @param[in] rows rows + @param[in] row_count row count. + @return row or NULL if not found. +*/ +const gs_param_table_row_t * gs_param_row_by_address(uint16_t addr, const gs_param_table_row_t * rows, size_t row_count); + +/** + Return table memory. + + @note handle with care - any read/write should be atomic to prevent inconsistent data. + + @param[in] tinst table instance + @param[out] return_size if not NULL, the memory size is returned. + @return pointer to the table's data memory. +*/ +void * gs_param_table_get_memory(gs_param_table_instance_t * tinst, size_t * return_size); + +/** + Return table rows. + + @param[in] tinst table instance + @param[out] return_count if not NULL, the row count is returned. + @return pointer to the table rows. +*/ +const gs_param_table_row_t * gs_param_table_get_rows(gs_param_table_instance_t * tinst, size_t * return_count); + +/** + Lock table (recursive). + + @param[in] tinst table instance + @return_gs_error_t +*/ +gs_error_t gs_param_table_lock(gs_param_table_instance_t * tinst); + +/** + Unlock table. + + Unlock must be called once for every time gs_param_table_lock() has been called. + + @param[in] tinst table instance + @return_gs_error_t +*/ +gs_error_t gs_param_table_unlock(gs_param_table_instance_t * tinst); + +/** + Free internal resources and clears instance. + + @param[in] tinst table instance + @return_gs_error_t +*/ +gs_error_t gs_param_table_free(gs_param_table_instance_t * tinst); + +/** + Print a single parameter on stream. + + @param[in] tinst table instanc. + @param[in] row row to print. + @param[in] list_data \a true includes parameter value. + @param[in] flags flags to control output format: #GS_PARAM_F_SHOW_SCIENTIFIC, #GS_PARAM_F_SHOW_HEX. + @param[in] out output stream. + @return_gs_error_t +*/ +gs_error_t gs_param_list_single_to_stream(gs_param_table_instance_t * tinst, const gs_param_table_row_t * row, bool list_data, uint32_t flags, FILE * out); + +/** + Print a single parameter on stdout. + + @param[in] tinst table instanc. + @param[in] row row to print. + @param[in] list_data \a true includes parameter value. + @return_gs_error_t +*/ +static inline gs_error_t gs_param_list_single(gs_param_table_instance_t * tinst, const gs_param_table_row_t * row, bool list_data) +{ + return gs_param_list_single_to_stream(tinst, row, list_data, 0, stdout); +} + +/** + Print entire table on stream. + + @param[in] tinst table instanc. + @param[in] list_data \a true includes parameter value. + @param[in] flags flags to control output format: #GS_PARAM_F_SHOW_SCIENTIFIC, #GS_PARAM_F_SHOW_HEX. + @param[in] out output stream. + @return_gs_error_t +*/ +gs_error_t gs_param_list_to_stream(gs_param_table_instance_t * tinst, bool list_data, uint32_t flags, FILE * out); + +/** + Print entire table on stdout. + + @param[in] tinst table instanc. + @param[in] list_data \a true includes parameter value. + @return_gs_error_t +*/ +static inline gs_error_t gs_param_list(gs_param_table_instance_t * tinst, bool list_data) +{ + return gs_param_list_to_stream(tinst, list_data, 0, stdout); +} + +/** + Convert string to parameter. + + @param[in] row row defining the parameter to convert. + @param[in] string string to convert. + @param[out] return_value user supplied buffer for returning the value - must be at least the size specified in \a row + @return_gs_error_t +*/ +gs_error_t gs_param_from_string(const gs_param_table_row_t * row, const char * string, void * return_value); + +/** + Convert parameter to string. + + @param[in] row row defining the parameter to convert. + @param[in] value parameter value to convert. + @param[in] with_type \a true includes data type. + @param[in] flags flags to control output format: #GS_PARAM_F_SHOW_SCIENTIFIC, #GS_PARAM_F_SHOW_HEX. + @param[out] buf user supplied buffer of \a buf_size bytes. + @param[in] buf_size size of \a buf in bytes. + @param[in] buf_pos buffer position to insert string. + @param[out] return_buf_written number of bytes written to \a buf. + @return_gs_error_t +*/ +gs_error_t gs_param_to_string2(const gs_param_table_row_t * row, const void * value, bool with_type, uint32_t flags, char * buf, unsigned int buf_size, unsigned int buf_pos, unsigned int * return_buf_written); + +/** + Convert parameter to string. + + @param[in] row row defining the parameter to convert. + @param[in] value parameter value to convert. + @param[in] with_type \a true includes data type. + @param[out] buf user supplied buffer of \a buf_size bytes. + @param[in] buf_size size of \a buf in bytes. + @param[in] buf_pos buffer position to insert string. + @param[out] return_buf_written number of bytes written to \a buf. + @return_gs_error_t +*/ +static inline gs_error_t gs_param_to_string(const gs_param_table_row_t * row, const void * value, bool with_type, char * buf, unsigned int buf_size, unsigned int buf_pos, unsigned int * return_buf_written) +{ + return gs_param_to_string2(row, value, with_type, 0, buf, buf_size, buf_pos, return_buf_written); +} + +/** + Convert parameter type to string. + + @param[in] type parameter type. + @return pointer to a static string. +*/ +const char * gs_param_type_to_string(gs_param_type_t type); + +/** + Return size of parameter type. + + @param[in] type parameter type. + @return size of parameter type in bytes. +*/ +uint8_t gs_param_type_size(gs_param_type_t type); + +/** + Get table checksum - little-endian. + @note Use/exchange gs_param_table_checksum_be(), as this is calculated the same on all platforms. + @param[in] tinst table instance. + @returns 16-bit fletcher checksum +*/ +uint16_t gs_param_table_checksum_le(gs_param_table_instance_t * tinst); + +/** + Get table checksum - big-endian/network-order (prefered). + @param[in] tinst table instance. + @returns 16-bit fletcher checksum +*/ +uint16_t gs_param_table_checksum_be(gs_param_table_instance_t * tinst); + +/** + Get table checksum - host-order (not cross-platform). + @deprecated use gs_param_table_checksum_be() + @param[in] tinst table instance. + @returns 16-bit fletcher checksum +*/ +uint16_t gs_param_table_checksum(gs_param_table_instance_t * tinst); + +/** + Get table checksum - big-endian. + @deprecated use gs_param_table_checksum_be() + @param[in] tinst table instance. + @returns 16-bit fletcher checksum +*/ +static inline uint16_t gs_param_table_checksum2(gs_param_table_instance_t * tinst) +{ + return gs_param_table_checksum_be(tinst); +} + +/** + Get/read parameter from table. + + @param[in] tinst table instanc. + @param[in] addr parameter address (offset in table). + @param[in] type parameter type. + @param[out] return_value value of parameter - user supplied memory of at least \a size size. + @param[in] size number of bytes to get/read - must match \a type, e.g. 4 bytes for an uint32_t. + @param[in] flags flags. + @return_gs_error_t +*/ +gs_error_t gs_param_get(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, void * return_value, size_t size, uint32_t flags); + +/** + Set/write parameter in table. + + @param[in] tinst table instanc. + @param[in] addr parameter address (offset in table). + @param[in] type parameter type. + @param[in] value value of parameter. + @param[in] size number of bytes to set/write - must match \a type, e.g. 4 bytes for an uint32_t. + @param[in] flags flags. + @return_gs_error_t +*/ +gs_error_t gs_param_set(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, const void * value, size_t size, uint32_t flags); + +/** + Get string parameter. + + @param[in] tinst table instanc. + @param[in] addr parameter address (offset in table). + @param[out] buf value of parameter - user supplied memory of at least parameter size + 1 to hold NUL termination. + @param[in] buf_size size of \a buf - ensure room for NUL termination. + @param[in] flags flags. + @return GS_ERROR_OVERFLOW if string + NUL termination exceeds \a buf_size. + @return_gs_error_t +*/ +gs_error_t gs_param_get_string(gs_param_table_instance_t * tinst, uint16_t addr, char * buf, size_t buf_size, uint32_t flags); + +/** + Set string parameter. + + @param[in] tinst table. + @param[in] addr parameter address (offset in table). + @param[in] value string to save - parameter must be able to hold string + NUL termination. + @param[in] flags flags. + @return GS_ERROR_OVERFLOW if string + NUL termination exceeds parameter size. + @return_gs_error_t +*/ +gs_error_t gs_param_set_string(gs_param_table_instance_t * tinst, uint16_t addr, const char * value, uint32_t flags); + +/** + Get data parameter. + + @param[in] tinst table instanc. + @param[in] addr parameter address (offset in table). + @param[out] buf value of parameter - user supplied memory of at least parameter size. + @param[in] buf_size size of \a buf. + @param[in] flags flags. + @return GS_ERROR_OVERFLOW if parameter size is greater than \a buf_size. + @return_gs_error_t +*/ +gs_error_t gs_param_get_data(gs_param_table_instance_t * tinst, uint16_t addr, void * buf, size_t buf_size, uint32_t flags); + +/** + Set data parameter. + + @param[in] tinst table instanc. + @param[in] addr parameter address (offset in table). + @param[in] value value of parameter. + @param[in] value_size size of \a value. + @param[in] flags flags. + @return GS_ERROR_OVERFLOW if parameter size is greater than \a buf_size. + @return_gs_error_t +*/ +gs_error_t gs_param_set_data(gs_param_table_instance_t * tinst, uint16_t addr, const void * value, size_t value_size, uint32_t flags); + +/** + Macro for expanding get/set functions. + @param[in] name function suffix name. + @param[in] native_type native type + @param[in] param_type parameter type +*/ +#define GS_PARAM_PASTE(name, native_type, param_type) \ + static inline gs_error_t gs_param_get_##name(gs_param_table_instance_t * tinst, uint16_t addr, native_type * buf, uint32_t flags) { \ + return gs_param_get(tinst, addr, param_type, buf, sizeof(*buf), flags); \ + } \ + static inline native_type gs_param_get_##name##_nc(gs_param_table_instance_t * tinst, uint16_t addr, uint32_t flags) { \ + native_type value = 0; \ + gs_param_get(tinst, addr, param_type, &value, sizeof(value), flags); \ + return value; \ + } \ + static inline gs_error_t gs_param_set_##name(gs_param_table_instance_t * tinst, uint16_t addr, native_type value, uint32_t flags) { \ + return gs_param_set(tinst, addr, param_type, &value, sizeof(value), flags); \ + } + +/** + Get/set boolean. +*/ +GS_PARAM_PASTE(bool, bool, GS_PARAM_BOOL) +/** + Get/set uint8. +*/ +GS_PARAM_PASTE(uint8, uint8_t, GS_PARAM_UINT8) +/** + Get/set uint16. +*/ +GS_PARAM_PASTE(uint16, uint16_t, GS_PARAM_UINT16) +/** + Get/set uint32. +*/ +GS_PARAM_PASTE(uint32, uint32_t, GS_PARAM_UINT32) +/** + Get/set uint64. +*/ +GS_PARAM_PASTE(uint64, uint64_t, GS_PARAM_UINT64) +/** + Get/set int8. +*/ +GS_PARAM_PASTE(int8, int8_t, GS_PARAM_INT8) +/** + Get/set int16. +*/ +GS_PARAM_PASTE(int16, int16_t, GS_PARAM_INT16) +/** + Get/set int32. +*/ +GS_PARAM_PASTE(int32, int32_t, GS_PARAM_INT32) +/** + Get/set int64. +*/ +GS_PARAM_PASTE(int64, int64_t, GS_PARAM_INT64) +/** + Get/set double. +*/ +GS_PARAM_PASTE(double, double, GS_PARAM_DOUBLE) +/** + Get/set float. +*/ +GS_PARAM_PASTE(float, float, GS_PARAM_FLOAT) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/include/gs/param/types.h b/gomspace/libparam_client/include/gs/param/types.h new file mode 100644 index 00000000..61efbb10 --- /dev/null +++ b/gomspace/libparam_client/include/gs/param/types.h @@ -0,0 +1,272 @@ +#ifndef GS_PARAM_TYPES_H +#define GS_PARAM_TYPES_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Parameter types. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Macros for accessing table row members. + These macros can be used to access members in a cross-platform way, compensating for the AVR8's memory model. + @{ +*/ +#define GS_PARAM_ARRAY_SIZE(p) gs_max(GS_PGM_UINT8((p)->array_size), 1) +#define GS_PARAM_SIZE(p) GS_PGM_UINT8((p)->size) +#define GS_PARAM_TYPE(p) GS_PGM_UINT8((p)->type) +#define GS_PARAM_ADDR(p) GS_PGM_UINT16((p)->addr) +#define GS_PARAM_FLAGS(p) GS_PGM_UINT8((p)->flags) +/** @} */ + +/** + Max parameter name - including 0 termination. + @note In some rare/old table definitions, the name may not be NULL terminated. +*/ +#define GS_PARAM_MAX_NAME 14 + +/** + Parameter flags. + Flags must be in range: bit 0 - 7, to avoid clash with other parts of the parameter system. +*/ +typedef enum { + /** + Parameter will be stored in configured auto-persist store when set. + @note Flag must be specified when setting the parameter. + */ + GS_PARAM_F_AUTO_PERSIST = (1 << 0), + /** + @deprecated Not supported in version 4.0 + */ + PARAM_F_READONLY = (1 << 1), + /** + Skip callback, when parameter is set. + @note Flag must be specified when setting the parameter. + */ + GS_PARAM_F_NO_CALLBACK = (1 << 2), + /** + Show/display parameter in hex. + */ + GS_PARAM_F_SHOW_HEX = (1 << 3), + /** + Show double/float using scientific notation. + */ + GS_PARAM_F_SHOW_SCIENTIFIC = (1 << 4), + + PARAM_F_NOCALLBACK = GS_PARAM_F_NO_CALLBACK, + PARAM_F_PERSIST = GS_PARAM_F_AUTO_PERSIST, +} gs_param_flags_t; + +/** + * Parameter types. + */ +typedef enum __attribute__((__packed__)) { + /** + Unsigned 8 bit (uint8_t). + */ + GS_PARAM_UINT8 = 0, + /** + Unsigned 16 bit (uint16_t). + */ + GS_PARAM_UINT16 = 1, + /** + Unsigned 32 bit (uint32_t). + */ + GS_PARAM_UINT32 = 2, + /** + Unsigned 64 bit (uint64_t). + */ + GS_PARAM_UINT64 = 3, + /** + Signed 8 bit (int8_t). + */ + GS_PARAM_INT8 = 4, + /** + Signed 16 bit (int16_t). + */ + GS_PARAM_INT16 = 5, + /** + Signed 32 bit (int32_t). + */ + GS_PARAM_INT32 = 6, + /** + Signed 64 bit (int64_t). + */ + GS_PARAM_INT64 = 7, + /** + @deprecated - use #GS_PARAM_UINT8 and #GS_PARAM_F_SHOW_HEX. + */ + PARAM_X8 = 8, + /** + @deprecated - use #GS_PARAM_UINT16 and #GS_PARAM_F_SHOW_HEX. + */ + PARAM_X16 = 9, + /** + @deprecated - use #GS_PARAM_UINT32 and #GS_PARAM_F_SHOW_HEX. + */ + PARAM_X32 = 10, + /** + @deprecated - use #GS_PARAM_UINT64 and #GS_PARAM_F_SHOW_HEX. + */ + PARAM_X64 = 11, + /** + Double. + */ + GS_PARAM_DOUBLE = 12, + /** + Float. + */ + GS_PARAM_FLOAT = 13, + /** + C or null-terminated string. + @note The specified \a size must include space for the NUL character. + */ + GS_PARAM_STRING = 14, + /** + Data (binary blob). + Binary blob: [0, 0x40, 0x4f] -> '00404f' + */ + GS_PARAM_DATA = 15, + /** + Boolean. + Expected same size as uint8_t. + */ + GS_PARAM_BOOL = 16, + + PARAM_UINT8 = GS_PARAM_UINT8, + PARAM_UINT16 = GS_PARAM_UINT16, + PARAM_UINT32 = GS_PARAM_UINT32, + PARAM_UINT64 = GS_PARAM_UINT64, + PARAM_INT8 = GS_PARAM_INT8, + PARAM_INT16 = GS_PARAM_INT16, + PARAM_INT32 = GS_PARAM_INT32, + PARAM_INT64 = GS_PARAM_INT64, + PARAM_DOUBLE = GS_PARAM_DOUBLE, + PARAM_FLOAT = GS_PARAM_FLOAT, + PARAM_STRING = GS_PARAM_STRING, + PARAM_DATA = GS_PARAM_DATA, + PARAM_BOOL = GS_PARAM_BOOL, +} gs_param_type_t; + +/** + Table row. + + A table row defines one parameter, and a table is defined by one or more rows. + + @note Make sure to update gs_param_table_checksum2(), if adding fields > 1 byte. + + @note AVR8: Table definitions must be located in \a program memory, i.e. must be const. +*/ +typedef struct __attribute__((__packed__)) { + /** + Address (or offset) in table. + */ + uint16_t addr; + /** + Type. + */ + gs_param_type_t type; + /** + Size of element. + uint32_t = 4, string[5] = 5 (4 characters + 1 for NUL), etc. + */ + uint8_t size; + /** + Array size. + Size greater than 1, will make the parameter an array - if the value is 0 or 1, the parameter is not an array. + */ + uint8_t array_size; + /** + Flags. + @see gs_param_flags_t + */ + uint8_t flags; + /** + Name (C string). + @note In some rare/old table definitions, the name may not be NUL terminated. + */ + char name[GS_PARAM_MAX_NAME]; +} gs_param_table_row_t; + +/** + Table instance. +*/ +typedef struct gs_param_table_instance gs_param_table_instance_t; + +/** + Table id. + + Tables can be associated with a number/id, which normally is unique on a specific node. +*/ +typedef uint8_t gs_param_table_id_t; + +/** + Undefined table id. +*/ +#define GS_PARAM_UNDEFINED_TABLE_ID 255 + +/** + Function for setting a parameter. + + @param[in] context user context/reference. + @param[in] tinst table instance. + @param[in] addr parameter address. + @param[in] type parameter type. + @param[in] item parameter value. + @param[in] size parameter size (e.g. how many bytes to copy from \a item). + @param[in] flags flags related to the operation - these may vary depending on the context. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_param_table_function_set_t)(void * context, gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, const void * item, size_t size, uint32_t flags); + +/** + Function for getting a parameter. + + @param[in] context user context/reference. + @param[in] tinst table instance. + @param[in] addr parameter address. + @param[in] type parameter type. + @param[out] item parameter buffer (provided by the caller). + @param[in] size parameter size (e.g. how many bytes to copy to \a item). + @param[in] flags flags related to the operation - these may vary depending on the context. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_param_table_function_get_t)(void * context, gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, void * item, size_t size, uint32_t flags); + +/** + Function interface for setting and getting parameters. + Functions will be invoked, when set/get is called on the table instance. +*/ +typedef struct { + /** + User context, provided in the callback functions. + */ + void * context; + /** + Called when setting a parameter. + */ + gs_param_table_function_set_t set; + /** + Called when getting a parameter. + */ + gs_param_table_function_get_t get; +} gs_param_function_interface_t; + +/** + Callback function for changed parameter. + See gs_param_table_create() for details. +*/ +typedef void (*gs_param_callback_func_t)(uint16_t addr, gs_param_table_instance_t * tinst); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libparam_client/src/bindings/python/pyparam.c b/gomspace/libparam_client/src/bindings/python/pyparam.c new file mode 100644 index 00000000..bbb35b7a --- /dev/null +++ b/gomspace/libparam_client/src/bindings/python/pyparam.c @@ -0,0 +1,1084 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +#include +#include + +#include +#include +#include +#include + +#if PY_MAJOR_VERSION == 3 +#define IS_PY3 +#endif + +static inline int to_int(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + return -1; +} + +/* static inline int rparam_get_string(char *out, int outlen, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_string(PyObject *self, PyObject *args) +{ + uint16_t addr; + uint16_t string_size; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "HHii|ii", &addr, &string_size, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + char buffer[string_size]; + memset(&buffer, 0, string_size); + int result = rparam_get_string(&buffer[0], string_size, addr, index_id, node, port, timeout); + return Py_BuildValue("is", result, buffer); +} + +/* static inline int rparam_set_string(char *in, int inlen, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_string(PyObject *self, PyObject *args) +{ + char* in; + int inlen; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "siHii|ii", &in, &inlen, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + return Py_BuildValue("i", rparam_set_string(in, inlen, addr, index_id, node, port, timeout)); +} + +/* static inline int rparam_get_string(char *out, int outlen, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_data(PyObject *self, PyObject *args) +{ + uint16_t addr; + uint16_t data_size; + int index_id; + int node; + int port = 7; + int timeout = 1000; + + if (!PyArg_ParseTuple(args, "HHii|ii", &addr, &data_size, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + char buffer[data_size]; + memset(&buffer, 0, data_size); + + //Reading as raw string: + int result = rparam_get_string(&buffer[0], data_size, addr, index_id, node, port, timeout); + + //Convert to string of HEX bytes + uint16_t bblen=2*data_size; + char bb[bblen+1]; + memset(bb,0, sizeof(bb)); + + char *bb_ptr = &bb[0]; + for (int i = 0; i < data_size; i++) { + sprintf(bb_ptr, "%02"PRIX8, ((uint8_t *) buffer)[i]); + bb_ptr+=2; + } + + return Py_BuildValue("is", result, bb); +} + +/* static inline int rparam_set_string(char *in, int inlen, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_data(PyObject *self, PyObject *args) +{ + char* in; + int inlen; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + gs_error_t error = GS_OK; + + if (!PyArg_ParseTuple(args, "siHii|ii", &in, &inlen, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + //inlen is number of bytes in DATA param in table, in reality the string will be twice as long, since it contains HEX-string... + // e.g., DEADBEEF00000000 is 16 characters but only corresponds to 8 bytes + char outbuffer[inlen]; + memset(outbuffer, 0, sizeof(outbuffer)); + + if ((inlen % 2) == 0) { + // validate data first - not to end up with invalid/strange data + for (int i = 0; i < inlen; ++i) { + if (to_int(in[i]) < 0) { + error = GS_ERROR_DATA; + break; + } + } + if (error == GS_OK) { + uint8_t * out = (uint8_t *) outbuffer; + for (int i = 0; i < inlen; i += 2, ++out) { + *out = (16 * to_int(in[i])) + to_int(in[i+1]); + } + error = GS_OK; + } + } else { + error = GS_ERROR_DATA; + } + + if (error != GS_OK) { + return Py_BuildValue("i", error); + } else { + return Py_BuildValue("i", rparam_set_string(outbuffer, inlen, addr, index_id, node, port, timeout)); + } +} + +/* static inline int rparam_get_uint8(uint8_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_uint8(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + uint8_t value; + int result = rparam_get_uint8(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("iB", result, value); +} + +/* static inline int rparam_set_uint8(uint8_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_uint8(PyObject *self, PyObject *args) +{ + uint8_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "BHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_uint8(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_uint16(uint16_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_uint16(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + uint16_t value; + int result = rparam_get_uint16(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("iH", result, value); +} + +/* static inline int rparam_set_uint16(uint16_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_uint16(PyObject *self, PyObject *args) +{ + uint16_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "HHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_uint16(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_uint32(uint32_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_uint32(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + uint32_t value; + int result = rparam_get_uint32(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("iI", result, value); +} + +/* static inline int rparam_set_uint32(uint32_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_uint32(PyObject *self, PyObject *args) +{ + uint32_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "IHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_uint32(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_uint64(uint64_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_uint64(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + uint64_t value; + int result = rparam_get_uint64(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("iK", result, value); +} + +/* static inline int rparam_set_uint64(uint64_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_uint64(PyObject *self, PyObject *args) +{ + uint64_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "KHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_uint64(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_int8(int8_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_int8(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int8_t value; + int result = rparam_get_int8(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("hb", result, value); +} + +/* static inline int rparam_set_int8(int8_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_int8(PyObject *self, PyObject *args) +{ + int8_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "bHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_int8(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_int16(int16_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_int16(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int16_t value; + int result = rparam_get_int16(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("ih", result, value); +} + +/* static inline int rparam_set_int16(int16_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_int16(PyObject *self, PyObject *args) +{ + int16_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "hHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_int16(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_int32(int32_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_int32(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int32_t value; + int result = rparam_get_int32(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("il", result, value); +} + +/* static inline int rparam_set_int32(int32_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_int32(PyObject *self, PyObject *args) +{ + int32_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "lHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_int32(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_int64(int64_t * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_int64(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int64_t value; + int result = rparam_get_int64(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("iL", result, value); +} + +/* static inline int rparam_set_int64(int64_t * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_int64(PyObject *self, PyObject *args) +{ + int64_t value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "LHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_int64(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_float(float * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_float(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + float value; + int result = rparam_get_float(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("if", result, value); +} + +/* static inline int rparam_set_float(float * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_float(PyObject *self, PyObject *args) +{ + float value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "fHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_float(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/* static inline int rparam_get_double(double * out, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_get_double(PyObject *self, PyObject *args) +{ + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Hii|ii", &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + double value; + int result = rparam_get_double(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("id", result, value); +} + +/* static inline int rparam_set_double(double * in, uint16_t addr, int index_id, int node, int port, int timeout) */ +static PyObject* pyrparam_set_double(PyObject *self, PyObject *args) +{ + double value; + uint16_t addr; + int index_id; + int node; + int port = 7; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "dHii|ii", &value, &addr, &index_id, &node, &port, &timeout)) { + Py_RETURN_NONE; + } + + int result = rparam_set_double(&value, addr, index_id, node, port, timeout); + return Py_BuildValue("i", result); +} + +/*gs_error_t gs_rparam_save_to_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id, + const char * store, const char * slot)*/ +static PyObject* pyrparam_save_to_store(PyObject *self, PyObject *args) +{ + uint8_t table_id; + char* store; + int node; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Bsi|i", &table_id, &store, &node, &timeout)) { + Py_RETURN_NONE; + } + + int result = gs_rparam_save_to_store(node, timeout, table_id, store, NULL); + return Py_BuildValue("i", result); +} +/*gs_error_t gs_rparam_load_from_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id, + const char * store, const char * slot)*/ +static PyObject* pyrparam_load_from_store(PyObject *self, PyObject *args) +{ + uint8_t table_id; + char* store; + int node; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "Bsi|i", &table_id, &store, &node, &timeout)) { + Py_RETURN_NONE; + } + + int result = gs_rparam_load_from_store(node, timeout, table_id, store, NULL); + return Py_BuildValue("i", result); +} + +/*gs_error_t gs_rparam_save(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to) */ +static PyObject* pyrparam_save(PyObject *self, PyObject *args) +{ + uint8_t table_id; + uint8_t file_id; + int node; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "BBi|i", &table_id, &file_id, &node, &timeout)) { + Py_RETURN_NONE; + } + + int result = gs_rparam_save(node, timeout, table_id, file_id); + return Py_BuildValue("i", result); +} + +/*gs_error_t gs_rparam_load(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to) */ +static PyObject* pyrparam_load(PyObject *self, PyObject *args) +{ + uint8_t table_id; + uint8_t file_id; + int node; + int timeout = 1000; + if (!PyArg_ParseTuple(args, "BBi|i", &file_id, &table_id, &node, &timeout)) { + Py_RETURN_NONE; + } + + int result = gs_rparam_load(node, timeout, file_id, table_id); + return Py_BuildValue("i", result); +} + +/*int rparam_download_table_spec_from_remote_and_save_to_file2(const char* fname, uint8_t node, uint8_t port, param_index_t* index, uint16_t* checksum, uint32_t timeout);*/ +static PyObject* pyrparam_download_table_spec_from_remote_and_save_to_file2(PyObject *self, PyObject *args) +{ + char* fname; + int fname_len; + int mem_id; + int node; + int port; + uint16_t checksum; + int timeout; + if (!PyArg_ParseTuple(args, "s#iiiHi", &fname, &fname_len, &node, &port, &mem_id, &checksum, &timeout)) { + Py_RETURN_NONE; + } + + GS_PARAM_TINST_VAR(tinst); + int result = gs_rparam_download_table_spec(tinst, fname, node, mem_id, timeout, &checksum); + gs_param_table_free(tinst); // free allocated resources by gs_rparam_download_table_spec() + return Py_BuildValue("i", result); +} + +/* gs_error_t gs_param_io_i2c_init(gs_param_io_t * io, uint8_t bus, uint8_t addr, bool big_endian); */ + +static PyObject* pyioparam_i2c_init(PyObject *self, PyObject *args) +{ + uint8_t bus; + uint8_t addr; + uint8_t big_endian; + if (!PyArg_ParseTuple(args, "BBB", &bus, &addr, &big_endian)) { + Py_RETURN_NONE; + } + gs_pp_t* io = malloc(sizeof(gs_pp_t)); + gs_error_t error = gs_pp_i2c_init(io, bus, addr, big_endian); + return Py_BuildValue("iO", error, PyCapsule_New(io, "gs_pp_t", NULL)); +} + +static PyObject* pyioparam_io_free(PyObject *self, PyObject *args) +{ + PyObject *io_capsule; + if (!PyArg_ParseTuple(args, "O", &io_capsule)) { + Py_RETURN_NONE; + } + free(PyCapsule_GetPointer(io_capsule, "gs_pp_t")); + Py_RETURN_NONE; +} + +/* +gs_error_t gs_pp_get_int8( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + int8_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_get_int8(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + int8_t value; + if (!PyArg_ParseTuple(args, "OBB", &io_capsule, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_int8(tmp, table_id, addr, &value, 1, 0); + if (error) { + value = 0; + } + return Py_BuildValue("ib", error, value); +} + +/* +gs_error_t gs_pp_set_int8( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + const int8_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_set_int8(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + int8_t value; + if (!PyArg_ParseTuple(args, "ObBB", &io_capsule, &value, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_int8(tmp, table_id, addr, &value, 1, 0); + return Py_BuildValue("i", error); +} + +/* +gs_error_t gs_pp_get_uint8( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + uint8_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_get_uint8(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + uint8_t value; + if (!PyArg_ParseTuple(args, "OBB", &io_capsule, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_uint8(tmp, table_id, addr, &value, 1, 0); + if (error) { + value = 0; + } + return Py_BuildValue("iB", error, value); +} + +/* +gs_error_t gs_pp_set_uint8( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + const uint8_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_set_uint8(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + uint8_t value; + if (!PyArg_ParseTuple(args, "OBBB", &io_capsule, &value, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_uint8(tmp, table_id, addr, &value, 1, 0); + return Py_BuildValue("i", error); +} + +/* +gs_error_t gs_pp_get_int16( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + int16_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_get_int16(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + int16_t value; + if (!PyArg_ParseTuple(args, "OBB", &io_capsule, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_int16(tmp, table_id, addr, &value, 1, 0); + if (error) { + value = 0; + } + return Py_BuildValue("ih", error, value); +} + +/* +gs_error_t gs_pp_set_int16( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + const int16_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_set_int16(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + int16_t value; + if (!PyArg_ParseTuple(args, "OhBB", &io_capsule, &value, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_int16(tmp, table_id, addr, &value, 1, 0); + return Py_BuildValue("i", error); +} + +/* +gs_error_t gs_pp_get_uint16( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + uint16_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_get_uint16(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + uint16_t value; + if (!PyArg_ParseTuple(args, "OBB", &io_capsule, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_uint16(tmp, table_id, addr, &value, 1, 0); + if (error) { + value = 0; + } + return Py_BuildValue("iH", error, value); +} + +/* +gs_error_t gs_pp_set_uint16( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + const uint16_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_set_uint16(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + uint16_t value; + if (!PyArg_ParseTuple(args, "OHBB", &io_capsule, &value, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_uint16(tmp, table_id, addr, &value, 1, 0); + return Py_BuildValue("i", error); +} + +/* +gs_error_t gs_pp_get_int32( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + int32_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_get_int32(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + int32_t value; + if (!PyArg_ParseTuple(args, "OBB", &io_capsule, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_int32(tmp, table_id, addr, &value, 1, 0); + if (error) { + value = 0; + } + return Py_BuildValue("ii", error, value); +} + +/* +gs_error_t gs_pp_set_int32( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + const int32_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_set_int32(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + int32_t value; + if (!PyArg_ParseTuple(args, "OiBB", &io_capsule, &value, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_int32(tmp, table_id, addr, &value, 1, 0); + return Py_BuildValue("i", error); +} + +/* +gs_error_t gs_pp_get_uint32( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + uint32_t * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_get_uint32(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + uint32_t value; + if (!PyArg_ParseTuple(args, "OBB", &io_capsule, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_uint32(tmp, table_id, addr, &value, 1, 0); + if (error) { + value = 0; + } + return Py_BuildValue("iI", error, value); +} +/* +gs_error_t gs_pp_set_uint32( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + const uint32_t * value, + size_t count, + uint32_t flags); +*/ + +static PyObject* pyioparam_io_set_uint32(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + uint32_t value; + if (!PyArg_ParseTuple(args, "OIBB", &io_capsule, &value, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_uint32(tmp, table_id, addr, &value, 1, 0); + return Py_BuildValue("i", error); +} +/* +gs_error_t gs_pp_get_float( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + float * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_get_float(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + float value; + if (!PyArg_ParseTuple(args, "OBB", &io_capsule, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_float(tmp, table_id, addr, &value, 1, 0); + if (error) { + value = 0; + } + return Py_BuildValue("if", error, value); +} + +/* +gs_error_t gs_pp_set_float( + gs_pp_t * io, + uint8_t table_id, + uint8_t addr, + const float * value, + size_t count, + uint32_t flags); +*/ +static PyObject* pyioparam_io_set_float(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint8_t addr; + float value; + if (!PyArg_ParseTuple(args, "OfBB", &io_capsule, &value, &table_id, &addr)) { + Py_RETURN_NONE; + } + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_float(tmp, table_id, addr, &value, 1, 0); + return Py_BuildValue("i", error); +} + +static PyObject* pyioparam_spi_init(PyObject* self, PyObject* args) +{ + uint8_t addr; + uint8_t big_endian; + if (!PyArg_ParseTuple(args, "BB", &addr, &big_endian)) { + Py_RETURN_NONE; + } + gs_pp_t* io = malloc(sizeof(gs_pp_t)); + gs_error_t error = gs_pp_spi_init(io, addr, big_endian); + return Py_BuildValue("iO", error, PyCapsule_New(io, "gs_pp_t", NULL)); +} + +/** + Get lock value + + @param[in] pp Handle for connection + @param[in] table_id Table ID + @param[out] value Lock state (0 = unlocked, 1 = locked) + @param[in] flags + @return_gs_error_t +gs_error_t gs_pp_get_table_lock(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags); +*/ + +static PyObject* pyioparam_get_table_lock(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint32_t flags = 0; + if (!PyArg_ParseTuple(args, "OB|I", &io_capsule, &table_id, &flags)) { + Py_RETURN_NONE; + } + bool result; + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_get_table_lock(tmp, table_id, &result, flags); + + uint8_t retval = result ? 1 : 0; + return Py_BuildValue("iB", error, retval); +} +/** + Set lock value + + @param[in] pp Handle for connection + @param[in] table_id Table ID + @param[in] value Lock state (0 = unlocked, 1 = locked) + @param[in] flags + @return_gs_error_t + + gs_error_t gs_pp_set_table_lock(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags); + */ + +static PyObject* pyioparam_set_table_lock(PyObject* self, PyObject* args) +{ + PyObject* io_capsule; + uint8_t table_id; + uint32_t flags = 0; + uint8_t status; + if (!PyArg_ParseTuple(args, "OBB|I", &io_capsule, &table_id, &status, &flags)) { + Py_RETURN_NONE; + } + bool setval = status ? true : false; + gs_pp_t* tmp = PyCapsule_GetPointer(io_capsule, "gs_pp_t"); + gs_error_t error = gs_pp_set_table_lock(tmp, table_id, &setval, flags); + return Py_BuildValue("i", error); +} +static PyMethodDef methods[] = { + + /* param/rparam_client.h */ + {"rparam_get_string", pyrparam_get_string, METH_VARARGS, ""}, + {"rparam_set_string", pyrparam_set_string, METH_VARARGS, ""}, + + {"rparam_get_float", pyrparam_get_float, METH_VARARGS, ""}, + {"rparam_set_float", pyrparam_set_float, METH_VARARGS, ""}, + {"rparam_get_double", pyrparam_get_double, METH_VARARGS, ""}, + {"rparam_set_double", pyrparam_set_double, METH_VARARGS, ""}, + + {"rparam_get_uint8", pyrparam_get_uint8, METH_VARARGS, ""}, + {"rparam_set_uint8", pyrparam_set_uint8, METH_VARARGS, ""}, + {"rparam_get_uint16", pyrparam_get_uint16, METH_VARARGS, ""}, + {"rparam_set_uint16", pyrparam_set_uint16, METH_VARARGS, ""}, + {"rparam_get_uint32", pyrparam_get_uint32, METH_VARARGS, ""}, + {"rparam_set_uint32", pyrparam_set_uint32, METH_VARARGS, ""}, + {"rparam_get_uint64", pyrparam_get_uint64, METH_VARARGS, ""}, + {"rparam_set_uint64", pyrparam_set_uint64, METH_VARARGS, ""}, + + {"rparam_get_int8", pyrparam_get_int8, METH_VARARGS, ""}, + {"rparam_set_int8", pyrparam_set_int8, METH_VARARGS, ""}, + {"rparam_get_int16", pyrparam_get_int16, METH_VARARGS, ""}, + {"rparam_set_int16", pyrparam_set_int16, METH_VARARGS, ""}, + {"rparam_get_int32", pyrparam_get_int32, METH_VARARGS, ""}, + {"rparam_set_int32", pyrparam_set_int32, METH_VARARGS, ""}, + {"rparam_get_int64", pyrparam_get_int64, METH_VARARGS, ""}, + {"rparam_set_int64", pyrparam_set_int64, METH_VARARGS, ""}, + + {"rparam_get_data", pyrparam_get_data, METH_VARARGS, ""}, + {"rparam_set_data", pyrparam_set_data, METH_VARARGS, ""}, + + {"rparam_save_to_store", pyrparam_save_to_store, METH_VARARGS, "" }, + {"rparam_load_from_store", pyrparam_load_from_store, METH_VARARGS, "" }, + {"rparam_save", pyrparam_save, METH_VARARGS, "" }, + {"rparam_load", pyrparam_load, METH_VARARGS, "" }, + + {"rparam_download_table_spec_from_remote_and_save_to_file2", pyrparam_download_table_spec_from_remote_and_save_to_file2, METH_VARARGS, ""}, + + + /* param/rparam_client.h */ + {"pp_i2c_init", pyioparam_i2c_init, METH_VARARGS, ""}, + {"pp_spi_init", pyioparam_spi_init, METH_VARARGS, ""}, + {"pp_get_int8", pyioparam_io_get_int8, METH_VARARGS, ""}, + {"pp_get_uint8", pyioparam_io_get_uint8, METH_VARARGS, ""}, + {"pp_set_int8", pyioparam_io_set_int8, METH_VARARGS, ""}, + {"pp_set_uint8", pyioparam_io_set_uint8, METH_VARARGS, ""}, + {"pp_get_int16", pyioparam_io_get_int16, METH_VARARGS, ""}, + {"pp_get_uint16", pyioparam_io_get_uint16, METH_VARARGS, ""}, + {"pp_set_int16", pyioparam_io_set_int16, METH_VARARGS, ""}, + {"pp_set_uint16", pyioparam_io_set_uint16, METH_VARARGS, ""}, + {"pp_get_int32", pyioparam_io_get_int32, METH_VARARGS, ""}, + {"pp_get_uint32", pyioparam_io_get_uint32, METH_VARARGS, ""}, + {"pp_set_int32", pyioparam_io_set_int32, METH_VARARGS, ""}, + {"pp_set_uint32", pyioparam_io_set_uint32, METH_VARARGS, ""}, + {"pp_get_float", pyioparam_io_get_float, METH_VARARGS, ""}, + {"pp_set_float", pyioparam_io_set_float, METH_VARARGS, ""}, + {"pp_io_free", pyioparam_io_free, METH_VARARGS, ""}, + {"pp_get_table_lock", pyioparam_get_table_lock, METH_VARARGS, ""}, + {"pp_set_table_lock", pyioparam_set_table_lock, METH_VARARGS, ""}, + + /* sentinel */ + {NULL, NULL, 0, NULL} +}; + +#ifdef IS_PY3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "libgsparam_client_py3", + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#ifdef IS_PY3 +PyMODINIT_FUNC PyInit_libgsparam_client_py3(void) { +#else +PyMODINIT_FUNC initlibgsparam_client_py2(void) +{ +#endif + +#ifdef IS_PY3 + PyObject* m = PyModule_Create(&moduledef); +#else + Py_InitModule("libgsparam_client_py2", methods); +#endif + +#ifdef IS_PY3 + return m; +#endif +} diff --git a/gomspace/libparam_client/src/pp/cmd/master_cmd.c b/gomspace/libparam_client/src/pp/cmd/master_cmd.c new file mode 100644 index 00000000..404f5472 --- /dev/null +++ b/gomspace/libparam_client/src/pp/cmd/master_cmd.c @@ -0,0 +1,418 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include + +static bool use_checksum; + +static gs_pp_t gs_pp; + +static int cmd_spi_init(gs_command_context_t *ctx) +{ + uint8_t slave; + gs_error_t error = gs_string_to_uint8(ctx->argv[1], &slave); + if (error) { + return error; + } + + bool big_endian = false; + if (ctx->argc > 2) { + error = gs_string_to_bool(ctx->argv[2], &big_endian); + if (error) { + return error; + } + } + + error = gs_pp_spi_init(&gs_pp, slave, big_endian); + if (error) { + memset(&gs_pp, 0, sizeof(gs_pp)); + return error; + } + + return GS_OK; +} + +static int cmd_i2c_init(gs_command_context_t *ctx) +{ + uint8_t bus; + gs_error_t error = gs_string_to_uint8(ctx->argv[1], &bus); + if (error) { + return GS_ERROR_ARG; + } + + uint8_t addr; + error = gs_string_to_uint8(ctx->argv[2], &addr); + if (error) { + return GS_ERROR_ARG; + } + + bool big_endian = false; + if (ctx->argc > 3) { + error = gs_string_to_bool(ctx->argv[3], &big_endian); + if (error) { + return GS_ERROR_ARG; + } + } + + error = gs_pp_i2c_init(&gs_pp, bus, addr, big_endian); + if (error) { + memset(&gs_pp, 0, sizeof(gs_pp)); + return error; + } + + return GS_OK; +} + +static int cmd_checksum(gs_command_context_t *ctx) +{ + if (ctx->argc > 1) { + if (gs_string_to_bool(ctx->argv[1], &use_checksum)) { + return GS_ERROR_ARG; + } + } + printf("Use CHECKSUM: %d\r\n", use_checksum); + return GS_OK; +} + +// get_xxxx +static int cmd_parse(gs_command_context_t *ctx, + uint32_t * table, uint32_t * addr) +{ + if (ctx->argc < 3) { + return GS_ERROR_ARG; + } + + gs_error_t error = gs_string_to_uint32(ctx->argv[1], table); + if (error || (*table > 7)) { + return GS_ERROR_ARG; + } + + error = gs_string_to_uint32(ctx->argv[2], addr); + if (error || (*addr > 255)) { + return GS_ERROR_ARG; + } + + return GS_OK; +} + +static int cmd_get_parse(gs_command_context_t * ctx, + uint32_t * table, uint32_t * addr, uint32_t * count) +{ + int res = cmd_parse(ctx, table, addr); + if (res == GS_OK) { + *count = 1; + if (ctx->argc > 3) { + gs_error_t error = gs_string_to_uint32(ctx->argv[3], count); + if (error || (*count > 100)) { + return GS_ERROR_ARG; + } + } + } + + return res; +} + +#define CMD_GET(_ctx, _type, _func, _format) \ + { \ + uint32_t table; \ + uint32_t addr; \ + uint32_t count; \ + int res = cmd_get_parse(_ctx, &table, &addr, &count); \ + if (res == GS_OK) { \ + _type value[count]; \ + gs_error_t error = _func(&gs_pp, table, addr, value, count, use_checksum ? GS_PP_FLAG_CHECKSUM : 0); \ + if (error) { \ + return error; \ + } \ + printf("value(s): "); \ + for (unsigned int i = 0; i < count; ++i) { \ + printf("%" _format " ", value[i]); \ + } \ + printf("\r\n"); \ + } \ + return res; \ + } + +static int cmd_get_int8(gs_command_context_t * ctx) +{ + CMD_GET(ctx, int8_t, gs_pp_get_int8, PRId8); +} + +static int cmd_get_int16(gs_command_context_t * ctx) +{ + CMD_GET(ctx, int16_t, gs_pp_get_int16, PRId16); +} + +static int cmd_get_int32(gs_command_context_t * ctx) +{ + CMD_GET(ctx, int32_t, gs_pp_get_int32, PRId32); +} + +static int cmd_get_uint8(gs_command_context_t * ctx) +{ + CMD_GET(ctx, uint8_t, gs_pp_get_uint8, PRIu8); +} + +static int cmd_get_uint16(gs_command_context_t * ctx) +{ + CMD_GET(ctx, uint16_t, gs_pp_get_uint16, PRIu16); +} + +static int cmd_get_uint32(gs_command_context_t * ctx) +{ + CMD_GET(ctx, uint32_t, gs_pp_get_uint32, PRIu32); +} + +static int cmd_get_float(gs_command_context_t * ctx) +{ + CMD_GET(ctx, float, gs_pp_get_float, "f"); +} + +#define CMD_SET(_ctx, _type, _func, _convert) \ + { \ + const unsigned int MAX_VALUES = 20; \ + uint32_t table; \ + uint32_t addr; \ + int res = cmd_parse(_ctx, &table, &addr); \ + if (res == GS_OK) { \ + _type value[MAX_VALUES]; \ + unsigned int count = 0; \ + for (int i = 3; (i < _ctx->argc) && (count < MAX_VALUES); ++i, ++count) { \ + res = _convert(_ctx->argv[i], &value[count]); \ + if (res) { \ + return GS_ERROR_DATA; \ + } \ + } \ + res = _func(&gs_pp, table, addr, value, count, use_checksum ? GS_PP_FLAG_CHECKSUM : 0); \ + } \ + return res; \ + } + +static int cmd_set_int8(gs_command_context_t * ctx) +{ + CMD_SET(ctx, int8_t, gs_pp_set_int8, gs_string_to_int8); +} + +static int cmd_set_int16(gs_command_context_t * ctx) +{ + CMD_SET(ctx, int16_t, gs_pp_set_int16, gs_string_to_int16); +} + +static int cmd_set_int32(gs_command_context_t * ctx) +{ + CMD_SET(ctx, int32_t, gs_pp_set_int32, gs_string_to_int32); +} + +static int cmd_set_uint8(gs_command_context_t * ctx) +{ + CMD_SET(ctx, uint8_t, gs_pp_set_uint8, gs_string_to_uint8); +} + +static int cmd_set_uint16(gs_command_context_t * ctx) +{ + CMD_SET(ctx, uint16_t, gs_pp_set_uint16, gs_string_to_uint16); +} + +static int cmd_set_uint32(gs_command_context_t * ctx) +{ + CMD_SET(ctx, uint32_t, gs_pp_set_uint32, gs_string_to_uint32); +} + +static int cmd_set_float(gs_command_context_t * ctx) +{ + CMD_SET(ctx, float, gs_pp_set_float, gs_string_to_float); +} + +static int cmd_get_table_lock(gs_command_context_t * ctx) +{ + uint32_t table; + gs_error_t res = gs_string_to_uint32(ctx->argv[1], &table); + if (res == GS_OK) { + if (table <= 7) { + bool lock; + res = gs_pp_get_table_lock(&gs_pp, table, &lock, GS_PP_FLAG_CHECKSUM); + if (res == GS_OK) { + const char locked[] = "locked\n"; + const char unlocked[] = "unlocked\n"; + const char * lock_state = (lock) ? locked : unlocked; + printf("Table %s", lock_state); + } + } else { + res = GS_ERROR_ARG; + } + } + return res; +} + +static int cmd_set_table_lock(gs_command_context_t * ctx) +{ + uint32_t table; + gs_error_t res = gs_string_to_uint32(ctx->argv[1], &table); + if (res == GS_OK) { + if (table <= 7) { + uint32_t lock; + res = gs_string_to_uint32(ctx->argv[2], &lock); + if (res == GS_OK) { + if(lock <= 1) { + res = gs_pp_set_table_lock(&gs_pp, table, (bool *)&lock, GS_PP_FLAG_CHECKSUM); + } else { + res = GS_ERROR_ARG; + } + } + } else { + res = GS_ERROR_ARG; + } + } + return res; +} + +static const gs_command_t gs_param_cmd_master_pp_sub[] = { + { + .name = "spi_init", + .help = "Initialize/setup SPI device", + .usage = " [big_endian]", + .handler = cmd_spi_init, + .mandatory_args = 1, + .optional_args = 1, + },{ + .name = "i2c_init", + .help = "Initialize/setup I2C device", + .usage = " [big_endian]", + .handler = cmd_i2c_init, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "checksum", + .help = "Enable/disable checksum", + .usage = "[0|1]", + .handler = cmd_checksum, + .optional_args = 1, + },{ + .name = "get_table_lock", + .help = "Get table lock (0 = unlocked, 1 = locked)", + .usage = "
", + .mandatory_args = 1, + .handler = cmd_get_table_lock, + },{ + .name = "set_table_lock", + .help = "Set table lock (0 = unlocked, 1 = locked)", + .usage = "
", + .mandatory_args = 2, + .handler = cmd_set_table_lock, + },{ + .name = "get_int8", + .help = "Get int8", + .usage = "
[count]", + .handler = cmd_get_int8, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "get_int16", + .help = "Get int16", + .usage = "
[count]", + .handler = cmd_get_int16, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "get_int32", + .help = "Get int32", + .usage = "
[count]", + .handler = cmd_get_int32, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "get_uint8", + .help = "Get uint8", + .usage = "
[count]", + .handler = cmd_get_uint8, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "get_uint16", + .help = "Get uint16", + .usage = "
[count]", + .handler = cmd_get_uint16, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "get_uint32", + .help = "Get uint32", + .usage = "
[count]", + .handler = cmd_get_uint32, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "get_float", + .help = "Get float", + .usage = "
[count]", + .handler = cmd_get_float, + .mandatory_args = 2, + .optional_args = 1, + },{ + .name = "set_int8", + .help = "Set int8", + .usage = "
[data] ...", + .handler = cmd_set_int8, + .mandatory_args = 3, + .optional_args = 20, + },{ + .name = "set_int16", + .help = "Set int16", + .usage = "
[data] ...", + .handler = cmd_set_int16, + .mandatory_args = 3, + .optional_args = 20, + },{ + .name = "set_int32", + .help = "Set int32", + .usage = "
[data] ...", + .handler = cmd_set_int32, + .mandatory_args = 3, + .optional_args = 20, + },{ + .name = "set_uint8", + .help = "Set uint8", + .usage = "
[data] ...", + .handler = cmd_set_uint8, + .mandatory_args = 3, + .optional_args = 20, + },{ + .name = "set_uint16", + .help = "Set uint16", + .usage = "
[data] ...", + .handler = cmd_set_uint16, + .mandatory_args = 3, + .optional_args = 20, + },{ + .name = "set_uint32", + .help = "Set uint32", + .usage = "
[data] ...", + .handler = cmd_set_uint32, + .mandatory_args = 3, + .optional_args = 20, + },{ + .name = "set_float", + .help = "Set float", + .usage = "
[data] ...", + .handler = cmd_set_float, + .mandatory_args = 3, + .optional_args = 20, + } +}; + +static const gs_command_t GS_COMMAND_ROOT gs_param_cmd_master_pp[] = { + { + .name = "pp", + .help = "Param Protocol interface", + .chain = GS_COMMAND_INIT_CHAIN(gs_param_cmd_master_pp_sub), + } +}; + +gs_error_t gs_pp_register_commands(void) +{ + return GS_COMMAND_REGISTER(gs_param_cmd_master_pp); +} diff --git a/gomspace/libparam_client/src/pp/i2c/i2c.c b/gomspace/libparam_client/src/pp/i2c/i2c.c new file mode 100644 index 00000000..912bbeb5 --- /dev/null +++ b/gomspace/libparam_client/src/pp/i2c/i2c.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include +#include +#include +#include +#include + +static gs_error_t gs_pp_i2c_write(gs_param_i2c_command_t cmd, gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, bool checksum) +{ + GS_CHECK_RANGE(table_id <= 7); + GS_CHECK_RANGE(addr <= 255); + GS_CHECK_RANGE((value_size > 0) && (value_size <= 31)); + + gs_param_i2c_set_request_t * request; + const size_t size = (sizeof(*request) + value_size + (checksum ? 1 : 0)); + request = alloca(size); + + request->header.domain_command = GS_I2C_SLAVE_DISPATCH_HEADER(GS_I2C_SLAVE_DISPATCH_DOMAIN_PARAM, cmd); + request->length_table = GS_PARAM_I2C_LENGTH_TABLE(value_size, table_id); + request->addr = addr; + memcpy(request->data, value, value_size); + + if (checksum) { + request->data[value_size] = gs_pp_checksum8(request, size - 1); + } + return gs_i2c_master_transaction(pp->pp.i2c.bus, pp->pp.i2c.addr, request, size, NULL, 0, 1000); + +} + +static gs_error_t gs_pp_i2c_read(gs_param_i2c_command_t cmd, gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, bool checksum) +{ + GS_CHECK_RANGE(table_id <= 7); + GS_CHECK_RANGE(addr <= 255); + GS_CHECK_RANGE((value_size > 0) && (value_size <= 31)); + + gs_param_i2c_get_request_t request; + memset(&request, 0, sizeof(request)); + request.length_table = GS_PARAM_I2C_LENGTH_TABLE(value_size, table_id); + request.addr = addr; + size_t request_size; + + uint8_t reply[value_size + sizeof(request.checksum)]; // + for checksum + memset(reply, 0, sizeof(reply)); + size_t reply_size; + + request.header.domain_command = GS_I2C_SLAVE_DISPATCH_HEADER(GS_I2C_SLAVE_DISPATCH_DOMAIN_PARAM, cmd); + + if (checksum) { + request.checksum = gs_pp_checksum8(&request, (sizeof(request) - sizeof(request.checksum))); + request_size = sizeof(request); + reply_size = sizeof(reply); + } else { + request_size = sizeof(request) - sizeof(request.checksum); + reply_size = sizeof(reply) - sizeof(request.checksum); + } + + gs_error_t error = gs_i2c_master_transaction(pp->pp.i2c.bus, pp->pp.i2c.addr, &request, request_size, reply, reply_size, 1000); + if (error == GS_OK) { + if (checksum) { + if (gs_pp_checksum8(reply, value_size) != reply[value_size]) { + return GS_ERROR_DATA; + } + } + memcpy(value, reply, value_size); + } + return error; + +} + +static gs_error_t gs_pp_i2c_get(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags) +{ + gs_param_i2c_command_t cmd; + bool checksum = false; + if (flags & GS_PP_FLAG_CHECKSUM) { + checksum = true; + cmd = GS_PARAM_I2C_COMMAND_GET_WITH_CHECKSUM; + } else { + cmd = GS_PARAM_I2C_COMMAND_GET; + } + return gs_pp_i2c_read(cmd, pp, table_id, addr, value, value_size, checksum); +} + +static gs_error_t gs_pp_i2c_set(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags) +{ + gs_param_i2c_command_t cmd; + bool checksum = false; + if (flags & GS_PP_FLAG_CHECKSUM) { + checksum = true; + cmd = GS_PARAM_I2C_COMMAND_SET_WITH_CHECKSUM; + } else { + cmd = GS_PARAM_I2C_COMMAND_SET; + } + return gs_pp_i2c_write(cmd, pp, table_id, addr, (void *)value, value_size, checksum); +} + +static gs_error_t gs_pp_i2c_set_table_lock(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags) +{ + gs_param_i2c_command_t cmd = GS_PARAM_I2C_COMMAND_SET_LOCK_WITH_CHECKSUM; + bool checksum = true; + return gs_pp_i2c_write(cmd, pp, table_id, 0, (void *)value, 1, checksum); +} + +static gs_error_t gs_pp_i2c_get_table_lock(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags) +{ + gs_param_i2c_command_t cmd = GS_PARAM_I2C_COMMAND_GET_LOCK_WITH_CHECKSUM; + bool checksum = true; + return gs_pp_i2c_read(cmd, pp, table_id, 0, value, 1, checksum); +} + +gs_error_t gs_pp_i2c_init(gs_pp_t * pp, uint8_t bus, uint8_t addr, bool big_endian) +{ + GS_CHECK_HANDLE(pp != NULL); + + memset(pp, 0, sizeof(*pp)); + + pp->get = gs_pp_i2c_get; + pp->set = gs_pp_i2c_set; + pp->set_table_lock = gs_pp_i2c_set_table_lock; + pp->get_table_lock = gs_pp_i2c_get_table_lock; + pp->big_endian = big_endian; + + pp->pp.i2c.bus = bus; + pp->pp.i2c.addr = addr; + + return GS_OK; +} diff --git a/gomspace/libparam_client/src/pp/pp.c b/gomspace/libparam_client/src/pp/pp.c new file mode 100644 index 00000000..e9a20cfd --- /dev/null +++ b/gomspace/libparam_client/src/pp/pp.c @@ -0,0 +1,189 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include + +static inline bool gs_pp_endian_convert(gs_pp_t * pp) +{ + return (pp && (pp->big_endian != gs_endian_big())); +} + +static gs_error_t gs_pp_get(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags) +{ + if (pp == NULL) { + return GS_ERROR_HANDLE; + } + if (pp->get == NULL) { + return GS_ERROR_NOT_IMPLEMENTED; + } + return pp->get(pp, table_id, addr, value, value_size, flags); +} + +static gs_error_t gs_pp_set(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags) +{ + if (pp == NULL) { + return GS_ERROR_HANDLE; + } + if (pp->set == NULL) { + return GS_ERROR_NOT_IMPLEMENTED; + } + return pp->set(pp, table_id, addr, value, value_size, flags); +} + +uint8_t gs_pp_checksum8(const void * data_in, size_t length) +{ + const uint8_t * data = data_in; + unsigned int checksum = 0; + for (unsigned int i = 0; i < length; ++i) { + checksum += *data++; + } + checksum &= 0xff; + return checksum ? checksum : 1; +} + +gs_error_t gs_pp_get_table_lock(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_HANDLE(pp != NULL); + if (pp->get_table_lock == NULL) { + return GS_ERROR_NOT_IMPLEMENTED; + } + return pp->get_table_lock(pp, table_id, value, flags); +} + +gs_error_t gs_pp_set_table_lock(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_HANDLE(pp != NULL); + if (pp->set_table_lock == NULL) { + return GS_ERROR_NOT_IMPLEMENTED; + } + return pp->set_table_lock(pp, table_id, value, flags); +} + +// int8_t +gs_error_t gs_pp_get_int8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int8_t * value, size_t count, uint32_t flags) +{ + return gs_pp_get_uint8(pp, table_id, addr, (uint8_t *) value, count, flags); +} + +gs_error_t gs_pp_set_int8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int8_t * value, size_t count, uint32_t flags) +{ + return gs_pp_set_uint8(pp, table_id, addr, (const uint8_t *) value, count, flags); +} + +// uint8_t + +gs_error_t gs_pp_get_uint8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint8_t * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + return gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags); +} + +gs_error_t gs_pp_set_uint8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint8_t * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags); +} + +// int16_t + +gs_error_t gs_pp_get_int16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int16_t * value, size_t count, uint32_t flags) +{ + return gs_pp_get_uint16(pp, table_id, addr, (uint16_t *) value, count, flags); +} + +gs_error_t gs_pp_set_int16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int16_t * value, size_t count, uint32_t flags) +{ + return gs_pp_set_uint16(pp, table_id, addr, (const uint16_t *) value, count, flags); +} + +// uint16_t + +gs_error_t gs_pp_get_uint16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint16_t * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + gs_error_t error = gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags); + if (gs_pp_endian_convert(pp)) { + gs_bswap_16_array(value, value, count); + } + return error; +} + +gs_error_t gs_pp_set_uint16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint16_t * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + uint16_t _converted[count]; + if (gs_pp_endian_convert(pp)) { + gs_bswap_16_array(value, _converted, count); + value = _converted; + } + return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags); +} + +// int32_t + +gs_error_t gs_pp_get_int32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int32_t * value, size_t count, uint32_t flags) +{ + return gs_pp_get_uint32(pp, table_id, addr, (uint32_t *) value, count, flags); +} + +gs_error_t gs_pp_set_int32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int32_t * value, size_t count, uint32_t flags) +{ + return gs_pp_set_uint32(pp, table_id, addr, (const uint32_t *) value, count, flags); +} + +// uint32_t + +gs_error_t gs_pp_get_uint32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint32_t * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + gs_error_t error = gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags); + if (gs_pp_endian_convert(pp)) { + gs_bswap_32_array(value, value, count); + } + return error; +} + +gs_error_t gs_pp_set_uint32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint32_t * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + uint32_t _converted[count]; + if (gs_pp_endian_convert(pp)) { + gs_bswap_32_array(value, _converted, count); + value = _converted; + } + return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags); +} + +gs_error_t gs_pp_get_float(gs_pp_t * pp, uint8_t table_id, uint8_t addr, float * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + gs_error_t error = gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags); + if (gs_pp_endian_convert(pp)) { + gs_bswap_float_array(value, value, count); + } + return error; +} + +gs_error_t gs_pp_set_float(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const float * value, size_t count, uint32_t flags) +{ + GS_CHECK_ARG(value != NULL); + GS_CHECK_ARG(count > 0); + float _converted[count]; + if (gs_pp_endian_convert(pp)) { + gs_bswap_float_array(value, _converted, count); + value = _converted; + } + return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags); +} diff --git a/gomspace/libparam_client/src/pp/spi/spi.c b/gomspace/libparam_client/src/pp/spi/spi.c new file mode 100644 index 00000000..7e594e9f --- /dev/null +++ b/gomspace/libparam_client/src/pp/spi/spi.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include +#include +#include +#include +#include + +static gs_error_t gs_pp_spi_get(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags) +{ + GS_CHECK_RANGE(table_id <= 7); + GS_CHECK_RANGE(addr <= 255); + + if (flags & GS_PP_FLAG_CHECKSUM) { + gs_param_spi_get_with_checksum_t * request; + const size_t size = (sizeof(*request) + value_size + 1); // +1 for CHECKSUM in returned data + request = alloca(size); + memset(request, 0, size); + request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_GET_WITH_CHECKSUM); + request->length_table = GS_PARAM_SPI_LENGTH_TABLE(value_size, table_id); + request->addr = addr; + request->checksum = gs_pp_checksum8(request, (sizeof(*request) - sizeof(request->filler))); + + gs_error_t error = gs_spi_master_transaction(pp->pp.spi.slave, request, request, size, 1000); + if (error == GS_OK) { + if (gs_pp_checksum8(request->data, value_size) != request->data[value_size]) { + return GS_ERROR_DATA; + } + memcpy(value, request->data, value_size); + } + return error; + + } else { + gs_param_spi_get_t * request; + const size_t size = (sizeof(*request) + value_size); + request = alloca(size); + memset(request, 0, size); + request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_GET); + request->length_table = GS_PARAM_SPI_LENGTH_TABLE(value_size, table_id); + request->addr = addr; + + gs_error_t error = gs_spi_master_transaction(pp->pp.spi.slave, request, request, size, 1000); + if (error == GS_OK) { + memcpy(value, request->data, value_size); + } + return error; + } +} + +static gs_error_t gs_pp_spi_set(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags) +{ + GS_CHECK_RANGE(table_id <= 7); + GS_CHECK_RANGE(addr <= 255); + + gs_param_spi_set_t * request; + const size_t size = (sizeof(*request) + value_size + ((flags & GS_PP_FLAG_CHECKSUM) ? 1 : 0)); + request = alloca(size); + if (flags & GS_PP_FLAG_CHECKSUM) { + request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_SET_WITH_CHECKSUM); + } else { + request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_SET); + } + request->length_table = GS_PARAM_SPI_LENGTH_TABLE(value_size, table_id); + request->addr = addr; + memcpy(request->data, value, value_size); + + if (flags & GS_PP_FLAG_CHECKSUM) { + request->data[value_size] = gs_pp_checksum8(request, size - 1); + } + + return gs_spi_master_transaction(pp->pp.spi.slave, request, NULL, size, 1000); +} + +gs_error_t gs_pp_spi_init(gs_pp_t * pp, uint8_t slave, bool big_endian) +{ + GS_CHECK_HANDLE(pp != NULL); + + memset(pp, 0, sizeof(*pp)); + + pp->get = gs_pp_spi_get; + pp->set = gs_pp_spi_set; + pp->set_table_lock = NULL; // Not implemented + pp->get_table_lock = NULL; // Not implemented + pp->big_endian = big_endian; + + pp->pp.spi.slave = slave; + + return GS_OK; +} diff --git a/gomspace/libparam_client/src/rparam/cmd/cmd_rparam.c b/gomspace/libparam_client/src/rparam/cmd/cmd_rparam.c new file mode 100644 index 00000000..de1bd6d8 --- /dev/null +++ b/gomspace/libparam_client/src/rparam/cmd/cmd_rparam.c @@ -0,0 +1,354 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include +#include +#include +#include +#include "../query.h" +#include +#include +#include +#include +#include +#include + +#define MAX_FILENAME 100 + +/** Remote param system setup */ +static gs_param_table_instance_t rparam_tinst; +static gs_rparam_query_handle_t rquery = {.timeout_ms = 10000}; +static char * rparam_wd; + +#define CHECK_TABLE() \ + if (rparam_tinst.rows == NULL) { \ + fprintf(ctx->out, "Run download or init to setup table\n"); \ + return GS_ERROR_NOT_FOUND; \ + } + +static int cmd_rparam_list(gs_command_context_t *ctx) +{ + CHECK_TABLE(); + return gs_param_list_to_stream(&rparam_tinst, true, 0, ctx->out); +} + +static void make_filename(char * fname, size_t fname_size) +{ + char cwd[MAX_FILENAME + 1]; + const char * wd; + if (gs_string_empty(rparam_wd) == false) { + wd = rparam_wd; + } else if (gs_getcwd(cwd, sizeof(cwd)) == GS_OK) { + wd = cwd; + } else { + wd = NULL; + } + if (gs_string_empty(wd) == false) { + snprintf(fname, fname_size, "%s/param-%d-%u.bin", wd, rquery.node, rquery.table_id); + } else { + fname[0] = 0; + } +} + +static int cmd_rparam_init_from_local_file(gs_command_context_t *ctx) +{ + if (gs_string_to_uint8(ctx->argv[1], &rquery.node)) { + return GS_ERROR_ARG; + } + if (ctx->argc > 2) { + if (gs_string_to_uint8(ctx->argv[2], &rquery.table_id)) { + return GS_ERROR_ARG; + } + } + + char fname[100]; + make_filename(fname, sizeof(fname)); + return gs_rparam_load_table_spec(&rparam_tinst, fname, &rquery.checksum); +} + +static int cmd_rparam_init_from_remote_node(gs_command_context_t *ctx) +{ + if (gs_string_to_uint8(ctx->argv[1], &rquery.node)) { + return GS_ERROR_ARG; + } + if (ctx->argc > 2) { + if (gs_string_to_uint8(ctx->argv[2], &rquery.table_id)) { + return GS_ERROR_ARG; + } + } + + char fname[100]; + make_filename(fname, sizeof(fname)); + return gs_rparam_download_table_spec(&rparam_tinst, fname, rquery.node, rquery.table_id, rquery.timeout_ms, &rquery.checksum); +} + +static int cmd_rparam_send(gs_command_context_t *ctx) +{ + CHECK_TABLE(); + + gs_error_t error = gs_rparam_query_send(&rquery, &rparam_tinst); + if (error == GS_OK) { + if (rquery.action == RPARAM_GET) { + const gs_param_table_row_t * last_print = NULL; + for (unsigned int i = 0; i < rquery.length / 2; ++i) { + const gs_param_table_row_t * row = gs_param_row_by_address(rquery.payload.addr[i], rparam_tinst.rows, rparam_tinst.row_count); + if (row != last_print) { // work-around to avoid duplicate lines for elements within same array + gs_param_list_single_to_stream(&rparam_tinst, row, true, 0, ctx->out); + last_print = row; + } + } + } + gs_rparam_query_reset(&rquery); + } + return error; +} + +static int cmd_rparam_get(gs_command_context_t *ctx) +{ + CHECK_TABLE(); + + gs_rparam_query_set_quiet(&rquery, false); + gs_error_t error = gs_rparam_query_get(&rquery, &rparam_tinst, ctx->argv[1]); + if ((error == GS_OK) && rquery.auto_send) { + error = cmd_rparam_send(ctx); + } + + return error; +} + +static int cmd_rparam_getall(gs_command_context_t *ctx) +{ + CHECK_TABLE(); + + fprintf(ctx->out, "Downloading table content for table %i from server %u\n", rquery.table_id, rquery.node); + gs_error_t error = gs_rparam_get_full_table(&rparam_tinst, rquery.node, rquery.table_id, rquery.checksum, rquery.timeout_ms); + if (error == GS_OK) { + gs_param_list_to_stream(&rparam_tinst, true, 0, ctx->out); + } + return error; +} + +static int cmd_rparam_set(gs_command_context_t *ctx) +{ + CHECK_TABLE(); + + gs_error_t error = gs_rparam_query_set(&rquery, &rparam_tinst, ctx->argv[1], &ctx->argv[2], ctx->argc - 2); + if ((error == GS_OK) && rquery.auto_send) { + error = cmd_rparam_send(ctx); + } + + return error; +} + +static int cmd_rparam_copy(gs_command_context_t *ctx) +{ + uint8_t from; + if (gs_string_to_uint8(ctx->argv[1], &from)) { + return GS_ERROR_ARG; + } + + uint8_t to; + if (gs_string_to_uint8(ctx->argv[2], &to)) { + return GS_ERROR_ARG; + } + + return gs_rparam_copy(rquery.node, rquery.timeout_ms, from, to); +} + +static int cmd_rparam_load(gs_command_context_t *ctx) +{ + uint8_t table_id; + if (gs_string_to_uint8(ctx->argv[2], &table_id)) { + return GS_ERROR_ARG; + } + uint8_t file_id; + if (gs_string_to_uint8(ctx->argv[1], &file_id)) { + // This may be a store - no way of validating + return gs_rparam_load_from_store(rquery.node, rquery.timeout_ms, table_id, ctx->argv[1], NULL); + } + + return gs_rparam_load(rquery.node, rquery.timeout_ms, file_id, table_id); +} + +static int cmd_rparam_save(gs_command_context_t *ctx) +{ + uint8_t table_id; + if (gs_string_to_uint8(ctx->argv[1], &table_id)) { + return GS_ERROR_ARG; + } + uint8_t file_id; + if (gs_string_to_uint8(ctx->argv[2], &file_id)) { + // This may be a store - no way of validating + return gs_rparam_save_to_store(rquery.node, rquery.timeout_ms, table_id, ctx->argv[2], NULL); + } + + return gs_rparam_save(rquery.node, rquery.timeout_ms, table_id, file_id); +} + +static int cmd_rparam_reset(gs_command_context_t *ctx) +{ + gs_rparam_query_reset(&rquery); + return GS_OK; +} + +static int cmd_rparam_set_autosend(gs_command_context_t *ctx) +{ + if (ctx->argc > 1) { + if (gs_string_to_bool(ctx->argv[1], &rquery.auto_send)) { + return GS_ERROR_ARG; + } + } + fprintf(ctx->out, "auto send: %d\r\n", rquery.auto_send); + return GS_OK; +} + +static int cmd_set_wd(gs_command_context_t *ctx) +{ + if (ctx->argc > 1) { + if (rparam_wd == NULL) { + rparam_wd = malloc(MAX_FILENAME + 1); + if (rparam_wd == NULL) { + return GS_ERROR_ALLOC; + } + } + strcpy(rparam_wd, ctx->argv[1]); + } + fprintf(ctx->out, "working directory: %s\r\n", rparam_wd ? rparam_wd : "not set"); + return GS_OK; +} + +static int cmd_rparam_set_timeout(gs_command_context_t *ctx) +{ + if (ctx->argc > 1) { + if (gs_string_to_uint32(ctx->argv[1], &rquery.timeout_ms)) { + return GS_ERROR_ARG; + } + } + fprintf(ctx->out, "timeout: %"PRIu32" mS\r\n", rquery.timeout_ms); + return GS_OK; +} + +static int cmd_rparam_set_checksum(gs_command_context_t *ctx) +{ + if (ctx->argc > 1) { + if (strcasecmp(ctx->argv[1], "magic") == 0) { + rquery.checksum = GS_RPARAM_MAGIC_CHECKSUM; + } else if (gs_string_to_uint16(ctx->argv[1], &rquery.checksum)) { + return GS_ERROR_ARG; + } + } + fprintf(ctx->out, "checksum: 0x%04x (magic: 0x%04x)\r\n", rquery.checksum, GS_RPARAM_MAGIC_CHECKSUM); + return GS_OK; +} + +static const gs_command_t rparam_commands[] = { + { + .name = "init", + .help = "Set server and load table spec. from file", + .usage = " [table-id]", + .handler = cmd_rparam_init_from_local_file, + .mandatory_args = 1, + .optional_args = 1, + },{ + .name = "download", + .help = "Set server and download table spec.", + .usage = " [table-id]", + .handler = cmd_rparam_init_from_remote_node, + .mandatory_args = 1, + .optional_args = 1, + },{ + .name = "getall", + .help = "Download full table contents from server", + .handler = cmd_rparam_getall, + .mandatory_args = GS_COMMAND_NO_ARGS, + },{ + .name = "list", + .help = "Lists the table specification", + .handler = cmd_rparam_list, + .mandatory_args = GS_COMMAND_NO_ARGS, + },{ + .name = "get", + .help = "Add a 'get' to the current query transaction", + .usage = "", + .handler = cmd_rparam_get, + .mandatory_args = 1, + },{ + .name = "set", + .help = "Add a 'set' to the current query transaction", + .usage = " [value] ...", + .handler = cmd_rparam_set, + .mandatory_args = 2, + .optional_args = 100, + },{ + .name = "copy", + .usage = " ", + .help = "Copy table to table (version <= 3 only)", + .handler = cmd_rparam_copy, + .mandatory_args = 2, + },{ + .name = "load", + .usage = " ", + .help = "Load table", + .handler = cmd_rparam_load, + .mandatory_args = 2, + },{ + .name = "save", + .usage = " ", + .help = "Save table", + .handler = cmd_rparam_save, + .mandatory_args = 2, + /* },{ */ + /* .name = "print", */ + /* .help = "Print the current query", */ + /* .handler = cmd_rparam_print, */ + /* .mandatory_args = GS_COMMAND_NO_ARGS, */ + },{ + .name = "reset", + .help = "Reset the current query", + .handler = cmd_rparam_reset, + .mandatory_args = GS_COMMAND_NO_ARGS, + },{ + .name = "send", + .help = "Send the current query", + .handler = cmd_rparam_send, + .mandatory_args = GS_COMMAND_NO_ARGS, + },{ + .name = "wd", + .help = "Set working directory for init/download", + .usage = "[path]", + .handler = cmd_set_wd, + .optional_args = 1, + },{ + .name = "timeout", + .help = "Set timeout", + .usage = "[timeout mS]", + .handler = cmd_rparam_set_timeout, + .optional_args = 1, + },{ + .name = "checksum", + .help = "Set checksum", + .usage = "[magic|]", + .handler = cmd_rparam_set_checksum, + .optional_args = 1, + },{ + .name = "autosend", + .usage = "[bool]", + .help = "Enable/disable autosend for set and get queries", + .handler = cmd_rparam_set_autosend, + .optional_args = 1, + } +}; + +static const gs_command_t GS_COMMAND_ROOT rparam_root_command[] = { + { + .name = "rparam", + .help = "Remote access to Parameter System", + .chain = GS_COMMAND_INIT_CHAIN(rparam_commands), + }, +}; + +gs_error_t gs_rparam_register_commands(void) +{ + return GS_COMMAND_REGISTER(rparam_root_command); +} diff --git a/gomspace/libparam_client/src/rparam/deprecated_rparam.c b/gomspace/libparam_client/src/rparam/deprecated_rparam.c new file mode 100644 index 00000000..418f468c --- /dev/null +++ b/gomspace/libparam_client/src/rparam/deprecated_rparam.c @@ -0,0 +1,6 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +// Old deprecated API uses common data - making it not thread-safe. +//gs_rparam_handle_t rparam_handle; diff --git a/gomspace/libparam_client/src/rparam/query.c b/gomspace/libparam_client/src/rparam/query.c new file mode 100644 index 00000000..79144e33 --- /dev/null +++ b/gomspace/libparam_client/src/rparam/query.c @@ -0,0 +1,212 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include "query.h" +#include +#include +#include +#include "../serialize_local.h" +#include +#include + +bool gs_rparam_query_is_set(gs_rparam_query_handle_t * handle) +{ + return (handle->length > 0); +} + +void gs_rparam_query_reset(gs_rparam_query_handle_t * handle) +{ + handle->action = RPARAM_GET; + handle->length = 0; + handle->get_size = 0; +} + +void gs_rparam_query_set_quiet(gs_rparam_query_handle_t * handle, bool quiet) +{ + handle->quiet = quiet; +} + +gs_error_t gs_rparam_query_get(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name) +{ + if (tinst->rows == NULL) + return GS_ERROR_NOT_FOUND; + + /* Ensure correct header */ + if (handle->action != RPARAM_GET) { + gs_rparam_query_reset(handle); + handle->action = RPARAM_GET; + } + + char shortname[GS_PARAM_MAX_NAME + 20]; + uint8_t array_index = 0; + bool is_array; + if (gs_param_parse_name_and_array_index(param_name, shortname, sizeof(shortname), &array_index, &is_array)) { + return GS_ERROR_ARG; + } + + const gs_param_table_row_t * param = gs_param_row_by_name(shortname, tinst->rows, tinst->row_count); + if (param == NULL) { + return GS_ERROR_NOT_FOUND; + } + + if (array_index >= GS_PARAM_ARRAY_SIZE(param)) { + return GS_ERROR_RANGE; + } + + unsigned int start; + unsigned int end; + if (is_array) { + start = array_index; + end = start + 1; + } else { + start = 0; + end = GS_PARAM_ARRAY_SIZE(param); + } + for (unsigned int i = start; i < end; ++i) { + + /* Size check */ + if (handle->get_size + param->size + sizeof(uint16_t) > GS_RPARAM_QUERY_MAX_PAYLOAD) { + return GS_ERROR_OVERFLOW; + } + + /* Add to query */ + handle->payload.addr[handle->length/2] = param->addr + (param->size * i); + handle->length += sizeof(uint16_t); + handle->get_size += param->size + sizeof(uint16_t); + } + + return GS_OK; +} + +gs_error_t gs_rparam_query_set(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name, char * values[], uint8_t value_count) +{ + /* Ensure correct header */ + if (handle->action != RPARAM_SET) { + gs_rparam_query_reset(handle); + handle->action = RPARAM_SET; + } + + char shortname[GS_PARAM_MAX_NAME + 20]; + uint8_t array_index = 0; + if (gs_param_parse_name_and_array_index(param_name, shortname, sizeof(shortname), &array_index, NULL)) { + return GS_ERROR_ARG; + } + + const gs_param_table_row_t * param = gs_param_row_by_name(shortname, tinst->rows, tinst->row_count); + if (param == NULL) { + return GS_ERROR_NOT_FOUND; + } + + if (array_index >= GS_PARAM_ARRAY_SIZE(param)) { + return GS_ERROR_RANGE; + } + + for (unsigned int i = 0; i < value_count; i++) { + + /* Parse input */ + uint8_t value[param->size]; + gs_error_t error = gs_param_from_string(param, values[i], value); + if (error) { + return error; + } + + unsigned int bytes = 0; + error = gs_param_serialize_item(param, param->addr + (param->size * (array_index + i)), value, F_TO_BIG_ENDIAN, + &handle->payload.packed[handle->length], GS_RPARAM_QUERY_MAX_PAYLOAD - handle->length, &bytes); + if (error) { + return error; + } + handle->length += bytes; + } + + return GS_OK; +} + +gs_error_t gs_rparam_query_send(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst) +{ + if (tinst->rows == NULL) { + return GS_ERROR_NOT_FOUND; + } + + if (handle->length == 0) { + return GS_ERROR_NO_DATA; + } + + gs_rparam_query_t * query; + + /* Allocate outgoing buffer */ + csp_packet_t * packet = csp_buffer_get(RPARAM_QUERY_LENGTH(query, GS_RPARAM_QUERY_MAX_PAYLOAD)); + if (packet == NULL) { + return GS_ERROR_NO_BUFFERS; + } + + csp_conn_t * conn = csp_connect(CSP_PRIO_HIGH, handle->node, GS_CSP_PORT_RPARAM, handle->timeout_ms, CSP_O_CRC32); + if (conn == NULL) { + csp_buffer_free(packet); + return GS_ERROR_IO; + } + + query = (gs_rparam_query_t *) packet->data; + + query->action = handle->action; + query->table_id = handle->table_id; + query->seq = 0; + query->total = 0; + query->length = csp_hton16(handle->length); + query->checksum = csp_hton16(handle->checksum); + + /* Copy payload to message */ + packet->length = RPARAM_QUERY_LENGTH(query, handle->length); + memcpy(&query->payload, &handle->payload, handle->length); + + /* Deal with endianness */ + if (handle->action == RPARAM_GET) { + for (unsigned int i = 0; i < (handle->length/2); i++) { + query->payload.addr[i] = csp_hton16(query->payload.addr[i]); + } + } + + /* Send packet */ + if (!csp_send(conn, packet, 0)) { + csp_buffer_free(packet); + csp_close(conn); + return GS_ERROR_IO; + } + + /* Read reply */ + packet = csp_read(conn, handle->timeout_ms); + if (packet == NULL) { + csp_close(conn); + return GS_ERROR_TIMEOUT; + } + + if (packet->length <= 1) { + gs_error_t error = GS_OK; + if (packet->length == 1) { + if (packet->data[0] == RPARAM_ERROR) { + error = GS_ERROR_DATA; + } + } else { + error = GS_ERROR_NO_DATA; + } + csp_buffer_free(packet); + csp_close(conn); + return error; + } + + /* We have a reply */ + gs_rparam_query_t * reply = (gs_rparam_query_t *) packet->data; + reply->length = csp_ntoh16(reply->length); + + gs_error_t error; + if (reply->action == RPARAM_REPLY) { + error = gs_param_deserialize(tinst, reply->payload.packed, reply->length, F_FROM_BIG_ENDIAN); + } else { + error = GS_ERROR_TYPE; + } + + csp_buffer_free(packet); + csp_close(conn); + return error; +} diff --git a/gomspace/libparam_client/src/rparam/query.h b/gomspace/libparam_client/src/rparam/query.h new file mode 100644 index 00000000..507a870a --- /dev/null +++ b/gomspace/libparam_client/src/rparam/query.h @@ -0,0 +1,110 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + + +#include + +/** + Rparam query handle. +*/ +typedef struct { + /** + CSP node. + */ + uint8_t node; + /** + Remote table id. + */ + gs_param_table_id_t table_id; + /** + Remote table (definition) checksum. + */ + uint16_t checksum; + /** + Timeout (mS). + */ + uint32_t timeout_ms; + /** + If quite - no output to stdout. + */ + bool quiet; + /** + Auto send. + */ + bool auto_send; + /** + Type/action. + */ + int action; + /** + Size of current query. + */ + unsigned int length; + /** + Estimated total 'get' size. + */ + unsigned int get_size; + /** + Space for payload data. + @note must be last in struct. + */ + union { + uint16_t addr[0]; + uint8_t packed[GS_RPARAM_QUERY_MAX_PAYLOAD]; + } payload; +} gs_rparam_query_handle_t; + +/** + Set whether rparam API should print to stdout or not. + + @param[in] handle handle + @param[in] quiet \a true print to stdout. +*/ +void gs_rparam_query_set_quiet(gs_rparam_query_handle_t * handle, bool quiet); + +/** + Return true if any query has been set. + + @param[in] handle handle + @return \a true if any query has been set. +*/ +bool gs_rparam_query_is_set(gs_rparam_query_handle_t * handle); + +/** + Add a 'get' query to the current query, after a succesfull rparam_query_send() + the parameter value can be read using rparam_queury_get_value() + + @param[in] handle handle + @param[in] tinst table. + @param[in] param_name name of the parameter. + @return_gs_error_t + @see rparam_query_send() +*/ +gs_error_t gs_rparam_query_get(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name); + +/** + Add a 'set' query to the current query. Use rparam_query_send() to execute the set query. + + @param[in] handle handle + @param[in] tinst table. + @param[in] param_name name of the parameter to set + @param[in] values array of values to set, multiple values can be set for array type parameters + @param[in] value_count number of elements in \a values + @return_gs_error_t + @see rparam_query_send() +*/ +gs_error_t gs_rparam_query_set(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name, char * values[], uint8_t value_count); + +/** + Send the current query + + @param[in] handle handle + @param[in] tinst table. + @return_gs_error_t +*/ +gs_error_t gs_rparam_query_send(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst); + +/** + Reset/clear the current query + @param[in] handle handle +*/ +void gs_rparam_query_reset(gs_rparam_query_handle_t * handle); diff --git a/gomspace/libparam_client/src/rparam/rparam.c b/gomspace/libparam_client/src/rparam/rparam.c new file mode 100644 index 00000000..d8ba79bd --- /dev/null +++ b/gomspace/libparam_client/src/rparam/rparam.c @@ -0,0 +1,474 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include +#include +#include +#include "../serialize_local.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static gs_error_t gs_rparam_command(uint8_t node, uint32_t timeout_ms, uint8_t action, uint8_t table_id, + const void * payload, size_t payload_size, char reply_ok) +{ + gs_rparam_query_t * query = alloca(RPARAM_QUERY_LENGTH(query, payload_size)); + query->action = action; + query->table_id = table_id; + query->length = csp_hton16(payload_size); + query->checksum = csp_hton16(GS_RPARAM_MAGIC_CHECKSUM); // Ignore checksum + query->seq = 0; + query->total = 0; + memcpy(&query->payload, payload, payload_size); + + /* Run single packet transaction */ + char reply = 0; + if (csp_transaction2(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, query, RPARAM_QUERY_LENGTH(query, payload_size), + &reply, 1, CSP_O_CRC32) <= 0) { + return GS_ERROR_IO; + } + + return (reply == reply_ok) ? GS_OK : GS_ERROR_UNKNOWN; +} + +gs_error_t gs_rparam_copy(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to) +{ + gs_rparam_query_payload_t payload; + payload.copy.from = from; + payload.copy.to = to; + return gs_rparam_command(node, timeout_ms, RPARAM_COPY, from, &payload, sizeof(payload.copy), RPARAM_COPY_OK); +} + +static gs_error_t gs_rparam_store(uint8_t node, uint32_t timeout_ms, uint8_t action, char reply_ok, uint8_t table_id, + const char * table, const char * store, const char * slot) +{ + gs_rparam_query_payload_store_t payload; + memset(&payload, 0, sizeof(payload)); + if (table && (gs_string_empty(table) == false)) { + GS_STRNCPY(payload.table, table); + } + if (store && (gs_string_empty(store) == false)) { + GS_STRNCPY(payload.store, store); + } + if (slot && (gs_string_empty(slot) == false)) { + GS_STRNCPY(payload.slot, slot); + } + return gs_rparam_command(node, timeout_ms, action, table_id, &payload, sizeof(payload), reply_ok); +} + +gs_error_t gs_rparam_save_to_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id, + const char * store, const char * slot) +{ + return gs_rparam_store(node, timeout_ms, RPARAM_SAVE_TO_STORE, RPARAM_SAVE_OK, table_id, NULL, store, slot); +} + +gs_error_t gs_rparam_load_from_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id, + const char * store, const char * slot) +{ + return gs_rparam_store(node, timeout_ms, RPARAM_LOAD_FROM_STORE, RPARAM_LOAD_OK, table_id, NULL, store, slot); +} + +gs_error_t gs_rparam_save(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to) +{ + gs_rparam_query_payload_t payload; + payload.copy.from = from; + payload.copy.to = to; + return gs_rparam_command(node, timeout_ms, RPARAM_SAVE, from, &payload, sizeof(payload.copy), RPARAM_SAVE_OK); +} + +gs_error_t gs_rparam_load(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to) +{ + gs_rparam_query_payload_t payload; + payload.copy.from = from; + payload.copy.to = to; + return gs_rparam_command(node, timeout_ms, RPARAM_LOAD, to, &payload, sizeof(payload.copy), RPARAM_LOAD_OK); +} + +static gs_error_t update_table(const char * func, + gs_param_table_instance_t * tinst, + gs_param_table_row_t * rows, unsigned int row_count, + uint16_t checksum) +{ + size_t memory_size = gs_param_calc_table_size(rows, row_count); + if ((tinst->memory == NULL) || (tinst->memory_size < memory_size)) { + // (re)allocate memory + if (memory_size == 0) { + return GS_ERROR_NOT_SUPPORTED; + } + memory_size = gs_max(1000U, memory_size); + void * memory = calloc(1, memory_size); + if (memory == NULL) { + return GS_ERROR_ALLOC; + } + + free(tinst->memory); + tinst->memory = memory; + tinst->memory_size = memory_size; + tinst->flags |= GS_PARAM_TABLE_F_ALLOC_MEMORY; + } + + free((void*)tinst->rows); + tinst->rows = rows; + tinst->row_count = row_count; + tinst->checksum_be = tinst->checksum_le = 0; + tinst->flags |= GS_PARAM_TABLE_F_ALLOC_ROWS; + + if ((checksum != gs_param_table_checksum_le(tinst)) && (checksum != gs_param_table_checksum_be(tinst))) { + log_error("%s: table specification has invalid checksum: %u - different from LE: %u and BE: %u", + func, checksum, gs_param_table_checksum_le(tinst), gs_param_table_checksum_be(tinst)); + return GS_ERROR_DATA; + } + + return GS_OK; +} + +gs_error_t gs_rparam_load_table_spec(gs_param_table_instance_t * tinst, const char* fname, uint16_t * return_checksum) +{ + GS_CHECK_HANDLE(tinst != NULL); + GS_CHECK_ARG(fname != NULL); + + FILE * fd = fopen(fname, "r"); + if (fd == NULL) { + return GS_ERROR_NOT_FOUND; + } + + struct stat file_stat; + if (fstat(fileno(fd), &file_stat) != 0) { + log_error("%s: failed to stat file [%s]", __FUNCTION__, fname); + fclose(fd); + return GS_ERROR_IO; + } + + void * rows = calloc(file_stat.st_size, 1); + if (rows == NULL) { + fclose(fd); + return GS_ERROR_ALLOC; + } + + uint16_t checksum = 0; + size_t rs1 = fread(&checksum, 1, sizeof(checksum), fd); + size_t rs2 = fread(rows, 1, file_stat.st_size, fd); + fclose(fd); + + const unsigned int single_row_size = sizeof(*tinst->rows); + const unsigned int all_row_size = (file_stat.st_size - sizeof(checksum)); + const unsigned int row_count = (all_row_size) / single_row_size; + if ((rs1 != sizeof(checksum)) || (rs2 != all_row_size) || (rs2 != (single_row_size * row_count))) { + log_error("%s: incomplete/invalid read, expected %u + %u - read %u + %u, single row size: %u", __FUNCTION__, + (unsigned int) sizeof(checksum), row_count, + (unsigned int) rs1, (unsigned int) rs2, single_row_size); + free(rows); + return GS_ERROR_IO; + } + + gs_error_t error = update_table(__FUNCTION__, tinst, rows, row_count, checksum); + + if (error == GS_OK) { + if (return_checksum) { + *return_checksum = checksum; + } + } + + return error; +} + +gs_error_t gs_rparam_download_table_spec(gs_param_table_instance_t * tinst, + const char * fname, + uint8_t node, + gs_param_table_id_t table_id, + uint32_t timeout_ms, + uint16_t * return_checksum) +{ + csp_conn_t * conn = csp_connect(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, CSP_O_CRC32); + if (conn == NULL) { + return GS_ERROR_IO; + } + + /* Allocate outgoing buffer */ + gs_rparam_query_t * query; + csp_packet_t * packet = csp_buffer_get(RPARAM_QUERY_LENGTH(query, 0)); + if (packet == NULL) { + csp_close(conn); + return GS_ERROR_NO_BUFFERS; + } + + // setup request + query = (gs_rparam_query_t *) packet->data; + query->action = RPARAM_TABLE; + query->table_id = table_id; + query->length = 0; + query->checksum = csp_hton16(GS_RPARAM_MAGIC_CHECKSUM); // Ignore checksum + query->seq = 0; + query->total = 0; + + packet->length = RPARAM_QUERY_LENGTH(query, 0); + if (!csp_send(conn, packet, 0)) { + csp_buffer_free(packet); + csp_close(conn); + return GS_ERROR_IO; + } + + /* Receive remote parameter table, in host byte order + * Note: This is necessary, because the SFP functions does not know the dataformat + * and hence cannot be converted server-side. */ + void * dataout = NULL; + int totalsize = 0; + int result = csp_sfp_recv(conn, &dataout, &totalsize, timeout_ms); + csp_close(conn); + + if (result < 0) { + free(dataout); + return GS_ERROR_IO; + } + + gs_param_table_row_t * rows = dataout; + const uint8_t row_count = totalsize / sizeof(*rows); + + /* Calculate checksum on table (before converting endians!) */ + const uint16_t checksum = gs_fletcher16(rows, totalsize); + + /* Autodetect Endians */ + int sum_first = 0; + int sum_last = 0; + for (unsigned int i = 0; i < row_count; i++) { + sum_first += (rows[i].addr & 0xFF00) >> 8; + sum_last += rows[i].addr & 0xFF; + } + + /* Correct endians */ + if (sum_first > sum_last) { + for (unsigned int i = 0; i < row_count; i++) { + rows[i].addr = (((rows[i].addr & 0xff00) >> 8) | ((rows[i].addr & 0x00ff) << 8)); + } + } + + gs_error_t error = update_table(__FUNCTION__, tinst, rows, row_count, checksum); + if (error == GS_OK) { + + if (return_checksum) { + *return_checksum = checksum; + } + + // If filename provided, store table specification to file. + if (gs_string_empty(fname) == false) { + FILE * fd = fopen(fname, "w"); + if (fd == NULL) { + log_error("%s: failed to open/create file: [%s]", __FUNCTION__, fname); + return GS_ERROR_IO; + } + const size_t ws1_size = sizeof(checksum); + const size_t ws1 = fwrite(&checksum, 1, ws1_size, fd); + const size_t ws2 = fwrite(rows, 1, totalsize, fd); + fclose(fd); + if ((ws1 != ws1_size) || (ws2 != (size_t) totalsize)) { + log_error("%s: failed to write %u + %d - wrote %u + %u", __FUNCTION__, + (unsigned int) sizeof(checksum), totalsize, (unsigned int) ws1, (unsigned int) ws2); + return GS_ERROR_IO; + } + } + } + + return error; +} + +gs_error_t gs_rparam_get_full_table(gs_param_table_instance_t * tinst, + uint8_t node, + gs_param_table_id_t table_id, + uint16_t checksum, + uint32_t timeout_ms) +{ + GS_CHECK_HANDLE(tinst != NULL); + GS_CHECK_HANDLE(tinst->rows != NULL); + GS_CHECK_HANDLE(tinst->memory != NULL); + + unsigned int expected_bytes = 0; + { + unsigned int param_pos = 0; + gs_error_t error = gs_param_serialize_full_table(tinst, ¶m_pos, GS_PARAM_SF_DRY_RUN, NULL, 10000, &expected_bytes); + if (error) { + return error; + } + } + + gs_rparam_query_t * query; + csp_packet_t * request = csp_buffer_get(RPARAM_QUERY_LENGTH(query, 0)); + if (request == NULL) { + return GS_ERROR_NO_BUFFERS; + } + + csp_conn_t * conn = csp_connect(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, CSP_O_CRC32); + if (!conn) { + csp_buffer_free(request); + return GS_ERROR_IO; + } + + query = (gs_rparam_query_t *) request->data; + query->action = RPARAM_GET; + query->table_id = table_id; + query->length = 0; // == get full table + query->checksum = csp_hton16(checksum); + query->seq = 0; + query->total = 0; + + request->length = RPARAM_QUERY_LENGTH(query, 0); + if (!csp_send(conn, request, timeout_ms)) { + csp_buffer_free(request); + csp_close(conn); + return GS_ERROR_IO; + } + + csp_packet_t * reply; + gs_error_t error = GS_OK; + unsigned int total_bytes = 0; + while ((reply = csp_read(conn, timeout_ms)) != NULL) { + + /* We have a reply */ + query = (void *) reply->data; + const uint16_t qlength = csp_ntoh16(query->length); + total_bytes += qlength; + const uint16_t seq = csp_ntoh16(query->seq); + const uint16_t total = csp_ntoh16(query->total); + + if (query->action == RPARAM_REPLY) { + error = gs_param_deserialize(tinst, query->payload.packed, qlength, F_FROM_BIG_ENDIAN); + } + csp_buffer_free(reply); + + if (error || (seq >= total)) { + break; + } + } + + if (reply == NULL) { + error = GS_ERROR_TIMEOUT; + } + + if ((error == GS_OK) && (expected_bytes != total_bytes)) { + log_warning("%s: expected %u != received %u bytes", __FUNCTION__, expected_bytes, total_bytes); + error = GS_ERROR_DATA; + } + + csp_close(conn); + + return error; +} + +gs_error_t gs_rparam_get(uint8_t node, + gs_param_table_id_t table_id, + uint16_t addr, + gs_param_type_t type, + uint16_t checksum, + uint32_t timeout_ms, + void * value, + size_t value_size) +{ + /* Calculate length */ + gs_rparam_query_t * query; + const size_t query_size = RPARAM_QUERY_LENGTH(query, sizeof(query->payload.addr[0])); + const size_t reply_payload_size = (value_size + sizeof(query->payload.addr[0])); + const size_t reply_size = RPARAM_QUERY_LENGTH(query, reply_payload_size); + + query = alloca(reply_size); + query->action = RPARAM_GET; + query->table_id = table_id; + query->checksum = csp_hton16(checksum); + query->seq = 0; + query->total = 0; + query->payload.addr[0] = csp_hton16(addr); + query->length = csp_hton16(sizeof(query->payload.addr[0])); + + /* Run single packet transaction */ + if (csp_transaction2(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, query, query_size, query, reply_size, CSP_O_CRC32) <= 0) { + return GS_ERROR_IO; + } + + /* We have a reply */ + query->length = csp_ntoh16(query->length); + + if (query->length != reply_payload_size) { + log_warning("%s: Invalid reply size %u - expected %u", __FUNCTION__, query->length, (unsigned int) reply_payload_size); + return GS_ERROR_DATA; + } + + /* Read address */ + query->payload.addr[0] = csp_betoh16(query->payload.addr[0]); + if (query->payload.addr[0] != addr) { + log_warning("%s: Invalid address in reply %u", __FUNCTION__, query->payload.addr[0]); + return GS_ERROR_DATA; + } + + /* Read value */ + memcpy(value, &query->payload.packed[2], value_size); + gs_param_betoh(type, value); + + return GS_OK; +} + +gs_error_t gs_rparam_set(uint8_t node, + gs_param_table_id_t table_id, + uint16_t addr, + gs_param_type_t type, + uint16_t checksum, + uint32_t timeout_ms, + const void * value, + size_t value_size) +{ + /* Calculate length */ + gs_rparam_query_t * query; + const size_t payload_size = (value_size + sizeof(query->payload.addr[0])); + const size_t query_size = RPARAM_QUERY_LENGTH(query, payload_size); + + query = alloca(query_size); + query->action = RPARAM_SET; + query->table_id = table_id; + query->seq = 0; + query->total = 0; + query->checksum = csp_hton16(checksum); + + /* Actual set query */ + unsigned int bytes = 0; + gs_error_t error = gs_param_serialize_item_direct(type, value_size, addr, value, F_TO_BIG_ENDIAN, query->payload.packed, payload_size, &bytes); + if (error) { + return error; + } + + /* Add to query */ + query->length = csp_hton16(bytes); + + /* Run single packet transaction */ + if (csp_transaction2(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, query, query_size, query, 1, CSP_O_CRC32) <= 0) { + return GS_ERROR_IO; + } + + /* We have a reply */ + query->length = csp_ntoh16(query->length); + + if ((query->action != RPARAM_SET_OK) || (query->length != bytes)) { + log_warning("%s: Invalid reply: size %u - expected %u, action %u - expected %u", + __FUNCTION__, query->length, bytes, query->action, RPARAM_SET_OK); + return GS_ERROR_DATA; + } + + return GS_OK; +} + +gs_error_t gs_rparam_query_get_value(gs_param_table_instance_t * tinst, const char* param_name, uint16_t param_no, void* val_p, size_t val_size) +{ + const gs_param_table_row_t * t = gs_param_row_by_name(param_name, tinst->rows, tinst->row_count); + if (t == NULL) { + return GS_ERROR_NOT_FOUND; + } + + if (val_size < t->size) { + return GS_ERROR_OVERFLOW; + } + + return gs_param_get(tinst, t->addr + (param_no * t->size), t->type, val_p, t->size, 0); +} diff --git a/gomspace/libparam_client/src/serialize.c b/gomspace/libparam_client/src/serialize.c new file mode 100644 index 00000000..d55fea98 --- /dev/null +++ b/gomspace/libparam_client/src/serialize.c @@ -0,0 +1,353 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include "serialize_local.h" +#include +#include +#include +#include +#include +#include +#include + +#include // need PARM_X??? definitions + +bool gs_param_betoh(gs_param_type_t type, void * item) +{ + if (item) { + switch (type) { + case GS_PARAM_UINT16: + case GS_PARAM_INT16: + case PARAM_X16: + { + *(uint16_t *) item = util_betoh16(*(uint16_t *) item); + return true; + } + case GS_PARAM_UINT32: + case GS_PARAM_INT32: + case PARAM_X32: + { + *(uint32_t *) item = util_betoh32(*(uint32_t *) item); + return true; + } + case GS_PARAM_UINT64: + case GS_PARAM_INT64: + case PARAM_X64: + { + *(uint64_t *) item = util_betoh64(*(uint64_t *) item); + return true; + } + case GS_PARAM_FLOAT: + { + *(float *) item = util_ntohflt(*(float *) item); + return true; + } + case GS_PARAM_DOUBLE: + { + *(double *) item = util_ntohdbl(*(double *) item); + return true; + } + + case GS_PARAM_UINT8: + case GS_PARAM_INT8: + case PARAM_X8: + case GS_PARAM_STRING: + case GS_PARAM_DATA: + case GS_PARAM_BOOL: + // no swap + break; + } + } + return false; +} + +bool gs_param_htobe(gs_param_type_t type, void * item) +{ + if (item) { + switch (type) { + case PARAM_UINT16: + case PARAM_INT16: + case PARAM_X16: + { + *(uint16_t *) item = util_htobe16(*(uint16_t *) item); + return true; + } + case PARAM_UINT32: + case PARAM_INT32: + case PARAM_X32: + { + *(uint32_t *) item = util_htobe32(*(uint32_t *) item); + return true; + } + case PARAM_UINT64: + case PARAM_INT64: + case PARAM_X64: + { + *(uint64_t *) item = util_htobe64(*(uint64_t *) item); + return true; + } + case PARAM_FLOAT: + { + *(float *) item = util_htonflt(*(float *) item); + return true; + } + case PARAM_DOUBLE: + { + *(double *) item = util_htondbl(*(double *) item); + return true; + } + + case PARAM_UINT8: + case PARAM_INT8: + case PARAM_X8: + case PARAM_STRING: + case PARAM_DATA: + case PARAM_BOOL: + // no swap + break; + } + } + return false; +} + +static bool gs_param_require_endian_swap(gs_param_type_t type) +{ + switch (type) { + case PARAM_UINT16: + case PARAM_INT16: + case PARAM_X16: + case PARAM_UINT32: + case PARAM_INT32: + case PARAM_X32: + case PARAM_UINT64: + case PARAM_INT64: + case PARAM_X64: + case PARAM_FLOAT: + case PARAM_DOUBLE: + // swap + break; + + case PARAM_UINT8: + case PARAM_INT8: + case PARAM_X8: + case PARAM_STRING: + case PARAM_DATA: + case PARAM_BOOL: + // no swap + return false; + + default: + break; + } + return true; +} + +static gs_error_t gs_param_serialize_array(gs_param_table_instance_t * tinst, const gs_param_table_row_t * param, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos) +{ + const unsigned int param_size = GS_PARAM_SIZE(param); + const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(param); + const gs_param_type_t param_type = GS_PARAM_TYPE(param); + + /* Calculate total parameter size (full array) */ + { + unsigned int size = param_size * param_array_size; + if ((flags & F_PACKED) == 0) { + size += param_array_size * sizeof(uint16_t); // address + } + + /* Return if parameter array would exceed maxbuf */ + if (*buf_pos + size > buf_size) { + return GS_ERROR_OVERFLOW; + } + } + + uint8_t value[param_size]; + gs_error_t error = GS_OK; + for (unsigned int j = 0; (j < param_array_size) && (error == GS_OK); j++) { + const uint16_t addr = GS_PARAM_ADDR(param) + (param_size * j); + error = gs_param_get(tinst, addr, param_type, value, param_size, 0); + if (error == GS_OK) { + error = gs_param_serialize_item(param, addr, value, flags, buf, buf_size, buf_pos); + } + } + + return error; +} + +gs_error_t gs_param_serialize_item_direct(gs_param_type_t param_type, unsigned int param_size, uint16_t addr, const void * item, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos) +{ + /* Check length */ + if ((((flags & F_PACKED) ? 0 : sizeof(addr)) + param_size + *buf_pos) > buf_size) { + return GS_ERROR_OVERFLOW; + } + + /* Include address if not packed */ + if ((flags & F_PACKED) == 0) { + + if (flags & F_TO_BIG_ENDIAN) { + addr = util_htobe16(addr); + } + + if ((flags & F_DRY_RUN) == 0) { + memcpy(&buf[*buf_pos], &addr, sizeof(addr)); + } + + *buf_pos += sizeof(addr); + } + + if ((flags & F_DRY_RUN) == 0) { + if (flags & F_TO_BIG_ENDIAN) { + void * tmp = alloca(param_size); // this must be aligned + memcpy(tmp, item, param_size); + gs_param_htobe(param_type, tmp); + item = tmp; + } + memcpy(&buf[*buf_pos], item, param_size); + } + + *buf_pos += param_size; + + return GS_OK; +} + +gs_error_t gs_param_serialize_item(const gs_param_table_row_t * param, uint16_t addr, const void * item, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos) +{ + const gs_param_type_t param_type = GS_PARAM_TYPE(param); + const unsigned int param_size = GS_PARAM_SIZE(param); + return gs_param_serialize_item_direct(param_type, param_size, addr, item, flags, buf, buf_size, buf_pos); +} + +gs_error_t gs_param_serialize_full_table(gs_param_table_instance_t * tinst, unsigned int * param_pos, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos) +{ + if (flags & GS_PARAM_SF_DRY_RUN) { + buf = NULL; + buf_size = -1; // Max size + } + gs_error_t error = GS_OK; + unsigned int i = *param_pos; + for (; i < tinst->row_count; i++) { + const gs_param_table_row_t * param = &tinst->rows[i]; + error = gs_param_serialize_array(tinst, param, flags, buf, buf_size, buf_pos); + if (error) { + break; + } + } + *param_pos = i; + return error; +} + +gs_error_t gs_param_serialize_list(gs_param_table_instance_t * tinst, + const uint16_t addr[], unsigned int addr_count, + unsigned int * param_pos, uint32_t flags, + uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos) +{ + if (tinst == NULL) { + return GS_ERROR_HANDLE; + } + + gs_error_t error = GS_OK; + unsigned int i = *param_pos; + for (; i < addr_count; ++i) { + const gs_param_table_row_t * param = gs_param_row_by_address(addr[i], tinst->rows, tinst->row_count); + if (param == NULL) { + continue; + } + + const gs_param_type_t param_type = GS_PARAM_TYPE(param); + const unsigned int param_size = GS_PARAM_SIZE(param); + uint8_t value[param_size]; + error = gs_param_get(tinst, addr[i], param_type, value, param_size, 0); + if (error) { + break; + } + error = gs_param_serialize_item(param, addr[i], value, flags, buf, buf_size, buf_pos); + if (error) { + break; + } + } + *param_pos = i; + + return error; +} + +gs_error_t gs_param_deserialize_item(gs_param_table_instance_t * tinst, + const gs_param_table_row_t * param, + uint16_t addr, + const void * item, + uint32_t flags) +{ + const gs_param_type_t param_type = GS_PARAM_TYPE(param); + const unsigned int param_size = GS_PARAM_SIZE(param); + + if (flags & F_FROM_BIG_ENDIAN) { + if (gs_param_require_endian_swap(param_type)) { + + // Copy to temporary storage, so we don't mess with input memory + void * tmp = alloca(param_size); + memcpy(tmp, item, param_size); + + gs_param_betoh(param_type, tmp); + + // Replace input pointer + item = tmp; + } + } + + gs_error_t error = GS_OK; + if ((flags & F_DRY_RUN) == 0) { + error = gs_param_set(tinst, addr, param_type, item, param_size, GS_PARAM_FLAGS(param)); + } + + return error; +} + +gs_error_t gs_param_deserialize(gs_param_table_instance_t * tinst, const uint8_t * buf, unsigned int buf_size, uint32_t flags) +{ + unsigned int pos = 0; + unsigned int count = 0; + gs_error_t error = GS_OK; + while ((pos < buf_size) && (error == GS_OK)) { + + if (flags & F_PACKED) { + /** PACKED */ + + /* Find in table */ + const gs_param_table_row_t * param = &tinst->rows[count]; + const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(param); + const unsigned int param_size = GS_PARAM_SIZE(param); + + /* For each item in array */ + for (unsigned int j = 0; (j < param_array_size) && (error == GS_OK); j++) { + uint16_t addr = GS_PARAM_ADDR(param) + (param_size * j); + error = gs_param_deserialize_item(tinst, param, addr, &buf[pos], flags); + pos += param_size; + } + + } else { + /** NOT PACKED */ + + /* Read address from data */ + uint16_t addr; + memcpy(&addr, &buf[pos], sizeof(addr)); + if (flags & F_FROM_BIG_ENDIAN) { + addr = util_betoh16(addr); + } + pos += sizeof(addr); + + /* Find in table */ + const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count); + if (param == NULL) { + return GS_ERROR_NOT_FOUND; + } + + /* Copy value */ + error = gs_param_deserialize_item(tinst, param, addr, &buf[pos], flags); + pos += GS_PARAM_SIZE(param); + } + + count++; + } + + return error; +} diff --git a/gomspace/libparam_client/src/serialize_local.h b/gomspace/libparam_client/src/serialize_local.h new file mode 100644 index 00000000..15de4a51 --- /dev/null +++ b/gomspace/libparam_client/src/serialize_local.h @@ -0,0 +1,9 @@ +#ifndef SRC_SERIALIZE_LOCAL_H +#define SRC_SERIALIZE_LOCAL_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +gs_error_t gs_param_serialize_item_direct(gs_param_type_t param_type, unsigned int param_size, uint16_t addr, const void * item, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos); + +#endif diff --git a/gomspace/libparam_client/src/string.c b/gomspace/libparam_client/src/string.c new file mode 100644 index 00000000..ddbc5094 --- /dev/null +++ b/gomspace/libparam_client/src/string.c @@ -0,0 +1,589 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include +#include +#include +#include +#include +#include +#include + +size_t gs_param_calc_table_size(const gs_param_table_row_t * rows, size_t row_count) +{ + if (rows && row_count) { + const gs_param_table_row_t * last_row = rows; + // table rows may not be in assending address- so we have to run through the entire table. + for (size_t i = 0; i < row_count; ++i, ++rows) { + if (GS_PARAM_ADDR(rows) > GS_PARAM_ADDR(last_row)) { + last_row = rows; + } + } + return (GS_PARAM_ADDR(last_row) + (GS_PARAM_SIZE(last_row) * GS_PARAM_ARRAY_SIZE(last_row))); + } + return 0; +} + +const gs_param_table_row_t * gs_param_row_by_name(const char * name, const gs_param_table_row_t * rows, size_t row_count) +{ + if (rows) { + for (unsigned int i = 0; i < row_count; ++i, ++rows) { + if (GS_PGM_STRNCASECMP(name, rows->name, GS_PARAM_MAX_NAME) == 0) { + return rows; + } + } + } + return NULL; +} + +const gs_param_table_row_t * gs_param_row_by_address(uint16_t addr, const gs_param_table_row_t * rows, size_t row_count) +{ + if (rows) { + for (unsigned int i = 0; i < row_count; ++i, ++rows) { + const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(rows); + const unsigned int param_size = GS_PARAM_SIZE(rows); + const unsigned int param_addr = GS_PARAM_ADDR(rows); + for (unsigned int j = 0; j < param_array_size; ++j) { + if ((param_addr + (j * param_size)) == addr) { + return rows; + } + } + } + } + return NULL; +} + +/** + memcpy is used, because data/value may not be aligned correctly and cause crash if accessed directly. +*/ +gs_error_t gs_param_to_string_buffer(const gs_param_table_row_t * param, const void * value, bool with_type, uint32_t flags, gs_bytebuffer_t *bb) +{ + flags |= param->flags; + const uint8_t param_type = GS_PARAM_TYPE(param); + switch (param_type) { + case GS_PARAM_BOOL: { + if (with_type == 1) { + gs_bytebuffer_printf(bb, "BL "); + } + gs_bytebuffer_printf(bb, "%s", *(uint8_t *) value ? "true" : "false"); + break; + } + case GS_PARAM_INT8: { + if (with_type == 1) { + gs_bytebuffer_printf(bb, "I8 "); + } + gs_bytebuffer_printf(bb, "%d", *(int8_t *) value); + break; + } + case PARAM_X8: + flags |= GS_PARAM_F_SHOW_HEX; + // fallthrough + case GS_PARAM_UINT8: { + if (with_type == 1) { + gs_bytebuffer_printf(bb, "U8 "); + } + if (flags & GS_PARAM_F_SHOW_HEX) { + gs_bytebuffer_printf(bb, "0x%02"PRIx8, *(uint8_t *) value); + } else { + gs_bytebuffer_printf(bb, "%u", *(uint8_t *) value); + } + break; + } + case GS_PARAM_INT16: { + int16_t tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "I16 "); + } + gs_bytebuffer_printf(bb, "%"PRId16, tmp); + break; + } + case PARAM_X16: + flags |= GS_PARAM_F_SHOW_HEX; + // fallthrough + case GS_PARAM_UINT16: { + uint16_t tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "U16 "); + } + if (flags & GS_PARAM_F_SHOW_HEX) { + gs_bytebuffer_printf(bb, "0x%04"PRIx16, tmp); + } else { + gs_bytebuffer_printf(bb, "%"PRIu16, tmp); + } + break; + } + case GS_PARAM_INT32: { + int32_t tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "I32 "); + } + gs_bytebuffer_printf(bb, "%"PRId32, tmp); + break; + } + case PARAM_X32: + flags |= GS_PARAM_F_SHOW_HEX; + // fallthrough + case GS_PARAM_UINT32: { + uint32_t tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "U32 "); + } + if (flags & GS_PARAM_F_SHOW_HEX) { + gs_bytebuffer_printf(bb, "0x%08"PRIx32, tmp); + } else { + gs_bytebuffer_printf(bb, "%"PRIu32, tmp); + } + break; + } +#ifdef PRIu64 + case GS_PARAM_INT64: { + int64_t tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "I64 "); + } + gs_bytebuffer_printf(bb, "%"PRId64, tmp); + break; + } + case PARAM_X64: + flags |= GS_PARAM_F_SHOW_HEX; + // fallthrough + case GS_PARAM_UINT64: { + uint64_t tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "U64 "); + } + if (flags & GS_PARAM_F_SHOW_HEX) { + gs_bytebuffer_printf(bb, "0x%016"PRIx64, tmp); + } else { + gs_bytebuffer_printf(bb, "%"PRIu64, tmp); + } + break; + } +#endif + case GS_PARAM_FLOAT: { + float tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "FLT "); + } + if (flags & GS_PARAM_F_SHOW_SCIENTIFIC) { + gs_bytebuffer_printf(bb, "%e", (double) tmp); + } else { + gs_bytebuffer_printf(bb, "%f", (double) tmp); + } + break; + } + case GS_PARAM_DOUBLE: { + double tmp; + memcpy(&tmp, value, sizeof(tmp)); + if (with_type == 1) { + gs_bytebuffer_printf(bb, "DBL "); + } + if (flags & GS_PARAM_F_SHOW_SCIENTIFIC) { + gs_bytebuffer_printf(bb, "%e", tmp); + } else { + gs_bytebuffer_printf(bb, "%f", tmp); + } + break; + } + case GS_PARAM_STRING: { + if (with_type == 1) { + gs_bytebuffer_printf(bb, "STR "); + } + gs_bytebuffer_append(bb, "\"", 1); + // handle missing NUL termination. + const size_t len = strnlen((const char*)value, GS_PARAM_SIZE(param)); + gs_bytebuffer_append(bb, value, len); + gs_bytebuffer_append(bb, "\"", 1); + break; + } + case GS_PARAM_DATA: { + if (with_type == 1) { + gs_bytebuffer_printf(bb, "DAT "); + } + for (int i = 0; i < GS_PARAM_SIZE(param); i++) { + gs_bytebuffer_printf(bb, "%02"PRIX8, ((uint8_t *) value)[i]); + } + break; + } + default: { + log_error("%s: Unknown param type %u", __FUNCTION__, param_type); + break; + } + } + + return GS_OK; +} + +gs_error_t gs_param_to_string2(const gs_param_table_row_t * param, const void * value, bool with_type, uint32_t flags, char * buf, unsigned int buf_size, unsigned int buf_pos, unsigned int * buf_written) +{ + GS_CHECK_ARG(buf_pos <= buf_size); + gs_bytebuffer_t bb; + gs_bytebuffer_init(&bb, &buf[buf_pos], (buf_size - buf_pos)); + gs_error_t error = gs_param_to_string_buffer(param, value, with_type, flags, &bb); + if (error == GS_OK) { + gs_bytebuffer_get_as_string(&bb, &error); // this will add NUL termination, but may truncate buffer + error = gs_bytebuffer_get_state(&bb); + if (buf_written) { + *buf_written = bb.used; + } + } + return error; +} + +const char * gs_param_type_to_string(gs_param_type_t type) +{ + switch (type) { + case GS_PARAM_BOOL: return "bool"; + case GS_PARAM_UINT8: return "uint8_t"; + case GS_PARAM_UINT16: return "uint16_t"; + case GS_PARAM_UINT32: return "uint32_t"; + case GS_PARAM_UINT64: return "uint65_t"; + case GS_PARAM_INT8: return "int8_t"; + case GS_PARAM_INT16: return "int16_t"; + case GS_PARAM_INT32: return "int32_t"; + case GS_PARAM_INT64: return "int64_t"; + case PARAM_X8: return "uint8_t"; + case PARAM_X16: return "uint16_t"; + case PARAM_X32: return "uint32_t"; + case PARAM_X64: return "uint64_t"; + case GS_PARAM_FLOAT: return "float"; + case GS_PARAM_DOUBLE: return "double"; + case GS_PARAM_STRING: return "char"; + case GS_PARAM_DATA: return "char"; + } + return ""; +} + +static inline int to_int(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + return -1; +} + +gs_error_t gs_param_from_string(const gs_param_table_row_t * param, const char * string, void * value) +{ + if ((param == NULL) || (string == NULL) || (value == NULL)) { + return GS_ERROR_ARG; + } + + if (GS_PARAM_TYPE(param) != GS_PARAM_STRING) { + // skip only space - not white-space, e.g. isspace() + for (; *string == ' '; ++string); + } + + gs_error_t error = GS_OK; + + switch(GS_PARAM_TYPE(param)) { + + case GS_PARAM_BOOL: + { + bool parsein = false; + error = gs_string_to_bool(string, &parsein); + if (error == GS_OK) { + *((uint8_t *) value) = parsein; + } + } + break; + + case GS_PARAM_UINT8: + { + uint8_t parsein; + error = gs_string_to_uint8(string, &parsein); + if (error == GS_OK) { + *((uint8_t *) value) = parsein; + } + } + break; + + case GS_PARAM_UINT16: + { + uint16_t parsein; + error = gs_string_to_uint16(string, &parsein); + if (error == GS_OK) { + *((uint16_t *) value) = parsein; + } + } + break; + + case GS_PARAM_UINT32: + { + uint32_t parsein; + error = gs_string_to_uint32(string, &parsein); + if (error == GS_OK) { + *((uint32_t *) value) = parsein; + } + } + break; + + case GS_PARAM_UINT64: + { + uint64_t parsein; + error = gs_string_to_uint64(string, &parsein); + if (error == GS_OK) { + *((uint64_t *) value) = parsein; + } + } + break; + + case GS_PARAM_INT8: + { + int8_t parsein; + error = gs_string_to_int8(string, &parsein); + if (error == GS_OK) { + *((int8_t *) value) = parsein; + } + } + break; + + case GS_PARAM_INT16: + { + int16_t parsein; + error = gs_string_to_int16(string, &parsein); + if (error == GS_OK) { + *((int16_t *) value) = parsein; + } + } + break; + + case GS_PARAM_INT32: + { + int32_t parsein; + error = gs_string_to_int32(string, &parsein); + if (error == GS_OK) { + *((int32_t *) value) = parsein; + } + } + break; + + case GS_PARAM_INT64: + { + int64_t parsein; + error = gs_string_to_int64(string, &parsein); + if (error == GS_OK) { + *((int64_t *) value) = parsein; + } + } + break; + + case PARAM_X8: + { + uint32_t parsein; + error = gs_string_hex_to_uint32(string, &parsein); + if (error == GS_OK) { + if (parsein <= UINT8_MAX) { + *((uint8_t *) value) = parsein; + } else { + error = GS_ERROR_OVERFLOW; + } + } + } + break; + + case PARAM_X16: + { + uint32_t parsein; + error = gs_string_hex_to_uint32(string, &parsein); + if (error == GS_OK) { + if (parsein <= UINT16_MAX) { + *((uint16_t *) value) = parsein; + } else { + error = GS_ERROR_OVERFLOW; + } + } + } + break; + + case PARAM_X32: + { + uint32_t parsein; + error = gs_string_hex_to_uint32(string, &parsein); + if (error == GS_OK) { + *((uint32_t *) value) = parsein; + } + } + break; + + case PARAM_X64: + { + uint64_t parsein; + error = gs_string_hex_to_uint64(string, &parsein); + if (error == GS_OK) { + *((uint64_t *) value) = parsein; + } + } + break; + + case GS_PARAM_FLOAT: + { + float parsein; + error = gs_string_to_float(string, &parsein); + if (error == GS_OK) { + *((float *) value) = parsein; + } + } + break; + + case GS_PARAM_DOUBLE: + { + double parsein; + error = gs_string_to_double(string, &parsein); + if (error == GS_OK) { + *((double *) value) = parsein; + } + } + break; + + case GS_PARAM_STRING: + { + const size_t ilen = strnlen(string, GS_PARAM_SIZE(param) + 1); // 0 terminator + if (ilen <= GS_PARAM_SIZE(param)) { + memset(value, 0, GS_PARAM_SIZE(param)); + memcpy(value, string, ilen); + } else { + error = GS_ERROR_OVERFLOW; + } + } + break; + + case GS_PARAM_DATA: + { + const size_t MAX_LEN = (GS_PARAM_SIZE(param) * 2); + const size_t ilen = strnlen(string, MAX_LEN + 1); + if (ilen > MAX_LEN) { + error = GS_ERROR_OVERFLOW; + } else if ((ilen % 2) == 0) { + + // validate data first - not to end up with invalid/strange data + for (unsigned int i = 0; i < ilen; ++i) { + if (to_int(string[i]) < 0) { + error = GS_ERROR_DATA; + break; + } + } + if (error == GS_OK) { + uint8_t * out = (uint8_t *) value; + memset(out, 0, GS_PARAM_SIZE(param)); + for (unsigned int i = 0; i < ilen; i += 2, ++out) { + *out = (16 * to_int(string[i])) + to_int(string[i+1]); + } + error = GS_OK; + } + } else { + error = GS_ERROR_DATA; + } + } + break; + } + + return error; +} + +gs_error_t gs_param_list_single_to_stream(gs_param_table_instance_t * tinst, const gs_param_table_row_t * param, + bool list_data, uint32_t flags, FILE * out) +{ + if (param == NULL) { + return GS_ERROR_HANDLE; + } + + gs_error_t error = GS_OK; + const uint16_t addr = GS_PARAM_ADDR(param); + + fprintf(out, " 0x%04X %-16.14"GS_PGM_FMT_STR, addr, param->name); // ensure missing NUL termination doesn't cause problems. + + if (list_data) { + const gs_param_type_t param_type = GS_PARAM_TYPE(param); + const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(param); + const unsigned int param_size = GS_PARAM_SIZE(param); + uint8_t value[param_size]; + char buf[100]; + for (unsigned int j = 0; (j < param_array_size) && (error == GS_OK); j++) { + error = gs_param_get(tinst, addr + (param_size * j), param_type, value, param_size, 0); + if (error == GS_OK) { + gs_param_to_string2(param, value, (j == 0) ? 1 : 0, flags, buf, sizeof(buf), 0, NULL); + fprintf(out, "%s ", buf); + } + } + } + fprintf(out, "\r\n"); + return error; +} + +gs_error_t gs_param_list_to_stream(gs_param_table_instance_t * tinst, bool list_data, uint32_t flags, FILE * out) +{ + GS_CHECK_HANDLE(tinst != NULL); + + gs_error_t error = GS_OK; + for (unsigned int i = 0; (i < tinst->row_count) && (error == GS_OK); ++i) { + error = gs_param_list_single_to_stream(tinst, &tinst->rows[i], list_data, flags, out); + } + + return error; +} + +gs_error_t gs_param_parse_name_and_array_index(const char * inp, char * name, size_t size_name, uint8_t * return_index, bool * return_is_array) +{ + if (inp == NULL) { + return GS_ERROR_ARG; + } + + uint8_t a_index; + size_t name_len; + gs_error_t error; + bool is_array; + const char * pai = strchr(inp, '['); // look for array index + if (pai) { + name_len = pai - inp; + char tmp[20]; + GS_STRNCPY(tmp, pai+1); + char * endp = strchr(tmp, ']'); + if (endp) { + *endp = 0; + } + error = gs_string_to_uint8(tmp, &a_index); + is_array = true; + } else { + error = GS_OK; + name_len = strlen(inp); + is_array = false; + a_index = 0; + } + + if (error == GS_OK) { + if (name_len >= size_name) { + error = GS_ERROR_OVERFLOW; + } else { + strncpy(name, inp, name_len); + name[name_len] = 0; + + // remove trailing white-space + if (name_len) { + for (int i = name_len-1; i >= 0; --i) { + if (name[i] && isspace((int)name[i])) { + name[i] = 0; + } else { + break; + } + } + } + + if (return_index) { + *return_index = (is_array) ? a_index : 0; + } + if (return_is_array) { + *return_is_array = is_array; + } + } + } + + return error; +} diff --git a/gomspace/libparam_client/src/table.c b/gomspace/libparam_client/src/table.c new file mode 100644 index 00000000..3aea93ad --- /dev/null +++ b/gomspace/libparam_client/src/table.c @@ -0,0 +1,393 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#define GS_PARAM_INTERNAL_USE 1 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static void _copy_data(gs_param_type_t type, void * dst, const void * src, size_t size) +{ +#if (GS_PARAM_ATOMIC_ACCESS) + switch (type) { + case GS_PARAM_UINT8: + case GS_PARAM_INT8: + case PARAM_X8: + case GS_PARAM_STRING: + case GS_PARAM_DATA: + case GS_PARAM_BOOL: + break; + + case GS_PARAM_UINT16: + case GS_PARAM_INT16: + case PARAM_X16: + if (!((intptr_t)src & 1) && !((intptr_t)dst & 1)) { + // (u)int16 aligned correctly + const uint16_t * s = src; + uint16_t * d = dst; + const unsigned int count = (size / sizeof(*d)); + for (unsigned int i = 0; i < count; ++i, ++d, ++s) { + *d = *s; + } + //printf("%s:int16\r\n", __FUNCTION__); + return; + } + break; + + case GS_PARAM_UINT32: + case GS_PARAM_INT32: + case PARAM_X32: + case GS_PARAM_FLOAT: + if (!((intptr_t)src & 3) && !((intptr_t)dst & 3)) { + // (u)int32 aligned correctly + const uint32_t * s = src; + uint32_t * d = dst; + const unsigned int count = (size / sizeof(*d)); + for (unsigned int i = 0; i < count; ++i, ++d, ++s) { + *d = *s; + } + //printf("%s:int32\r\n", __FUNCTION__); + return; + } + break; + + case GS_PARAM_UINT64: + case GS_PARAM_INT64: + case PARAM_X64: + case GS_PARAM_DOUBLE: + break; + } +#endif + + // fallback - do byte copy + memcpy(dst, src, size); +} + +gs_error_t gs_param_table_lock(gs_param_table_instance_t * tinst) +{ + if (tinst->lock) { + return gs_mutex_lock(tinst->lock); + } + return GS_OK; +} + +gs_error_t gs_param_table_unlock(gs_param_table_instance_t * tinst) +{ + if (tinst->lock) { + return gs_mutex_unlock(tinst->lock); + } + return GS_OK; +} + +static gs_error_t gs_param_table_lock_free(gs_param_table_instance_t * tinst) +{ + if (tinst && tinst->lock) { + gs_mutex_destroy(tinst->lock); + tinst->lock = NULL; + } + return GS_OK; +} + +gs_error_t gs_param_table_free(gs_param_table_instance_t * tinst) +{ + if (tinst) { + if (tinst->flags & GS_PARAM_TABLE_F_ALLOC_MEMORY) { + free(tinst->memory); + } + if (tinst->flags & GS_PARAM_TABLE_F_ALLOC_ROWS) { + free((void*)tinst->rows); + } + if (tinst->lock) { + gs_param_table_lock_free(tinst); + } + memset(tinst, 0, sizeof(*tinst)); + } + return GS_OK; +} + +static void checksum_update(gs_param_table_instance_t * tinst) +{ + const uint16_t no_swap = gs_fletcher16(tinst->rows, (sizeof(*tinst->rows) * tinst->row_count)); + + // fletcher16 with swapped fields > 1 byte + gs_fletcher16_t f16; + gs_fletcher16_init(&f16); + for (unsigned int i = 0; i < tinst->row_count; ++i) { + gs_param_table_row_t row = tinst->rows[i]; + row.addr = gs_bswap_16(row.addr); + gs_fletcher16_update(&f16, &row, sizeof(row)); + } + const uint16_t swap = gs_fletcher16_finalize(&f16); + + if (gs_endian_big()) { + tinst->checksum_be = no_swap; + tinst->checksum_le = swap; + } else { + tinst->checksum_be = swap; + tinst->checksum_le = no_swap; + } +} + +uint16_t gs_param_table_checksum_be(gs_param_table_instance_t * tinst) +{ + if (tinst && tinst->rows && tinst->row_count) { + if (tinst->checksum_be == 0) { + checksum_update(tinst); + } + return tinst->checksum_be; + } + return 0; +} + +uint16_t gs_param_table_checksum_le(gs_param_table_instance_t * tinst) +{ + if (tinst && tinst->rows && tinst->row_count) { + if (tinst->checksum_le == 0) { + checksum_update(tinst); + } + return tinst->checksum_le; + } + return 0; +} + +uint16_t gs_param_table_checksum(gs_param_table_instance_t * tinst) +{ + if (gs_endian_big()) { + return gs_param_table_checksum_be(tinst); + } else { + return gs_param_table_checksum_le(tinst); + } +} + +gs_error_t gs_param_get(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, void * value, size_t value_size, uint32_t flags) +{ + if (tinst == NULL) { + return GS_ERROR_HANDLE; + } + + if ((addr + value_size) > tinst->memory_size) { + return GS_ERROR_RANGE; + } + + gs_error_t error = GS_ERROR_NOT_SUPPORTED; + if (tinst->function_interface.get) { + error = (tinst->function_interface.get)(tinst->function_interface.context, tinst, addr, type, value, value_size, flags); + } else if (tinst->memory) { + gs_param_table_lock(tinst); + _copy_data(type, value, ((uint8_t*)(tinst->memory)) + addr, value_size); + gs_param_table_unlock(tinst); + error = GS_OK; + } + + if ((error == GS_OK) && (flags & GS_PARAM_SF_TO_BIG_ENDIAN) && !gs_endian_big()) { + gs_param_htobe(type, value); + } + + return error; +} + +gs_error_t gs_param_set(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, const void * value, size_t value_size, uint32_t flags) +{ + if (tinst == NULL) { + return GS_ERROR_HANDLE; + } + + if ((addr + value_size) > tinst->memory_size) { + return GS_ERROR_RANGE; + } + + if ((flags & GS_PARAM_SF_FROM_BIG_ENDIAN) && !gs_endian_big()) { + void * tmp = alloca(value_size); // this must be aligned + memcpy(tmp, value, value_size); + value = tmp; + gs_param_betoh(type, tmp); + } + + gs_error_t error = GS_ERROR_NOT_SUPPORTED; + if (tinst->function_interface.set) { + error = (tinst->function_interface.set)(tinst->function_interface.context, tinst, addr, type, value, value_size, flags); + } else if (tinst->memory) { + gs_param_table_lock(tinst); + _copy_data(type, ((uint8_t*)(tinst->memory)) + addr, value, value_size); + gs_param_table_unlock(tinst); + + if (tinst->auto_persist.set && (flags & GS_PARAM_F_AUTO_PERSIST)) { + (tinst->auto_persist.set)(tinst, addr, type, value, value_size, flags); + } + error = GS_OK; + } + + // Callbacks + if ((error == GS_OK) && tinst->callback && ((flags & GS_PARAM_F_NO_CALLBACK) == 0)) { + (tinst->callback)(addr, tinst); + } + + return error; +} + +uint8_t gs_param_type_size(gs_param_type_t type) +{ + switch (type) { + case GS_PARAM_UINT8: + case GS_PARAM_INT8: + case PARAM_X8: + case GS_PARAM_STRING: + case GS_PARAM_DATA: + return sizeof(int8_t); + case GS_PARAM_INT16: + case GS_PARAM_UINT16: + case PARAM_X16: + return sizeof(int16_t); + case GS_PARAM_INT32: + case GS_PARAM_UINT32: + case PARAM_X32: + return sizeof(int32_t); + case GS_PARAM_INT64: + case GS_PARAM_UINT64: + case PARAM_X64: + return sizeof(int64_t); + case GS_PARAM_DOUBLE: + return sizeof(double); + case GS_PARAM_FLOAT: + return sizeof(float); + case GS_PARAM_BOOL: + return sizeof(bool); + } + return 0; +} + +void * gs_param_table_get_memory(gs_param_table_instance_t * tinst, size_t * return_size) +{ + if (tinst && tinst->memory) { + if (return_size) { + *return_size = tinst->memory_size; + } + return tinst->memory; + } + return NULL; +} + +const gs_param_table_row_t * gs_param_table_get_rows(gs_param_table_instance_t * tinst, size_t * return_count) +{ + if (tinst && tinst->rows && tinst->row_count) { + if (return_count) { + *return_count = tinst->row_count; + } + return tinst->rows; + } + return NULL; +} + +size_t gs_param_table_instance_size(void) +{ + return sizeof(gs_param_table_instance_t); +} + +gs_param_table_instance_t * gs_param_table_instance_clear(void * var, size_t var_size) +{ + gs_param_table_instance_t * tinst = NULL; + if (var && (var_size >= sizeof(*tinst))) { + tinst = (gs_param_table_instance_t *) var; + memset(tinst, 0, sizeof(*tinst)); + } + return tinst; +} + +gs_param_table_instance_t * gs_param_table_instance_alloc(void) +{ + return calloc(1, sizeof(gs_param_table_instance_t)); +} + +gs_error_t gs_param_get_string(gs_param_table_instance_t * tinst, uint16_t addr, char * buf, size_t buf_size, uint32_t flags) +{ + if (tinst == NULL) { + return GS_ERROR_HANDLE; + } + if (buf == NULL) { + return GS_ERROR_ARG; + } + + const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count); + if (param == NULL) { + return GS_ERROR_RANGE; + } + + if (buf_size <= param->size) { + return GS_ERROR_OVERFLOW; + } + + gs_error_t error = gs_param_get(tinst, addr, param->type, buf, param->size, flags); + buf[param->size] = 0; + return error; +} + +gs_error_t gs_param_set_string(gs_param_table_instance_t * tinst, uint16_t addr, const char * value, uint32_t flags) +{ + if (tinst == NULL) { + return GS_ERROR_HANDLE; + } + if (value == NULL) { + return GS_ERROR_ARG; + } + + const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count); + if (param == NULL) { + return GS_ERROR_RANGE; + } + + const size_t len = strlen(value) + 1; + if (len > GS_PARAM_SIZE(param)) { + return GS_ERROR_OVERFLOW; + } + + return gs_param_set(tinst, addr, param->type, value, len, flags); // flags have full control +} + +gs_error_t gs_param_get_data(gs_param_table_instance_t * tinst, uint16_t addr, void * buf, size_t buf_size, uint32_t flags) +{ + if (tinst == NULL) { + return GS_ERROR_HANDLE; + } + if (buf == NULL) { + return GS_ERROR_ARG; + } + + const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count); + if (param == NULL) { + return GS_ERROR_RANGE; + } + + if (buf_size < param->size) { + return GS_ERROR_OVERFLOW; + } + + return gs_param_get(tinst, addr, param->type, buf, param->size, flags); +} + +gs_error_t gs_param_set_data(gs_param_table_instance_t * tinst, uint16_t addr, const void * value, size_t value_size, uint32_t flags) +{ + if (tinst == NULL) { + return GS_ERROR_HANDLE; + } + if (value == NULL) { + return GS_ERROR_ARG; + } + + const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count); + if (param == NULL) { + return GS_ERROR_RANGE; + } + + if (value_size > GS_PARAM_SIZE(param)) { + return GS_ERROR_OVERFLOW; + } + + return gs_param_set(tinst, addr, param->type, value, value_size, flags); // flags have full control +} diff --git a/gomspace/libparam_client/wscript b/gomspace/libparam_client/wscript new file mode 100644 index 00000000..ea023583 --- /dev/null +++ b/gomspace/libparam_client/wscript @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. + +import os +import gs_gcc +import gs_doc + +APPNAME = 'param_client' + + +def options(ctx): + ctx.load('gs_gcc gs_doc') + gs_gcc.gs_recurse(ctx) + + gr = ctx.add_option_group('libparam client options') + gr.add_option('--param_client-disable-cmd', action='store_true', help='Disable GOSH commands') + gr.add_option('--param-enable-atomic-access', action='store_true', help='Enable atomic read/write of 16/32/float') + + +def configure(ctx): + ctx.load('gs_gcc gs_doc') + + ctx.env.append_unique('USE_PARAM_CLIENT', ['gscsp', 'util']) + + ctx.env.append_unique('FILES_PARAM_CLIENT', ['src/*.c', 'src/rparam/*.c', + 'src/pp/*.c', 'src/pp/i2c/*.c', 'src/pp/spi/*.c']) + + if not ctx.options.param_client_disable_cmd: + ctx.env.append_unique('FILES_PARAM_CLIENT', ['src/rparam/cmd/*.c', 'src/pp/cmd/*.c']) + + if ctx.options.param_enable_atomic_access: + ctx.env.append_unique('DEFINES_PARAM_CLIENT', ['GS_PARAM_ATOMIC_ACCESS=1']) + + ctx.gs_register_handler(function='param_gen_4_0', filepath='./tools/waf_param.py') + ctx.gs_register_handler(function='param_gen_4_2', filepath='./tools/waf_param.py') + ctx.gs_register_handler(function='param_gen_4_3', filepath='./tools/waf_param.py') + + ctx.gs_add_doxygen(exclude=['*/include/deprecated/param/*', '*/include/gs/param/internal/*']) + + gs_gcc.gs_recurse(ctx) + + +def build(ctx): + gs_gcc.gs_recurse(ctx) + + public_include = ctx.gs_include(name=APPNAME, + includes=['include', 'include/deprecated', 'include/deprecated/param']) + + if ctx.env.GS_ARCH not in ['avr8']: + ctx.gs_objects(source=ctx.path.ant_glob(ctx.env.FILES_PARAM_CLIENT), + target=APPNAME, + defines=ctx.env.DEFINES_PARAM_CLIENT, + use=ctx.env.USE_PARAM_CLIENT + [public_include]) + + ctx.gs_shlib(source=ctx.path.ant_glob(ctx.env.FILES_PARAM_CLIENT), + target=APPNAME, + defines=ctx.env.DEFINES_PARAM_CLIENT, + gs_use_shlib=ctx.env.USE_PARAM_CLIENT + [public_include]) + + ctx.gs_python_bindings(source=ctx.path.ant_glob('src/bindings/python/pyparam.c'), + target=APPNAME, + gs_use_shlib=ctx.env.USE_PARAM_CLIENT + [APPNAME, public_include], + package='libparam') + + +def gs_dist(ctx): + gs_gcc.gs_recurse(ctx) + ctx.add_default_files(source_module=True) diff --git a/gomspace/libutil/include/conf_util.h b/gomspace/libutil/include/conf_util.h new file mode 100644 index 00000000..d96c3665 --- /dev/null +++ b/gomspace/libutil/include/conf_util.h @@ -0,0 +1,12 @@ +/* WARNING! All changes made to this file will be lost! */ + +#ifndef W_INCLUDE_CONF_UTIL_H_WAF +#define W_INCLUDE_CONF_UTIL_H_WAF + +#define UTIL_LITTLE_ENDIAN 1 +/* #undef UTIL_BIG_ENDIAN */ +#define GS_CONSOLE_HISTORY_LEN 10 +#define GS_CONSOLE_INPUT_LEN 100 +/* #undef GS_LOG_ENABLE_ISR_LOGS */ + +#endif /* W_INCLUDE_CONF_UTIL_H_WAF */ diff --git a/gomspace/libutil/include/deprecated/gs/gosh/command/command.h b/gomspace/libutil/include/deprecated/gs/gosh/command/command.h new file mode 100644 index 00000000..540afea4 --- /dev/null +++ b/gomspace/libutil/include/deprecated/gs/gosh/command/command.h @@ -0,0 +1,49 @@ +#ifndef GS_GOSH_COMMAND_COMMAND_H +#define GS_GOSH_COMMAND_COMMAND_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + Legacy header file - use gs/util/gosh/command.h +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CMD_ERROR_NONE GS_OK +#define CMD_ERROR_FAIL GS_ERROR_UNKNOWN +#define CMD_ERROR_SYNTAX GS_ERROR_ARG +#define CMD_ERROR_NOMEM GS_ERROR_ALLOC +#define CMD_ERROR_INVALID GS_ERROR_DATA +#define CMD_ERROR_NOTFOUND GS_ERROR_NOT_FOUND + +#define CMD_HIDDEN GS_COMMAND_FLAG_HIDDEN + +#define __root_command GS_COMMAND_ROOT +#define __sub_command GS_COMMAND_SUB + +#define INIT_CHAIN(__list) GS_COMMAND_INIT_CHAIN(__list) +#define command_register(__cmd) GS_COMMAND_REGISTER(__cmd) + +typedef struct command command_t; + +static inline const char * command_args(gs_command_context_t *ctx) +{ + return gs_command_args(ctx); +} + +static inline int command_run(char *line) +{ + gs_error_t result = GS_OK; + gs_error_t error = gs_command_run(line, &result); + if (error == GS_OK) { + return result; + } + return error; +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/deprecated/gs/gosh/gosh/getopt.h b/gomspace/libutil/include/deprecated/gs/gosh/gosh/getopt.h new file mode 100644 index 00000000..e0e40329 --- /dev/null +++ b/gomspace/libutil/include/deprecated/gs/gosh/gosh/getopt.h @@ -0,0 +1,22 @@ +#ifndef GS_GOSH_GOSH_GETOPT_H +#define GS_GOSH_GOSH_GETOPT_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + Legacy header file - use gs/util/gosh/getopt.h +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static inline int gosh_getopt(gs_command_context_t *ctx, const char *opts) +{ + return gs_command_getopt(ctx, opts); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/deprecated/gs/gosh/util/console.h b/gomspace/libutil/include/deprecated/gs/gosh/util/console.h new file mode 100644 index 00000000..a8d1c94d --- /dev/null +++ b/gomspace/libutil/include/deprecated/gs/gosh/util/console.h @@ -0,0 +1,43 @@ +#ifndef GS_GOSH_UTIL_CONSOLE_H +#define GS_GOSH_UTIL_CONSOLE_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + Legacy header file - use gs/util/gosh/console.h +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static inline int console_init(void) +{ + return gs_console_init(); +} + +static inline int console_exit(void) +{ + return gs_console_exit(); +} + +static inline void console_set_hostname(const char *host) +{ + gs_console_set_prompt(host); +} + +static inline void console_clear(void) +{ + gs_console_clear(); +} + +static inline void console_update(void) +{ + gs_console_update(); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/deprecated/util/color_printf.h b/gomspace/libutil/include/deprecated/util/color_printf.h new file mode 100644 index 00000000..a2129460 --- /dev/null +++ b/gomspace/libutil/include/deprecated/util/color_printf.h @@ -0,0 +1,26 @@ +#ifndef DEPRECATED_UTIL_COLOR_PRINTF_H +#define DEPRECATED_UTIL_COLOR_PRINTF_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +typedef enum color_printf_e { + /* Colors */ + COLOR_COLORS = GS_COLOR_COLORS, + COLOR_NONE = GS_COLOR_NONE, + COLOR_BLACK = GS_COLOR_BLACK, + COLOR_RED = GS_COLOR_RED, + COLOR_GREEN = GS_COLOR_GREEN, + COLOR_YELLOW = GS_COLOR_YELLOW, + COLOR_BLUE = GS_COLOR_BLUE, + COLOR_MAGENTA = GS_COLOR_MAGENTA, + COLOR_CYAN = GS_COLOR_CYAN, + COLOR_WHITE = GS_COLOR_WHITE, + /* Attributes */ + COLOR_ATTRS = GS_COLOR_ATTRS, + COLOR_BOLD = GS_COLOR_BOLD, +} color_printf_t; + +#define color_printf gs_color_printf + +#endif diff --git a/gomspace/libutil/include/gs/uthash/utarray.h b/gomspace/libutil/include/gs/uthash/utarray.h new file mode 100644 index 00000000..145f3631 --- /dev/null +++ b/gomspace/libutil/include/gs/uthash/utarray.h @@ -0,0 +1,231 @@ +/* +Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic array implementation using macros + */ +#ifndef UTARRAY_H +#define UTARRAY_H + +#define UTARRAY_VERSION 1.9.9 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#define oom() exit(-1) + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd=*_icd; \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + a=(UT_array*)malloc(sizeof(UT_array)); \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+by) > ((a)->n)) { \ + while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) ))) + +#define utarray_insert(a,p,j) do { \ + if (j > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,1); \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if (j > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + size_t _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + size_t _ut_i; \ + if (dst->i > (size_t)(num)) { \ + if ((dst)->icd.dtor) { \ + for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \ + (dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \ + } \ + } \ + } else if (dst->i < (size_t)(num)) { \ + utarray_reserve(dst,num-dst->i); \ + if ((dst)->icd.init) { \ + for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \ + (dst)->icd.init(utarray_eltptr(dst,_ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \ + } \ + } \ + dst->i = num; \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta((dst),(src),utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < len; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \ + } \ + } \ + if ((a)->i > (pos+len)) { \ + memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \ + (((a)->i)-(pos+len))*((a)->icd.sz)); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new((a),(u)); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(size_t)(a)->icd.sz) : -1) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char **_src = (char**)src, **_dst = (char**)dst; + *_dst = (*_src == NULL) ? NULL : strdup(*_src); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc) free(*eltc); +} +static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL}; + +#endif /* UTARRAY_H */ diff --git a/gomspace/libutil/include/gs/uthash/uthash.h b/gomspace/libutil/include/gs/uthash/uthash.h new file mode 100644 index 00000000..c8c6d25c --- /dev/null +++ b/gomspace/libutil/include/gs/uthash/uthash.h @@ -0,0 +1,960 @@ +/* +Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#define DECLTYPE(x) +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ +#if defined (_WIN32) +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#include +#elif defined(__WATCOMC__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#else +#include +#endif + +#define UTHASH_VERSION 1.9.9 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + out=NULL; \ + if (head) { \ + unsigned _hf_bkt,_hf_hashv; \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0 +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + replaced=NULL; \ + HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ + if (replaced!=NULL) { \ + HASH_DELETE(hh,head,replaced); \ + }; \ + HASH_ADD(hh,head,fieldname,keylen_in,add); \ +} while(0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)(keyptr); \ + (add)->hh.keylen = (unsigned)(keylen_in); \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + unsigned _hd_bkt; \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count; \ + char *_prev; \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %u, actual %u\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619; \ + } \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned char *_hj_key=(unsigned char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned char *_sfh_key=(unsigned char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e) { \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#define HASH_OVERHEAD(hh,head) \ + ((head) ? ( \ + (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + (sizeof(UT_hash_table)) + \ + (HASH_BLOOM_BYTELEN)))) : 0) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/gomspace/libutil/include/gs/uthash/utlist.h b/gomspace/libutil/include/gs/uthash/utlist.h new file mode 100644 index 00000000..b5f3f04c --- /dev/null +++ b/gomspace/libutil/include/gs/uthash/utlist.h @@ -0,0 +1,757 @@ +/* +Copyright (c) 2007-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.9 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#elif defined(__ICCARM__) +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list,next) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list,next) ((elt)->next) +#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define _PREV(elt,list,prev) ((elt)->prev) */ +#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list,next); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = head1; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ + LL_APPEND2_VS2008(head,add,next) + +#define LL_APPEND2_VS2008(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ + LL_DELETE2_VS2008(head,del,next) + +#define LL_DELETE2_VS2008(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + head = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#undef LL_DELETE2 +#define LL_DELETE2 LL_DELETE2_VS2008 +#undef LL_APPEND2 +#define LL_APPEND2 LL_APPEND2_VS2008 +#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ +#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ +#endif +/* end VS2008 replacements */ + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +{ \ + counter = 0; \ + LL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for(el=head;el;el=(el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#define LL_REPLACE_ELEM(head, el, add) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_PREPEND_ELEM(head, el, add) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) \ + + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + _tmp = (head2)->prev; \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + (head1)->prev = _tmp; \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +{ \ + counter = 0; \ + DL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for(el=head;el;el=(el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_PREPEND_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ +} while (0) \ + + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +{ \ + counter = 0; \ + CDL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#define CDL_REPLACE_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ +} while (0) \ + +#endif /* UTLIST_H */ + diff --git a/gomspace/libutil/include/gs/uthash/utstring.h b/gomspace/libutil/include/gs/uthash/utstring.h new file mode 100644 index 00000000..867442c8 --- /dev/null +++ b/gomspace/libutil/include/gs/uthash/utstring.h @@ -0,0 +1,393 @@ +/* +Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic string implementation using macros + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 1.9.9 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include +#include +#include +#include +#define oom() exit(-1) + +typedef struct { + char *d; + size_t n; /* allocd size */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + (s)->d = (char*)realloc((s)->d, (s)->n + amt); \ + if ((s)->d == NULL) oom(); \ + (s)->n += amt; \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,100); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + s = (UT_string*)calloc(sizeof(UT_string),1); \ + if (!s) oom(); \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += l; \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((unsigned)((s)->i)) + +#define utstring_body(s) ((s)->d) + +_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + while (1) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && ((size_t) n < (s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +#ifdef __GNUC__ +/* support printf format checking (2=the format string, 3=start of varargs) */ +static void utstring_printf(UT_string *s, const char *fmt, ...) + __attribute__ (( format( printf, 2, 3) )); +#endif +_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +/******************************************************************************* + * begin substring search functions * + ******************************************************************************/ +/* Build KMP table from left to right. */ +_UNUSED_ static void _utstring_BuildTable( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = 0; + j = i - 1; + P_KMP_Table[i] = j; + while (i < (long) P_NeedleLen) + { + while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j]; + } + i++; + j++; + if (i < (long) P_NeedleLen) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i] = P_KMP_Table[j]; + } + else + { + P_KMP_Table[i] = j; + } + } + else + { + P_KMP_Table[i] = j; + } + } + + return; +} + + +/* Build KMP table from right to left. */ +_UNUSED_ static void _utstring_BuildTableR( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = P_NeedleLen - 1; + j = i + 1; + P_KMP_Table[i + 1] = j; + while (i >= 0) + { + while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j + 1]; + } + i--; + j--; + if (i >= 0) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; + } + else + { + P_KMP_Table[i + 1] = j; + } + } + else + { + P_KMP_Table[i + 1] = j; + } + } + + return; +} + + +/* Search data from left to right. ( Multiple search mode. ) */ +_UNUSED_ static long _utstring_find( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from left to right. */ + i = j = 0; + while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) + { + while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i]; + } + i++; + j++; + if (i >= (int)P_NeedleLen) + { + /* Found. */ + V_FindPosition = j - i; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( Multiple search mode. ) */ +_UNUSED_ static long _utstring_findR( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from right to left. */ + j = (P_HaystackLen - 1); + i = (P_NeedleLen - 1); + while ( (j >= 0) && (j >= i) ) + { + while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i + 1]; + } + i--; + j--; + if (i < 0) + { + /* Found. */ + V_FindPosition = j + 1; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from left to right. ( One time search mode. ) */ +_UNUSED_ static long utstring_find( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = s->i - V_StartPosition; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_find(s->d + V_StartPosition, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + if (V_FindPosition >= 0) + { + V_FindPosition += V_StartPosition; + } + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( One time search mode. ) */ +_UNUSED_ static long utstring_findR( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = V_StartPosition + 1; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_findR(s->d, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} +/******************************************************************************* + * end substring search functions * + ******************************************************************************/ + +#endif /* UTSTRING_H */ diff --git a/gomspace/libutil/include/gs/util/base16.h b/gomspace/libutil/include/gs/util/base16.h new file mode 100644 index 00000000..0fddccc5 --- /dev/null +++ b/gomspace/libutil/include/gs/util/base16.h @@ -0,0 +1,90 @@ +#ifndef GS_UTIL_BASE16_H +#define GS_UTIL_BASE16_H +/* + * Copyright (C) 2010 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/** + @file + + Encoding and decoding base16 arrays to and from strings. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Calculate length of base16-encoded data + + @param raw_len Raw data length + @return Encoded string length (excluding NUL) +*/ +static inline size_t base16_encoded_len(size_t raw_len) +{ + return (2 * raw_len); +} + +/** + Calculate maximum length of base16-decoded string + @param encoded Encoded string + @return Maximum length of raw data +*/ +static inline size_t base16_decoded_max_len(const char *encoded) +{ + return ((strlen(encoded) + 1) / 2); +} + +/** + Base16-encode data + + The buffer must be the correct length for the encoded string. Use + something like + + char buf[ base16_encoded_len ( len ) + 1 ]; + + (the +1 is for the terminating NUL) to provide a buffer of the + correct size. + + @param raw Raw data + @param len Length of raw data + @param encoded Buffer for encoded string +*/ +void base16_encode(const uint8_t *raw, size_t len, char *encoded); + +/** + Base16-decode data + + The buffer must be large enough to contain the decoded data. Use + something like + + char buf[ base16_decoded_max_len ( encoded ) ]; + + to provide a buffer of the correct size. + + @param encoded Encoded string + @param raw Raw data + @return Length of raw data, or negative error (gs_error_t) +*/ +int base16_decode(const char *encoded, uint8_t *raw); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/bytebuffer.h b/gomspace/libutil/include/gs/util/bytebuffer.h new file mode 100644 index 00000000..ad727e01 --- /dev/null +++ b/gomspace/libutil/include/gs/util/bytebuffer.h @@ -0,0 +1,173 @@ +#ifndef GS_UTIL_BYTEBUFFER_h +#define GS_UTIL_BYTEBUFFER_h +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Byte buffer provides formatting/serialzing of text/binary data. The buffer keeps track of used space, and prevents overrun. + + The current buffer state can be checked using gs_bytebuffer_state(). + + @dontinclude bytebuffer/bytebuffer_test.c + @skip TEST_gs_bytebuffer_use_case + @until } +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Buffer handle. + Never access handle members directly. +*/ +typedef struct { + /** + Internal: Pointer to user supplied buffer. + @see gs_bytebuffer_init() + */ + uint8_t * buffer; + /** + Internal: Size of user supplied buffer. + @see gs_bytebuffer_init() + */ + size_t size; + /** + Internal: Number of bytes used. + */ + size_t used; + /** + Internal: FUTURE: Committed used + */ + size_t committed_used; + /** + Internal: flags to keep track of buffer state. + */ + uint8_t flags; +} gs_bytebuffer_t; + +/** + Initialize buffer. + + @param[in] bb handle. + @param[in] buffer user supplied buffer of \a buffer_size size (bytes). If NULL, the buffer will keep track of required bytes. + @param[in] buffer_size size of \a buffer. + @return_gs_error_t +*/ +gs_error_t gs_bytebuffer_init(gs_bytebuffer_t * bb, void * buffer, size_t buffer_size); + +/** + Insert data using vprintf. + + @param[in] bb handle. + @param[in] format printf syntax for formatting data + @param[in] ap variable argument list. +*/ +void gs_bytebuffer_vprintf(gs_bytebuffer_t * bb, const char * format, va_list ap); + +/** + Insert data using printf. + + @param[in] bb handle. + @param[in] format printf syntax for formatting data +*/ +void gs_bytebuffer_printf(gs_bytebuffer_t * bb, const char * format, ...) __attribute__ ((format (__printf__, 2, 3))); + +/** + Append data to buffer. + + @param[in] bb handle. + @param[in] data data to append to buffer. + @param[in] length length of data (bytes). +*/ +void gs_bytebuffer_append(gs_bytebuffer_t * bb, const void * data, size_t length); + +/** + Append string to buffer. + + @param[in] bb handle. + @param[in] string string to append to buffer. +*/ +void gs_bytebuffer_append_string(gs_bytebuffer_t * bb, const char * string); + +/** + Append string to buffer. + + @param[in] bb handle. + @param[in] string string to append to buffer. + @param[in] max_length max characters to append from \a string. +*/ +void gs_bytebuffer_append_string_max(gs_bytebuffer_t * bb, const char * string, size_t max_length); + +/** + Return buffer as string - enforcing NUL termination. + + This will always add a NUL termination (zero), which may lead to overflow/truncation of the string. + The NUL termination is NOT added to \a used count. + + @param[in] bb handle. + @param[out] error optional, state of buffer - see gs_bytebuffer_error(). + @return C-string (NUL terminated) +*/ +char * gs_bytebuffer_get_as_string(gs_bytebuffer_t * bb, gs_error_t * error); + +/** + Return buffer state. + + @param[in] bb handle. + @return GS_ERROR_OVERFLOW if data has been truncated. + @return GS_ERROR_DATA in case of error during formatting. + @return_gs_error_t +*/ +gs_error_t gs_bytebuffer_get_state(gs_bytebuffer_t * bb); + +/** + Return buffer (user supplied). + + @param[in] bb handle. +*/ +static inline void * gs_bytebuffer_get_buffer(gs_bytebuffer_t * bb) +{ + return bb->buffer; +} + +/** + Return buffer size (user supplied). + + @param[in] bb handle. + @return buffer size +*/ +static inline size_t gs_bytebuffer_get_size(gs_bytebuffer_t * bb) +{ + return bb->size; +} + +/** + Return number of free bytes. + + @param[in] bb handle. + @return number of free bytes. +*/ +static inline size_t gs_bytebuffer_get_free(gs_bytebuffer_t * bb) +{ + return (bb->size) ? (bb->size - bb->used) : 0; +} + +/** + Return number of used bytes. + + @param[in] bb handle. + @return used bytes. +*/ +static inline size_t gs_bytebuffer_get_used(gs_bytebuffer_t * bb) +{ + return bb->used; +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/byteorder.h b/gomspace/libutil/include/gs/util/byteorder.h new file mode 100644 index 00000000..3d2d6bef --- /dev/null +++ b/gomspace/libutil/include/gs/util/byteorder.h @@ -0,0 +1,341 @@ +#ifndef GS_UTIL_BYTEORDER_H +#define GS_UTIL_BYTEORDER_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Convert numbers between host and network order. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Convert value from host order to network order + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_htons(uint16_t value); + +/** + Convert value from network order to host order + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_ntohs(uint16_t value); + +/** + Convert value from host order to network order + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_htonl(uint32_t value); + +/** + Convert value from network order to host order + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_ntohl(uint32_t value); + +/** + Convert value from host order to network order + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_hton16(uint16_t value); + +/** + Convert value from host order to network order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_hton16_array(const uint16_t * from, uint16_t * to, size_t count); + +/** + Convert value from network order to host order + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_ntoh16(uint16_t value); + +/** + Convert value from network order to host order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_ntoh16_array(const uint16_t * from, uint16_t * to, size_t count); + +/** + Convert value from host order to network order + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_hton32(uint32_t value); + +/** + Convert value from host order to network order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_hton32_array(const uint32_t * from, uint32_t * to, size_t count); + +/** + Convert value from network order to host order + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_ntoh32(uint32_t value); + +/** + Convert value from network order to host order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_ntoh32_array(const uint32_t * from, uint32_t * to, size_t count); + +/** + Convert value from host order to network order + @param[in] value value to convert. + @return converted value. +*/ +uint64_t util_hton64(uint64_t value); + +/** + Convert value from host order to network order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_hton64_array(const uint64_t * from, uint64_t * to, size_t count); + +/** + Convert value from network order to host order + @param[in] value value to convert. + @return converted value. +*/ +uint64_t util_ntoh64(uint64_t value); + +/** + Convert value from network order to host order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_ntoh64_array(const uint64_t * from, uint64_t * to, size_t count); + +/** + Convert value from host order to network order + @param[in] value value to convert. + @return converted value. +*/ +float util_htonflt(float value); + +/** + Convert value from host order to network order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_htonflt_array(const float * from, float * to, size_t count); + +/** + Convert value from network order to host order + @param[in] value value to convert. + @return converted value. +*/ +float util_ntohflt(float value); + +/** + Convert value from network order to host order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_ntohflt_array(const float * from, float * to, size_t count); + +/** + Convert value from host order to network order + @param[in] value value to convert. + @return converted value. +*/ +double util_htondbl(double value); + +/** + Convert value from host order to network order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_htondbl_array(const double * from, double * to, size_t count); + +/** + Convert value from network order to host order + @param[in] value value to convert. + @return converted value. +*/ +double util_ntohdbl(double value); + +/** + Convert value from network order to host order + @param[in] from value to convert. + @param[out] to value converted. + @param[in] count element count +*/ +void util_ntohdbl_array(const double * from, double * to, size_t count); + +/** + Convert value from host order to big endian. + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_htobe16(uint16_t value); + +/** + Convert value from host order to little endian. + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_htole16(uint16_t value); + +/** + Convert value from big endian to host order. + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_betoh16(uint16_t value); + +/** + Convert value from little endian to host order. + @param[in] value value to convert. + @return converted value. +*/ +uint16_t util_letoh16(uint16_t value); + +/** + Convert value from host order to big endian. + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_htobe32(uint32_t value); + +/** + Convert value from host order to little endian. + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_htole32(uint32_t value); + +/** + Convert value from big endian to host order. + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_betoh32(uint32_t value); + +/** + Convert value from little endian to host order. + @param[in] value value to convert. + @return converted value. +*/ +uint32_t util_letoh32(uint32_t value); + +/** + Convert value from host order to big endian. + @param[in] value value to convert. + @return converted value. +*/ +uint64_t util_htobe64(uint64_t value); + +/** + Convert value from host order to little endian. + @param[in] value value to convert. + @return converted value. +*/ +uint64_t util_htole64(uint64_t value); + +/** + Convert value from big endian to host order. + @param[in] value value to convert. + @return converted value. +*/ +uint64_t util_betoh64(uint64_t value); + +/** + Convert value from little endian to host order. + @param[in] value value to convert. + @return converted value. +*/ +uint64_t util_letoh64(uint64_t value); + +/** + Byte swap. + @param[in] value value to byteswap. + @return swapped value +*/ +uint16_t gs_bswap_16(uint16_t value); + +/** + Byte swap array. + @param[in] from from address. + @param[out] to to address. + @param[in] count element count. +*/ +void gs_bswap_16_array(const uint16_t * from, uint16_t * to, size_t count); + +/** + Byte swap. + @param[in] value value to byteswap. + @return swapped value +*/ +uint32_t gs_bswap_32(uint32_t value); + +/** + Byte swap array. + @param[in] from from address. + @param[out] to to address. + @param[in] count element count. +*/ +void gs_bswap_32_array(const uint32_t * from, uint32_t * to, size_t count); + +/** + Byte swap. + @param[in] value value to byteswap. + @return swapped value +*/ +uint64_t gs_bswap_64(uint64_t value); + +/** + Byte swap array. + @param[in] from from address. + @param[out] to to address. + @param[in] count element count. +*/ +void gs_bswap_64_array(const uint64_t * from, uint64_t * to, size_t count); + +/** + Byte swap. + @param[in] value value to byteswap. + @return swapped value +*/ +float gs_bswap_float(float value); + +/** + Byte swap array. + @param[in] from from address. + @param[out] to to address. + @param[in] count element count. +*/ +void gs_bswap_float_array(const float * from, float * to, size_t count); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/check.h b/gomspace/libutil/include/gs/util/check.h new file mode 100644 index 00000000..23920161 --- /dev/null +++ b/gomspace/libutil/include/gs/util/check.h @@ -0,0 +1,54 @@ +#ifndef GS_UTIL_CHECK_H +#define GS_UTIL_CHECK_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Argument checking. + + Logs can be enabled through a define. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if (GS_CHECK_LOG) +#define GS_CHECK_HANDLE(check) if (!(check)) { log_error("Invalid handle - assert: " GS_DEF2STRING(check)); return GS_ERROR_HANDLE;} +#define GS_CHECK_ARG(check) if (!(check)) { log_error("Invalid argument - assert: " GS_DEF2STRING(check)); return GS_ERROR_ARG;} +#define GS_CHECK_SUPPORTED(check) if (!(check)) { log_error("Not supported - assert: " GS_DEF2STRING(check)); return GS_ERROR_NOT_SUPPORTED;} +#define GS_CHECK_RANGE(check) if (!(check)) { log_error("Invalid range - assert: " GS_DEF2STRING(check)); return GS_ERROR_RANGE;} +#else +/** + Perform evalution of 'check' and return GS_ERROR_HANDLE if not 'true'. +*/ +#define GS_CHECK_HANDLE(check) if (!(check)) { return GS_ERROR_HANDLE;} +/** + Perform evalution of 'check' and return GS_ERROR_ARG if not 'true'. +*/ +#define GS_CHECK_ARG(check) if (!(check)) { return GS_ERROR_ARG;} +/** + Perform evalution of 'check' and return GS_ERROR_NOT_SUPPORTED if not 'true'. +*/ +#define GS_CHECK_SUPPORTED(check) if (!(check)) { return GS_ERROR_NOT_SUPPORTED;} +/** + Perform evalution of 'check' and return GS_ERROR_RANGE if not 'true'. +*/ +#define GS_CHECK_RANGE(check) if (!(check)) { return GS_ERROR_RANGE;} +#endif + +/** + Assert on 'value'. + + @deprecated use GS_STATIC_ASSERT() +*/ +#define GS_CHECK_STATIC_ASSERT(condition, name) GS_STATIC_ASSERT(condition, name) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/clock.h b/gomspace/libutil/include/gs/util/clock.h new file mode 100644 index 00000000..1d4a9548 --- /dev/null +++ b/gomspace/libutil/include/gs/util/clock.h @@ -0,0 +1,88 @@ +#ifndef GS_UTIL_CLOCK_H +#define GS_UTIL_CLOCK_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Get/set time (including RTC), convert to/from string. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Returns real time/clock (UTC - time since Epoch/1970). + + If the platform supports a Real Time Clock, the RTC is normally read on first call. An offset is calculated for the relative clock, which + then is used to calculate the actual time. + + @note clock_get_time() is proto-typed in libcsp as weak, but with different argument which MUST match gs_timestamp_t. + @param[out] time user allocated buffer, contaning the current UTC time. +*/ +void gs_clock_get_time(gs_timestamp_t * time); + +/** + Set real time/clock (UTC). + If the platform supports a Real Time Clock, the RTC is also updated. + @param[in] time UTC time. + @return_gs_error_t +*/ +gs_error_t gs_clock_set_time(const gs_timestamp_t * time); + +/** + Returns elapsed time since some unspecified starting point. + @param[out] time user allocated buffer, receives elapsed time. + @see gs_time_rel_ms() +*/ +void gs_clock_get_monotonic(gs_timestamp_t * time); + +/** + Returns number of elapsed nano-seconds since some unspecified starting point. + @return nano-seconds. +*/ +uint64_t gs_clock_get_nsec(void); + +/** + Buffer length for containing full ISO8601 timestamp - including zero (0) termination. +*/ +#define GS_CLOCK_ISO8601_BUFFER_LENGTH 21 + +/** + Convert UTC to a ISO8601 string. + ISO8601 timestamp: 2017-03-30T06:20:45Z + @param[in] utc_time UTC time. + @param[out] buffer user allocated buffer. + @param[in] buffer_size size of \a buf. + @return_gs_error_t +*/ +gs_error_t gs_clock_to_iso8601_string(const gs_timestamp_t * utc_time, char * buffer, size_t buffer_size); + +/** + Convert UTC to a ISO8601 string. + ISO8601 timestamp: 2017-03-30T06:20:45Z + @param[in] utc_sec UTC seconds. + @param[out] buffer user allocated buffer. + @param[in] buffer_size size of \a buf. + @return_gs_error_t +*/ +gs_error_t gs_clock_to_iso8601_string2(uint32_t utc_sec, char * buffer, size_t buffer_size); + +/** + Convert string (UTC time) to timstamp. + Parse string as: + 1. \.\ - number of seconds elapsed since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). + 2. YYYY-MM-DDTHH:MM:SSZ - ISO8601 + @param[in] str time + @param[out] ts time + @return_gs_error_t +*/ +gs_error_t gs_clock_from_string(const char * str, gs_timestamp_t * ts); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/crc32.h b/gomspace/libutil/include/gs/util/crc32.h new file mode 100644 index 00000000..f2be6775 --- /dev/null +++ b/gomspace/libutil/include/gs/util/crc32.h @@ -0,0 +1,55 @@ +#ifndef GS_UTIL_CRC32_H +#define GS_UTIL_CRC32_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + CRC32 checksumes. + + https://en.wikipedia.org/wiki/Cyclic_redundancy_check. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Return init/seed value for CRC-32. + @return initial/seed value for CRC-32, using 0xffffffff. + @see gs_crc32_update(), gs_crc32_finalize() +*/ +uint32_t gs_crc32_init(void); + +/** + Update CRC-32. + @param[in] crc current CRC-32 + @param[in] block start of memory block. + @param[in] length length of \a block. + @return updated CRC-32. + @see gs_crc32_init(), gs_crc32_finalize() +*/ +uint32_t gs_crc32_update(uint32_t crc, const void * block, size_t length); + +/** + Return finalized CRC-32. + @param[in] crc Checksum is finalized by xor'ing 0xffffffff. + @return finalized CRC-32. + @see gs_crc32_init(), gs_crc32_update() +*/ +uint32_t gs_crc32_finalize(uint32_t crc); + +/** + Return finalized CRC-32 on amemory block. + + @param[in] block block to calculate CRC-32 on. + @param[in] length length/size of \a block. + @return finalized CRC-32. +*/ +uint32_t gs_crc32(const void *block, size_t length); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/crc8.h b/gomspace/libutil/include/gs/util/crc8.h new file mode 100644 index 00000000..99b14d0a --- /dev/null +++ b/gomspace/libutil/include/gs/util/crc8.h @@ -0,0 +1,55 @@ +#ifndef GS_UTIL_CRC8_H +#define GS_UTIL_CRC8_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + CRC8 checksumes. + + https://en.wikipedia.org/wiki/Cyclic_redundancy_check. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Return init/seed value for CRC-8. + @return initial/seed value for CRC-8, using 0xff. + @see gs_crc8_update(), gs_crc8_finalize() +*/ +uint8_t gs_crc8_init(void); + +/** + Update CRC-8. + @param[in] crc current CRC-8 + @param[in] block start of memory block. + @param[in] length length of \a block. + @return updated CRC-8. + @see gs_crc8_init(), gs_crc8_finalize() +*/ +uint8_t gs_crc8_update(uint8_t crc, const void * block, size_t length); + +/** + Return finalized CRC-8. + @param[in] crc Checksum is finalized by xor'ing 0xffffffff. + @return finalized CRC-8. + @see gs_crc8_init(), gs_crc8_update() +*/ +uint8_t gs_crc8_finalize(uint8_t crc); + +/** + Return finalized CRC-8 on amemory block. + + @param[in] block block to calculate CRC-8 on. + @param[in] length length/size of \a block. + @return finalized CRC-8. +*/ +uint8_t gs_crc8(const void *block, size_t length); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/delay.h b/gomspace/libutil/include/gs/util/delay.h new file mode 100644 index 00000000..d205b48c --- /dev/null +++ b/gomspace/libutil/include/gs/util/delay.h @@ -0,0 +1,42 @@ +#ifndef GS_UTIL_DELAY_H +#define GS_UTIL_DELAY_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Delay execution. + + @note Most implementations uses busy waiting. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Delay for number of microseconds. + @note Linux doesn't busy wait. + @param us Number of microseconds to wait +*/ +void gs_delay_us(uint32_t us); + +/** + Return current counter used for us delays + @return timestamp in us +*/ +uint16_t gs_delay_ts_get(void); + +/** + Wait until delay has passed since timestamp + + @param[in] ts Timestamp in us + @param[in] delay The requested delay since ts +*/ +void gs_delay_from_ts(uint16_t ts, uint16_t delay); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/can/can.h b/gomspace/libutil/include/gs/util/drivers/can/can.h new file mode 100644 index 00000000..27f7acd5 --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/can/can.h @@ -0,0 +1,122 @@ +#ifndef GS_UTIL_DRIVERS_CAN_CAN_H +#define GS_UTIL_DRIVERS_CAN_CAN_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + CAN interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default log group for CAN driver. +*/ +GS_LOG_GROUP_EXTERN(gs_can_log); + +/** + Bit-rate (default). +*/ +#define GS_CAN_DEFAULT_BPS 1000000 + +/** + Callback for handling received data (from CAN driver). + @param[in] device hardware device + @param[in] canMsgId standard or extended message id. + @param[in] extendedMsgId \a true if extended id, \a false if standard id. + @param[in] data pointer to data. + @param[in] data_size size of data. + @param[in] nowMs current relative time in mS. + @param[in] user_data user data. + @param[in] cswitch If called from within an ISR (embedded platform), this will none NULL. +*/ +typedef void (*gs_can_rxdata_callback_t)(int hdl, + uint32_t canMsgId, + bool extendedMsgId, + const void * data, + size_t data_size, + uint32_t nowMs, + void * user_data, + gs_context_switch_t * cswitch); + +/** + Send CAN message with standard id (11 bits). + @param[in] device hardware device + @param[in] canMsgId standard CAN message id. + @param[in] data pointer to data. + @param[in] data_size size of data. + @param[in] timeout_ms timeout in mS. + @return GS_ERROR_FULL if Tx queue is full + @return_gs_error_t +*/ +gs_error_t gs_can_send_standard(uint8_t device, uint32_t canMsgId, const void * data, size_t data_size, int timeout_ms); + +/** + Send CAN message with exended id (29 bits). + @param[in] device hardware device + @param[in] canExtMsgId exteneded message id. + @param[in] data pointer to data. + @param[in] data_size size of data. + @param[in] timeout_ms timeout in mS. + @return GS_ERROR_FULL if Tx queue is full + @return_gs_error_t +*/ +gs_error_t gs_can_send_extended(uint8_t device, uint32_t canExtMsgId, const void * data, size_t data_size, int timeout_ms); + +/** + Set filter and callback for standard message id. + @param[in] device hardware device + @param[in] canMsgId standard message id. + @param[in] mask filter mask. + @param[in] rx_callback callback function. + @param[in] rx_user_data user data provided in callback. + @return GS_ERROR_FULL if all message id slots are used. + @return_gs_error_t +*/ +gs_error_t gs_can_set_standard_filter_mask(uint8_t device, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * rx_user_data); + +/** + Set filter and callback for extended message id. + @param[in] device hardware device + @param[in] canExtMsgId extended message id. + @param[in] mask filter mask. + @param[in] rx_callback callback function. + @param[in] rx_user_data user data provided in callback. + @return GS_ERROR_FULL if all message id slots are used. + @return_gs_error_t +*/ +gs_error_t gs_can_set_extended_filter_mask(uint8_t device, uint32_t canExtMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * rx_user_data); + +/** + Stop CAN layer. + If a CAN transceiver is present and controlled, it will be disabled. + @param[in] device hardware device + @return_gs_error_t +*/ +gs_error_t gs_can_stop(uint8_t device); + +/** + Start CAN layer. + Clear all buffers and start CAN. + If a CAN transceiver is present and controlled, it will be enabled. + @param[in] device hardware device + @return_gs_error_t +*/ +gs_error_t gs_can_start(uint8_t device); + +/** + Get current CAN layer error state. + @param[in] device hardware device + @param[out] restart_required \a true if CAN layer should be re-started. Pass NULL, if not wanted. + @return_gs_error_t +*/ +gs_error_t gs_can_error_state(uint8_t device, bool * restart_required); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/gpio/gpio.h b/gomspace/libutil/include/gs/util/drivers/gpio/gpio.h new file mode 100644 index 00000000..ff2803c0 --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/gpio/gpio.h @@ -0,0 +1,91 @@ +#ifndef GS_UTIL_DRIVERS_GPIO_GPIO_H +#define GS_UTIL_DRIVERS_GPIO_GPIO_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + GPIO interface provides a generic interface toward hardware GPIO's. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + GPIO definition. +*/ +typedef struct { + //! Chip/group/port number which the GPIO belongs to. + uint16_t port; + //! The pin number of the GPIO. + uint16_t pin; +} gs_gpio_t; + +/** + GPIO interrupt function. +*/ +typedef void (*gs_gpio_isr_t)(gs_context_switch_t * cswitch); + +/** + Configuration for interrupt related to a GPIO. +*/ +typedef struct { + //! True if it shall trigger on rising edge. + bool rising_edge; + //! True if it shall trigger on falling edge. + bool falling_edge; + //! True if it shall have high priority (if nested isr supported). + bool high_priority; + //! ISR to be called on trigger. + gs_gpio_isr_t isr; +} gs_interrupt_conf_t; + +/** + GPIO get value + + @param[in] gpio The gpio to read + @param[in] value Returned GPIO value (true/false = High/Low) + @return_gs_error_t +*/ +gs_error_t gs_gpio_get(gs_gpio_t gpio, bool *value); + +/** + GPIO get value without error check + + @param[in] gpio The gpio to read + @return GPIO value (true/false = High/Low) +*/ +bool gs_gpio_get_nc(gs_gpio_t gpio); + +/** + GPIO set value + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @return_gs_error_t +*/ +gs_error_t gs_gpio_set(gs_gpio_t gpio, bool value); + +/** + GPIO set value without error check + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) +*/ +void gs_gpio_set_nc(gs_gpio_t gpio, bool value); + +/** + Initialize GPIO as an external interrupt pin. + + @param[in] gpio The gpio to configure + @param[in] conf Configuration of interrupt pin + @return_gs_error_t + */ +gs_error_t gs_gpio_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/i2c/common.h b/gomspace/libutil/include/gs/util/drivers/i2c/common.h new file mode 100644 index 00000000..895847d3 --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/i2c/common.h @@ -0,0 +1,88 @@ +#ifndef GS_UTIL_DRIVERS_I2C_COMMON_H +#define GS_UTIL_DRIVERS_I2C_COMMON_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Common (master and slave) I2C definitions. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default log group for I2C driver. +*/ +GS_LOG_GROUP_EXTERN(gs_i2c_log); + +/** + I2C mode. +*/ +typedef enum { + //! Master mode + GS_I2C_MASTER = 0, + //! Multimaster mode + GS_I2C_MULTI_MASTER = 1, + //! Slave mode + GS_I2C_SLAVE = 2, +} gs_i2c_mode_t; + +/** + Cross-platform I2C configuration. +*/ +typedef struct { + //! Data order, True: MSB first, False: LSB first (default = True) + bool data_order_msb; + //! Device mode (master, multimaster, or slave) + gs_i2c_mode_t mode; + //! Address of node in multimaster and slave mode (not used in master mode) + uint16_t addr; + //! Bits per second (default is #GS_I2C_DEFAULT_BPS) + uint32_t bps; + //! Address size in bits, 7, 8 or 10 bits (default/prefered is #GS_I2C_DEFAULT_ADDRESS_SIZE) + uint8_t addrbits; +} gs_i2c_config_t; + +/** + Cross-platform I2C configuration. + @deprecated use gs_i2c_config_t. +*/ +typedef gs_i2c_config_t gs_i2c_bus_config_t; + +/** + Default bit-rate. +*/ +#define GS_I2C_DEFAULT_BPS 100000 + +/** + Default address size. +*/ +#define GS_I2C_DEFAULT_ADDRESS_SIZE 7 + +/** + Default data order (MSB). +*/ +#define GS_I2C_DEFAULT_DATA_ORDER_MSB 1 + +/** + Speed (command line sub-option). +*/ +#define GS_I2C_COMMAND_LINE_SPEED "speed" + +/** + Device (command line sub-option). +*/ +#define GS_I2C_COMMAND_LINE_DEVICE "device" + +/** + Address (command line sub-option). +*/ +#define GS_I2C_COMMAND_LINE_ADDRESS "address" + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/i2c/master.h b/gomspace/libutil/include/gs/util/drivers/i2c/master.h new file mode 100644 index 00000000..169d5d2a --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/i2c/master.h @@ -0,0 +1,32 @@ +#ifndef GS_UTIL_DRIVERS_I2C_MASTER_H +#define GS_UTIL_DRIVERS_I2C_MASTER_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + I2C master interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Perform transaction to I2C slave. + @param[in] device hardware device (bus) + @param[in] addr slave address + @param[in] tx transmit buffer + @param[in] txlen number of bytes to transmit + @param[out] rx receive buffer - can be NULL. + @param[in] rxlen number of bytes to receive. + @param[in] timeout_ms timeout in milliseconds, primarily for locking the I2C channel. + @return_gs_error_t +*/ +gs_error_t gs_i2c_master_transaction(uint8_t device, uint8_t addr, const void * tx, size_t txlen, void * rx, size_t rxlen, int timeout_ms); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/i2c/slave.h b/gomspace/libutil/include/gs/util/drivers/i2c/slave.h new file mode 100644 index 00000000..540000e3 --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/i2c/slave.h @@ -0,0 +1,79 @@ +#ifndef GS_UTIL_DRIVERS_I2C_SLAVE_H +#define GS_UTIL_DRIVERS_I2C_SLAVE_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + I2C slave interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Start/enable I2C bus reception. + + Reception should not automatically be enabled by their init() functions, as this will complicate adding additional layers/hooks. + + @param[in] device I2C bus (handle) + @return_gs_error_t +*/ +gs_error_t gs_i2c_slave_start(uint8_t device); + +/** + Rx callback. + + Function called when data has been received on the bus (I2C write operation complete). + + @param[in] device I2C bus (handle). + @param[in] rx receive buffer. + @param[in] rx_length number of bytes received. + @param_cswitch +*/ +typedef void (* gs_i2c_slave_receive_t)(uint8_t device, const uint8_t * rx, size_t rx_length, gs_context_switch_t * cswitch); + +/** + Set rx callback. + + @param[in] device I2C bus (handle). + @param[in] rx Rx callback. + @return_gs_error_t +*/ +gs_error_t gs_i2c_slave_set_rx(uint8_t device, gs_i2c_slave_receive_t rx); + +/** + Get rx buffer callback. + + Function called from driver, for getting a pointer to the rx buffer. + + @param[in] device I2C bus (handle). +*/ +typedef void * (* gs_i2c_slave_get_rx_buf_t)(uint8_t device); + +/** + Set rx buffer get callback. + + @param[in] device I2C bus (handle). + @param[in] get_rx_buf get rx buffer callback. + @param[in] buf_length length of buffer retrieved with this callback. + @return_gs_error_t +*/ +gs_error_t gs_i2c_slave_set_get_rx_buf(uint8_t device, gs_i2c_slave_get_rx_buf_t get_rx_buf, size_t buf_length); + +/** + Set response data. + + @param[in] device I2C bus (handle). + @param[in] tx pointer to data. NOTE: data is not copied due to performance, so data must stay valid until the response has been sent. + @param[in] tx_length length of data. + @return_gs_error_t +*/ +gs_error_t gs_i2c_slave_set_response(uint8_t device, const uint8_t * tx, size_t tx_length); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/spi/common.h b/gomspace/libutil/include/gs/util/drivers/spi/common.h new file mode 100644 index 00000000..069a346e --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/spi/common.h @@ -0,0 +1,66 @@ +#ifndef GS_UTIL_DRIVERS_SPI_COMMON_H +#define GS_UTIL_DRIVERS_SPI_COMMON_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Common (master and slave) SPI definitions. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default log group for SPI driver. +*/ +GS_LOG_GROUP_EXTERN(gs_spi_log); + +/** + SPI mode - clock polarity and phase. +*/ +typedef enum { + /** + Polarity = 0, Phase = 0 (default). + */ + GS_SPI_MODE_CPOL0_CPHA0 = 0, + /** + Polarity = 0, Phase = 1. + */ + GS_SPI_MODE_CPOL0_CPHA1 = 1, + /** + Polarity = 1, Phase = 0. + */ + GS_SPI_MODE_CPOL1_CPHA0 = 2, + /** + Polarity = 1, Phase = 1. + */ + GS_SPI_MODE_CPOL1_CPHA1 = 3 +} gs_spi_mode_t; + +/** + Default bit-rate. +*/ +#define GS_SPI_DEFAULT_BPS 400000 + +/** + Speed (command line sub-option). +*/ +#define GS_SPI_COMMAND_LINE_SPEED "speed" + +/** + Slave (command line sub-option). +*/ +#define GS_SPI_COMMAND_LINE_SLAVE "slave" + +/** + Device (command line sub-option). +*/ +#define GS_SPI_COMMAND_LINE_DEVICE "device" + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/spi/master.h b/gomspace/libutil/include/gs/util/drivers/spi/master.h new file mode 100644 index 00000000..986f1ce4 --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/spi/master.h @@ -0,0 +1,95 @@ +#ifndef GS_UTIL_DRIVERS_SPI_MASTER_H +#define GS_UTIL_DRIVERS_SPI_MASTER_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + SPI master interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Cross-platform master SPI configuration. +*/ +typedef struct { + /** + Data order, \a True: MSB first, \a False: LSB first + Default: \a True. + */ + bool data_order_msb; + /** + Bits per second. + Default: #GS_SPI_DEFAULT_BPS. + */ + uint32_t bps; + /** + Mode, specifying polarity and phase. + Default: #GS_SPI_MODE_CPOL0_CPHA0. + */ + gs_spi_mode_t mode; + /** + Character size in bits, 8-16 bits. + Default: 8 bits (prefered). + */ + uint8_t bits; +} gs_spi_master_slave_config_t; + +/** + Single master transaction. +*/ +typedef struct { + /** + Pointer to tx data, or NULL if no tx. + */ + const void *tx; + /** + Pointer to rx buffer, or NULL if no rx. + */ + void *rx; + /** + Size/length of rx/tx (bytes). + */ + size_t size; +} gs_spi_master_trans_t; + +/** + Close/free slave. + Freeing resources associated with the slave. + @param[in] slave SPI slave + @return_gs_error_t +*/ +gs_error_t gs_spi_master_close_slave(uint8_t slave); + +/** + Perform transaction to/from a pre-configured SPI slave. + Basically for i < size: send tx[i] and receive rx[i]. + @note: 8 bit SPI character size required! + @param[in] slave SPI slave + @param[in] tx tx buffer + @param[out] rx rx buffer - can be NULL. + @param[in] size number of to send and also receive. + @param[in] timeout_ms timeout in milliseconds, primarily for locking the SPI device. + @return_gs_error_t +*/ +gs_error_t gs_spi_master_transaction(uint8_t slave, const void * tx, void * rx, size_t size, int timeout_ms); + +/** + Perform N transaction to/from a pre-configured SPI slave within one chip selection + @note: 8 bit SPI character size required! + @param[in] slave SPI slave + @param[in] trans Pointer to transactions + @param[in] count Number of transactions (rx and/or tx) to complete + @param[in] timeout_ms timeout in milliseconds, primarily for locking the SPI device. + @return_gs_error_t +*/ +gs_error_t gs_spi_master_transactions(uint8_t slave, gs_spi_master_trans_t *trans, size_t count, int timeout_ms); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/spi/slave.h b/gomspace/libutil/include/gs/util/drivers/spi/slave.h new file mode 100644 index 00000000..0be02a8e --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/spi/slave.h @@ -0,0 +1,84 @@ +#ifndef GS_UTIL_DRIVERS_SPI_SLAVE_H +#define GS_UTIL_DRIVERS_SPI_SLAVE_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + SPI slave interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Cross-platform slave SPI configuration. +*/ +typedef struct { + /** + Data order, \a True: MSB first, \a False: LSB first + Default: \a True. + */ + bool data_order_msb; + /** + Mode, specifying polarity and phase. + Default: #GS_SPI_MODE_CPOL0_CPHA0. + */ + gs_spi_mode_t mode; + /** + Character size in bits, 8-16 bits. + Default: 8 bits (prefered). + */ + uint8_t bits; +} gs_spi_slave_config_t; + +/** + Start/enable SPI device reception. + + Reception should not automatically be enabled by their init() functions, as this will complicate adding additional layers/hooks. + + @param[in] device SPI device (handle) + @return_gs_error_t +*/ +gs_error_t gs_spi_slave_start(uint8_t device); + +/** + Rx callback. + + Function called as data is recevied on the device. + + @param[in] device SPI device (handle). + @param[in] rx_buffer Pointer to start of rx buffer. + @param[in] rx number of bytes received so far. + @param[in] new_request \a true on the first callback of new data, \a false on receiving additional data during same \a chip-select. Can be used to bring receiver back in sync with new request. + @param_cswitch + @return total number of bytes to receive before next call back. Return 0 to ignore rest of data - no additional call backs will be done for current SPI transaction. +*/ +typedef uint8_t (* gs_spi_slave_receive_t)(uint8_t device, const uint8_t * rx_buffer, size_t rx, bool new_request, gs_context_switch_t * cswitch); + +/** + Set rx callback. + + @param[in] device SPI device (handle). + @param[in] rx Rx callback. + @return_gs_error_t +*/ +gs_error_t gs_spi_slave_set_rx(uint8_t device, gs_spi_slave_receive_t rx); + +/** + Set response data. + + @param[in] device SPI device (handle). + @param[in] offset offset (in bytes) for the response, counted from start of request, i.e. offset of 2 means data will be sent as the 3rd byte. + @param[in] tx pointer to data. NOTE: data is not copied due to performance, so data must stay valid until the response has been sent. + @param[in] size size of data. + @return_gs_error_t +*/ +gs_error_t gs_spi_slave_set_response(uint8_t device, size_t offset, const uint8_t * tx, size_t size); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/sys/memory.h b/gomspace/libutil/include/gs/util/drivers/sys/memory.h new file mode 100644 index 00000000..ca3862df --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/sys/memory.h @@ -0,0 +1,92 @@ +#ifndef GS_UTIL_DRIVERS_SYS_MEMORY_H +#define GS_UTIL_DRIVERS_SYS_MEMORY_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Cross platform memory status API. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + RAM status + Containing different size parameters describing RAM usage. + All sizes are in bytes. + If a parameter is not available/supported on a specific platform, the parameter is set to -1. + */ +typedef struct { + //! total size of RAM + long total; + //! max available RAM for allocation after initialization of of global/static variables + long max_available; + //! available RAM at runtime for dynamic allocation + long available; + //! Lowest registered available RAM since boot + long min_available; +} gs_mem_ram_stat_t; + +/** + RAM types + Defines the different RAM types (external/internal) supported on + the various platforms. + */ +typedef enum { + GS_MEM_RAM_TYPE_INTERNAL = 0,//!< Internal RAM type + GS_MEM_RAM_TYPE_EXTERNAL //!< External RAM type +} gs_mem_ram_type_t; + +/** + Get status of internal RAM + + @param[out] ram_stat RAM status, each member of struct is -1 if not supported on specific platform + @return_gs_error_t + */ +gs_error_t gs_mem_get_int_ram_stat(gs_mem_ram_stat_t * ram_stat); + +/** + Get status of external RAM + + @param[out] ram_stat RAM status, each member of struct is -1 if not supported on specific platform + @return_gs_error_t + */ +gs_error_t gs_mem_get_ext_ram_stat(gs_mem_ram_stat_t * ram_stat); + + +/** + Get status of selected RAM + + @param[in] type RAM type to query status for + @param[out] ram_stat RAM status, each member of struct is -1 if not supported on specific platform + @return_gs_error_t + */ +gs_error_t gs_mem_get_ram_stat(gs_mem_ram_type_t type, gs_mem_ram_stat_t * ram_stat); + + +/** + Get default RAM type + + returns the default RAM type used for allocations (Heap). + @return gs_mem_ram_type_t + */ +gs_mem_ram_type_t gs_mem_get_ram_default(); + + +/** + Print RAM status. + + @param[in] ram_stat RAM status + @param[in] out output stream + @return_gs_error_t + */ +gs_error_t gs_mem_print_ram_stat(gs_mem_ram_stat_t * ram_stat, FILE * out); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/drivers/watchdog/device.h b/gomspace/libutil/include/gs/util/drivers/watchdog/device.h new file mode 100644 index 00000000..613e511e --- /dev/null +++ b/gomspace/libutil/include/gs/util/drivers/watchdog/device.h @@ -0,0 +1,61 @@ +#ifndef GS_UTIL_DRIVERS_HW_WATCHDOG_H +#define GS_UTIL_DRIVERS_HW_WATCHDOG_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Hardward watchdog (HWWD) device interface. + + Hardware Watchdog interface which provides a generic interface towards + any HWWD. Most HWWD implementation should be able to fit behind + this interface, with just a small "adaption" layer needed. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Hardware watchdog driver interface. +*/ +typedef struct gs_watchdog_dev_ops gs_watchdog_dev_ops_t; + +/** + Hardware watchdog (HWWD) device structure + + Structure that describes the HWWD device and holds + the parameters needed for storing e.g. timeout values etc. +*/ +typedef struct gs_watchdog_device { + int id; /**< An ID for the HWWD device - This is currently not used. */ + const gs_watchdog_dev_ops_t *ops; /**< Pointer to ops struct defining the operations a HWWD device supports. */ + unsigned int timeout; /**< The timeout value that the HWWD device should be configured with. */ + unsigned int pretimeout; /**< The pretimeout (if supported) by the HWWD device */ + unsigned int min_timeout; /**< Minimum timeout value supported by the HWWD device */ + unsigned int max_timeout; /**< Maximum timeout value supported by the HWWD device */ + void *driver_data; /**< Pointer to driver specific data can be used by the HWWD driver impl. */ +} gs_watchdog_device_t; + +/** + Hardware watchdog driver interface. +*/ +struct gs_watchdog_dev_ops +{ + /* mandatory operations */ + gs_error_t (*start)(gs_watchdog_device_t *); /**< Starts the HWWD device */ + gs_error_t (*stop)(gs_watchdog_device_t *); /**< Stops the HWWD device */ + gs_error_t (*ping)(gs_watchdog_device_t *); /**< Polls the HWWD device and restart count-down */ + /* optional operations */ + gs_error_t (*set_timeout)(gs_watchdog_device_t *, unsigned int); /**< (Optional) Set timeout of the HWWD device */ + gs_error_t (*set_pretimeout)(gs_watchdog_device_t *, unsigned int); /**< (Optional) Set Pre-timeout of the HWWD device */ + gs_error_t (*restart)(gs_watchdog_device_t *); /**< (Optional) Restart the HWWD device */ + unsigned int (*get_timeleft)(gs_watchdog_device_t *); /**< (Optional) Get time left until HWWD device times out. */ + int (*status)(gs_watchdog_device_t *); /**< (Optional) Reads status of the HWWD device */ +}; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/endian.h b/gomspace/libutil/include/gs/util/endian.h new file mode 100644 index 00000000..15e22ae1 --- /dev/null +++ b/gomspace/libutil/include/gs/util/endian.h @@ -0,0 +1,53 @@ +#ifndef GS_UTIL_ENDIAN_H +#define GS_UTIL_ENDIAN_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Detecting endian type. +*/ + +// generated by waf configure, defines either UTIL_BIG_ENDIAN or UTIL_LITTLE_ENDIAN +#include "../../conf_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !UTIL_BIG_ENDIAN && !UTIL_LITTLE_ENDIAN + #error No endian defined +#endif +#if UTIL_BIG_ENDIAN && UTIL_LITTLE_ENDIAN + #error Both big and little endian defined +#endif + +#include + +/** + Returns \a true if platform is big endian. +*/ +static inline bool gs_endian_big(void) +{ +#if (UTIL_BIG_ENDIAN) + return true; +#else + return false; +#endif +} + +/** + Returns \a true if platform is little endian. +*/ +static inline bool gs_endian_little(void) +{ +#if (UTIL_LITTLE_ENDIAN) + return true; +#else + return false; +#endif +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/error.h b/gomspace/libutil/include/gs/util/error.h new file mode 100644 index 00000000..d1743165 --- /dev/null +++ b/gomspace/libutil/include/gs/util/error.h @@ -0,0 +1,199 @@ +#ifndef GS_UTIL_ERROR_H +#define GS_UTIL_ERROR_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Common error code definitions. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Common/generic error codes. + Based on POSIX \a errno values, but negative instead of positive. +*/ +typedef enum gs_error_t { + /** + Success - ok (POSIX). + */ + GS_OK = 0, + /** + Operation not permitted (POSIX.1: EPERM). + */ + GS_ERROR_PERM = -1, + /** + Interrupted system call (or Interrupted function call) (POSIX: EINTR). + */ + GS_ERROR_INTR = -4, + /** + Input/output error (POSIX.1: EIO) + */ + GS_ERROR_IO = -5, + /** + Resource temporarily unavailable (may be the same value as EWOULDBLOCK) (POSIX.1: EAGAIN). + */ + GS_ERROR_AGAIN = -11, + /** + Cannot allocate memory (or Not enough space) (POSIX.1: ENOMEM). + */ + GS_ERROR_ALLOC = -12, + /** + Permission denied (POSIX.1: EACCES). + */ + GS_ERROR_ACCESS = -13, + /** + Device or resource busy (POSIX.1: EBUSY). + */ + GS_ERROR_BUSY = -16, + /** + File exists (POSIX.1-2001: EEXIST). + */ + GS_ERROR_EXIST = -17, + /** + Invalid argument (POSIX.1: EINVAL). + */ + GS_ERROR_ARG = -22, + /** + Function not implemented (POSIX.1: ENOSYS) + */ + GS_ERROR_NOT_IMPLEMENTED = -38, + /** + Value too large to be stored in data type (POSIX.1: EOVERFLOW). + Example: trying to put 50 characters into a 10 character array. + @see GS_ERROR_RANGE. + */ + GS_ERROR_OVERFLOW = -75, + /** + Operation not supported (POSIX.1: ENOTSUP) + */ + GS_ERROR_NOT_SUPPORTED = -95, + /** + Address already in use (POSIX.1: EADDRINUSE). + */ + GS_ERROR_IN_USE = -98, + /** + Connection reset (POSIX.1-2001: ECONNRESET). + */ + GS_ERROR_CONNECTION_RESET = -104, + /** + No buffer space available (POSIX.1 (XSI STREAMS option): ENOBUFS). + */ + GS_ERROR_NO_BUFFERS = -105, + /** + Timeout (POSIX.1-2001: ETIMEDOUT). + */ + GS_ERROR_TIMEOUT = -110, + /** + Connection already in progress (POSIX.1-2001: EALREADY). + */ + GS_ERROR_ALREADY_IN_PROGRESS = -114, + + /** + Handle error (GOMspace). + */ + GS_ERROR_HANDLE = -2000, // from errno.h: #define __ELASTERROR 2000 /* Users can add values starting here */ + /** + Not found (GOMspace). + */ + GS_ERROR_NOT_FOUND = -2001, + /** + Full (GOMspace). + */ + GS_ERROR_FULL = -2002, + /** + Range error (GOMspace). + Example: specifying 120 hours, where only 0-23 is valid. + @see GS_ERROR_OVERFLOW + */ + GS_ERROR_RANGE = -2003, + /** + Data error (GOMspace). + */ + GS_ERROR_DATA = -2004, + /** + Unknown error (GOMspace). + @note avoid use - use specific error to improve debugging/troubleshooting. + */ + GS_ERROR_UNKNOWN = -2005, + /** + No data available (GOMspace). + */ + GS_ERROR_NO_DATA = -2006, + /** + Stale data - not updated (GOMspace). + */ + GS_ERROR_STALE = -2007, + /** + Type error (GOMspace). + */ + GS_ERROR_TYPE = -2008, + /** + Ambiguous error (GOMspace). + */ + GS_ERROR_AMBIGUOUS = -2009, + /** + State error (GOMspace). + */ + GS_ERROR_STATE = -2010, + +} gs_error_t; + +/** + * Convert an error code to a string. + * Uses standard POSIX strerror() under the hood. + * @param[in] error error to convert. If negative (e.g. \a gs_error_t), it is first converted to a positive value. + * @return string usefull for logging purposes (should not be used for programatically processing). + */ +const char * gs_error_string(int error); + +/** + Convert standard POSIX \a errno to gs_error_t. + @param[in] error POSIX error code (errno). + @return convert error code, by simply converting to a negative number. +*/ +gs_error_t gs_error(int error); + +#if (GS_UTIL_DEPRECATED_ERROR_CODES) +/** + Legacy error definitions. + @deprecated Use standard gs_error_t codes - these defines are only kept, so very old code (not yet update to use #gs_error_t) can compile. + @{ +*/ +#define E_NO_ERR -1 +#define E_NO_DEVICE -2 +#define E_MALLOC_FAIL -3 +#define E_THREAD_FAIL -4 +#define E_NO_QUEUE -5 +#define E_INVALID_BUF_SIZE -6 +#define E_INVALID_PARAM -7 +#define E_NO_SS -8 +#define E_GARBLED_BUFFER -9 +#define E_FLASH_ERROR -10 +#define E_BOOT_SER -13 +#define E_BOOT_DEBUG -14 +#define E_BOOT_FLASH -15 +#define E_TIMEOUT -16 +#define E_NO_BUFFER -17 +#define E_OUT_OF_MEM -18 +#define E_FAIL -19 +/** @} */ + +/** + Converts legacy error definitions to string. + @deprecated Use standard gs_error_t codes - this function is only kept, so very old code (not yet update to use #gs_error_t) can compile. + @param[in] code error code + @return string describing the error. +*/ +const char * error_string(int code) __attribute__((deprecated)); + +#endif // GS_UTIL_DEPRECATED_ERROR_CODES + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/fletcher.h b/gomspace/libutil/include/gs/util/fletcher.h new file mode 100644 index 00000000..5b24c23c --- /dev/null +++ b/gomspace/libutil/include/gs/util/fletcher.h @@ -0,0 +1,89 @@ +#ifndef GS_UTIL_FLETCHER_H +#define GS_UTIL_FLETCHER_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Fletcher16 checksum, +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Fletcher16 checksum (read using copy function). + + Data is read from \a data, using the specified \a memcpyfcn function. + + @param[in] data data. + @param[in] size number of \a data bytes. + @param[in] memcpyfcn memory copy function. If NULL is specified, standard memcpy will be used. + @returns fletcher16 checksum +*/ +uint16_t gs_fletcher16_memcpy(const void * data, size_t size, void * (*memcpyfcn)(void *, const void *, size_t)); + +/** + Fletcher16 checksum (read from program memory). + + AVR8: reads from program memory. + Other architectures: identical to gs_fletcher16(). + + @param[in] data_in data. + @param[in] size number of \a data bytes. + @returns fletcher16 checksum +*/ +uint16_t gs_fletcher16_P(const void * data_in, size_t size); + +/** + Fletcher16 checksum. + + @param[in] data data. + @param[in] size number of \a data bytes. + @returns fletcher16 checksum +*/ +uint16_t gs_fletcher16(const void * data, size_t size); + +/** + Fletcher16 working set. + @see gs_fletcher16_init(), gs_fletcher16_update(), gs_fletcher16_finalize() +*/ +typedef struct { + /** + Sum1 - internal. + */ + uint16_t sum1; + /** + Sum2 - internal. + */ + uint16_t sum2; +} gs_fletcher16_t; + +/** + Initialize fletcher16 working set. + @param[in] f16 working set. +*/ +void gs_fletcher16_init(gs_fletcher16_t * f16); + +/** + Update fletcher16 checksum. + @param[in] f16 working set. + @param[in] data data. + @param[in] size number of \a data bytes. +*/ +void gs_fletcher16_update(gs_fletcher16_t * f16, const void * data, size_t size); + +/** + Finalize fletcher16 checksum and return it. + + @param[in] f16 working set. + @returns fletcher16 checksum +*/ +uint16_t gs_fletcher16_finalize(gs_fletcher16_t * f16); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/function_scheduler.h b/gomspace/libutil/include/gs/util/function_scheduler.h new file mode 100644 index 00000000..229c5031 --- /dev/null +++ b/gomspace/libutil/include/gs/util/function_scheduler.h @@ -0,0 +1,79 @@ +#ifndef GS_UTIL_FUNCTION_SCHEDULER +#define GS_UTIL_FUNCTION_SCHEDULER +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Function scheduler. + + Simple framework for invoking functions at intervals. + + Instead of creating a lot of tasks (which uses memory), this framework can be used to schedule execution of functions at specified intervals. + + Once setup, calling gs_function_scheduler_execute_ms() will execute all functions timed out and return the time, until the next function has + to be executed or max timeout specified (or max wait time supported on the platform). + + The API supports multiple schedulers, but is not thread-safe. + + @note Do NOT use for time critical control, as the actual time interval is influenced by the host thread and other scheduled functions. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Scheduler handle. +*/ +typedef struct gs_function_scheduler gs_function_scheduler_t; + +/** + Function callback. + + @return timeout in mS until next callback. +*/ +typedef uint32_t (*gs_function_scheduler_function_t)(void * user_data); + +/** + Initialize scheduler. + Memory is allocated once for \a max_entries. + @param[in] max_timeout_ms max timeout in mS. + @param[in] max_entries max number of entries for this scheduler. + @param[out] scheduler reference to created scheduler. + @return_gs_error_t +*/ +gs_error_t gs_function_scheduler_create(uint32_t max_timeout_ms, unsigned int max_entries, gs_function_scheduler_t ** scheduler); + +/** + Free scheduler (release resources). + @param[in] scheduler scheduler. + @return_gs_error_t +*/ +gs_error_t gs_function_scheduler_destroy(gs_function_scheduler_t * scheduler); + +/** + Execute scheduled function(s) and returns number of mS until next execute must be called again. + + @note Return type is \a int to prevent overflow on platforms where int is less than 32 bits. + + @param[in] scheduler scheduler. + @return next timeout in mS. +*/ +int gs_function_scheduler_execute_ms(gs_function_scheduler_t * scheduler); + +/** + Register function to be executed at mS intervals. + @param[in] scheduler scheduler. + @param[in] first_timeout_ms mS until first execution. + @param[in] func function to execute. + @param[in] user_data function user data. + @return_gs_error_t +*/ +gs_error_t gs_function_scheduler_register_ms(gs_function_scheduler_t * scheduler, uint32_t first_timeout_ms, gs_function_scheduler_function_t func, void * user_data); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/gosh/command.h b/gomspace/libutil/include/gs/util/gosh/command.h new file mode 100644 index 00000000..8187152e --- /dev/null +++ b/gomspace/libutil/include/gs/util/gosh/command.h @@ -0,0 +1,503 @@ +#ifndef GS_UTIL_GOSH_COMMAND_H +#define GS_UTIL_GOSH_COMMAND_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Command framework. + + Provides a simple way of organizing commands in a hierarchy. A command is a text string mapping to a function - supporting arguments. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Max langth of a command (including NUL termination). +*/ +#define GS_COMMAND_MAX_LEN_COMMAND 20 + +/** + Flag for hiding command in help and tab-complete. +*/ +#define GS_COMMAND_FLAG_HIDDEN 0x02 + +/** + 'root' command attribute. + + On embedded (none Linux) systems, it is prefered to store as much as possible in \a program memory, in order to save RAM. + This is accomplished by tagging all \a root commands with this attribute, which instructs the linker to put all commands in a named + section. This section is then through the linker-script, placed in \a program memory. + The command framework can read commands directly from this section, and therefore doesn't need an RAM to maintain the list. + + The gs_command_register() must still be called for all \a root commands, which ensures that the linker doesn't throw away the + command objects, due to missing code reference. + + On a Linux system, the commands are not group in a section. Instead gs_command_register() dynamicly builds a list with the commands. + + @see gs_command_register() +*/ +#if (__linux__ == 0) +#define GS_COMMAND_ROOT __attribute__ ((section(".commands"))) +#else +#define GS_COMMAND_ROOT +#endif + +/** + Sub command attribute, + + Only necesasry on AVR8, due to its memory model. +*/ +#define GS_COMMAND_SUB GS_PGM_OBJECT + +/** + Macro for initializing command chains. +*/ +#define GS_COMMAND_INIT_CHAIN(__list) {.list = __list, .count = GS_ARRAY_SIZE(__list)} + +/** + Macro for registering commands. + + @see gs_command_register() +*/ +#define GS_COMMAND_REGISTER(__cmd) gs_command_register(__cmd, GS_ARRAY_SIZE(__cmd)) + +/** + Command reference. + @note Use gs_command_t instead of 'struct command'. +*/ +typedef struct command gs_command_t; + +/** + Commands context reference + @note Use gs_command_context_t instead of struct command_context + */ +typedef struct command_context gs_command_context_t; + +/** + Command output interface +*/ +typedef struct command_io_functions { + /** + Function interface for setting result + @param output_ctx pointer to output context for the given impl. + @param group Group name specifies the group that a given key/value pair belongs to. + @param key key name + @param value string value of the result + @return_gs_error_t + */ + gs_error_t (*set_result)(gs_command_context_t *ctx, const char *group, const char *key, const char *value); + /** + Function interface for flushing results. Used by the command handler to ensure output/results + are flushed to stdout/File or any other receiver of the output. + @param output_ctx pointer to output context for the given impl. + @return_gs_error_t + */ + gs_error_t (*flush)(gs_command_context_t *ctx); + /** + Function interface for waiting for key/input + @param output_ctx pointer to output context for the given impl. + @param ch pointer to character returned by function + @param timeout_ms maximum time to wait of the character. + @return_gs_error_t + */ + gs_error_t (*wait_for_key)(gs_command_context_t *ctx, int *ch, int timeout_ms); +} gs_command_io_functions_t; + + + +/** + Command context for executing a command. +*/ +struct command_context { + /** + Input (raw) command line, including arguments. + */ + const char * command_line; + + /** + Command being executed. + */ + const gs_command_t * command; + + /** + Number of arguments (standard argc style). + */ + int argc; + + /** + Argument array (standard argv style). + */ + char **argv; + + /** + FILE handle for capturing stdout from command. + */ + FILE* out; + + /** + getopt variable. + */ + int optind; + + /** + getopt variable. + */ + int optopt; + + /** + getopt variable. + */ + char *optarg; + + /** + getopt variable. + */ + int optsp; + + /** + Function interface for I/O operations + */ + const gs_command_io_functions_t *io_functions; + + /** + Pointer for storing the context used by the I/O functions + */ + void * io_ctx; +}; + +/** + Command logging call-back + + logs information on the command called. + @param[in] cmd_line command line string + @param[in] ret return code from command execution framework + @param[in] cmd_ret return code from the executed command + @param[in] start time_stamp when command execution started. + @param[in] end time_stamp when command execution completed. + @param[in] ctx context pointer for the logger. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_command_log_t)(const char *cmd_line, gs_error_t ret, gs_error_t cmd_ret, gs_timestamp_t start, gs_timestamp_t end, void * ctx); + +/** + Command handler. +*/ +typedef int (*gs_command_handler_t)(gs_command_context_t * ctx); + +/** + Completer call-back (tab complete). + + @param[in] ctx command context. + @param[in] arg_to_complete argument to complete + @return #GS_OK Found at least 1 match. + The \a completer is expected to have completed more of the command line. + If the framework detects multiple matches, the framework will proceed as if #GS_ERROR_AMBIGUOUS was returned. + The framework doesn't expect anything to be printed to \a out, but will update/refresh the console line. + @return #GS_ERROR_AMBIGUOUS Ambiguous - multiple matches or force the framework to show help. + The \a completer may have extended/completed more of the command line. + The framework expects the \a completer to have printed to \a out, and will show help/usage for the command on a new line. + @return #GS_ERROR_NOT_FOUND (or others) No matches found or no more arguments to complete. + The framewrok doesn't expect anything to be printed to \a out, and will not update the console. +*/ +typedef gs_error_t (*gs_command_completer_t)(gs_command_context_t * ctx, int arg_to_complete); + +/** + Add token - helper to 'tab complete' argument(s). + + @param[in] ctx command context. + @param[in] token possible completion - the API will find the common part. + @param[in] exact \a true if \a token is an exact match - all other added tokens will be ignored. + @return number of tokens added. +*/ +unsigned int gs_command_completer_add_token(gs_command_context_t * ctx, const char * token, bool exact); + +/** + Chain element for chaning sub-commands. +*/ +typedef struct { + /** + Command list. + */ + const gs_command_t * list; + /** + Number of commands in the \a list. + */ + unsigned int count; +} gs_command_chain_t; + +/** + Signals no command arguments in command definition, see mandatory arguments. +*/ +#define GS_COMMAND_NO_ARGS 255 + +/** + Command definition. +*/ +struct command { +#if (__AVR__) + char name[GS_COMMAND_MAX_LEN_COMMAND]; + char help[50]; + char usage[50]; +#else + /** + Name. + */ + const char * const name; + /** + Help text. + */ + const char * const help; + /** + Usage text. + */ + const char * const usage; +#endif + /** + Command handler - the "actual command function". + */ + gs_command_handler_t handler; +#if (__AVR__ == 0) + /** + Completer function - helps completing an argument. + */ + gs_command_completer_t completer; +#endif + /** + Sub-command (if any). + */ + gs_command_chain_t chain; + /** + Mode/flags. + See #GS_COMMAND_FLAG_HIDDEN. + */ + unsigned int mode; + /** + Number of mandatory (minimum) arguments. + + @note Due to backwards compatibility, 0 (zero) cannot be used to signal no arguments - use #GS_COMMAND_NO_ARGS instead, if command doesn't take any arguments (mandatory or optional). + */ + uint8_t mandatory_args; + /** + Number of optional arguments. + */ + uint8_t optional_args; + /** + Filler for future use. + */ + uint8_t filler[2]; +}; + +/** + Returns the arguments as a string, where arguments are separated by spaces. + @param ctx command context. + @return Pointer to concatenated arguments +*/ +const char * gs_command_args(gs_command_context_t *ctx); + +/** + Execute command. + @deprecated Replaced by gs_command_execute & gs_command_execute_stdio + + Looks up a command and executes it. If the command is executed (this function returns GS_OK), the command's return + result is stored in \a command_result. + + @param[in] command Command to execute, including arguments separated by spaces. + @param[out] command_result Result returned by \a command (if executed). Use \a NULL to ignore result. + @return #GS_ERROR_NOT_FOUND if command wasn't found. + @return #GS_ERROR_ARG if number of argumenets exceeds \a mandatory + \a optional count. + @return_gs_error_t +*/ +gs_error_t gs_command_run(const char * command, gs_error_t * command_result); + +/** + Execute command. + + Looks up a command and executes it. If the command is executed (this function returns GS_OK), the command's return + result is stored in \a command_result. + + @param[in] command Command to execute, including arguments separated by spaces. + @param[out] command_result Result returned by \a command (if executed). Use \a NULL to ignore result. + @param[in] out output (FILE) stream + @param[in] iof Pointer to function interface of IO operations + @param[in] iof_ctx Pointer to context used by the IO function interface + @return #GS_ERROR_NOT_FOUND if command wasn't found. + @return #GS_ERROR_ARG if number of argumenets exceeds \a mandatory + \a optional count. + @return_gs_error_t +*/ +gs_error_t gs_command_execute(const char * command, gs_error_t * command_result, FILE *out, const gs_command_io_functions_t * iof, void * iof_ctx); + +/** + Execute command. + + Looks up a command and executes it. If the command is executed (this function returns GS_OK), the command's return + result is stored in \a command_result. The results are printed on stdout and input captured on stdin. + + @param[in] command Command to execute, including arguments separated by spaces. + @param[out] command_result Result from command. Use \a NULL to ignore result. + @return #GS_OK if command was executed - result returned in \a command_result. Otherwise an error indicating why the command wasn't executed. +*/ +gs_error_t gs_command_execute_stdio(const char * command, gs_error_t * command_result); + +/** + Set output + + Sets output from command, using the io function struct in ctx. + + @param[in] ctx the command context + @param[in] group a string specifying the group of the result. Leave blank if not used. + @param[in] key a string specifying the key/name of the result variable. + @param[in] value a string representation of the result value. + @return_gs_error_t +*/ +gs_error_t gs_command_set_output(gs_command_context_t *ctx, const char* group, const char* key, const char* value); + +/** + Set output + + Sets output from command using printf formatting, using the io function struct in ctx. + + @param[in] ctx the command context + @param[in] group a string specifying the group of the result. Leave blank if not used. + @param[in] key a string specifying the key/name of the result variable. + @param[in] format printf syntax for formatting data + @return_gs_error_t +*/ +gs_error_t gs_command_set_output_printf(gs_command_context_t *ctx, const char* group, const char* key, const char * format, ...); + +/** + Flush output/Results + + Instruct the command output stream & results to be flushed from it's buffers. + + @param[in] ctx the command context + @return_gs_error_t +*/ +gs_error_t gs_command_flush_output(gs_command_context_t *ctx); + +/** + Wait for any key input + + Instruct the command input stream to wait for any key. + + @param[in] ctx the command context + @param[in] timeout_ms timeout, < 0: block forever, 0: poll, > 0: wait number of milliseconds. + @return true if command should proceed (either because of key press present or if no input stream available) +*/ +bool gs_command_wait_any_key(gs_command_context_t *ctx, int timeout_ms); + +/** + Wait for key input + + Instruct the io stream to wait for a key, and return the pressed key in ch. + + @param[in] ctx the command context + @param[out] ch the character that was read on the input stream + @param[in] timeout_ms timeout, < 0: block forever, 0: poll, > 0: wait number of milliseconds. + @return #GS_OK if key was read + @return #GS_ERROR_HANDLE if no input stream is present + @return #GS_ERROR_TIMEOUT on timeout. +*/ +gs_error_t gs_command_wait_key(gs_command_context_t *ctx, int* ch, int timeout_ms); + +/** + Register commands. + + gs_command_init() must be called prior to registering any commands. + + See #GS_COMMAND_ROOT for details. + + @param[in] cmds Pointer to command array + @param[in] cmd_count Number of commands in command array + @return_gs_error_t +*/ +gs_error_t gs_command_register(const gs_command_t * cmds, size_t cmd_count); + +/** + Initialize command system and register default commands. + + Registers following commands: gs_log_register_commands() and gs_command_register_default_commands(). + + @param[in] min_stack_size Minimum stack size required for executing commands. The stack size will be used by other sub-systems such as gs_console, g-script. Stack size may be ignored on some platforms, e.g. Linux. + @return_gs_error_t + @see gs_command_init_no_commands() +*/ +gs_error_t gs_command_init(size_t min_stack_size); + +/** + Initialize command system (without any default commands). + + @param[in] min_stack_size Minimum stack size required for executing commands. The stack size will be used by other sub-systems such as gs_console, g-script. Stack size may be ignored on some platforms, e.g. Linux. + @return_gs_error_t + @see gs_command_init() +*/ +gs_error_t gs_command_init_no_commands(size_t min_stack_size); + + +/** + Register a call-back used for logging of command execution. + + @param[in] log_cb the logging call back. + @param[in] log_ctx pointer to context data. Set to NULL if not used. + @return_gs_error_t +*/ +gs_error_t gs_command_register_logger(gs_command_log_t log_cb, void *log_ctx); + +/** + Default implementation of the command logger, that can be used if no other + custom command logger is provided by the system. + + @param[in] cmd_line command line string + @param[in] ret return code provided by the command execution function. + @param[in] cmd_ret return code provided by the executed command. + @param[in] t_start time stamp when command execution started. + @param[in] t_end time stamp when command execution completed. + @param[in] log_ctx context for the command logger. + @return_gs_error_t +*/ +gs_error_t gs_command_logger_default(const char* cmd_line, gs_error_t ret, gs_error_t cmd_ret, gs_timestamp_t t_start, gs_timestamp_t t_end, void * log_ctx); + +/** + Return minimum stack size. + @return minimm stack size required for executing commands. The minimum stack size is set by call to gs_command_init(). +*/ +size_t gs_command_get_stack_size(void); + +/** + Register set of default commands. + @return_gs_error_t +*/ +gs_error_t gs_command_register_default_commands(void); + +/** + Split line into argc/argv. + + @param[in] line line to split - the line will be chop up into argv. + @param[out] argc argc count. + @param[out] argv argv array. + @param[in] max_argc max argv elements. + @return \a true if successfull, else \a false. +*/ +bool gs_command_build_argv(char *line, int *argc, char **argv, int max_argc); + +/** + Parse options. + + Adapted from AT&T public domain source from: + http://www.informatica.co.cr/unix-source-code/research/1985/1103.html + + @param[in] ctx command context. + @param[in] opts options + @return option character +*/ +int gs_command_getopt(gs_command_context_t *ctx, const char *opts); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/gosh/console.h b/gomspace/libutil/include/gs/util/gosh/console.h new file mode 100644 index 00000000..e0c9c42a --- /dev/null +++ b/gomspace/libutil/include/gs/util/gosh/console.h @@ -0,0 +1,123 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +#ifndef GS_UTIL_GOSH_CONSOLE_H +#define GS_UTIL_GOSH_CONSOLE_H +/** + @file + + Console (stdin/stdout) interface for running commands. + + This assumes a VT102 terminal emulator, and tries to fix some of minicom's quirks with e.g. home/end keys. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Initialize the API and console. + + @deprecated version 3.4, use gs_console_start() + + @return_gs_error_t +*/ +gs_error_t gs_console_init(void); + +/** + Restores terminal settings (only relevant on Linux). + + @deprecated version 3.4, this is handled by an installed exit-handler in gs_console_start(). + + @return_gs_error_t +*/ +gs_error_t gs_console_exit(void); + +/** + Set console prompt. + + @param[in] prompt user prompt - the API only stores the pointer, so do not modify/delete content. NULL or empty string is ignored (no change). +*/ +void gs_console_set_prompt(const char * prompt); + +/** + Clear the console screen +*/ +void gs_console_clear(void); + +/** + Update console. +*/ +void gs_console_update(void); + +/** + Create console thread. + + The console thread reads from stdin and writes to stdout. + + The thread is created with low priority, #GS_THREAD_PRIORITY_LOW. + + @deprecated version 3.4, use gs_console_start() + + @param[out] handle handle to created thread - thread will be created joinable if supported by platform. Use NULL, if not wanted. + @return_gs_error_t +*/ +gs_error_t gs_console_create_thread(gs_thread_t * handle); + +/** + Create console thread with priority. + + The console thread reads from stdin and writes to stdout. + + @deprecated version 3.4, use gs_console_start() + + @param[in] priority thread priority, normally #GS_THREAD_PRIORITY_LOW. + @param[out] handle handle to created thread - thread will be created joinable if supported by platform. Use NULL, if not wanted. + @return_gs_error_t +*/ +gs_error_t gs_console_create_thread_with_priority(gs_thread_priority_t priority, gs_thread_t * handle); + +/** + @anchor GS_CONSOLE_F + @defgroup GS_CONSOLE_F Console flags. + Use with gs_console_start() to configure behaviour. + @{ +*/ +/** + Linux only: no signal handlers installed (e.g. to catch SIG_TERM). + @see gs_console_start() +*/ +#define GS_CONSOLE_F_NO_SIGNAL_HANDLER 0x0001 +/** @} */ + +/** + Start console thread (priority: #GS_THREAD_PRIORITY_LOW). + + The console thread reads from stdin and writes to stdout. The thread is created with low priority, #GS_THREAD_PRIORITY_LOW. + + Linux: Changes terminal settings and installs an atexit() handler to restore the settings, Signal handlers will be installed to catch SIG_TERM -> exit() and ignore SIG_INT (controlled by option on command line) - unless #GS_CONSOLE_F_NO_SIGNAL_HANDLER is specified. + + @param[in] prompt set console prompt by calling gs_console_set_prompt(). + @param[in] flags configure behaviour, see @ref GS_CONSOLE_F definitions. + @return #GS_ERROR_EXIST if console thread already created. + @return_gs_error_t +*/ +gs_error_t gs_console_start(const char * prompt, uint32_t flags); + +/** + Stop (and join with) console thread. + + @note This is only supported on Linux. + + The thread is interrupted using pthread_cancel(), which does not guarantee \a clean shutdown if the thread is busy executing a command. + + @return #GS_ERROR_NOT_SUPPORTED if not supported on current platform. + @return #GS_ERROR_HANDLE if no console has been started with gs_console_start(). + @return_gs_error_t +*/ +gs_error_t gs_console_stop(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/hexdump.h b/gomspace/libutil/include/gs/util/hexdump.h new file mode 100644 index 00000000..43a085e5 --- /dev/null +++ b/gomspace/libutil/include/gs/util/hexdump.h @@ -0,0 +1,53 @@ +#ifndef GS_UTIL_HEXDUMP_H +#define GS_UTIL_HEXDUMP_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Dump memory as hex numbers and ascii characters. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Dump memory to an output stream. + @param[in] src memory address. + @param[in] len number of bytes to dump. + @param[in] disp_addr display address, used instead of \a src. + @param[in] out output stream. +*/ +void gs_hexdump_to_stream(const void * src, size_t len, const void * disp_addr, FILE* out); + +/** + Dump memory on stdout. + + @param[in] src memory address. + @param[in] len number of bytes to dump. +*/ +static inline void gs_hexdump(const void *src, size_t len) +{ + gs_hexdump_to_stream(src, len, src, stdout); +} + +/** + Dump memory on stdout. + @param[in] src memory address. + @param[in] len number of bytes to dump. + @param[in] disp_addr display address, used instead of \a src. +*/ +static inline void gs_hexdump_addr(const void * src, size_t len, const void * disp_addr) +{ + gs_hexdump_to_stream(src, len, disp_addr, stdout); +} + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/gomspace/libutil/include/gs/util/linux/argp.h b/gomspace/libutil/include/gs/util/linux/argp.h new file mode 100644 index 00000000..b8eff835 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/argp.h @@ -0,0 +1,40 @@ +#ifndef GS_UTIL_LINUX_ARGP_H +#define GS_UTIL_LINUX_ARGP_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Extensions to GNU argp parser (convenience functions). +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Wrapper for argp_parse. + + This function will call exit/terminate the process, if parsing fails. + + \a argv may be re-organized. + + @param[in] argp argp struct. + @param[in] argc argument count, i.e. standard argc. + @param[in] argv argument array, i.e. standard argv. + @param[in] flags future use. + @param[out] arg_index first unparsed option (-> argv modified). + @param[in] revision program revision, e.g. 3.0.1-12-g0cf1b59+. +*/ +void gs_argp_parse(const struct argp * argp, + int argc, char ** argv, + unsigned int flags, int * arg_index, + const char * revision); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/command_line.h b/gomspace/libutil/include/gs/util/linux/command_line.h new file mode 100644 index 00000000..d9dbc3a3 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/command_line.h @@ -0,0 +1,42 @@ +#ifndef GS_UTIL_LINUX_COMMAND_LINE_H +#define GS_UTIL_LINUX_COMMAND_LINE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Command line support. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Command line options for ignoring CTRL-C +*/ +extern const struct argp_child gs_console_command_line_ignore_ctrlc_argp; + +/** + Command line options for adding -h (help). +*/ +const struct argp_child gs_help_command_line_argp; + +/** + Return if ctrl-c ignored on command line. + @return \a true i ctrl-c ignored. +*/ +bool gs_command_line_ignore_ctrlc(void); + +/** + Return program name based on argv[0]. + @param[in] argv expected to be argv[0] amd point to the program name (possibly with full path). + @return program name. +*/ +const char * gs_command_line_program_name(const char * argv); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/drivers/can/can.h b/gomspace/libutil/include/gs/util/linux/drivers/can/can.h new file mode 100644 index 00000000..f04b3f83 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/drivers/can/can.h @@ -0,0 +1,29 @@ +#ifndef GS_UTIL_LINUX_DRIVERS_CAN_CAN_H +#define GS_UTIL_LINUX_DRIVERS_CAN_CAN_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Linux CAN interface. + + @note Only 1 filter/mask can be set, using gs_can_set_standard_filter_mask() or gs_can_set_extended_filter_mask() +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Open and initialize a CAN handle. + @param[in] ifname name of CAN interface. + @param[out] handle opened CAN handle. + @return_gs_error_t +*/ +gs_error_t gs_can_open(const char * ifname, int * handle); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio.h b/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio.h new file mode 100644 index 00000000..f04cc1f5 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio.h @@ -0,0 +1,146 @@ +#ifndef GS_UTIL_LINUX_DRIVERS_GPIO_H +#define GS_UTIL_LINUX_DRIVERS_GPIO_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + @brief GPIO interface + + GPIO interface provides a generic interface where specific GPIO drivers can be plugged in. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + GomSpace linux driver GPIO get value + + @param[in] gpio The gpio to read + @param[in] value Returned GPIO value (true/false = High/Low) + @param[in] driver_data data to specific driver + + @return_gs_error_t +*/ +typedef gs_error_t (*gs_gpio_get_t)(gs_gpio_t gpio, bool *value, void * driver_data); + +/** + GomSpace linux driver GPIO get value without error check + + @param[in] gpio The gpio to read + @param[in] driver_data data to specific driver + + @return GPIO value (true/false = High/Low) +*/ +typedef bool (*gs_gpio_get_nc_t)(gs_gpio_t gpio, void * driver_data); + +/** + GomSpace linux driver GPIO set value + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @param[in] driver_data data to specific driver + + @return_gs_error_t +*/ +typedef gs_error_t (*gs_gpio_set_t)(gs_gpio_t gpio, bool value, void * driver_data); + +/** + GomSpace linux driver GPIO set value without error check + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @param[in] driver_data data to specific driver +*/ +typedef void (*gs_gpio_set_nc_t)(gs_gpio_t gpio, bool value, void * driver_data); + +/** + GomSpace linux driver initialize GPIO as an external interrupt pin + + @param[in] gpio The gpio to configure + @param[in] conf Configuration of interrupt pin + @param[in] driver_data data to specific driver + + @return_gs_error_t + */ +typedef gs_error_t (*gs_gpio_init_as_interrupt_t)(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data); + + +/** + Every port. + */ +#define GS_GPIO_ALL_PORTS UINT16_MAX + +/** + Every pin. + */ +#define GS_GPIO_ALL_PINS UINT16_MAX + +/** + GPIO driver. + */ +typedef struct { + /** + Function for handling GPIO get. + */ + gs_gpio_get_t get_handler; + /** + Function for handling GPIO get no check. + */ + gs_gpio_get_nc_t get_nc_handler; + /** + Function for handling GPIO set. + */ + gs_gpio_set_t set_handler; + /** + Function for handling GPIO set no check. + */ + gs_gpio_set_nc_t set_nc_handler; + /** + Function for handling GPIO initialize as interrupt. + */ + gs_gpio_init_as_interrupt_t init_as_interrupt_handler; +} gs_gpio_driver_t; + + +/** + GPIO driver entry + */ +typedef struct { + /** + GPIO port, to which the driver is used (if GS_GPIO_ALL_PORTS, then all ports uses this driver). + */ + uint16_t port; + /** + GPIO pin, to which the driver is used (if GS_GPIO_ALL_PINS, then all pins uses this driver). + */ + uint16_t pin; + /** + GPIO driver. + */ + const gs_gpio_driver_t * driver; + /** + Driver specific data. + */ + void * driver_data; +} gs_gpio_driver_entry_t; + +/** + Register a driver. + + A specific driver can be assigned to a port and pin or it can be assigned to all pins and/or all ports. + + The latest registered driver, which fit the GPIO, is the one used. + + @param[in] driver_entry driver and configuration to be registered + @return_gs_error_t + */ +gs_error_t gs_gpio_register_driver(const gs_gpio_driver_entry_t * driver_entry); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio_sysfs.h b/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio_sysfs.h new file mode 100644 index 00000000..0f95e5aa --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio_sysfs.h @@ -0,0 +1,91 @@ +#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_SYSFS_H_ +#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_SYSFS_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + @brief Linux GPIO driver based on sysfs. + This driver needs to be registered in the generic GPIO linux driver @see 'gs/util/linux/drivers/gpio/gpio.h' +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + GPIO sysfs driver data. + + @note Driver takes no driver data, so a NULL pointer is valid +*/ +typedef void * gs_gpio_sysfs_driver_data_t; + +/** + GPIO sysfs driver interface. +*/ +extern const gs_gpio_driver_t gs_gpio_sysfs_driver; + +/** + GPIO sysfs initialize + + @param[in] gpio The gpio to initialize + @param[in] output Direction of pin (True/False = Output/Input) + @param[in] init_value Pin state if configured as output (True/False = High/Low) + @param[in] active_low if set pin is configured as active low (so a gs_gpio_sysfs_set with 1 will actually set value low) + @return_gs_error_t + */ +gs_error_t gs_gpio_sysfs_initialize(gs_gpio_t gpio, bool output, bool init_value, bool active_low); + +/** + GPIO sysfs get value + + @param[in] gpio The gpio to read + @param[in] value Returned GPIO value (true/false = High/Low) + @param[in] driver_data data to driver (not used) + @return_gs_error_t +*/ +gs_error_t gs_gpio_sysfs_get(gs_gpio_t gpio, bool *value, void * driver_data); + +/** + GPIO sysfs get value without error check + + @param[in] gpio The gpio to read + @param[in] driver_data data to driver (not used) + @return GPIO value (true/false = High/Low) +*/ +bool gs_gpio_sysfs_get_nc(gs_gpio_t gpio, void * driver_data); + +/** + GPIO sysfs set value + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @param[in] driver_data data to driver (not used) + @return_gs_error_t +*/ +gs_error_t gs_gpio_sysfs_set(gs_gpio_t gpio, bool value, void * driver_data); + +/** + GPIO sysfs set value without error check + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @param[in] driver_data data to driver (not used) +*/ +void gs_gpio_sysfs_set_nc(gs_gpio_t gpio, bool value, void * driver_data); + +/** + Initialize GPIO sysfs as an external interrupt pin + + @param[in] gpio The gpio to configure + @param[in] conf Configuration of interrupt pin + @param[in] driver_data data to driver (not used) + @return_gs_error_t + */ +gs_error_t gs_gpio_sysfs_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio_virtual.h b/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio_virtual.h new file mode 100644 index 00000000..e61b70a4 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio_virtual.h @@ -0,0 +1,125 @@ +#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_VIRTUAL_H_ +#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_VIRTUAL_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + @brief Linux GPIO driver to be used in unit tests. + This driver needs to be registered in the generic GPIO linux driver @see 'gs/util/linux/drivers/gpio/gpio.h' +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + GPIO virtual driver data. + + @note Driver takes no driver data, so a NULL pointer is valid +*/ +typedef void * gs_gpio_virtual_driver_data_t; + +/** + GPIO virtual driver interface. +*/ +extern const gs_gpio_driver_t gs_gpio_virtual_driver; + +/** + GPIO virtual driver entry, where all ports and pins are routed to virtual driver + */ +extern const gs_gpio_driver_entry_t gs_gpio_virtual_driver_entry_all; + +/** + GPIO virtual initialize + + @param[in] gpio The gpio to initialize + @param[in] output Direction of pin (True/False = Output/Input) + @param[in] value Pin state if configured as output (True/False = High/Low) + @return_gs_error_t + */ +gs_error_t gs_gpio_virtual_initialize(gs_gpio_t gpio, bool output, bool value); + +/** + GPIO virtual get value + + @param[in] gpio The gpio to read + @param[in] value Returned GPIO value (true/false = High/Low) + @param[in] driver_data data to driver (not used) + @return_gs_error_t +*/ +gs_error_t gs_gpio_virtual_get(gs_gpio_t gpio, bool *value, void * driver_data); + +/** + GPIO virtual get value without error check + + @param[in] gpio The gpio to read + @param[in] driver_data data to driver (not used) + @return GPIO value (true/false = High/Low) +*/ +bool gs_gpio_virtual_get_nc(gs_gpio_t gpio, void * driver_data); + +/** + GPIO virtual set value + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @param[in] driver_data data to driver (not used) + @return_gs_error_t +*/ +gs_error_t gs_gpio_virtual_set(gs_gpio_t gpio, bool value, void * driver_data); + +/** + GPIO virtual set value without error check + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @param[in] driver_data data to driver (not used) +*/ +void gs_gpio_virtual_set_nc(gs_gpio_t gpio, bool value, void * driver_data); + +/** + Initialize GPIO virtual as an external interrupt pin + + @param[in] gpio The gpio to configure + @param[in] conf Configuration of interrupt pin + @param[in] driver_data data to driver (not used) + @return_gs_error_t + */ +gs_error_t gs_gpio_virtual_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data); + +/** + Force set a pin + + This sets a pin regardless if it is configured as input, output or interrupt + If the pin is configured as interrupt, the registered ISR's will be called within this function, + if the transition matches (rising/falling) + + @note This function is specific to this driver and is should not be registered. + + @param[in] gpio The gpio to set + @param[in] value GPIO value (true/false = High/Low) + @return_gs_error_t + */ +gs_error_t gs_gpio_virtual_force_set(gs_gpio_t gpio, bool value); + +/** + Get transitions + + This gives the number of transitions ((high -> low) + (low -> high)), + since last time this function was called at this pin. This function resets the counter of the pin. + An even number means, that the pin has the same state as it was initialized to. + + @note This function is specific to this driver and should not be registered + + @param[in] gpio The gpio, of which transitions are given + @param[out] transitions Number of transitions + @return + */ +gs_error_t gs_gpio_virtual_get_transistions(gs_gpio_t gpio, uint32_t * transitions); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/drivers/i2c/i2c.h b/gomspace/libutil/include/gs/util/linux/drivers/i2c/i2c.h new file mode 100644 index 00000000..858c26a2 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/drivers/i2c/i2c.h @@ -0,0 +1,198 @@ +#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_I2C_I2C_H_ +#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_I2C_I2C_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + @brief Linux I2C plugin driver +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + GomSpace linux driver I2C master transaction. + + @see 'gs/util/drivers/i2c/master.h' + + @param[in] device I2C device + @param[in] addr I2C address + @param[in] tx tx buffer + @param[in] txlen bytes to be sent + @param[out] rx rx buffer + @param[in] rxlen bytes to be received + @param[in] timeout_ms timeout in milliseconds + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (* gs_i2c_master_transaction_t)(uint8_t device, uint8_t addr, const void * tx, size_t txlen, + void * rx, size_t rxlen, int timeout_ms, void * driver_data); + +/** + GomSpace linux driver I2C slave start. + + @see 'gs/util/drivers/i2c/slave.h' + + @param[in] device I2C device + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (* gs_i2c_slave_start_t)(uint8_t device, void * driver_data); + +/** + GomSpace linux driver I2C set rx callback + + @see 'gs/util/drivers/i2c/slave.h' + + @param[in] device I2C device + @param[in] rx rx callback + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (* gs_i2c_slave_set_rx_t)(uint8_t device, gs_i2c_slave_receive_t rx, void * driver_data); + +/** + GomSpace linux driver I2C slave set 'get_rx_buffer' callback. + + @see 'gs/util/drivers/i2c/slave.h' + + @param[in] device I2C device + @param[in] get_rx_buf get_rx_buf callback + @param[in] buf_length length of buffer received by calling callback + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (* gs_i2c_slave_set_get_rx_buf_t)(uint8_t device, gs_i2c_slave_get_rx_buf_t get_rx_buf, size_t buf_length, void * driver_data); + +/** + GomSpace linux driver I2C slave set slave response. + + @see 'gs/util/drivers/i2c/slave.h' + + @param[in] device I2C device + @param[in] tx tx buffer + @param[in] tx_length bytes to be send + @param[in] driver_data data to specific driver + @return_gs_error_t +*/ +typedef gs_error_t (* gs_i2c_slave_set_response_t)(uint8_t device, const uint8_t * tx, size_t tx_length, void * driver_data); + +/** + Every I2C device ([0 : 254]). + */ +#define GS_I2C_ALL_DEVICES 255 + +/** + Every I2C address (0 : 127]). + */ +#define GS_I2C_ALL_ADDR 255 + +/** + I2C master driver. + */ +typedef struct { + /** + Function for handling master transactions. + */ + gs_i2c_master_transaction_t master_transaction_handler; +} gs_i2c_master_driver_t; + + +/** + I2C master driver entry + */ +typedef struct { + /** + I2C device, to which the driver is used (if GS_I2C_ALL_DEVICES, then all devices uses this driver). + */ + uint8_t device; + /** + I2C addr, to which the driver is used (if GS_I2C_ALL_ADDR, then all addr on given device uses this driver). + */ + uint8_t addr; + /** + I2C master driver. + */ + const gs_i2c_master_driver_t * driver; + /** + Driver specific data. + */ + void * driver_data; +} gs_i2c_master_driver_entry_t; + + +/** + I2C slave driver + */ +typedef struct { + /** + Function for handling slave start. + */ + gs_i2c_slave_start_t start_handler; + /** + Function for handling the 'setting of rx callback'. + */ + gs_i2c_slave_set_rx_t set_rx_handler; + /** + Function for handling setting of an 'rx buff get' function. + */ + gs_i2c_slave_set_get_rx_buf_t set_get_rx_buf_handler; + /** + Function for handling 'set response'. + */ + gs_i2c_slave_set_response_t set_response_handler; +} gs_i2c_slave_driver_t; + + +/** + I2C slave driver entry. + */ +typedef struct { + /** + I2C device, to which the driver is used (if GS_I2C_ALL_DEVICES, then all devices uses this driver). + */ + uint8_t device; + /** + I2C slave driver. + */ + const gs_i2c_slave_driver_t * driver; + /** + Driver specific data. + */ + void * driver_data; +} gs_i2c_slave_driver_entry_t; + + +/** + Register a master driver. + + A specific driver can be assigned to a specific address and device + or it can be registered to every address on a device or every address on every device. + + The latest registered driver, which fit the device an address, is the one used. + + @param[in] driver_entry driver and configuration to be registered + @return_gs_error_t + */ +gs_error_t gs_i2c_master_register_driver(const gs_i2c_master_driver_entry_t * driver_entry); + +/** + Register a slave driver + + A specific driver can be assigned to a specific device or a driver can be assigned to every device. + + The latest registered driver, which fit the device, is the one used. + + @param[in] driver_entry driver and configuration to be registered + @return_gs_error_t + */ +gs_error_t gs_i2c_slave_register_driver(const gs_i2c_slave_driver_entry_t * driver_entry); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/drivers/spi/spi.h b/gomspace/libutil/include/gs/util/linux/drivers/spi/spi.h new file mode 100644 index 00000000..24e5ae22 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/drivers/spi/spi.h @@ -0,0 +1,175 @@ +#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_SPI_SPI_H_ +#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_SPI_SPI_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Linux SPI plugin driver +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Linux driver SPI master transactions. + + @see 'gs/util/drivers/spi/master.h' + + @param[in] slave SPI slave + @param[in] trans Pointer to transactions + @param[in] count Number of transactions (rx and/or tx) to complete + @param[in] timeout_ms timeout in milliseconds, primarily for locking the SPI device. + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (*gs_spi_master_transactions_t)(uint8_t slave, gs_spi_master_trans_t *trans, size_t count, + int timeout_ms, void * driver_data); + +/** + Linux driver SPI slave start. + + @see 'gs/util/drivers/spi/slave.h' + + @param[in] device SPI device (handle) + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (* gs_spi_slave_start_t)(uint8_t device, void * driver_data); + +/** + Linux driver SPI set rx callback + + @see 'gs/util/drivers/spi/slave.h' + + @param[in] device SPI device (handle). + @param[in] rx Rx callback. + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (* gs_spi_slave_set_rx_t)(uint8_t device, gs_spi_slave_receive_t rx, void * driver_data); + +/** + Linux driver SPI slave set slave response. + + @see 'gs/util/drivers/spi/slave.h' + + @param[in] device SPI device (handle). + @param[in] offset offset (in bytes) for the response, counted from start of request, i.e. offset of 2 means data will be sent as the 3rd byte. + @param[in] tx pointer to data. NOTE: data is not copied due to performance, so data must stay valid until the response has been sent. + @param[in] size size of data. + @param[in] driver_data data to specific driver + @return_gs_error_t + */ +typedef gs_error_t (* gs_spi_slave_set_response_t)(uint8_t device, size_t offset, const uint8_t * tx, size_t size, void * driver_data); + +/** + Every SPI slave ([0 : 254]). + */ +#define GS_SPI_ALL_SLAVES 255 + +/** + Every SPI device (0 : 254]). + */ +#define GS_SPI_ALL_DEVICES 255 + + +/** + SPI master driver. + */ +typedef struct { + /** + Function for handling master transactions. + */ + gs_spi_master_transactions_t master_transactions_handler; +} gs_spi_master_driver_t; + + +/** + SPI master driver entry + */ +typedef struct { + /** + SPI slave, to which the driver is used (if #GS_SPI_ALL_SLAVES, then all slaves uses this driver). + */ + uint8_t slave; + /** + SPI master driver. + */ + const gs_spi_master_driver_t * driver; + /** + Driver specific data. + */ + void * driver_data; +} gs_spi_master_driver_entry_t; + + +/** + SPI slave driver + */ +typedef struct { + /** + Function for handling slave start. + */ + gs_spi_slave_start_t start_handler; + /** + Function for handling the 'setting of rx callback'. + */ + gs_spi_slave_set_rx_t set_rx_handler; + /** + Function for handling 'set response'. + */ + gs_spi_slave_set_response_t set_response_handler; +} gs_spi_slave_driver_t; + + +/** + SPI slave driver entry. + */ +typedef struct { + /** + SPI device, to which the driver is used (if #GS_SPI_ALL_DEVICES, then all devices uses this driver). + */ + uint8_t device; + /** + SPI slave driver. + */ + const gs_spi_slave_driver_t * driver; + /** + Driver specific data. + */ + void * driver_data; +} gs_spi_slave_driver_entry_t; + + +/** + Register a master driver. + + A specific driver can be assigned to a slave or it can be assigned to every slave. + + The latest registered driver, which fit the slave, is the one used. + + @param[in] driver_entry driver and configuration to be registered + @return_gs_error_t + */ +gs_error_t gs_spi_master_register_driver(const gs_spi_master_driver_entry_t * driver_entry); + +/** + Register a slave driver + + A specific driver can be assigned to a specific device or a driver can be assigned to every device. + + The latest registered driver, which fit the device, is the one used. + + @param[in] driver_entry driver and configuration to be registered + @return_gs_error_t + */ +gs_error_t gs_spi_slave_register_driver(const gs_spi_slave_driver_entry_t * driver_entry); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/exitcode.h b/gomspace/libutil/include/gs/util/linux/exitcode.h new file mode 100644 index 00000000..35e89f06 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/exitcode.h @@ -0,0 +1,40 @@ +#ifndef GS_UTIL_LINUX_EXITCODE_H +#define GS_UTIL_LINUX_EXITCODE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + "standard" Linux exit codes. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Program completed ok (from stdlib.h) +*/ +#define GS_EXITCODE_OK EXIT_SUCCESS + +/** + Program terminated due to an error (from stdlib.h). +*/ +#define GS_EXITCODE_ERROR EXIT_FAILURE + +/** + Program terminated due to invalid usage, eg argument (from sysexits.h). +*/ +#define GS_EXITCODE_USAGE EX_USAGE + +/** + Program terminated due to a signal (from [TLDP](http://www.tldp.org/LDP/abs/html/exitcodes.html)). +*/ +#define GS_EXITCODE_SIGNAL(sig) (128 + sig) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/function.h b/gomspace/libutil/include/gs/util/linux/function.h new file mode 100644 index 00000000..b918993d --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/function.h @@ -0,0 +1,49 @@ +#ifndef GS_UTIL_LINUX_FUNCTION_H +#define GS_UTIL_LINUX_FUNCTION_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Function interface - invokes a function by name. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Function prototype. + @param[in] arg argument provided to gs_function_invoke(). + @return_gs_error_t +*/ +typedef gs_error_t (*gs_function_t)(void * arg); + +/** + Register \a function by name. + + @param[in] short_name short name for function, used by gs_function_invoke() to find function to invoke. + @param[in] long_name long name (unique) for function, used by gs_function_invoke() to find function to invoke. + @param[in] function function to be invoked by gs_function_invoke() + @return #GS_ERROR_FULL if registry is full. + @return_gs_error_t +*/ +gs_error_t gs_function_register(const char * short_name, const char * long_name, gs_function_t function); + +/** + Invoke \a function by name. + + The return value is from the registered function, except for #GS_ERROR_NOT_IMPLEMENTED. + + @param[in] name registered function name. + @param[in] arg argument for function. + @return #GS_ERROR_NOT_IMPLEMENTED if the \a name isn't found. + @return_gs_error_t +*/ +gs_error_t gs_function_invoke(const char * name, void * arg); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/rtc.h b/gomspace/libutil/include/gs/util/linux/rtc.h new file mode 100644 index 00000000..fa063f76 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/rtc.h @@ -0,0 +1,28 @@ +#ifndef GS_UTIL_LINUX_RTC_H +#define GS_UTIL_LINUX_RTC_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Real Time Clock interface (linux). +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Register Real Time Clock interface. + @note Setting the RTC will normally require special permission. + @param[in] get if true, get will be registered. + @param[in] set if true, set will be registered. + @return_gs_error_t +*/ +gs_error_t gs_rtc_register_linux(bool get, bool set); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/signal.h b/gomspace/libutil/include/gs/util/linux/signal.h new file mode 100644 index 00000000..b3c280e7 --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/signal.h @@ -0,0 +1,40 @@ +#ifndef GS_UTIL_LINUX_SIGNAL_H +#define GS_UTIL_LINUX_SIGNAL_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Signal helpers - catch and ignore. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Standard Linux signal handler. +*/ +typedef void (*gs_signal_handler)(int signal, siginfo_t *si, void *context); + +/** + Register/catch signal and invoke handler. + @param[in] signal signal to catch. + @param[in] handler signal handler. If \a handler is NULL, a default handler will be invoked, which calls exit(#GS_EXITCODE_SIGNAL + signal). + @return_gs_error_t +*/ +gs_error_t gs_signal_catch(int signal, gs_signal_handler handler); + +/** + Ignore signal + @param[in] signal signal to ignore. + @return_gs_error_t +*/ +gs_error_t gs_signal_ignore(int signal); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/linux/sysfs_helper.h b/gomspace/libutil/include/gs/util/linux/sysfs_helper.h new file mode 100644 index 00000000..ad05a6fe --- /dev/null +++ b/gomspace/libutil/include/gs/util/linux/sysfs_helper.h @@ -0,0 +1,30 @@ +#ifndef GS_UTIL_SYSFS_HELPER_H +#define GS_UTIL_SYSFS_HELPER_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Sysfs interface. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Sysfs write (GPIO). +*/ +gs_error_t gs_sysfs_write_file(const char *path, const char *value); + +/** + Sysfs read (GPIO). +*/ +gs_error_t gs_sysfs_read_file(const char *path, char *value, size_t len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/log.h b/gomspace/libutil/include/gs/util/log.h new file mode 100644 index 00000000..13659adf --- /dev/null +++ b/gomspace/libutil/include/gs/util/log.h @@ -0,0 +1,15 @@ +#ifndef GS_UTIL_LOG_H +#define GS_UTIL_LOG_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Log interface. + + The log interface supports logging to different group. + + Logging is done through groups (domains), which can runtime be re-configured with level. +*/ +#include + +#endif diff --git a/gomspace/libutil/include/gs/util/log/appender/appender.h b/gomspace/libutil/include/gs/util/log/appender/appender.h new file mode 100644 index 00000000..29a0c140 --- /dev/null +++ b/gomspace/libutil/include/gs/util/log/appender/appender.h @@ -0,0 +1,189 @@ +#ifndef GS_UTIL_LOG_APPENDER_APPENDER_H +#define GS_UTIL_LOG_APPENDER_APPENDER_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Log Appender interface. + + The log appender interface supports logging to different "stores". + Logging is done through groups, which can be registered to different log appenders. + Each log appender has it's own filter (level mask). + Examples of log appenders could be: console, file, vmem, ... +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + Log appender (forward declaration) + All log groups log to one or more appenders. The Log appender is responsible + for putting the actual log data to a store/console or some other log medium. +*/ +typedef struct gs_log_appender gs_log_appender_t; + +/** + Log appender record iterator callback function + + @param[in] ctx context data for iterator. + @param[in] level log level of record being iterated + @param[in] ts timestamp of record being iterated + @param[in] group group string (zero terminated) of record being iterated + @param[in] msg message string (zero terminated) of record being iterated + @return true/false: Return false to discontinue iteration. +*/ +typedef bool (*gs_log_record_iterator_t)(void *ctx, gs_log_level_t level, const gs_timestamp_t *ts, const char *group, const char *msg); + +/** + Log appender driver interface +*/ +typedef struct { + /** appender init function */ + gs_error_t (*init)(gs_log_appender_t *appender); + /** appender function */ + void (*append)(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va); + /** appender function for isr context */ + void (*append_isr)(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va); + /** appender function for getting appender details string */ + void (*info)(gs_log_appender_t *appender, char * info_str, uint8_t str_size); + /** appender function for iterating stored appenders log history */ + void (*hist)(gs_log_appender_t *appender, void * ctx, gs_log_record_iterator_t iter); + /** appender function for clearing it's log history */ + void (*clear)(gs_log_appender_t *appender); + /** appender function for flushing cached log entries to it's store. + This is only relevant for appenders implementing a log cache. */ + void (*flush)(gs_log_appender_t *appender); +} gs_log_appender_driver_t; + +/** + Log appender + All log groups log to one or more appenders. The Log appender is responsible + for putting the actual log data to a store/console or some other log medium. +*/ +struct gs_log_appender { + /** Name of the appender */ + const char * name; + /** appender driver interface */ + const gs_log_appender_driver_t * drv; + /** appender driver configuration data */ + const void * drv_config; + /** appender driver data - dynamic/internal data */ + void * drv_data; + /** appender level mask */ + uint8_t mask; +}; + +/** + Register an appender for the given log group. + All logging, where the mask matches the groups \a level_mask, will be forwarded to this appender. + + @param[in] group_name Name of the group. + @param[in] appender_name Name of appender to register for this group. + @return gs_error_t +*/ +gs_error_t gs_log_group_register_appender(const char * group_name, const char * appender_name); + +/** + Log appender iterator callback function + + @param[in] ctx context data for iterator. + @param[in] appender log appender being iterated + + @return true/false: Return false to discontinue iteration. +*/ +typedef bool (*gs_log_appender_iterator_t)(void *ctx, gs_log_appender_t * appender); + +/** + Iterate all or specific log appender(s). + + @param[in] name name of log appender, or NULL/\"all\" for all groups. + @param[in] ctx user context data. + @param[in] iter iterator, return \a true to continue, \a false to break iteration. + @return_gs_error_t +*/ +gs_error_t gs_log_appender_iterate(const char * name, void * ctx, gs_log_appender_iterator_t iter); + +/** + Iterate registered appenders for a specific group. + + @param[in] group log group to iterate appenders on. + @param[in] ctx user context data. + @param[in] iter appender iterator, return \a true to continue, \a false to break iteration. + @return gs_error_t +*/ +gs_error_t gs_log_group_appender_iterate(gs_log_group_t * group, void * ctx, gs_log_appender_iterator_t iter); + +/** + Register log appender. + + The log appender will be registered and initialized (if the appender has en init function, see #gs_log_appender_driver_t) + + The appender will not be attached to any log groups. For registering an appender to a group, use gs_log_group_register_appender() + + @param[in] appender appender - must stay in memory during the life-time of the application + @return_gs_error_t +*/ +gs_error_t gs_log_appender_register(gs_log_appender_t *appender); + +/** + Add log appender(s). + + The log appender will be registered and initialized (if the appender has en init function, see #gs_log_appender_driver_t) + + The appender will not be attached to any log groups. For registering an appender to a group, use gs_log_group_register_appender() + + @deprecated impossible to determine which appender fails, use gs_log_appender_register() + @param[in] appenders array of appender(s) - must stay in memory during the life-time of the application + @param[in] count array count - number of appenders. + @return_gs_error_t +*/ +gs_error_t gs_log_appender_add(gs_log_appender_t *appenders, uint16_t count); + +/** + Set log appender level mask. + + @param[in] appender_name log appender name + @param[in] mask level mask to set. + @return_gs_error_t +*/ +gs_error_t gs_log_appender_set_level_mask(const char * appender_name, uint8_t mask); + +/** + Get log appender level mask. + + @param[in] appender_name log appender name + @param[out] mask returned current level mask. + @return_gs_error_t +*/ +gs_error_t gs_log_appender_get_level_mask(const char * appender_name, uint8_t *mask); + +/** + Iterate log history for all or specific log appender. + + @param[in] name name of log appender, or NULL/\"all\" for all appenders. + @param[in] ctx user context data for iterator. + @param[in] iter iterator, return \a true to continue, \a false to break iteration. + @return gs_error_t +*/ +gs_error_t gs_log_appender_history_iterate(const char * name, void * ctx, gs_log_record_iterator_t iter); + +/** + Flush all log appenders data to storage. + + This will call the flush API (if implemented) for all log appenders + available on the system. This should be called on regular basis from + a system thread to ensure all cached data is correctly flushed to their + stores. + + @return gs_error_t +*/ +gs_error_t gs_log_appender_flush_all(); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/log/appender/console.h b/gomspace/libutil/include/gs/util/log/appender/console.h new file mode 100644 index 00000000..37f63fc5 --- /dev/null +++ b/gomspace/libutil/include/gs/util/log/appender/console.h @@ -0,0 +1,57 @@ +#ifndef GS_UTIL_LOG_APPENDER_CONSOLE_H +#define GS_UTIL_LOG_APPENDER_CONSOLE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Console log appender - logs to stdout. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Log appender for console + + This log appender is the standard appender which is always available + on any system. The appender should be registered to the root group, + in order to get console/stdio logs. +*/ +extern gs_log_appender_t gs_log_appender_console; + +/** + Log appender for console callback type + + This callback function can be used for registering a user defined logger function if + the default can not be used for the given system. + + @param[in] appender pointer to the console appender. + @param[in] level log level for log message + @param[in] group log group for log message + @param[in] ts timestamp for log message + @param[in] format format of message in printf style + @param[in] va variable argument list in printf style + + @return void +*/ +typedef void (*gs_log_appender_console_cb_t)(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va); + +/** + Set Log appender for console callback + + When set, the given callback is called instead of the default console log function. + To revert back to the default console log function, call this function with NULL as parameter. + + @param[in] cb callback to use for console logging. + + @return gs_error_t +*/ +gs_error_t gs_log_appender_console_set_cb(gs_log_appender_console_cb_t cb); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/log/appender/simple_file.h b/gomspace/libutil/include/gs/util/log/appender/simple_file.h new file mode 100644 index 00000000..ab2537a6 --- /dev/null +++ b/gomspace/libutil/include/gs/util/log/appender/simple_file.h @@ -0,0 +1,41 @@ +#ifndef GS_UTIL_LOG_APPENDER_SIMPLE_FILE_H +#define GS_UTIL_LOG_APPENDER_SIMPLE_FILE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Simple log-file appender. +*/ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Simple File Log Appender driver configuration +*/ +typedef struct gs_log_appender_simple_file_config { + /** + Name of file to create/write logs to + */ + const char *filename; + /** + Truncate the file, when opening the log file. + */ + bool truncate; + /** + Uee local time stamps when logging to log file, otherwise UTC. + */ + bool use_local_time; +} gs_log_appender_simple_file_config_t; + +/** + Log appender for file. +*/ +extern const gs_log_appender_driver_t gs_log_appender_simple_file_driver; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/log/log.h b/gomspace/libutil/include/gs/util/log/log.h new file mode 100644 index 00000000..53470a75 --- /dev/null +++ b/gomspace/libutil/include/gs/util/log/log.h @@ -0,0 +1,853 @@ +#ifndef GS_UTIL_LOG_LOG_H +#define GS_UTIL_LOG_LOG_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Log interface. + + Logging is done through groups (domains), where the level mask can be changed runtime. +*/ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Name of the root log group +*/ +#define GS_LOG_GROUP_ROOT "root" + +/** + Log levels. + + The levels can easily be mapped to standard syslog severity levels (https://en.wikipedia.org/wiki/Syslog). +*/ +typedef enum { + /** + Trace (more detailed than \a debug). + + syslog: maps to \a debug (or \a trace if supported). + */ + GS_LOG_TRACE = 0, + /** + Debug. + + syslog: maps to \a debug. + */ + GS_LOG_DEBUG = 1, + /** + Informational. + + syslog: maps to \a informational. + */ + GS_LOG_INFO = 2, + /** + Normal but significant conditions. + + syslog: maps to \a notice. + */ + GS_LOG_NOTICE = 3, + /** + Warning. + + syslog: maps to \a warning. + */ + GS_LOG_WARNING = 4, + /** + Error. + + syslog: maps to \a error. + */ + GS_LOG_ERROR = 5, + + /** + Trace (more detailed than \a debug). + @deprecated use #GS_LOG_TRACE + */ + LOG_TRACE = GS_LOG_TRACE, + /** + Debug. + @deprecated use #GS_LOG_DEBUG + */ + LOG_DEBUG = GS_LOG_DEBUG, + /** + Informational. + @deprecated use #GS_LOG_INFO + */ + LOG_INFO = GS_LOG_INFO, + /** + Normal but significant conditions. + @deprecated use #GS_LOG_NOTICE + */ + LOG_NOTICE = GS_LOG_NOTICE, + /** + Warning. + @deprecated use #GS_LOG_WARNING + */ + LOG_WARNING = GS_LOG_WARNING, + /** + Error. + @deprecated use #GS_LOG_ERROR + */ + LOG_ERROR = GS_LOG_ERROR, +} gs_log_level_t; + +/** + Log categories. + + The category is a way of grouping information about which sub-systems have logged. It is primarily used in the \a + telemetry table, to indicate what sub-systems have logged an \a error or \a warning - indicating a possible problem. + + Up to 32 categories are supported (stored in a uint32). + + Categories should be unique within a single node. However, nothing happens if categories clashes - it will only be more difficult to determine what part of the system logged. + + Standard categories are defined from #GS_LOG_CAT_1 and up. Products or mission specific software should start from #GS_LOG_CAT_32 and down. +*/ +typedef enum { + //! Standard, used for #GS_LOG_CAT_DEFAULT + GS_LOG_CAT_1 = 1 << 0, + //! Standard, used for #GS_LOG_CAT_DRIVER + GS_LOG_CAT_2 = 1 << 1, + //! Standard, used for #GS_LOG_CAT_CSP + GS_LOG_CAT_3 = 1 << 2, + //! Standard, used for #GS_LOG_CAT_PARAM + GS_LOG_CAT_4 = 1 << 3, + //! Standard, used for #GS_LOG_CAT_FILE_SYSTEM + GS_LOG_CAT_5 = 1 << 4, + //! Standard, used for #GS_LOG_CAT_COMMAND + GS_LOG_CAT_6 = 1 << 5, + //! Standard, used for #GS_LOG_CAT_HK + GS_LOG_CAT_7 = 1 << 6, + //! Standard, used for #GS_LOG_CAT_FP + GS_LOG_CAT_8 = 1 << 7, + //! Standard, used for #GS_LOG_CAT_ADCS + GS_LOG_CAT_9 = 1 << 8, + GS_LOG_CAT_10 = 1 << 9, + GS_LOG_CAT_11 = 1 << 10, + GS_LOG_CAT_12 = 1 << 11, + GS_LOG_CAT_13 = 1 << 12, + GS_LOG_CAT_14 = 1 << 13, + GS_LOG_CAT_15 = 1 << 14, + GS_LOG_CAT_16 = 1 << 15, +#if (__AVR__ == 0) + GS_LOG_CAT_17 = 1 << 16, + GS_LOG_CAT_18 = 1 << 17, + GS_LOG_CAT_19 = 1 << 18, + GS_LOG_CAT_20 = 1 << 19, + GS_LOG_CAT_21 = 1 << 20, + GS_LOG_CAT_22 = 1 << 21, + GS_LOG_CAT_23 = 1 << 22, + GS_LOG_CAT_24 = 1 << 23, + GS_LOG_CAT_25 = 1 << 24, + GS_LOG_CAT_26 = 1 << 25, + GS_LOG_CAT_27 = 1 << 26, + GS_LOG_CAT_28 = 1 << 27, + GS_LOG_CAT_29 = 1 << 28, + GS_LOG_CAT_30 = 1 << 29, + GS_LOG_CAT_31 = 1 << 30, + //! Product or mission specific - start here and down + GS_LOG_CAT_32 = 1 << 31, +#endif +} gs_log_category_t; + +/** + @defgroup reserved_log_categories Reserved/assigned log categories. + These categories are assigned/reserved for certain sub-systems. + @{ +*/ + /** + Default, used if nothing else fits. + */ +#define GS_LOG_CAT_DEFAULT GS_LOG_CAT_1 + /** + Driver layer. + */ +#define GS_LOG_CAT_DRIVER GS_LOG_CAT_2 + /** + CSP. + */ +#define GS_LOG_CAT_CSP GS_LOG_CAT_3 + /** + Parameter system. + */ +#define GS_LOG_CAT_PARAM GS_LOG_CAT_4 + /** + File system. + */ +#define GS_LOG_CAT_FILE_SYSTEM GS_LOG_CAT_5 + /** + Command framework and execution. + */ +#define GS_LOG_CAT_COMMAND GS_LOG_CAT_6 + /** + Housekeeping System. + */ +#define GS_LOG_CAT_HK GS_LOG_CAT_7 + /** + Flight Planner. + */ +#define GS_LOG_CAT_FP GS_LOG_CAT_8 + /** + ADCS + */ +#define GS_LOG_CAT_ADCS GS_LOG_CAT_9 +/** @} */ + +struct gs_log_list; /* forward declared private log list struct */ +/** + Log list type (private) + + Private gs_log_list type. +*/ +typedef struct gs_log_list gs_log_list_t; + +/** + Log group. + All logs are logged to a \a group. The group contains the current log level mask, + which controls whether the log is carried through or not. +*/ +typedef struct { + /** + Name of log group. + */ + const char * name; + /** + Category, see #gs_log_category_t. + */ + uint32_t category; + /** + Current level mask, see #gs_log_level_t. + */ + uint8_t mask; + /** + Is group additive, if \a true (default) logging will be done on both root appenders and this groups appenders - if \a false, logging will only be done to this groups appenders. + */ + bool additivity; + /** + Private list of appenders. + */ + gs_log_list_t * appenders; +#if (__AVR__) + uint16_t dummy_align; +#endif +} gs_log_group_t; + +/** + Log masks (levels converted to mask). + @{ +*/ +/** + Trace level enabled. +*/ +#define GS_LOG_TRACE_MASK (1 << GS_LOG_TRACE) +/** + Debug level enabled. +*/ +#define GS_LOG_DEBUG_MASK (1 << GS_LOG_DEBUG) +/** + Info level enabled. +*/ +#define GS_LOG_INFO_MASK (1 << GS_LOG_INFO) +/** + Notice level enabled. +*/ +#define GS_LOG_NOTICE_MASK (1 << GS_LOG_NOTICE) +/** + Warning level enabled. +*/ +#define GS_LOG_WARNING_MASK (1 << GS_LOG_WARNING) +/** + Error level enabled. +*/ +#define GS_LOG_ERROR_MASK (1 << GS_LOG_ERROR) +/** + All levels enabled. +*/ +#define GS_LOG_ALL_MASK (GS_LOG_TRACE_MASK | GS_LOG_DEBUG_MASK | GS_LOG_INFO_MASK | GS_LOG_NOTICE_MASK | GS_LOG_WARNING_MASK | GS_LOG_ERROR_MASK) +/** + Default levels enabled - #GS_LOG_ERROR, #GS_LOG_WARNING and #GS_LOG_NOTICE. +*/ +#define GS_LOG_DEFAULT_MASK (GS_LOG_ERROR_MASK | GS_LOG_WARNING_MASK | GS_LOG_NOTICE_MASK) +/** + Trace level enabled. + @deprecated use #GS_LOG_TRACE_MASK +*/ +#define LOG_TRACE_MASK GS_LOG_TRACE_MASK +/** + Debug level enabled. + @deprecated use #GS_LOG_DEBUG_MASK +*/ +#define LOG_DEBUG_MASK GS_LOG_DEBUG_MASK +/** + Info level enabled. + @deprecated use #GS_LOG_INFO_MASK +*/ +#define LOG_INFO_MASK GS_LOG_INFO_MASK +/** + Notice level enabled. + @deprecated use #GS_LOG_NOTICE_MASK +*/ +#define LOG_NOTICE_MASK GS_LOG_NOTICE_MASK +/** + Warning level enabled. + @deprecated use #GS_LOG_WARNING_MASK +*/ +#define LOG_WARNING_MASK GS_LOG_WARNING_MASK +/** + Error level enabled. + @deprecated use #GS_LOG_ERROR_MASK +*/ +#define LOG_ERROR_MASK GS_LOG_ERROR_MASK +/** + All levels enabled. + @deprecated use #GS_LOG_ALL_MASK +*/ +#define LOG_ALL_MASK GS_LOG_ALL_MASK +/** + Default levels enabled - #GS_LOG_ERROR, #GS_LOG_WARNING and #GS_LOG_NOTICE. + @deprecated use #GS_LOG_DEFAULT_MASK +*/ +#define LOG_DEFAULT_MASK GS_LOG_DEFAULT_MASK +/**@}*/ + +/** + Define/Create a log group. + + @note name clash: This defines a variable, which potentially is \a global, meaning possibility of name clashes. Therefore log group should always + be prefixed with something that makes it fairly unique, i.e. component name. Example: gs_a3200dock_log - log group used by liba3200dock library. + + @param[in] group name of variables created. See note above about name clash. + @param[in] name_in display name + @param[in] cat_in log group category + @param[in] level_mask log level mask. +*/ +#define GS_LOG_GROUP(group, name_in, cat_in, level_mask) \ + gs_log_group_t group##_s = {.name = name_in, .category = cat_in, \ + .mask = level_mask, .additivity = true, \ + .appenders = NULL}; \ + gs_log_group_t * group = &group##_s + +/** + Define log group with initial mask for \a print and \a store. + + @note name clash: This defines a variable, which potentially is \a global, meaning possibility of name clashes. Therefore log group should always + be prefixed with something that makes it fairly unique, i.e. component name. Example: gs_a3200dock_log - log group used by liba3200dock library. + + @deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(...) instead. + + @param[in] group name of variables created. See note above about name clash. + @param[in] name_in display name + @param[in] print_mask enable mask for \a print. + @param[in] store_mask enable mask for \a store. +*/ +#define LOG_GROUP_MASKED(group, name_in, print_mask, store_mask) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, (print_mask | store_mask)) + +/** + Declare log group as external (defined else where). + + @param[in] group the log group variable defined elsewhere. +*/ +#define GS_LOG_GROUP_EXTERN(group) extern gs_log_group_t * group + +/** + Define log group - levels are #GS_LOG_DEFAULT_MASK + + @deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(..) instead. +*/ +#define LOG_GROUP(group, name_in) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, LOG_DEFAULT_MASK) + +/** + Define verbose log group - all levels are enabled (#GS_LOG_ALL_MASK) + + @deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(..) instead. +*/ +#define LOG_GROUP_VERBOSE(group, name_in) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, LOG_ALL_MASK) + +/** + Define silent log group - all levels are disabled. + + @deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(..) instead. +*/ +#define LOG_GROUP_SILENT(group, name_in) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, 0) + +/** + Declare log group as external (defined else where). + + @deprecated use #GS_LOG_GROUP_EXTERN(...) instead. +*/ +#define LOG_GROUP_EXTERN(group) GS_LOG_GROUP_EXTERN(group) + +/** + Default log group. + This can be overridden by a define +*/ +extern gs_log_group_t * LOG_DEFAULT; + +/** + Initializes the log system. + + @param[in] with_console_appender Enable/Disable console log appender + @return_gs_error_t +*/ +gs_error_t gs_log_init(bool with_console_appender); + +/** + Set log group level mask. + + @param[in] group_name log group name + @param[in] mask level mask to set. + @return_gs_error_t +*/ +gs_error_t gs_log_group_set_level_mask(const char * group_name, uint8_t mask); + +/** + Get log group level mask. + + @param[in] group_name log group name + @param[out] mask returned current level mask. + @return_gs_error_t +*/ +gs_error_t gs_log_group_get_level_mask(const char * group_name, uint8_t *mask); + +/** + Log group iterator callback function + + @param[in] ctx context data for iterator. + @param[in] group log group being iterated. + + @return true/false: Return false to discontinue iteration. +*/ +typedef bool (*gs_log_group_iterator_t)(void *ctx, gs_log_group_t * group); + +/** + Iterate all or specific log group(s). + + @param[in] group_name name of log group, or NULL/\"all\" for all groups. + @param[in] ctx user context data. + @param[in] iter iterator, return \a true to continue, \a false to break iteration. + @return_gs_error_t +*/ +gs_error_t gs_log_group_iterate(const char * group_name, void * ctx, gs_log_group_iterator_t iter); + +/** + Register a log group in the log system. + + The log group will be added to a system wide list of log groups, enabling list and set of level. + + @note The group must remain valid during the life-time of the application. + + @param[in] group The log group to be added to the system. + @return_gs_error_t +*/ +gs_error_t gs_log_group_register(gs_log_group_t *group); + +/** + Register a log group in the log system. + + @note The group must stay in memory during the life-time of the application + @see gs_log_group_register() + @param[in] group The log group to be added to the system. + @return_gs_error_t +*/ +static inline gs_error_t gs_log_group_add(gs_log_group_t *group) +{ + return gs_log_group_register(group); +} + +/** + Checks if a level is enabled on a log group + + @param[in] group The log group to check. + @param[in] level The log level to check if it's set on the group. + @return bool (true if enabled / false if not enabled) +*/ +bool gs_log_group_is_level_enabled(gs_log_group_t *group, gs_log_level_t level); + +/** + Convert string to log level. + + @param[in] str log level. + @param[out] return_level converted log level. + @return_gs_error_t +*/ +gs_error_t gs_log_string_to_level(const char * str, gs_log_level_t * return_level); + +/** + Convert level to single character. + + @param[in] level log level + @return single character representing the \a level. +*/ +char gs_log_level_to_char(gs_log_level_t level); + + +/** + Register Log commands. + @return_gs_error_t +*/ +gs_error_t gs_log_register_commands(void); + +/** + Generic log. + @note This function should not be called directly, use log macros. + + @param level log level + @param group log group. If NULL, the \a default log group will be used. + @param format Format string (printf style). +*/ +void gs_log(gs_log_level_t level, gs_log_group_t * group, const char * format, ...) __attribute__ ((format (__printf__, 3, 4))); + +/** + Generic log from ISR. + @note This function should not be called directly, use log macros. + + @param level log level + @param group log group. If NULL, the \a default log group will be used. + @param format Format string (printf style). +*/ +void gs_log_isr(gs_log_level_t level, gs_log_group_t * group, const char * format, ...) __attribute__ ((format (__printf__, 3, 4))); + +/** + Generic log (va_list). + @note This function should not be called directly, use log macros. + + @param level log level + @param group log group. If NULL, the \a default log group will be used. + @param format Format string (printf style). + @param args arguments for \a format. +*/ +void gs_log_va(gs_log_level_t level, gs_log_group_t * group, const char * format, va_list args); + +/** + Enable/disable color in \a print logs. + Default is \a enabled/true. + + @param[in] color \a true to enable color, \a false disable color. +*/ +void gs_log_set_print_color(bool color); + +/** + Level to color (begin). + + @param[in] level log level. + @return color string. +*/ +const char * gs_log_level_to_color_begin(gs_log_level_t level); + +/** + Level to color (end). + + @return color string. +*/ +const char * gs_log_level_to_color_end(void); + +/** + Take a level as input an create a level mask enabling all + levels with priority >= level. + + If level is e.g. LOG_INFO, the mask will enable Error, Warn & Info. + + * @param level the log level. + * @return level mask + */ +uint8_t gs_log_level_to_mask(gs_log_level_t level); + +/** + Convert string to log mask. + + Format: [+-]level[,[+-]level] + + + add level, - remove level. + + @param[in] str log mask + @param[in] current_mask current mask, used when input format contains + or -. + @param[out] return_mask converted log mask. + @return_gs_error_t +*/ +gs_error_t gs_log_string_to_mask(const char *str, uint8_t current_mask, uint8_t * return_mask); + +#if !(__DOXYGEN__) +/** + Internal macro for checking if log is enabled, before making log. +*/ +#define __gs_log(level, group, format, ...) \ + if (group->mask & (1 << level)) { \ + gs_log(level, group, GS_PGM_STR(format), ##__VA_ARGS__); \ + } + +/** + Internal macro for checking if log is enabled for isr, before making log. +*/ +#define __gs_log_isr(level, group, format, ...) \ + if (group->mask & (1 << level)) { \ + gs_log_isr(level, group, GS_PGM_STR(format), ##__VA_ARGS__); \ + } + +/** + Internal macro used for performing a log only once. + @note This creates a \a static \a variable. +*/ +#define __gs_log_once(level, group, format, ...) \ + ({ \ + static bool print_once; \ + if (!print_once) { \ + print_once = true; \ + __gs_log(level, group, format, ##__VA_ARGS__); \ + } \ + }) +#endif // __DOXYGEN__ + +/** + Default compile-time enabling/disabling of all levels + Unless levels are individually defined, this will be the default value. +*/ +#if !defined(GS_LOG_DISABLE_ALL) +#define GS_LOG_DISABLE_ALL 0 +#endif + +/** + Disable \a error level compile-time by defining a value > 0 +*/ +#if !defined(GS_LOG_DISABLE_ERROR) +#define GS_LOG_DISABLE_ERROR GS_LOG_DISABLE_ALL +#endif + +/** + Disable \a warning level compile-time by defining a value > 0 +*/ +#if !defined(GS_LOG_DISABLE_WARNING) +#define GS_LOG_DISABLE_WARNING GS_LOG_DISABLE_ALL +#endif + +/** + Disable \a notice level compile-time by defining a value > 0 +*/ +#if !defined(GS_LOG_DISABLE_NOTICE) +#define GS_LOG_DISABLE_NOTICE GS_LOG_DISABLE_ALL +#endif + +/** + Disable \a info level compile-time by defining a value > 0 +*/ +#if !defined(GS_LOG_DISABLE_INFO) +#define GS_LOG_DISABLE_INFO GS_LOG_DISABLE_ALL +#endif + +/** + Disable \a debug level compile-time by defining a value > 0 +*/ +#if !defined(GS_LOG_DISABLE_DEBUG) +#define GS_LOG_DISABLE_DEBUG GS_LOG_DISABLE_ALL +#endif + +/** + Disable \a trace level compile-time by defining a value > 0 +*/ +#if !defined(GS_LOG_DISABLE_TRACE) +#define GS_LOG_DISABLE_TRACE GS_LOG_DISABLE_ALL +#endif + +/** + Log \a error to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_error(format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log(LOG_ERROR, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a error from ISR to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_error_isr(format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log_isr(LOG_ERROR, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a error to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_error_group(group, format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log(LOG_ERROR, (group), format, ##__VA_ARGS__); } + +/** + Log \a error only once to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_error_once(format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log_once(LOG_ERROR, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a error only once to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_error_once_group(group, format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log_once(LOG_ERROR, (group), format, ##__VA_ARGS__); } + +/** + Log \a warning to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_warning(format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log(LOG_WARNING, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a warning from ISR to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_warning_isr(format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log_isr(LOG_WARNING, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a warning to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_warning_group(group, format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log(LOG_WARNING, (group), format, ##__VA_ARGS__); } + +/** + Log \a warning only once to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_warning_once(format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log_once(LOG_WARNING, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a warning only once to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_warning_once_group(group, format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log_once(LOG_WARNING, (group), format, ##__VA_ARGS__); } + +/** + Log \a notice to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_notice(format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log(LOG_NOTICE, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a notice from ISR to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_notice_isr(format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log_isr(LOG_NOTICE, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a notice to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_notice_group(group, format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log(LOG_NOTICE, (group), format, ##__VA_ARGS__); } + +/** + Log \a notice only once to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_notice_once(format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log_once(LOG_NOTICE, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a notice only once to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_notice_once_group(group, format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log_once(LOG_NOTICE, (group), format, ##__VA_ARGS__); } + +/** + Log \a info to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_info(format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log(LOG_INFO, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a info from ISR to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_info_isr(format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log_isr(LOG_INFO, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a info to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_info_group(group, format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log(LOG_INFO, (group), format, ##__VA_ARGS__); } + +/** + Log \a info only once to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_info_once(format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log_once(LOG_INFO, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a info only once to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_info_once_group(group, format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log_once(LOG_INFO, (group), format, ##__VA_ARGS__); } + +/** + Log \a debug to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_debug(format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log(LOG_DEBUG, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a debug from ISR to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_debug_isr(format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log_isr(LOG_DEBUG, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a debug to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_debug_group(group, format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log(LOG_DEBUG, (group), format, ##__VA_ARGS__); } + +/** + Log \a debug only once to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_debug_once(format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log_once(LOG_DEBUG, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a debug only once to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_debug_once_group(group, format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log_once(LOG_DEBUG, (group), format, ##__VA_ARGS__); } + +/** + Log \a trace to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_trace(format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log(LOG_TRACE, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a trace from ISR to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_trace_isr(format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log_isr(LOG_TRACE, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a trace to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_trace_group(group, format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log(LOG_TRACE, (group), format, ##__VA_ARGS__); } + +/** + Log \a trace only once to default group (LOG_DEFAULT). + @param[in] format Format string (printf style). +*/ +#define log_trace_once(format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log_once(LOG_TRACE, LOG_DEFAULT, format, ##__VA_ARGS__); } + +/** + Log \a trace only once to group. + @param[in] group log group (gs_log_group_t *). + @param[in] format Format string (printf style). +*/ +#define log_trace_once_group(group, format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log_once(LOG_TRACE, (group), format, ##__VA_ARGS__); } + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/minmax.h b/gomspace/libutil/include/gs/util/minmax.h new file mode 100644 index 00000000..4b9edf74 --- /dev/null +++ b/gomspace/libutil/include/gs/util/minmax.h @@ -0,0 +1,67 @@ +#ifndef GS_UTIL_MINMAX_H +#define GS_UTIL_MINMAX_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Min/max utilities. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Return minimum value. + @param[in] x value + @param[in] y value + @return the lowest value of the input parameters. +*/ +#define gs_min(x,y) ({ \ + __typeof__ (x) _x = (x); \ + __typeof__ (y) _y = (y); \ + _x < _y ? _x : _y; }) + +/** + Return maximum value. + @param[in] x value + @param[in] y value + @return the maximum value of the input parameters. +*/ +#define gs_max(x,y) ({ \ + __typeof__ (x) _x = (x); \ + __typeof__ (y) _y = (y); \ + _x > _y ? _x : _y; }) + +/** + Return minimum value. + @param[in] x value + @param[in] y value + @param[in] z value + @return the lowest value of the input parameters. +*/ +#define gs_min3(x,y,z) gs_min(gs_min((x),(y)), (z)) + +/** + Return maximum value. + @param[in] x value + @param[in] y value + @param[in] z value + @return the maximum value of the input parameters. +*/ +#define gs_max3(x,y,z) gs_max(gs_max((x),(y)), (z)) + +/** + Clamp value within min/max. + @param[in] x value + @param[in] _max max value + @param[in] _min min value + @return value between min and max. +*/ +#define gs_clamp(x, _min, _max) ({ \ + gs_min(gs_max((x), (_min)), (_max)); }) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/mutex.h b/gomspace/libutil/include/gs/util/mutex.h new file mode 100644 index 00000000..b5a411f7 --- /dev/null +++ b/gomspace/libutil/include/gs/util/mutex.h @@ -0,0 +1,63 @@ +#ifndef GS_UTIL_MUTEX_H +#define GS_UTIL_MUTEX_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Mutex (recursive). + + The mutex API wraps POSIX \a pthread_mutex and FreeRTOS \a mutex. + + @note Mutex can not be used from within an ISR routine - use gs_sem instead. +*/ + +#include +#if __linux__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if __linux__ +/** + Mutex handle. +*/ +typedef pthread_mutex_t * gs_mutex_t; +#else +typedef struct gs_freertos_mutex_t * gs_mutex_t; +#endif + +/** + Create mutex. + @param[out] mutex handle. + @return error code. +*/ +gs_error_t gs_mutex_create(gs_mutex_t * mutex); + +/** + Destroy mutex - free resources. + @param[in] mutex handle. + @return error code. +*/ +gs_error_t gs_mutex_destroy(gs_mutex_t mutex); + +/** + Lock mutex. + @param[in] mutex handle. + @return error code. +*/ +gs_error_t gs_mutex_lock(gs_mutex_t mutex); + +/** + Unlock mutex. + @param[in] mutex handle. + @return error code. +*/ +gs_error_t gs_mutex_unlock(gs_mutex_t mutex); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/pgm.h b/gomspace/libutil/include/gs/util/pgm.h new file mode 100644 index 00000000..04e39013 --- /dev/null +++ b/gomspace/libutil/include/gs/util/pgm.h @@ -0,0 +1,162 @@ +#ifndef GS_UTIL_PROGMEM_H +#define GS_UTIL_PROGMEM_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Macros for handling special memory access. + + On most targets/processors, constant data/strings are located in the program space and can be read in the same way as data in the data space. + However, on a few targets (e.g. avr/avr8), data/strings must be marked in a special way in order to go into the program space, see #GS_PGM_STR() + + Using following macros, will make it easier to make cross-platform code and avoid \#if/\#endif. + These macros should only be used where the code also needs to run on avr/avr8. + + @note Including this header on avr/avr8 will REDEFINE printf!. + + http://www.atmel.com/webdoc/avrlibcreferencemanual/group__avr__pgmspace.html. + http://www.nongnu.org/avr-libc/user-manual/pgmspace.html. +*/ + +#include +#if defined(__AVR__) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__AVR__) || (__DOXYGEN__) +/** + Special program/data memory handling. +*/ +#define GS_PGM 1 + +/** + Place object in program space (must be const). + Example: static const uint8_t data8[] GS_PGM_OBJECT = {1, 255}; +*/ +#define GS_PGM_OBJECT PROGMEM + +/** + Place const string in program space. + By default the string goes into data, uses thereby uses up space. + Once the string is placed in program space, xx_P functions must be used to access them - see #GS_PGM_PRINTF. + @note printf is re-defined by including this header +*/ +#define GS_PGM_STR(str) PSTR(str) + +/** + Read uint8 from program space (near). +*/ +#define GS_PGM_UINT8(value) pgm_read_byte(&(value)) + +/** + Read uint8 from program space using a pointer (near). +*/ +#define GS_PGM_UINT8_BY_PTR(value) pgm_read_byte(value) + +/** + Read word from program space (near). +*/ +#define GS_PGM_UINT16(value) pgm_read_word(&(value)) +/** + Read word from program space using a pointer (near). +*/ +#define GS_PGM_UINT16_BY_PTR(value) pgm_read_word(value) + +/** + Read dword from program space (near). +*/ +#define GS_PGM_UINT32(value) pgm_read_dword(&(value)) +/** + Read word from program space using a pointer (near). +*/ +#define GS_PGM_UINT32_BY_PTR(value) pgm_read_dword(value) + +/** + Memcpy from program space (near). + @param[in] dst destination. + @param[in] src source - program space. + @param[in] n number of bytes to copy +*/ +#define GS_PGM_MEMCPY(dst, src, n) memcpy_P(dst, src, n) + +/** + String compare (program space) + @param[in] s1 string 1 + @param[in] s2 string 2 - program space. + @param[in] n max number of bytes to compare +*/ +#define GS_PGM_STRNCMP(s1,s2,n) strncmp_P(s1, s2, n) + +/** + String compare (program space) + @param[in] s1 string 1 + @param[in] s2 string 2 - program space. + @param[in] n max number of bytes to compare +*/ +#define GS_PGM_STRNCASECMP(s1,s2,n) strncasecmp_P(s1, s2, n) + +/** + String formatting character for referencing a string placed in programs space. +*/ +#define GS_PGM_FMT_STR "S" + +/** + printf (format string in program space). + Example: print \a param->name (from prgram space) and \a value from data space, using a format string in program space. + GS_PGM_PRINTF(GS_PGM_STR("%"GS_PGM_FMT_STR", %d"), param->name, value) +*/ +#define GS_PGM_PRINTF(format, ...) printf_P(format, ##__VA_ARGS__) + +/** + vprintf (format string in program space). +*/ +#define GS_PGM_VPRINTF(format, va) vfprintf_P(stdout, format, va) + +/** + snprintf (format string in program space). +*/ +#define GS_PGM_SNPRINTF(buf, bufsize, format, ...) snprintf_P(buf, bufsize, format, ##__VA_ARGS__) + +/** + vsnprintf (format string in program space). +*/ +#define GS_PGM_VSNPRINTF(buf, bufsize, format, va) vsnprintf_P(buf, bufsize, format, va) + +/** + redefines printf (puts format string in program space) + */ +#undef printf +#define printf(format, ...) GS_PGM_PRINTF(GS_PGM_STR(format), ## __VA_ARGS__) + +#else + +#undef GS_PGM + +#define GS_PGM_OBJECT +#define GS_PGM_STR(str) (str) +#define GS_PGM_UINT8(value) (value) +#define GS_PGM_UINT8_BY_PTR(value) (*(value)) +#define GS_PGM_UINT16(value) (value) +#define GS_PGM_UINT16_BY_PTR(value) (*(value)) +#define GS_PGM_UINT32(value) (value) +#define GS_PGM_UINT32_BY_PTR(value) (*(value)) +#define GS_PGM_MEMCPY(dst, src, size) memcpy(dst, src, size) +#define GS_PGM_STRNCMP(s1,pgmstr,size) strncmp(s1, pgmstr, size) +#define GS_PGM_STRNCASECMP(s1,pgmstr,size) strncasecmp(s1, pgmstr, size) + +#define GS_PGM_FMT_STR "s" +#define GS_PGM_PRINTF(format, ...) printf(format, ## __VA_ARGS__) +#define GS_PGM_VPRINTF(format, va) vprintf(format, va) +#define GS_PGM_SNPRINTF(buf, bufsize, format, ...) snprintf(buf, bufsize, format, ##__VA_ARGS__) +#define GS_PGM_VSNPRINTF(buf, bufsize, format, va) vsnprintf(buf, bufsize, format, va) + +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/queue.h b/gomspace/libutil/include/gs/util/queue.h new file mode 100644 index 00000000..43b7e9ae --- /dev/null +++ b/gomspace/libutil/include/gs/util/queue.h @@ -0,0 +1,102 @@ +#ifndef GS_UTIL_QUEUE_H +#define GS_UTIL_QUEUE_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Queue. + + The queue API wraps FreeRTOS \a queue. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if __linux__ +/** + Queue handle. +*/ +typedef struct gs_pthread_queue * gs_queue_t; +#else +typedef struct gs_freertos_queue_t * gs_queue_t; +#endif + +/** + Create queue. + + @param[in] items max number of items on the queue. + @param[in] item_size size of item (bytes). + @param[out] queue created queue. + @return_gs_error_t +*/ +gs_error_t gs_queue_create(size_t items, size_t item_size, gs_queue_t * queue); + +/** + Destroy queue - free resources. + + @param[in] queue handle. + @return_gs_error_t +*/ +gs_error_t gs_queue_destroy(gs_queue_t queue); + +/** + Enqueue object on queue. + @param[in] queue handle. + @param[in] value pointer to object, size specified at gs_queue_create(). + @param_int_timeout_ms + @return_gs_error_timeout + @return_gs_error_t +*/ +gs_error_t gs_queue_enqueue(gs_queue_t queue, const void *value, int timeout_ms); + +/** + Enqueue object on queue from within an ISR. + @param[in] queue handle. + @param[in] value pointer to object, size specified at gs_queue_create(). + @param[in] cswitch context switch. + @return GS_ERROR_FULL if queue is full. + @return_gs_error_t +*/ +gs_error_t gs_queue_enqueue_isr(gs_queue_t queue, const void * value, gs_context_switch_t * cswitch); + +/** + Dequeue object from queue. + @param[in] queue handle. + @param[out] buf element - size specified in gs_queue_create(). + @param_int_timeout_ms + @return_gs_error_timeout + @return_gs_error_t +*/ +gs_error_t gs_queue_dequeue(gs_queue_t queue, int timeout_ms, void *buf); + +/** + Dequeue object from queue from within an ISR. + @param[in] queue handle. + @param[in] cswitch context switch. + @param[out] buf element - size specified in gs_queue_create(). + @return GS_ERROR_NOT_FOUND if no elements in queue. + @return_gs_error_t +*/ +gs_error_t gs_queue_dequeue_isr(gs_queue_t queue, gs_context_switch_t * cswitch, void * buf); + +/** + Return queue size. + @param[in] queue handle. + @return queue size +*/ +unsigned int gs_queue_size(gs_queue_t queue); + +/** + Return queue size from within an ISR. + @param[in] queue handle. + @return queue size +*/ +unsigned int gs_queue_size_isr(gs_queue_t queue); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/rtc.h b/gomspace/libutil/include/gs/util/rtc.h new file mode 100644 index 00000000..b1988925 --- /dev/null +++ b/gomspace/libutil/include/gs/util/rtc.h @@ -0,0 +1,62 @@ +#ifndef GS_UTIL_RTC_H +#define GS_UTIL_RTC_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Real Time Clock interface. + + The RTC driver is used by gs_clock_get_time() and gs_clock_set_time(). +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Platform supporting RTC must register the driver, before the rest of the system can access it. + @see gs_rtc_register() +*/ +typedef struct { + /** + Call-back for getting RTC time. + @param[out] time user allocated struct for returning time. + */ + gs_error_t (*get_time)(void * driver_data, gs_timestamp_t * time); + /** + Call-back for setting RTC time. + @param[in] time user allocated struct for returning time. + */ + gs_error_t (*set_time)(void * driver_data, const gs_timestamp_t * time); +} gs_rtc_driver_t; + +/** + Register RTC driver. + @param[in] driver driver - data/struct must remain valid as long as registered. + @param[in] driver_data driver specific data, forwarded to driver when set/get is called. + @return_gs_error_t +*/ +gs_error_t gs_rtc_register(const gs_rtc_driver_t * driver, void * driver_data); + +/** + Return GS_OK if RTC is supported. +*/ +gs_error_t gs_rtc_supported(void); + +/** + Set RTC. +*/ +gs_error_t gs_rtc_get_time(gs_timestamp_t * time); + +/** + Get RTC. +*/ +gs_error_t gs_rtc_set_time(const gs_timestamp_t * time); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/sem.h b/gomspace/libutil/include/gs/util/sem.h new file mode 100644 index 00000000..4afd4d7d --- /dev/null +++ b/gomspace/libutil/include/gs/util/sem.h @@ -0,0 +1,75 @@ +#ifndef GS_UTIL_SEM_H +#define GS_UTIL_SEM_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Semaphore. + + The semaphore API wraps POSIX \a semaphore and FreeRTOS \a counted semaphore. + + Main difference is that FreeRTOS uses different API calls, when called from within + an ISR routine. +*/ + +#include +#if __linux__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if __linux__ +/** + Semaphore handle. +*/ +typedef sem_t * gs_sem_t; +#else +typedef struct gs_freertos_sem_t * gs_sem_t; +#endif + +/** + Create semaphore. + @param[in] initialValue initial value. + @param[out] sem created semaphore. + @return_gs_error_t +*/ +gs_error_t gs_sem_create(unsigned int initialValue, gs_sem_t * sem); + +/** + Destroy semaphore - free resources. + @param[in] sem handle. + @return_gs_error_t +*/ +gs_error_t gs_sem_destroy(gs_sem_t sem); + +/** + Wait for semaphore to be signaled. + @param[in] sem handle. + @param_int_timeout_ms + @return_gs_error_timeout + @return_gs_error_t +*/ +gs_error_t gs_sem_wait(gs_sem_t sem, int timeout_ms); + +/** + Post/signal semaphore. + @param[in] sem handle. + @return_gs_error_t +*/ +gs_error_t gs_sem_post(gs_sem_t sem); + +/** + Post/signal semaphore from within a ISR. + @param[in] sem handle. + @param[in] cswitch context switch. + @return_gs_error_t +*/ +gs_error_t gs_sem_post_isr(gs_sem_t sem, gs_context_switch_t * cswitch); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/stdio.h b/gomspace/libutil/include/gs/util/stdio.h new file mode 100644 index 00000000..992d4dda --- /dev/null +++ b/gomspace/libutil/include/gs/util/stdio.h @@ -0,0 +1,117 @@ +#ifndef GS_UTIL_STDIO_H +#define GS_UTIL_STDIO_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + GomSpace extensions to standard \a stdio.h. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Put character on stdout. +*/ +gs_error_t gs_stdio_putchar(int ch); + +/** + Read character from stdin with timeout. + @param[in] timeout_ms timeout, < 0: block forever, 0: poll, > 0: wait number of milli seconds. + @param[out] ch character read. If NULL, one character from stdin is still consumed - but nothing returned. + @return GS_ERROR_TIMEOUT on timeout + @return_gs_error_t +*/ +gs_error_t gs_stdio_getchar_timed(int timeout_ms, int *ch); + +/** + Read character from stdin. + Blocks until a character is available. + @param[out] ch character read. If NULL, one character from stdin is still consumed - but nothing returned. + @return_gs_error_t +*/ +static inline gs_error_t gs_stdio_getchar(int * ch) +{ + return gs_stdio_getchar_timed(-1, ch); +} + +/** + Read characters from stdin. + Blocks until all characters are read. + @param[in,out] buf user supplied buffer for receiving characters. + @param[in] n number of characters to read. + @return_gs_error_t +*/ +gs_error_t gs_stdio_get(char * buf, size_t n); + +/** + Write characters to stdout. + Blocks until characters are written. + @param[in] buf characters to write. + @param[in] n number of characters to write. + @param[in] text if \a true, new lines (\\n) are converted to \\r\\n. + @return_gs_error_t +*/ +gs_error_t gs_stdio_put(const char * buf, size_t n, bool text); + +/** + Pattern for printing a byte as binary. + @see GS_STDIO_BYTETOBINARY() +*/ +#define GS_STDIO_BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d" + +/** + Macro for splitting a byte info 'bits'. +*/ +#define GS_STDIO_BYTETOBINARY(byte) \ + (byte & 0x80 ? 1 : 0), \ + (byte & 0x40 ? 1 : 0), \ + (byte & 0x20 ? 1 : 0), \ + (byte & 0x10 ? 1 : 0), \ + (byte & 0x08 ? 1 : 0), \ + (byte & 0x04 ? 1 : 0), \ + (byte & 0x02 ? 1 : 0), \ + (byte & 0x01 ? 1 : 0) + +/** + Color definitions for gs_color_printf() + @see gs_color_printf() +*/ +typedef enum { + /** + Colors. + */ + GS_COLOR_COLORS = 0x00ff, + GS_COLOR_NONE = 0, + GS_COLOR_BLACK = 1, + GS_COLOR_RED = 2, + GS_COLOR_GREEN = 3, + GS_COLOR_YELLOW = 4, + GS_COLOR_BLUE = 5, + GS_COLOR_MAGENTA = 6, + GS_COLOR_CYAN = 7, + GS_COLOR_WHITE = 8, + /** + Attributes + */ + GS_COLOR_ATTRS = 0xff00, + GS_COLOR_BOLD = 0x100, +} gs_color_printf_t; + +/** + Printf with colors on stdout. + + Using the standard terminal escape sequences for setting the color. + @param[in] color color settings. + @param[in] format standard printf format string. +*/ +void gs_color_printf(gs_color_printf_t color, const char * format, ...) __attribute__ ((format (__printf__, 2, 3))); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/string.h b/gomspace/libutil/include/gs/util/string.h new file mode 100644 index 00000000..034af8c6 --- /dev/null +++ b/gomspace/libutil/include/gs/util/string.h @@ -0,0 +1,391 @@ +#ifndef GS_UTIL_STRING_H +#define GS_UTIL_STRING_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + String utilitizes. + + All string parsing functions will return #GS_OK if the string was parsed entirely. + If the string contains characters that are not part of the selected base, the functions will return #GS_ERROR_DATA. + If the value parsed is bigger than the output type, the functions will return #GS_ERROR_OVERFLOW. + Spaces are ignored by all functions. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Macro helper for concatening tokens. +*/ +#define GS_STRINGZ(x) #x + +/** + Stringify a preprocessing token. +*/ +#define GS_DEF2STRING(x) GS_STRINGZ(x) + +/** + * Strncpy (using size of destination) and forced zero termination. + */ +#define GS_STRNCPY(dst,src) strncpy(dst,src,GS_ARRAY_SIZE(dst));dst[GS_ARRAY_SIZE(dst)-1] = 0 + +/** + Convert string to int32 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_int32(const char * string, int32_t * value); + +/** + Convert string to uint32 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_uint32(const char * string, uint32_t * value); + +/** + Convert string to int64 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_int64(const char * string, int64_t * value); + +/** + Convert string to uint64 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_uint64(const char * string, uint64_t * value); + +/** + Convert string to int8 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 12 (decimal), 0x12 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_int8(const char * string, int8_t * value); + +/** + Convert string to uint8 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 12 (decimal), 0x12 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_uint8(const char * string, uint8_t * value); + +/** + Convert string to int16 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_int16(const char * string, int16_t * value); + +/** + Convert string to uint16 (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_uint16(const char * string, uint16_t * value); + +/** + Convert string to uint32 (hexadecimal). + Accepts: hexadecimal (no leading 0x), e.g. a123, A123. + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_hex_to_uint32(const char * string, uint32_t * value); + +/** + Convert string to uint64 (hexadecimal). + Accepts: hexadecimal (no leading 0x), e.g. a123, A123. + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_hex_to_uint64(const char * string, uint64_t * value); + +/** + Convert string to boolean. + Accepts: true, false, on, off, 1, 0 (ignores case) + @param[in] string string to convert. + @param[out] pvalue converted value + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_bool(const char * string, bool * pvalue); + +/** + Convert string to float. + @param[in] string string to convert. + @param[out] pvalue converted value + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_float(const char * string, float * pvalue); + +/** + Convert string to double. + @param[in] string string to convert. + @param[out] pvalue converted value + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_double(const char * string, double * pvalue); + +/** + Return string for boolean value (true or false). + @param[in] value value + @return \a 'true' if input is true, else \a 'false'. +*/ +const char * gs_string_from_bool(bool value); + +/** + Convert string to pointer (decimal or hexadecimal). + Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal) + @param[in] string string to convert. + @param[out] value converted value + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +gs_error_t gs_string_to_pointer(const char * string, void ** value); + +/** + Format size as Bytes, Kilo or Mega. + + Output examples: \a 512.0B, \a 1.0K and \a 1.0M. + + @param[in] size size in bytes + @param[out] buffer formatted size + @param[in] buffer_size size of \a buf + @return GS_ERROR_OVERFLOW if the resulting value is larger than the output type + @return GS_ERROR_DATA if the input string could not be parsed completely + @return GS_ERROR_ARG if the input string is a NULL pointer +*/ +char * gs_string_bytesize(long size, char *buffer, size_t buffer_size); + +/** + GS implementation of gcc's strtol + Instead of setting errno this function takes a pointer to err which is set + the same way as with gcc's strtol + + @param[in] nptr input string + @param[out] endptr the pointer to the end of the string parsed + @param[in] base number system (10 or 16) + @param[out] err return value if overflow + @return converted value +*/ +int32_t gs_string_strto32int(const char *nptr, char **endptr, uint8_t base, gs_error_t * err); + +/** + GS implementation of gcc's strtoul + Instead of setting errno this function takes a pointer to err which is set + the same way as with gcc's strtoul + + @param[in] nptr input string + @param[out] endptr the pointer to the end of the string parsed + @param[in] base number system (10 or 16) + @param[out] err return value if overflow + @return converted value +*/ +uint64_t gs_string_strto64uint(const char *nptr, char **endptr, uint8_t base, gs_error_t * err); + +/** + GS implementation of gcc's strtoul + Instead of setting errno this function takes a pointer to err which is set + the same way as with gcc's strtoul + + @param[in] nptr input string + @param[out] endptr the pointer to the end of the string parsed + @param[in] base number system (10 or 16) + @param[out] err return value if overflow + @return converted value +*/ +int64_t gs_string_strto64int(const char *nptr, char **endptr, uint8_t base, gs_error_t * err); + +/** + GS implementation of gcc's strtoul + Instead of setting errno this function takes a pointer to err which is set + the same way as with gcc's strtoul + + @param[in] nptr input string + @param[out] endptr the pointer to the end of the string parsed + @param[in] base number system (10 or 16) + @param[out] err return value if overflow + @return converted value +*/ +uint32_t gs_string_strto32uint(const char *nptr, char **endptr, uint8_t base, gs_error_t * err); + +/** + Returns pointer to first none-space character. + + @param[in] string string + @return NULL if \a string is NULL, otherwise first none-space character. +*/ +const char * gs_string_skip_leading_spaces(const char * string); + +/** + Check if a string is NULL or empty. + + @param[in] string string + @return true if string is empty or NULL. +*/ +bool gs_string_empty(const char * string); + +/** + Case-insentive wilcard match (similiar to fnmatch). + + Supports following wildcard(s): + - * (asterix) zero or more characters. + + This may be extended in future versions and will not be considered a break of the API. + + @param[in] pattern pattern to match against \a string. + @param[in] string string to match against \a pattern + @return \a true if match, else \ false +*/ +bool gs_string_match(const char * pattern, const char * string); + +/** + Returns \a true if string contains wildcards. + + @param[in] string string to check for wildcards. + @return \a true if string contains wildcards recognized by gs_string_match(). +*/ +bool gs_string_has_wildcards(const char * string); + +/** + Trim string in buffer by removing leading/trailing white space. + + Uses isspace(c). + + @param[in] buffer buffer to trim. + @param[in] buffer_size size of \a buffer. +*/ +void gs_string_trim(char * buffer, size_t buffer_size); + +/** + Returns \a true if string ends with endswith. + + @param[in] string string to check + @param[in] endswith string that string should end with + @return \a true if string endswith endswith +*/ +bool gs_string_endswith(const char * string, const char * endswith); + +/** + Extract suboption from a string. + + @param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\". + @param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present). + @param[out] buf user buffer for returning value of sub-option. + @param[in] buf_size size of \a buf user buffer. + @return_gs_error_t +*/ +gs_error_t gs_string_get_suboption(const char * options, const char * suboption, char * buf, size_t buf_size); + +/** + Extract suboption (as string) from a string. + + @param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\". + @param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present). + @param[in] def default value, returned if sub-option isn't found. + @param[out] buf user buffer for returning value of sub-option. + @param[in] buf_size size of \a buf user buffer. + @return If the sub-option isn't found, the \a def default value will be copied to \a buf and #GS_OK will be returned. + @return_gs_error_t +*/ +gs_error_t gs_string_get_suboption_string(const char * options, const char * suboption, const char * def, char * buf, size_t buf_size); + +/** + Extract suboption (as uint8) from a string. + + @param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\". + @param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present). + @param[in] def default value, returned if sub-option isn't found. + @param[out] value user supplied buffer for returning the value. + @return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned. + @return_gs_error_t +*/ +gs_error_t gs_string_get_suboption_uint8(const char * options, const char * suboption, uint8_t def, uint8_t * value); + +/** + Extract suboption (as uint16) from a string. + + @param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\". + @param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present). + @param[in] def default value, returned if sub-option isn't found. + @param[out] value user supplied buffer for returning the value. + @return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned. + @return_gs_error_t +*/ +gs_error_t gs_string_get_suboption_uint16(const char * options, const char * suboption, uint16_t def, uint16_t * value); + +/** + Extract suboption (as uint32) from a string. + + @param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\". + @param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present). + @param[in] def default value, returned if sub-option isn't found. + @param[out] value user supplied buffer for returning the value. + @return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned. + @return_gs_error_t +*/ +gs_error_t gs_string_get_suboption_uint32(const char * options, const char * suboption, uint32_t def, uint32_t * value); + +/** + Extract suboption (as bool) from a string. + + @param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\". + @param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present). + @param[in] def default value, returned if sub-option isn't found. + @param[out] value user supplied buffer for returning the value. + @return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned. + @return_gs_error_t +*/ +gs_error_t gs_string_get_suboption_bool(const char * options, const char * suboption, bool def, bool * value); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/test/cmocka.h b/gomspace/libutil/include/gs/util/test/cmocka.h new file mode 100644 index 00000000..43648627 --- /dev/null +++ b/gomspace/libutil/include/gs/util/test/cmocka.h @@ -0,0 +1,136 @@ +#ifndef GS_UTIL_TEST_CMOCKA_H +#define GS_UTIL_TEST_CMOCKA_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Cmocka extensions. + + Official site for cmocka https://cmocka.org. +*/ + +#include + +// cmocka +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !(__DOXYGEN__) +// internal helpers - use macros +void _gs_assert_int_equal(const intptr_t a, const intptr_t b, bool equal, const char * const file, const int line); +void _gs_assert_uint_equal(const uintptr_t a, const uintptr_t b, bool equal, bool hex, const char * const file, const int line); +void _gs_assert_error_equal(const int a, const int b, bool equal, const char * const file, const int line); +void _gs_assert_float_equal(const float a, const float b, const float diff, bool equal, const char * const file, const int line); +void _gs_assert_double_equal(const double a, const double b, const double diff, bool equal, const char * const file, const int line); +#endif + +/** + Assert int (print value as signed). +*/ +#define GS_ASSERT_INT_EQUAL(a,b) _gs_assert_int_equal((intptr_t) (a), (intptr_t) (b), true, __FILE__, __LINE__) +/** + Assert unsigned int (print value as unsigned). +*/ +#define GS_ASSERT_UINT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, false, __FILE__, __LINE__) +/** + Assert int (print value as hex). +*/ +#define GS_ASSERT_XINT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, true, __FILE__, __LINE__) +/** + Assert #gs_error_t (print value and error text). +*/ +#define GS_ASSERT_ERROR_EQUAL(a,b) _gs_assert_error_equal(a, b, true, __FILE__, __LINE__) +/** + Assert #GS_OK (print value and error text). +*/ +#define GS_ASSERT_ERROR_OK(a) _gs_assert_error_equal(a, GS_OK, true, __FILE__, __LINE__) +/** + Assert float (print value as signed). +*/ +#define GS_ASSERT_FLOAT_EQUAL(a,b,diff) _gs_assert_float_equal(a, b, diff, true, __FILE__, __LINE__) +/** + Assert double (print value as signed). +*/ +#define GS_ASSERT_DOUBLE_EQUAL(a,b,diff) _gs_assert_double_equal(a, b, diff, true, __FILE__, __LINE__) + +/** + Assert int (print value as signed). +*/ +#define GS_ASSERT_INT_NOT_EQUAL(a,b) _gs_assert_int_equal((intptr_t) (a), (intptr_t) (b), false, __FILE__, __LINE__) +/** + Assert unsigned int (print value as unsigned). +*/ +#define GS_ASSERT_UINT_NOT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), false, false, __FILE__, __LINE__) +/** + Assert int (print value as hex). +*/ +#define GS_ASSERT_XINT_NOT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), false, true, __FILE__, __LINE__) +/** + Assert #GS_OK (print value and error text). +*/ +#define GS_ASSERT_ERROR_NOT_EQUAL(a,b) _gs_assert_error_equal(a, b, false, __FILE__, __LINE__) + +/** + Code reference. +*/ +#define GS_REF() __FILE__,__LINE__ +/** + Assert int with code reference (print value as signed). +*/ +#define GS_ASSERT_INT_EQUAL_REF(a,b,file,line) _gs_assert_int_equal((intptr_t) (a), (intptr_t) (b), true, file, file, line) +/** + Assert unsigned int with code reference (print value as unsigned). +*/ +#define GS_ASSERT_UINT_EQUAL_REF(a,b,file,line) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, false, file, line) +/** + Assert int with code reference (print value as hex). +*/ +#define GS_ASSERT_XINT_EQUAL_REF(a,b,file,line) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, true, file, line) +/** + Assert #gs_error_t with code reference (print value and error text). +*/ +#define GS_ASSERT_ERROR_EQUAL_REF(a,b,file,line) _gs_assert_error_equal(a, b, true, file, line) +/** + Assert #GS_OK with code reference (print value and error text). +*/ +#define GS_ASSERT_ERROR_OK_REF(a,file,line) _gs_assert_error_equal(a, GS_OK, true, file, line) + +/** + Run \a cmocka test group. + + @param[in] name name of test. If name is \a tests and GS_TEST_NAME is set, GS_TEST_NAME will be used instead. + @param[in] tests array of tests. + @param[in] num_tests number of tests. + @param[in] setup setup function, can be NULL. + @param[in] teardown teardown function, can be NULL. + @return 0 on success. +*/ +static inline int gs_cmocka_run_group_tests(const char *name, + const struct CMUnitTest * const tests, + const size_t num_tests, + CMFixtureFunction setup, + CMFixtureFunction teardown) +{ +#ifdef GS_TEST_NAME // set by buildtools::gs_test_cmocka.py + if (strcasecmp(name, "tests") == 0) { + name = GS_DEF2STRING(GS_TEST_NAME); + } +#endif + return _cmocka_run_group_tests(name, tests, num_tests, setup, teardown); +} + +#ifdef GS_TEST_NAME +// hi-jack cmocka's macro +#undef cmocka_run_group_tests +#define cmocka_run_group_tests(tests, setup, teardown) gs_cmocka_run_group_tests(GS_DEF2STRING(tests), tests, GS_ARRAY_SIZE(tests), setup, teardown) +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/test/command.h b/gomspace/libutil/include/gs/util/test/command.h new file mode 100644 index 00000000..d2227017 --- /dev/null +++ b/gomspace/libutil/include/gs/util/test/command.h @@ -0,0 +1,80 @@ +#ifndef GS_UTIL_TEST_COMMAND_H +#define GS_UTIL_TEST_COMMAND_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Command Test framework. + + Provides a simple way of unit-testing/validating commands. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Validate command execution. + + Runs a commands and validates the output/results agains the inputs. + Asserts if the results does not match. + + @param[in] cmd command (including arguments) to execute. + @param[in] ret expected return code from the command execution framework. + @param[in] cmd_ret expected return code from the commands handler. This is only validated if (ret = GS_OK). + @param[in] std_in string with expected command input. + @param[in] std_out string with expected command output. Wildcards (*\/?) are supported. + @param[in] file string with file name. + @param[in] line string with line no. + @return void +*/ +void _gs_assert_command_validate(const char *cmd, gs_error_t ret, gs_error_t cmd_ret, const char *std_in, const char *std_out, const char * const file, const int line); + +/** + Validate command results returned from last command execution. + Asserts if the results does not match. + + @param[in] no the result no to verify. + @param[in] group string with expected group id. Wildcards (*\/?) are supported. + @param[in] key string with expected key. Wildcards (*\/?) are supported. + @param[in] value string with expected value. Wildcards (*\/?) are supported. + @param[in] file string with file name. + @param[in] line string with line no. + @return void +*/ +void _gs_assert_command_validate_last_result(unsigned int no, const char *group, const char *key, const char *value, const char * const file, const int line); + +/** + Validate command execution. + + Runs a commands and validates the output/results agains the inputs. + Asserts if the results does not match. + + @param[in] cmd command (including arguments) to execute. + @param[in] ret expected return code from the command execution framework. + @param[in] cmd_ret expected return code from the commands handler. This is only validated if (ret = GS_OK). + @param[in] std_in string with expected command input. + @param[in] std_out string with expected command output. Wildcards (*\/?) are supported. + @return void +*/ +#define GS_ASSERT_COMMAND(cmd,ret,cmd_ret,std_in,std_out) _gs_assert_command_validate(cmd,ret,cmd_ret,std_in,std_out, __FILE__, __LINE__); + +/** + Validate command results returned from last command execution. + Asserts if the results does not match. + + @param[in] no the result no to verify. + @param[in] group string with expected group id. Wildcards (*\/?) are supported. + @param[in] key string with expected key. Wildcards (*\/?) are supported. + @param[in] value string with expected value. Wildcards (*\/?) are supported. + @return void +*/ +#define GS_ASSERT_COMMAND_RESULT(no,group,key,value) _gs_assert_command_validate_last_result(no,group,key,value, __FILE__, __LINE__); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/test/log.h b/gomspace/libutil/include/gs/util/test/log.h new file mode 100644 index 00000000..13322953 --- /dev/null +++ b/gomspace/libutil/include/gs/util/test/log.h @@ -0,0 +1,88 @@ +#ifndef GS_UTIL_TEST_LOG_H +#define GS_UTIL_TEST_LOG_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Log Test framework. + + Provides a simple way of veriyfing logs generated during unit-testing. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Assert log count - internal helper function. +*/ +void gs_assert_log_count(int level, unsigned int count, const char * file, int line); + +/** + Assert log messag and count - internal helper function. +*/ +void gs_assert_log(unsigned int stack_index, unsigned int count, gs_log_level_t level, const char * pattern, const char * file, int line); + +/** + Initialize framework, by installing a callback for \a print. + @param[in] verbose of \a true, logs will be printed on stdout. +*/ +void gs_test_log_init(bool verbose); + +/** + Clear log stats. +*/ +void gs_test_log_clear(void); + +/** + Assert number of error logs. +*/ +#define GS_ASSERT_LOG_ERROR(cnt) gs_assert_log_count(LOG_ERROR, cnt, __FILE__, __LINE__); + +/** + Assert number of warning logs. +*/ +#define GS_ASSERT_LOG_WARNING(cnt) gs_assert_log_count(LOG_WARNING, cnt, __FILE__, __LINE__); + +/** + Assert number of notice logs. +*/ +#define GS_ASSERT_LOG_NOTICE(cnt) gs_assert_log_count(LOG_NOTICE, cnt, __FILE__, __LINE__); + +/** + Assert number of info logs. +*/ +#define GS_ASSERT_LOG_INFO(cnt) gs_assert_log_count(LOG_INFO, cnt, __FILE__, __LINE__); + +/** + Assert number of debug logs. +*/ +#define GS_ASSERT_LOG_DEBUG(cnt) gs_assert_log_count(LOG_DEBUG, cnt, __FILE__, __LINE__); + +/** + Assert number of trace logs. +*/ +#define GS_ASSERT_LOG_TRACE(cnt) gs_assert_log_count(LOG_TRACE, cnt, __FILE__, __LINE__); + +/** + Assert number of all logs. +*/ +#define GS_ASSERT_LOG_ALL(cnt) gs_assert_log_count(-1, cnt, __FILE__, __LINE__); + +/** + Assert/find number of entries matching level and pattern. +*/ +#define GS_ASSERT_LOG(count,level,pattern) gs_assert_log(-1, count, level, pattern, __FILE__, __LINE__) + +/** + Assert log at stack index against matching level and pattern. +*/ +#define GS_ASSERT_LOG_AT(stack_index,level,pattern) gs_assert_log(stack_index, 1, level, pattern, __FILE__, __LINE__) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/thread.h b/gomspace/libutil/include/gs/util/thread.h new file mode 100644 index 00000000..37340818 --- /dev/null +++ b/gomspace/libutil/include/gs/util/thread.h @@ -0,0 +1,173 @@ +#ifndef GS_UTIL_THREAD_H +#define GS_UTIL_THREAD_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Thread/task API based on POSIX standard. + + The thread API wraps POSIX \a pthread and FreeRTOS \a task. +*/ + +#include +#if __linux__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if __linux__ +/** + Thread handle. +*/ +typedef pthread_t gs_thread_t; +#else +typedef struct gs_freertos_task_t * gs_thread_t; +#endif + +/** + Type used to declare thread stack buffer for gs_thread_create_with_stack. +*/ +typedef uint32_t gs_stack_type_t; + +/** + Thread priorities. + These values are mapped to platform specific values. +*/ +typedef enum { + /** + Idle (lowest) priority. + Typical use: Not much - runs when nothing else runs. + FreeRTOS: Idle thread. + */ + GS_THREAD_PRIORITY_IDLE = 5, + /** + Low priority. + Typical use: Service applications, e.g. servicing requests from the outside. + GOMspace: housekeeping, GOSH. + */ + GS_THREAD_PRIORITY_LOW = 10, + /** + Normal priority. + Typical use: Control - the primary application(s). + */ + GS_THREAD_PRIORITY_NORMAL = 15, + /** + High priority. + Typical use: Drivers off loading data from hardware to software buffers. + GOMspace: csp_route_task. + */ + GS_THREAD_PRIORITY_HIGH = 20, + /** + High priority. + Typical use: Very time critical threads. No long, time consuming processing. + FreeRTOS: Timer thread. + */ + GS_THREAD_PRIORITY_CRITICAL = 25, +} gs_thread_priority_t; + +/** + Thread function. +*/ +typedef void * (*gs_thread_func_t)(void * parameter); + +/** + Create thread as joinable. + @note only supported on linux. The thread must be joined to free all resources. +*/ +#define GS_THREAD_CREATE_JOINABLE 0x0001 + +/** + Create thread (or task on some platforms). + + pthread/Posix supports exit value and join, FreeRTOS supports neither (perhaps in the future), so API is designed after Posix. + + FreeRTOS: a thread must always terminate with a call to gs_thread_exit(). + linux: a thread is by default created detached, unless #GS_THREAD_CREATE_JOINABLE is specified. + + @param[in] name name of thread. Ignored on Linux. + @param[in] func function for thread to execute. + @param[in] parameter parameter parsed to the thread function. + @param[in] stack_size number of bytes to allocate for stack - not used/supported on all platforms. Ignored on Linux. + @param[in] priority thread priority. Ignored on Linux. + @param[in] flags flags to control creation, see #GS_THREAD_CREATE_JOINABLE. + @param[out] handle handle to the created thread, use NULL if not wanted. + @return_gs_error_t +*/ +gs_error_t gs_thread_create(const char * const name, + gs_thread_func_t func, + void * parameter, + size_t stack_size, + gs_thread_priority_t priority, + uint32_t flags, + gs_thread_t * handle); + +/** + Create thread (or task on some platforms) with user supplied buffer for stack. + + pthread/Posix supports exit value and join, FreeRTOS supports neither (perhaps in the future), so API is designed after Posix. + + FreeRTOS: a thread must always terminate with a call to gs_thread_exit(). + FreeRTOS v9.0 must be compiled with configSUPPORT_STATIC_ALLOCTION set to 1 - otherwise warning log is printed and user supplied + stack buffer is discarded + linux: a thread is by default created detached, unless #GS_THREAD_CREATE_JOINABLE is specified. + stack_buf is ignored. + + @param[in] name name of thread. Ignored on Linux. + @param[in] func function for thread to execute. + @param[in] parameter parameter parsed to the thread function. + @param[in] stack_size size of the user supplied stack buffer - not used/supported on all platforms. Ignored on Linux. + @param[in] stack_buf User supplied stack buffer - not used/supported on all platforms. Ignored on Linux. + @param[in] priority thread priority. Ignored on Linux. + @param[in] flags flags to control creation, see #GS_THREAD_CREATE_JOINABLE. + @param[out] handle handle to the created thread, use NULL if not wanted. + @return_gs_error_t +*/ +gs_error_t gs_thread_create_with_stack(const char * const name, + gs_thread_func_t func, + void * parameter, + size_t stack_size, + gs_stack_type_t *stack_buf, + gs_thread_priority_t priority, + uint32_t flags, + gs_thread_t * handle); + +/** + Exit current thread. + @param[in] exit_value exit value. +*/ +void gs_thread_exit(void * exit_value) __attribute__ ((noreturn)); + +/** + Sleep for X milli-seconds. + @note FreeRTOS: minimum sleep time depends on ticks per milli-second. A thread is suspended minimum 1 tick - unless \a time_ms is 0, in which case yield is called. + @deprecated use gs_time_sleep_ms() + @param[in] time_ms milli-seconds to sleep. +*/ +void gs_thread_sleep_ms(uint32_t time_ms); + +/** + Join with a terminated thread. + + @note Only supported on Linux and primarily used for testing. + @note This is not based on pthread_cancel(), so the user must have signaled the thread to stop - otherwise this will hang forever. + + @param[in] thread handle. + @param[out] return_retval return value from thread, use NULL if not wanted. + @return_gs_error_t +*/ +gs_error_t gs_thread_join(gs_thread_t thread, void ** return_retval); + +/** + Block thread forever. + + Primarily used in Linux applications main() to block main thread. +*/ +void gs_thread_block(void) __attribute__ ((noreturn)); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/time.h b/gomspace/libutil/include/gs/util/time.h new file mode 100644 index 00000000..d4425906 --- /dev/null +++ b/gomspace/libutil/include/gs/util/time.h @@ -0,0 +1,95 @@ +#ifndef GS_UTIL_TIME_H +#define GS_UTIL_TIME_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Releative time. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Converts minutes to seconds. +*/ +#define GS_TIME_MINS_TO_SECS(m) (m * 60) + +/** + Converts hours to seconds. +*/ +#define GS_TIME_HOURS_TO_SECS(h) (h * GS_TIME_MINS_TO_SECS(60)) + +/** + Converts days to seconds. +*/ +#define GS_TIME_DAYS_TO_SECS(d) (d * GS_TIME_HOURS_TO_SECS(24)) + +/** + Return relative time (milli seconds). + @note This will eventually wrap on all platforms - platform must wrap on 32 bit. + @return relativ milli seconds +*/ +uint32_t gs_time_rel_ms(void); + +/** + Return relative time (milli seconds). + @note This will eventually wrap on all platforms - platform must wrap on 32 bit. + @return relativ milli seconds +*/ +uint32_t gs_time_rel_ms_isr(void); + +/** + Returns seconds since process started. + @note On some platforms (e.g. Linux), first call will set offset and + first call it therefor not thread-safe. + @return seconds since boot (or process startup). +*/ +uint32_t gs_time_uptime(void); + +/** + Return time difference, compensating for time wrap due to 32 bit. + @note the function can not detect multiple time wraps, so function using it should + take action within 32 bit time. + @param[in] ref_ms reference time. + @param[in] now_ms current time. + @returns ms difference, compensating for time wrapping (if now_ms is less than ref_ms). +*/ +uint32_t gs_time_diff_ms(uint32_t ref_ms, uint32_t now_ms); + +/** + Sleep for X milli-seconds. + No busy waiting. + @note FreeRTOS: minimum sleep time depends on ticks per second. Suspends execution for minimum 1 tick - unless \a time is 0, in which case yield is called. + @param[in] time_ms milli-seconds to sleep. +*/ +void gs_time_sleep_ms(uint32_t time_ms); + +/** + Sleep X milli-seconds relative to reference. + + This sleep function uses a reference \a ref_ms to compensate for variance in processing time. + + No busy waiting. + + @param[in,out] ref_ms time reference. + @param[in] sleep_ms how many milli-seconds to sleep - relative to reference. + @return \a true if sleep time relative to last reference couldn't be done (reference reset), \a false if normal sleep was done. +*/ +bool gs_time_sleep_until_ms(uint32_t * ref_ms, uint32_t sleep_ms); + +/** + Sleep for X nano-seconds. + No busy waiting. + @note FreeRTOS: minimum sleep time depends on ticks per second. Suspends execution for minimum 1 tick - unless \a time is 0, in which case yield is called. + @param[in] time_ns nano-seconds to sleep. +*/ +void gs_time_sleep_ns(uint64_t time_ns); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/timestamp.h b/gomspace/libutil/include/gs/util/timestamp.h new file mode 100644 index 00000000..80fef6da --- /dev/null +++ b/gomspace/libutil/include/gs/util/timestamp.h @@ -0,0 +1,73 @@ +#ifndef GS_UTIL_TIMESTAMP_H +#define GS_UTIL_TIMESTAMP_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Timestamp utilities, for add, subtract, compare, copy, etc. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Number of nano seconds per second. +*/ +#define GS_TIMESTAMP_NSEC_PER_SEC 1000000000 + +/** + Portable time structure. + + Stanadard timespec_t is non-portable, so this structure must be used instead +*/ +typedef struct { + /** Seconds. */ + uint32_t tv_sec; + /** Nano seconds. */ + uint32_t tv_nsec; +} gs_timestamp_t; + +/** + @deprecated Use gs_timestamp_t +*/ +typedef gs_timestamp_t timestamp_t; + +/** + Add 2 timestamp's (t1 = t1 + t2). + @param[in,out] t1 timestamp + @param[in] t2 timestamp. + @return 0 on success, otherwise -1 +*/ +int timestamp_add(gs_timestamp_t * t1, const gs_timestamp_t * t2); + +/** + Subtract 2 timestamp's (t1 = t1 - t2) + @param[in,out] t1 timestamp + @param[in] t2 timestamp. + @return 0 on success, otherwise -1 +*/ +int timestamp_diff(gs_timestamp_t * t1, const gs_timestamp_t * t2); + +/** + Check if t2 is greate than t1. + @param[in] t1 time to compare + @param[in] t2 time to compare + @return 1 if t2 > t1, else 0. -1 on bad arguments. +*/ +int timestamp_ge(const gs_timestamp_t * t1, const gs_timestamp_t * t2); + +/** + Copy timestamp. + @param[in] from from timestamp + @param[out] to to timestamp + @return 0 on success, otherwise -1 +*/ +int timestamp_copy(const gs_timestamp_t * from, gs_timestamp_t * to); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/types.h b/gomspace/libutil/include/gs/util/types.h new file mode 100644 index 00000000..2c0d0597 --- /dev/null +++ b/gomspace/libutil/include/gs/util/types.h @@ -0,0 +1,114 @@ +#ifndef GS_UTIL_TYPES_H +#define GS_UTIL_TYPES_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Base type definitions and functions. + + In some rare cases, it is impossible to make code that works on all platforms. In these cases the following defines may be used to + exclude/include code: + | define | Platform | + | :----: | :---- | + | \_\_AVR\_\_ | 8 bit, e.g. atmega1281, atmega2560, attiny25, attiny44, attiny84 | + | \_\_linux\_\_ | 32/64 bit, Linux based | + + +*/ + +#include // intXX_t +#include // bool +#include // size_t + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Control static declaration at compile time. + Allows unit tests to access internal functions or variables. + @note Static declared variables are initialized to zero by the compiler - BUT if you use GS_NO_STATIC instead of static, they will not be initialized. +*/ +#if GS_NO_STATIC +#define GS_STATIC +#else +#define GS_STATIC static +#endif + +/** + Convert integer to pointer. +*/ +#define GS_TYPES_INT2PTR(value) ((void*)(intptr_t)(value)) + +/** + Convert integer to pointer. +*/ +#define GS_TYPES_UINT2PTR(value) ((void*)(uintptr_t)(value)) + +/** + Convert pointer to integer. +*/ +#define GS_TYPES_PTR2INT(value) ((intptr_t)(void*)(value)) + +/** + Convert pointer to integer. +*/ +#define GS_TYPES_PTR2UINT(value) ((uintptr_t)(void*)(value)) + +/** + Assert on 'value'. + + Example: + GS_STATIC_ASSERT(sizeof(int) >= 2, int_must_be_at_least_16bit); + fails if size of (int) is less than 2 bytes. +*/ +#define GS_STATIC_ASSERT(condition, name) typedef char name[(condition) ? 1 : -1] + +/** + Context switch state. + Used by FreeRTOS when waking a higher priority task/thread from within an ISR. + The actual struct is defined in libembed. +*/ +typedef struct gs_context_switch gs_context_switch_t; + +/** + Return element count of array. +*/ +#define GS_ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +/** + Address union. +*/ +typedef union { + /** + Normal address pointer. + */ + void* p; + /** + Address pointer as an unsigned value. + */ + uintptr_t u; +} gs_address_t; + +/** + @cond HIDDEN_SYMBOLS + Compile check size of primitives (just to be sure, that they are what we expect). +*/ +GS_STATIC_ASSERT(sizeof(gs_address_t) == sizeof(void*), unexpected_address_void_pointer_size); +GS_STATIC_ASSERT(sizeof(gs_address_t) == sizeof(uintptr_t), unexpected_address_uintptr_size); +GS_STATIC_ASSERT(sizeof(bool) == sizeof(uint8_t), unexpected_bool_size); +GS_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), unexpected_float_size); +#if (__AVR__) +// avr/avr8 is 8 bit +GS_STATIC_ASSERT(sizeof(int) == sizeof(int16_t), unexpected_int_size_on_avr8); +#else +// rest should be 32 or 64 bit +GS_STATIC_ASSERT(sizeof(int) == sizeof(int32_t), unexpected_int_size); +GS_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t), unexpected_double_size); +#endif +/** @endcond */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/unistd.h b/gomspace/libutil/include/gs/util/unistd.h new file mode 100644 index 00000000..a8b65845 --- /dev/null +++ b/gomspace/libutil/include/gs/util/unistd.h @@ -0,0 +1,32 @@ +#ifndef GS_UTIL_UNISTD_H +#define GS_UTIL_UNISTD_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + GomSpace extensions to standard \a unistd.h. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Get current working directory. + + @note Linux uses standard getcwd(). + + @param[out] buf user supplied buffer for returning path. + @param[in] bufsize size of \a buf. + @return #GS_ERROR_NOT_FOUND if no current directory is set. + @return #GS_ERROR_RANGE if \a buf is too small + @return_gs_error_t +*/ +gs_error_t gs_getcwd(char * buf, size_t bufsize); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/vmem.h b/gomspace/libutil/include/gs/util/vmem.h new file mode 100644 index 00000000..19576a63 --- /dev/null +++ b/gomspace/libutil/include/gs/util/vmem.h @@ -0,0 +1,194 @@ +#ifndef GS_UTIL_VMEM_H +#define GS_UTIL_VMEM_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Virtual memory interface. + + The API provides support for accessing different hardware components using a common API, by providing a component specific driver. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Virtual memory mapping. +*/ +typedef struct gs_vmem gs_vmem_t; + +/** + VMEM driver write. + + @param[in] vmem vmem entry. + @param[in] to Address where to write data to. + @param[in] from Address where to write data from. + @param[in] size Number of bytes to write. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_vmem_write_function_t)(const gs_vmem_t * vmem, void* to, const void * from, size_t size); + +/** + VMEM driver read. + + @param[in] vmem vmem entry. + @param[in] to Address where to read data to. + @param[in] from Address where to read data from. + @param[in] size Number of bytes to read. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_vmem_read_function_t)(const gs_vmem_t * vmem, void* to, const void * from, size_t size); + +/** + VMEM driver lock. + + @param[in] vmem vmem entry. + @param[in] on Enable/Disable lock. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_vmem_lock_function_t)(const gs_vmem_t * vmem, bool on); + +/** + VMEM driver information. + + Return relevant information for the VMEM driver. + + @param[in] vmem vmem entry. + @param[in] buffer user allocated buffer for returning information. + @param[in] buffer_size size (length) of \a buffer. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_vmem_info_function_t)(const gs_vmem_t * vmem, char * buffer, size_t buffer_size); + +/** + VMEM driver interface. +*/ +typedef struct { + /** + Write function. + */ + const gs_vmem_write_function_t write; + /** + Read function. + */ + const gs_vmem_read_function_t read; + /** + Lock function. + */ + const gs_vmem_lock_function_t lock; + /** + Information function. + */ + const gs_vmem_info_function_t info; +} gs_vmem_driver_t; + +/** + Virtual memory mapping. + + @note Call gs_vmem_set_map() for registering mappings. +*/ +struct gs_vmem { + /** + Logical name of enry. + */ + const char *const name; + /** + Virtual memory start. + */ + gs_address_t virtmem; + /** + Physical memory start. + This address only makes sense for the VMEM driver. + */ + gs_address_t physmem; + /** + Size of memory block. + */ + const size_t size; + /** + Driver function. + */ + const gs_vmem_driver_t* drv; + /** + Driver data. + */ + const void* drv_data; +}; + +/** + Set VMEM mapping. + Must be done for the API to work. + @param[in] map VMEM mapping table, must be terminated with an NULL entry. + @return_gs_error_t +*/ +gs_error_t gs_vmem_set_map(const gs_vmem_t * map); + +/** + Return VMEM map. +*/ +const gs_vmem_t * gs_vmem_get_map(void); + +/** + Print all VMEM entries to stdout. + @param[in] out output stream + @return_gs_error_t +*/ +gs_error_t gs_vmem_list(FILE * out); + +/** + Get VMEM entry by name. + @param[in] name name of VMEM entry. + @return VMEM mapping or NULL if not found. +*/ +const gs_vmem_t * gs_vmem_get_by_name(const char * name); + +/** + Lock/un-lock VMEM area. + @param[in] name name of VMEM entry. + @param[in] on Enable/Disable lock. + @return GS_ERROR_NOT_FOUND area not found. + @return GS_ERROR_NOT_SUPPORTED if locking isn't supported. + @return_gs_error_t +*/ +gs_error_t gs_vmem_lock_by_name(const char * name, bool on); + +/** + Lock/un-lock all VMEM areas. + @param[in] on lock on or off. + @return_gs_error_t +*/ +gs_error_t gs_vmem_lock_all(bool on); + +/** + memcpy on VMEM. + @note if no VMEM entries are found, a normal memcpy is called with the provided pointers. + @param[in] to to location. + @param[in] from from location. + @param[in] size number of bytes to copy. +*/ +void* gs_vmem_cpy(void* to, const void* from, size_t size); + +/** + Macro for calling gs_vmem_cpy(). +*/ +#define GS_VMEM_CPY(to, from, size) gs_vmem_cpy(to, from, size) + +/** + Macro for calling gs_vmem_cpy(). + @deprecated Use gs_vmem_cpy() directly. +*/ +#define VMEM_CPY(to, from, size) gs_vmem_cpy(to, from, size) + +/** + Register VMEM commands. + @return_gs_error_t +*/ +gs_error_t gs_vmem_register_commands(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/watchdog/watchdog.h b/gomspace/libutil/include/gs/util/watchdog/watchdog.h new file mode 100644 index 00000000..30d2bd30 --- /dev/null +++ b/gomspace/libutil/include/gs/util/watchdog/watchdog.h @@ -0,0 +1,143 @@ +#ifndef GS_UTIL_WATCHDOG_WATCHDOG_H +#define GS_UTIL_WATCHDOG_WATCHDOG_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Software watchdog client interface. + + The software watchdog (SWWD) enables having multiple instances of a Watchdog. + The software watchdog manages the HW watchdog, and will ultimately + trigger the HW watchdog, if one or more clients are not servicing the + software watchdog. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Software Watchdog handle +*/ +typedef struct gs_swwd_hdl gs_swwd_hdl_t; + +/** + Software watchdog callback function. + + Called by the SWWD upon timeout. + @param[in] userdata user data provided on gs_swwd_register() +*/ +typedef void (*gs_swwd_callback_function_t)(void * userdata); + +/** + Watchdog timeout action. +*/ +typedef enum { + /** + Reset system on timeout (stops touching the hardware watchdog). + Once the watchdog has timeout, the watchdog cannot be re-activated. + */ + GS_SWWD_TIMEOUT_ACTION_RESET = 0, + /** + Log 'warning' on timeout, but otherwise ignore the timeout. + The watchdog can re-activated by touching the watchdog again. + */ + GS_SWWD_TIMEOUT_ACTION_LOG = 1, +} gs_swwd_timeout_action_t; + +/** + Create the software watchdog back-end. + + Only one SWWD back-end can exist at any given time. + + @param[in] max_clients The maximum number of Software Watchog clients supported. + @param[in] dev The HW Watchdog device to use. + @return_gs_error_t +*/ +gs_error_t gs_swwd_create(uint32_t max_clients, gs_watchdog_device_t *dev); + +/** + Destroy the Software Watchdog back-end (and stop the SWWD monitor task if started). + + @param[in] timeout_s Maximum number of seconds to allow this operation to complete. + @return_gs_error_t +*/ +gs_error_t gs_swwd_destroy(uint32_t timeout_s); + +/** + Check for expired software watchdog clients. This function is only to be used if the + SWWD monitor task is not started. Otherwise the SWWD task will handle this in the back- + ground. I.e: + - In passive mode this function must be called periodically to check for expired + clients, and service the HW watchdog. + - In active mode this function is called in background by the SWWD monitor task. + + The interval between these checks should be much less that the HW watchdog + timeout period, to ensure that the HW Watchdog is correctly serviced. + Calling this e.g. every 1-3 seconds will be a good default. + + @param[out] num_expired The number of SW Watchog clients currently expired. + @return_gs_error_t +*/ +gs_error_t gs_swwd_check_expired_clients(uint32_t *num_expired); + +/** + Register/create a new software watchdog instance + + @param[out] wdt_handle A reference to software watchdog handle + @param[in] timeout Timeout in seconds. + @param[in] callback Callback function which is called on timeout. NULL if unused. + @param[in] userdata Pointer to user data used in the callback function. Ignored if callback is NULL. + @param[in] client_name A descriptive name given by the user in order to identify the watchdog/client - the pointer must remain valid as long as the watchdog is registered. + @param[in] action what action to take, when/if the watchdog times out. + @return_gs_error_t +*/ +gs_error_t gs_swwd_register_with_action(gs_swwd_hdl_t ** wdt_handle, uint32_t timeout, gs_swwd_callback_function_t callback, void * userdata, const char *client_name, gs_swwd_timeout_action_t action); + +/** + Register/create a software watchdog with action \a reset on timeout. + + @param[out] wdt_handle A reference to software watchdog handle + @param[in] timeout Timeout in seconds before the software watchdog fires. + @param[in] callback Callback function which is called on timeout. NULL if unused. + @param[in] userdata Pointer to user data used in the callback function. Ignored if callback is NULL. + @param[in] client_name A descriptive name given by the user in order to identify the watchdog/client - the pointer must remain valid as long as the watchdog is registered. + @return_gs_error_t +*/ +static inline gs_error_t gs_swwd_register(gs_swwd_hdl_t ** wdt_handle, uint32_t timeout, gs_swwd_callback_function_t callback, void * userdata, const char *client_name) +{ + return gs_swwd_register_with_action(wdt_handle, timeout, callback, userdata, client_name, GS_SWWD_TIMEOUT_ACTION_RESET); +} + +/** + De-Register a Software Watchdog instance + + @param[in] wdt_handle A software watchdog handle + @return_gs_error_t +*/ +gs_error_t gs_swwd_deregister(gs_swwd_hdl_t ** wdt_handle); + +/** + Touch Software Watchdog to reset the timer + + @param[in] wdt_handle A software watchdog handle + @return_gs_error_t +*/ +gs_error_t gs_swwd_touch(gs_swwd_hdl_t * wdt_handle); + +/** + Set timeout of the Software Watchdog. + + @param[in] wdt_handle A software watchdog handle + @param[in] timeout Timeout in seconds before the SWWD fires. + @return_gs_error_t +*/ +gs_error_t gs_swwd_set_timeout(gs_swwd_hdl_t * wdt_handle, uint32_t timeout); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/watchdog/watchdog_task.h b/gomspace/libutil/include/gs/util/watchdog/watchdog_task.h new file mode 100644 index 00000000..833f1511 --- /dev/null +++ b/gomspace/libutil/include/gs/util/watchdog/watchdog_task.h @@ -0,0 +1,45 @@ +#ifndef GS_UTIL_WATCHDOG_WATCHDOG_TASK_H +#define GS_UTIL_WATCHDOG_WATCHDOG_TASK_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Software Watchdog server/task interface + + The Software Watchdog task implements the core (backend) functionality of the the software watchdog. + The Client API for the SW watchdog is implemented in watchdog.h + + @note This API is not thread safe! +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Start the Software Watchdog monitor task if the SWWD is to be used as a + separate task (active mode). + In this case the SWWD task will monitor expired clients in the background + and the polling API gs_swwd_check_expired_clients() needs not to be called by + the user. + + @return_gs_error_t +*/ + +gs_error_t gs_swwd_monitor_task_start(); + +/** + Stops the Software Watchdog monitor task + + @param[in] timeout_s Maximum number of seconds to allow this operation to complete. + + @return_gs_error_t +*/ +gs_error_t gs_swwd_monitor_task_stop(uint32_t timeout_s); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libutil/include/gs/util/zip/zip.h b/gomspace/libutil/include/gs/util/zip/zip.h new file mode 100644 index 00000000..04c87974 --- /dev/null +++ b/gomspace/libutil/include/gs/util/zip/zip.h @@ -0,0 +1,62 @@ +#ifndef LIBUTIL_ZIP_ZIP_UTILS_H +#define LIBUTIL_ZIP_ZIP_UTILS_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Compress/decompress API based on zlib compressed data format specification standards. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + Compress file. + + @param[in] src file to be compressed. + @param[out] dest compressed output file. + @return_gs_error_t +*/ +int gs_zip_compress_file(const char *src, const char *dest); + +/** + Decompress file. + + @param[in] src file to be secompressed. + @param[out] dest decompressed output file. + @return_gs_error_t +*/ +int gs_zip_decompress_file(const char *src, const char *dest); + +/** + Compress data. + + @param[in] src pointer to the data to be compressed. + @param[in] src_len size of the data. + @param[out] dest pointer to the compressed data. + @param[out] dest_len pointer to the size of the compressed data. + @return_gs_error_t +*/ +int gs_zip_compress_data(const unsigned char *src, uint32_t src_len, unsigned char *dest, uint32_t *dest_len); + +/** + Decompress data. + + @param[in] src pointer to the data to be decompressed. + @param[in] src_len size of the data. + @param[out] dest pointer to the decompressed data. + @param[out] dest_len size of the destination memory area. + @param[out] decomp_len pointer to the size of the decompressed data. + @return_gs_error_t +*/ +int gs_zip_decompress_data(const unsigned char *src, uint32_t src_len, unsigned char *dest, uint32_t dest_len, uint32_t *decomp_len); + + +#ifdef __cplusplus +} +#endif +#endif /* LIBUTIL_ZIP_ZIP_UTILS_H */ diff --git a/gomspace/libutil/src/base16.c b/gomspace/libutil/src/base16.c new file mode 100644 index 00000000..da3a83c6 --- /dev/null +++ b/gomspace/libutil/src/base16.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +void base16_encode(const uint8_t * raw, size_t len, char *encoded) +{ + const uint8_t *raw_bytes = raw; + char *encoded_bytes = encoded; + size_t remaining = len; + + for (; remaining--; encoded_bytes += 2) + snprintf(encoded_bytes, 3, "%02X", *(raw_bytes++)); + +} + +int base16_decode(const char *encoded, uint8_t *raw) +{ + uint8_t *raw_bytes = raw; + if (encoded) { + const char *encoded_bytes = encoded; + char buf[3]; + char *endp; + + while (encoded_bytes[0]) { + if (!encoded_bytes[1]) { + log_error("Base16-encoded string \"%s\" has invalid length\n", + encoded); + return GS_ERROR_ARG; + } + memcpy(buf, encoded_bytes, 2); + buf[2] = '\0'; + *(raw_bytes++) = (uint8_t) strtoul(buf, &endp, 16); + if (*endp != '\0') { + log_error("Base16-encoded string \"%s\" has invalid byte \"%s\"\n", + encoded, buf); + return GS_ERROR_ARG; + } + encoded_bytes += 2; + } + } + return (int)(raw_bytes - raw); +} diff --git a/gomspace/libutil/src/bindings/python/pyutil.c b/gomspace/libutil/src/bindings/python/pyutil.c new file mode 100644 index 00000000..b2924ba6 --- /dev/null +++ b/gomspace/libutil/src/bindings/python/pyutil.c @@ -0,0 +1,73 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +#if PY_MAJOR_VERSION == 3 +#define IS_PY3 +#endif + +/** + * Helpers + */ + +static PyObject* pyutil_get_clock_time(PyObject *self, PyObject *args) { + gs_timestamp_t ts; + gs_clock_get_time(&ts); + return Py_BuildValue("II", ts.tv_sec, ts.tv_nsec); +} + + +static PyObject* pyutil_error_string(PyObject *self, PyObject *args) +{ + int error; + if (!PyArg_ParseTuple(args, "i", &error)) + { + Py_RETURN_NONE; + } + return Py_BuildValue("s", gs_error_string(error)); +} + +static PyMethodDef methods[] = { + + /* helpers */ + {"get_clock_time", pyutil_get_clock_time, METH_NOARGS, ""}, + {"error_string", pyutil_error_string, METH_VARARGS, ""}, + + /* sentinel */ + {NULL, NULL, 0, NULL} +}; + +#ifdef IS_PY3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "libgsutil_py3", + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#ifdef IS_PY3 +PyMODINIT_FUNC PyInit_libgsutil_py3(void) { +#else +PyMODINIT_FUNC initlibgsutil_py2(void) { +#endif + +#ifdef IS_PY3 + PyObject* m = PyModule_Create(&moduledef); +#else + Py_InitModule("libgsutil_py2", methods); +#endif + +#ifdef IS_PY3 + return m; +#endif +} + diff --git a/gomspace/libutil/src/bytebuffer.c b/gomspace/libutil/src/bytebuffer.c new file mode 100644 index 00000000..49b5a495 --- /dev/null +++ b/gomspace/libutil/src/bytebuffer.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +#define GS_BYTEBUFFER_F_FAILED 0x01 +#define GS_BYTEBUFFER_F_OVERRUN 0x02 + +gs_error_t gs_bytebuffer_init(gs_bytebuffer_t * bb, void * buffer, size_t buffer_size) +{ + GS_CHECK_HANDLE(bb != NULL); + memset(bb, 0, sizeof(*bb)); + if (buffer) { + if (buffer_size < 2) { + // must always have room for NUL termination. + return GS_ERROR_ARG; + } + bb->buffer = buffer; + bb->size = buffer_size; + } else { + // dry run - don't insert anything in buffer, but increment used + } + + return GS_OK; +} + +void gs_bytebuffer_vprintf(gs_bytebuffer_t * bb, const char * format, va_list ap) +{ + int res; + if (bb->buffer == NULL) { + // dry run + char buf[3]; + res = vsnprintf(buf, 0, format, ap); + if (res >= 0) { + bb->used += res; + } + } else { + const size_t free_bytes = gs_bytebuffer_get_free(bb); + res = vsnprintf((char*)&bb->buffer[bb->used], free_bytes, format, ap); + if (res > 0) { + if ((size_t)res >= free_bytes) { + // over run + bb->flags |= GS_BYTEBUFFER_F_OVERRUN; + bb->used = bb->size; + bb->buffer[bb->size - 1] = 0; + } else { + bb->used += res; + } + } + } + if (res < 0) { + bb->flags |= GS_BYTEBUFFER_F_FAILED; + } +} + +void gs_bytebuffer_printf(gs_bytebuffer_t * bb, const char * format, ...) +{ + va_list ap; + va_start(ap, format); + gs_bytebuffer_vprintf(bb, format, ap); + va_end(ap); +} + +void gs_bytebuffer_append(gs_bytebuffer_t * bb, const void * data, size_t length) +{ + if (bb->buffer == NULL) { + // dry run + bb->used += length; + } else { + const size_t free_bytes = gs_bytebuffer_get_free(bb); + if (free_bytes >= length) { + memcpy(&bb->buffer[bb->used], data, length); + bb->used += length; + } else { + memcpy(&bb->buffer[bb->used], data, free_bytes); + bb->flags |= GS_BYTEBUFFER_F_OVERRUN; + bb->used += free_bytes; + } + } +} + +void gs_bytebuffer_append_string(gs_bytebuffer_t * bb, const char * string) +{ + if (gs_string_empty(string) == false) { + gs_bytebuffer_append(bb, string, strlen(string)); + } +} + +void gs_bytebuffer_append_string_max(gs_bytebuffer_t * bb, const char * string, size_t max_length) +{ + if (gs_string_empty(string) == false) { + gs_bytebuffer_append(bb, string, strnlen(string, max_length)); + } +} + +char * gs_bytebuffer_get_as_string(gs_bytebuffer_t * bb, gs_error_t * error) +{ + if (bb && bb->buffer) { + // handle NUL termination + if (bb->used < bb->size) { + bb->buffer[bb->used] = 0; + } else { + // overrun - truncation buffer + bb->flags |= GS_BYTEBUFFER_F_OVERRUN; + bb->buffer[bb->used - 1] = 0; + } + if (error) { + *error = gs_bytebuffer_get_state(bb); + } + return (char*) bb->buffer; + } + return NULL; +} + +gs_error_t gs_bytebuffer_get_state(gs_bytebuffer_t * bb) +{ + if (bb) { + if (bb->flags & GS_BYTEBUFFER_F_FAILED) { + return GS_ERROR_DATA; + } + if (bb->flags & GS_BYTEBUFFER_F_OVERRUN) { + return GS_ERROR_OVERFLOW; + } + return GS_OK; + } + return GS_ERROR_HANDLE; +} diff --git a/gomspace/libutil/src/byteorder.c b/gomspace/libutil/src/byteorder.c new file mode 100644 index 00000000..e00c9737 --- /dev/null +++ b/gomspace/libutil/src/byteorder.c @@ -0,0 +1,323 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include + +/* Convert 16-bit number from host byte order to network byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_hton16(uint16_t h16) { +#if UTIL_BIG_ENDIAN + return h16; + +#elif UTIL_LITTLE_ENDIAN + return (uint16_t)(((h16 & 0xff00) >> 8) | + ((h16 & 0x00ff) << 8)); +#endif +} + +/* Convert 16-bit number from network byte order to host byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_ntoh16(uint16_t n16) { + return util_hton16(n16); +} + +/* Convert 32-bit number from host byte order to network byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_hton32(uint32_t h32) { +#if UTIL_BIG_ENDIAN + return h32; + +#elif UTIL_LITTLE_ENDIAN + return (((h32 & 0xff000000) >> 24) | + ((h32 & 0x000000ff) << 24) | + ((h32 & 0x0000ff00) << 8) | + ((h32 & 0x00ff0000) >> 8)); +#endif +} + +/* Convert 32-bit number from network byte order to host byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_ntoh32(uint32_t n32) { + return util_hton32(n32); +} + +/* Convert 64-bit number from host byte order to network byte order */ +extern inline uint64_t __attribute__ ((__const__)) util_hton64(uint64_t h64) { +#if UTIL_BIG_ENDIAN + return h64; + +#elif UTIL_LITTLE_ENDIAN + return (((h64 & 0xff00000000000000LL) >> 56) | + ((h64 & 0x00000000000000ffLL) << 56) | + ((h64 & 0x00ff000000000000LL) >> 40) | + ((h64 & 0x000000000000ff00LL) << 40) | + ((h64 & 0x0000ff0000000000LL) >> 24) | + ((h64 & 0x0000000000ff0000LL) << 24) | + ((h64 & 0x000000ff00000000LL) >> 8) | + ((h64 & 0x00000000ff000000LL) << 8)); +#endif +} + +/* Convert 64-bit number from host byte order to network byte order */ +extern inline uint64_t __attribute__ ((__const__)) util_ntoh64(uint64_t n64) { + return util_hton64(n64); +} + +/* Convert float from host byte order to network byte order */ +extern inline float __attribute__ ((__const__)) util_htonflt(float f) { +#if UTIL_BIG_ENDIAN + return f; + +#elif UTIL_LITTLE_ENDIAN + union v { + float f; + uint32_t i; + }; + union v val; + val.f = f; + val.i = util_hton32(val.i); + return val.f; +#endif +} + +/* Convert float from host byte order to network byte order */ +extern inline float __attribute__ ((__const__)) util_ntohflt(float f) { + return util_htonflt(f); +} + +/* Convert double from host byte order to network byte order */ +extern inline double __attribute__ ((__const__)) util_htondbl(double d) { +#if UTIL_BIG_ENDIAN + return d; + +#elif UTIL_LITTLE_ENDIAN + union v { + double d; + uint64_t i; + }; + union v val; + val.d = d; + val.i = util_hton64(val.i); + return val.d; +#endif +} + +/* Convert float from host byte order to network byte order */ +extern inline double __attribute__ ((__const__)) util_ntohdbl(double d) { + return util_htondbl(d); +} + +/* Convert 16-bit number from host byte order to big endian byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_htobe16(uint16_t h16) { + return util_hton16(h16); +} + +/* Convert 16-bit number from host byte order to little endian byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_htole16(uint16_t h16) { +#if UTIL_LITTLE_ENDIAN + return h16; + +#elif UTIL_BIG_ENDIAN + return (uint16_t)(((h16 & 0xff00) >> 8) | + ((h16 & 0x00ff) << 8)); +#endif +} + +/* Convert 16-bit number from big endian byte order to little endian byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_betoh16(uint16_t be16) { + return util_ntoh16(be16); +} + +/* Convert 16-bit number from little endian byte order to host byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_letoh16(uint16_t le16) { + return util_htole16(le16); +} + +/* Convert 32-bit number from host byte order to big endian byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_htobe32(uint32_t h32) { + return util_hton32(h32); +} + +/* Convert 32-bit number from little endian byte order to host byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_htole32(uint32_t h32) { +#if UTIL_LITTLE_ENDIAN + return h32; + +#elif UTIL_BIG_ENDIAN + return (((h32 & 0xff000000) >> 24) | + ((h32 & 0x000000ff) << 24) | + ((h32 & 0x0000ff00) << 8) | + ((h32 & 0x00ff0000) >> 8)); +#endif +} + +/* Convert 32-bit number from big endian byte order to host byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_betoh32(uint32_t be32) { + return util_ntoh32(be32); +} + +/* Convert 32-bit number from little endian byte order to host byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_letoh32(uint32_t le32) { + return util_htole32(le32); +} + +/* Convert 64-bit number from host byte order to big endian byte order */ +extern inline uint64_t __attribute__ ((__const__)) util_htobe64(uint64_t h64) { + return util_hton64(h64); +} + +/* Convert 64-bit number from host byte order to little endian byte order */ +extern inline uint64_t __attribute__ ((__const__)) util_htole64(uint64_t h64) { +#if UTIL_LITTLE_ENDIAN + return h64; + +#elif UTIL_BIG_ENDIAN + return (((h64 & 0xff00000000000000LL) >> 56) | + ((h64 & 0x00000000000000ffLL) << 56) | + ((h64 & 0x00ff000000000000LL) >> 40) | + ((h64 & 0x000000000000ff00LL) << 40) | + ((h64 & 0x0000ff0000000000LL) >> 24) | + ((h64 & 0x0000000000ff0000LL) << 24) | + ((h64 & 0x000000ff00000000LL) >> 8) | + ((h64 & 0x00000000ff000000LL) << 8)); +#endif +} + +/* Convert 64-bit number from big endian byte order to host byte order */ +extern inline uint64_t __attribute__ ((__const__)) util_betoh64(uint64_t be64) { + return util_ntoh64(be64); +} + +/* Convert 64-bit number from little endian byte order to host byte order */ +extern inline uint64_t __attribute__ ((__const__)) util_letoh64(uint64_t le64) { + return util_htole64(le64); +} + +/* Convert 16-bit number from host byte order to network byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_htons(uint16_t h16) { + return util_hton16(h16); +} + +/* Convert 16-bit number from network byte order to host byte order */ +extern inline uint16_t __attribute__ ((__const__)) util_ntohs(uint16_t n16) { + return util_ntoh16(n16); +} + +/* Convert 32-bit number from host byte order to network byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_htonl(uint32_t h32) { + return util_hton32(h32); +} + +/* Convert 32-bit number from network byte order to host byte order */ +extern inline uint32_t __attribute__ ((__const__)) util_ntohl(uint32_t n32) { + return util_ntoh32(n32); +} + +#define BYTEORDER_ARRAY(convert, from, to, count) { \ + for (unsigned int i = 0; i < count; ++i, ++from, ++to) { \ + *to = convert(*from); \ + } \ + } + +void util_hton16_array(const uint16_t * from, uint16_t * to, size_t count) +{ + BYTEORDER_ARRAY(util_hton16, from, to, count); +} + +void util_hton32_array(const uint32_t * from, uint32_t * to, size_t count) +{ + BYTEORDER_ARRAY(util_hton32, from, to, count); +} + +void util_hton64_array(const uint64_t * from, uint64_t * to, size_t count) +{ + BYTEORDER_ARRAY(util_hton64, from, to, count); +} + +void util_ntoh16_array(const uint16_t * from, uint16_t * to, size_t count) +{ + BYTEORDER_ARRAY(util_ntoh16, from, to, count); +} + +void util_ntoh32_array(const uint32_t * from, uint32_t * to, size_t count) +{ + BYTEORDER_ARRAY(util_ntoh32, from, to, count); +} + +void util_ntoh64_array(const uint64_t * from, uint64_t * to, size_t count) +{ + BYTEORDER_ARRAY(util_ntoh64, from, to, count); +} + +void util_htonflt_array(const float * from, float * to, size_t count) +{ + BYTEORDER_ARRAY(util_htonflt, from, to, count); +} + +void util_ntohflt_array(const float * from, float * to, size_t count) +{ + BYTEORDER_ARRAY(util_ntohflt, from, to, count); +} + +void util_htondbl_array(const double * from, double * to, size_t count) +{ + BYTEORDER_ARRAY(util_htondbl, from, to, count); +} + +void util_ntohdbl_array(const double * from, double * to, size_t count) +{ + BYTEORDER_ARRAY(util_ntohdbl, from, to, count); +} + +uint16_t gs_bswap_16(uint16_t value) +{ + return (uint16_t)(((value & 0xff00) >> 8) | + ((value & 0x00ff) << 8)); +} + +void gs_bswap_16_array(const uint16_t * from, uint16_t * to, size_t count) +{ + BYTEORDER_ARRAY(gs_bswap_16, from, to, count); +} + +uint32_t gs_bswap_32(uint32_t value) +{ + return (((value & 0xff000000) >> 24) | + ((value & 0x000000ff) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8)); +} + +void gs_bswap_32_array(const uint32_t * from, uint32_t * to, size_t count) +{ + BYTEORDER_ARRAY(gs_bswap_32, from, to, count); +} + +uint64_t gs_bswap_64(uint64_t value) +{ + return (((value & 0xff00000000000000LL) >> 56) | + ((value & 0x00000000000000ffLL) << 56) | + ((value & 0x00ff000000000000LL) >> 40) | + ((value & 0x000000000000ff00LL) << 40) | + ((value & 0x0000ff0000000000LL) >> 24) | + ((value & 0x0000000000ff0000LL) << 24) | + ((value & 0x000000ff00000000LL) >> 8) | + ((value & 0x00000000ff000000LL) << 8)); +} + +void gs_bswap_64_array(const uint64_t * from, uint64_t * to, size_t count) +{ + BYTEORDER_ARRAY(gs_bswap_64, from, to, count); +} + +float gs_bswap_float(float value) +{ + union v { + float f; + uint32_t i; + } val; + val.f = value; + val.i = gs_bswap_32(val.i); + return val.f; +} + +void gs_bswap_float_array(const float * from, float * to, size_t count) +{ + BYTEORDER_ARRAY(gs_bswap_float, from, to, count); +} diff --git a/gomspace/libutil/src/clock.c b/gomspace/libutil/src/clock.c new file mode 100644 index 00000000..ac215df6 --- /dev/null +++ b/gomspace/libutil/src/clock.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include + +#if !__AVR__ +#include +#endif + +gs_error_t gs_clock_to_iso8601_string2(uint32_t utc_sec, char * buf, size_t buf_size) +{ + if ((buf == NULL) || (buf_size == 0)) { + return GS_ERROR_ARG; + } + +#if __AVR__ + int res = snprintf(buf, buf_size, "%"PRIu32"Z", utc_sec); + if ((res < 0) || ((size_t)res >= buf_size)) { + buf[buf_size - 1] = 0; + return GS_ERROR_RANGE; + } +#else + const time_t time_seconds = (time_t) utc_sec; + struct tm tm_buf; + struct tm * tm = gmtime_r(&time_seconds, &tm_buf); + if (tm == NULL) { + int res = snprintf(buf, buf_size, "%ldZ", time_seconds); + if ((res < 0) || ((size_t)res >= buf_size)) { + buf[buf_size - 1] = 0; + } + return GS_ERROR_DATA; + } + + // ISO8601 timestamp: 2017-03-30T06:20:45Z + int res = snprintf(buf, buf_size, "%04d-%02d-%02dT%02d:%02d:%02dZ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + if ((res < 0) || ((size_t)res >= buf_size)) { + buf[buf_size - 1] = 0; + return GS_ERROR_RANGE; + } +#endif + + return GS_OK; +} + +gs_error_t gs_clock_to_iso8601_string(const gs_timestamp_t * utc_time, char * buf, size_t buf_size) +{ + if (utc_time == NULL) { + return GS_ERROR_ARG; + } + + return gs_clock_to_iso8601_string2(utc_time->tv_sec, buf, buf_size); +} + +gs_error_t gs_clock_from_string(const char * str, gs_timestamp_t * ts) +{ + if (!str || !str[0] || !ts) { + return GS_ERROR_ARG; + } + + // check for . + { + uint32_t sec; + uint32_t nsec; + int res = sscanf(str, "%" SCNu32 ".%" SCNu32, &sec, &nsec); + if (res == 2) { + ts->tv_sec = sec; + ts->tv_nsec = nsec; + return GS_OK; + } + } + +#if !__AVR__ + // check for ISO8601 + { + struct tm tm; + memset(&tm, 0, sizeof(tm)); // no daylight saving + //int res = sscanf(str, "%" SCNd32 "-%" SCNd32 "-%" SCNd32 "T%" SCNd32 ":%" SCNd32 ":%" SCNd32 "Z", + int res = sscanf(str, "%d-%d-%dT%d:%d:%dZ", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + if ((res == 6) && + (tm.tm_year >= 1970) && (tm.tm_year <= 2038) && + (tm.tm_mon >= 1) && (tm.tm_mon <= 12) && + (tm.tm_mday >= 1) && (tm.tm_mday <= 31) && + (tm.tm_hour >= 0) && (tm.tm_hour <= 23) && + (tm.tm_min >= 0) && (tm.tm_min <= 59) && + (tm.tm_sec >= 0) && (tm.tm_sec <= 60)) + { + tm.tm_year -= 1900; + tm.tm_mon -= 1; + +#if __linux__ + // not posix compliant + time_t sec = timegm(&tm); +#else + // embedded platforms do not have timezones/daylight-saving - so standard mktime works + time_t sec = mktime(&tm); +#endif + if (sec >= 0) { + ts->tv_sec = (uint32_t) sec; + ts->tv_nsec = 0; + return GS_OK; + } + } + } +#endif + + return GS_ERROR_DATA; +} diff --git a/gomspace/libutil/src/crc32.c b/gomspace/libutil/src/crc32.c new file mode 100644 index 00000000..90d21832 --- /dev/null +++ b/gomspace/libutil/src/crc32.c @@ -0,0 +1,79 @@ +/* + * efone - Distributed internet phone system. + * + * (c) 1999,2000 Krzysztof Dabrowski + * (c) 1999,2000 ElysiuM deeZine + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Based on implementation by Finn Yannick Jacobs + */ + +#include +#include + +static const uint32_t crc_tab[256] GS_PGM_OBJECT = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +#define CALC_CRC32_STEP(crc,byte) (((crc >> 8) & 0x00FFFFFF) ^ GS_PGM_UINT32_BY_PTR(&crc_tab[(crc ^ byte) & (uint32_t) 0xFF])) + +uint32_t gs_crc32_init(void) +{ + return 0xFFFFFFFF; +} + +uint32_t gs_crc32_update(uint32_t crc, const void * block, size_t length) +{ + if (block && length) { + const uint8_t * u8 = block; + for (unsigned int i = 0; i < length; i++) { + crc = CALC_CRC32_STEP(crc, *u8++); + } + } + return crc; +} + +uint32_t gs_crc32_finalize(uint32_t crc) +{ + return (crc ^ 0xFFFFFFFF); +} + +uint32_t gs_crc32(const void * block, size_t length) +{ + return gs_crc32_finalize(gs_crc32_update(gs_crc32_init(), block, length)); +} diff --git a/gomspace/libutil/src/crc8.c b/gomspace/libutil/src/crc8.c new file mode 100644 index 00000000..aa810b31 --- /dev/null +++ b/gomspace/libutil/src/crc8.c @@ -0,0 +1,68 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +static const uint8_t crc_tab[256] GS_PGM_OBJECT = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + + +#define CALC_CRC8_STEP(crc,byte) ((crc >> 8) ^ GS_PGM_UINT8_BY_PTR(&crc_tab[(crc ^ byte) & (uint8_t) 0xFF])) + +uint8_t gs_crc8_init(void) +{ + return 0xFF; +} + +uint8_t gs_crc8_update(uint8_t crc, const void * block, size_t length) +{ + if (block && length) { + const uint8_t * u8 = block; + for (unsigned int i = 0; i < length; i++) { + crc = CALC_CRC8_STEP(crc, *u8++); + } + } + return crc; +} + +uint8_t gs_crc8_finalize(uint8_t crc) +{ + return (crc ^ 0x00); +} + +uint8_t gs_crc8(const void * block, size_t length) +{ + return gs_crc8_finalize(gs_crc8_update(gs_crc8_init(), block, length)); +} diff --git a/gomspace/libutil/src/drivers/can/can.c b/gomspace/libutil/src/drivers/can/can.c new file mode 100644 index 00000000..c08eaddb --- /dev/null +++ b/gomspace/libutil/src/drivers/can/can.c @@ -0,0 +1,6 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +// deifne common log group. +GS_LOG_GROUP(gs_can_log, "can", GS_LOG_CAT_DRIVER, GS_LOG_DEFAULT_MASK); diff --git a/gomspace/libutil/src/drivers/i2c/i2c.c b/gomspace/libutil/src/drivers/i2c/i2c.c new file mode 100644 index 00000000..acd9e60e --- /dev/null +++ b/gomspace/libutil/src/drivers/i2c/i2c.c @@ -0,0 +1,6 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +// define common log group. +GS_LOG_GROUP(gs_i2c_log, "i2c", GS_LOG_CAT_DRIVER, GS_LOG_DEFAULT_MASK); diff --git a/gomspace/libutil/src/drivers/spi/spi.c b/gomspace/libutil/src/drivers/spi/spi.c new file mode 100644 index 00000000..eea8153a --- /dev/null +++ b/gomspace/libutil/src/drivers/spi/spi.c @@ -0,0 +1,6 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +// define common log group. +GS_LOG_GROUP(gs_spi_log, "spi", GS_LOG_CAT_DRIVER, GS_LOG_DEFAULT_MASK); diff --git a/gomspace/libutil/src/drivers/sys/memory.c b/gomspace/libutil/src/drivers/sys/memory.c new file mode 100644 index 00000000..365dcf4a --- /dev/null +++ b/gomspace/libutil/src/drivers/sys/memory.c @@ -0,0 +1,37 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +static const char * long_to_string(char * buf, size_t buf_size, long lvalue) +{ + if (lvalue >= 0) { + snprintf(buf, buf_size, "%ld", lvalue); + return buf; + } + return "Unknown"; +} + +gs_error_t gs_mem_get_ram_stat(gs_mem_ram_type_t type, gs_mem_ram_stat_t * ram_stat) +{ + if (type == GS_MEM_RAM_TYPE_INTERNAL) { + return gs_mem_get_int_ram_stat(ram_stat); + } else if (type == GS_MEM_RAM_TYPE_EXTERNAL) { + return gs_mem_get_ext_ram_stat(ram_stat); + } + + /* Unsupported memory type */ + return GS_ERROR_NOT_SUPPORTED; +} + +gs_error_t gs_mem_print_ram_stat(gs_mem_ram_stat_t * ram_stat, FILE * out) +{ + GS_CHECK_ARG(ram_stat != NULL); + char buf[20]; + + fprintf(out, "Total: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->total)); + fprintf(out, "Max available: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->max_available)); + fprintf(out, "Min available: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->min_available)); + fprintf(out, "Available: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->available)); + return GS_OK; +} diff --git a/gomspace/libutil/src/error.c b/gomspace/libutil/src/error.c new file mode 100644 index 00000000..57e42abd --- /dev/null +++ b/gomspace/libutil/src/error.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#define GS_UTIL_DEPRECATED_ERROR_CODES 1 + +#include +#include +#include +#include + +#ifdef __AVR__ +const char * gs_error_string(int error) +{ + /** + avr: const strings are not automatically stored in program space (see gs/util/pgm.h), and if stored + in program space, they require special formatting in logs (i.e. "%S"). + So we settle for simple error string, with the error nnumber - no need to change log/(s)printf etc. + @note: solution is not 100% thread/task safe. + */ + static char buffer[15]; // large enough to always keep zero termination, due to no thread/task lock + snprintf(buffer, sizeof(buffer), "%d", error); + return buffer; +} +#else +const char * gs_error_string(int error) +{ + switch (error) { + case GS_OK: return "GS_OK(0)"; + case GS_ERROR_PERM: return GS_DEF2STRING(GS_ERROR_PERM) "(-1)"; + case GS_ERROR_INTR: return GS_DEF2STRING(GS_ERROR_INTR) "(-4)"; + case GS_ERROR_IO: return GS_DEF2STRING(GS_ERROR_IO) "(-5)"; + case GS_ERROR_AGAIN: return GS_DEF2STRING(GS_ERROR_AGAIN) "(-11)"; + case GS_ERROR_ALLOC: return GS_DEF2STRING(GS_ERROR_ALLOC) "(-12)"; + case GS_ERROR_ACCESS: return GS_DEF2STRING(GS_ERROR_ACCESS) "(-13)"; + case GS_ERROR_BUSY: return GS_DEF2STRING(GS_ERROR_BUSY) "(-16)"; + case GS_ERROR_EXIST: return GS_DEF2STRING(GS_ERROR_EXIST) "(-17)"; + case GS_ERROR_ARG: return GS_DEF2STRING(GS_ERROR_ARG) "(-22)"; + case GS_ERROR_NOT_IMPLEMENTED: return GS_DEF2STRING(GS_ERROR_NOT_IMPLEMENTED) "(-38)"; + case GS_ERROR_OVERFLOW: return GS_DEF2STRING(GS_ERROR_OVERFLOW) "(-75)"; + case GS_ERROR_NOT_SUPPORTED: return GS_DEF2STRING(GS_ERROR_NOT_SUPPORTED) "(-95)"; + case GS_ERROR_IN_USE: return GS_DEF2STRING(GS_ERROR_IN_USE) "(-98)"; + case GS_ERROR_CONNECTION_RESET: return GS_DEF2STRING(GS_ERROR_CONNECTION_RESET) "(-104)"; + case GS_ERROR_NO_BUFFERS: return GS_DEF2STRING(GS_ERROR_NO_BUFFERS) "(-105)"; + case GS_ERROR_TIMEOUT: return GS_DEF2STRING(GS_ERROR_TIMEOUT) "(-110)"; + case GS_ERROR_ALREADY_IN_PROGRESS: return GS_DEF2STRING(GS_ERROR_ALREADY_IN_PROGRESS) "(-114)"; + + case GS_ERROR_HANDLE: return GS_DEF2STRING(GS_ERROR_HANDLE) "(-2000)"; + case GS_ERROR_NOT_FOUND: return GS_DEF2STRING(GS_ERROR_NOT_FOUND) "(-2001)"; + case GS_ERROR_FULL: return GS_DEF2STRING(GS_ERROR_FULL) "(-2002)"; + case GS_ERROR_RANGE: return GS_DEF2STRING(GS_ERROR_RANGE) "(-2003)"; + case GS_ERROR_DATA: return GS_DEF2STRING(GS_ERROR_DATA) "(-2004)"; + case GS_ERROR_UNKNOWN: return GS_DEF2STRING(GS_ERROR_UNKNOWN) "(-2005)"; + case GS_ERROR_NO_DATA: return GS_DEF2STRING(GS_ERROR_NO_DATA) "(-2006)"; + case GS_ERROR_STALE: return GS_DEF2STRING(GS_ERROR_STALE) "(-2007)"; + case GS_ERROR_TYPE: return GS_DEF2STRING(GS_ERROR_TYPE) "(-2008)"; + case GS_ERROR_AMBIGUOUS: return GS_DEF2STRING(GS_ERROR_AMBIGUOUS) "(-2009)"; + case GS_ERROR_STATE: return GS_DEF2STRING(GS_ERROR_STATE) "(-2010)"; + } + + // as fallback we use standard POSIX error string + const int posix_error = abs(error); + return strerror(posix_error); +} +#endif + +gs_error_t gs_error(int error) +{ + return (abs(error) * -1); +} + +#ifndef __AVR__ +const char * error_string(int code) +{ + switch (code) { + case E_NO_ERR: + return "No error"; + case E_NO_DEVICE: + return "No device"; + case E_MALLOC_FAIL: + return "Malloc fail"; + case E_THREAD_FAIL: + return "Thread failure"; + case E_NO_QUEUE: + return "No such queue"; + case E_INVALID_BUF_SIZE: + return "Invalid buffer size"; + case E_INVALID_PARAM: + return "Invalid paramater"; + case E_NO_SS: + return "No such subsystem"; + case E_GARBLED_BUFFER: + return "Rubbish in buffer"; + case E_FLASH_ERROR: + return "FLASH error"; + case E_BOOT_SER: + return "Thread boot fail: serial driver"; + case E_BOOT_DEBUG: + return "Thread boot fail: debug console"; + case E_BOOT_FLASH: + return "Thread boot fail: flash driver"; + case E_NO_BUFFER: + return "No buffer"; + default: + return "Unknown error"; + } +} +#endif diff --git a/gomspace/libutil/src/fletcher.c b/gomspace/libutil/src/fletcher.c new file mode 100644 index 00000000..a9dab265 --- /dev/null +++ b/gomspace/libutil/src/fletcher.c @@ -0,0 +1,77 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +uint16_t gs_fletcher16_memcpy(const void * data_in, size_t count, void * (*memcpyfcn)(void *, const void *, size_t)) +{ + if (memcpyfcn == NULL) { + memcpyfcn = &memcpy; + } + + uint16_t sum1 = 0; + uint16_t sum2 = 0; + + if (data_in && count) { + const uint8_t * data = data_in; + for (unsigned int idx = 0; idx < count; ++idx) { + uint8_t byte; + (*memcpyfcn)(&byte, &data[idx], 1); + sum1 = (uint16_t)((sum1 + byte) % 255); + sum2 = (uint16_t)((sum2 + sum1) % 255); + } + } + return (uint16_t)((sum2 << 8) | sum1); +} + +uint16_t gs_fletcher16_P(const void * data_in, size_t count) +{ + uint16_t sum1 = 0; + uint16_t sum2 = 0; + + if (data_in && count) { + const uint8_t * data = data_in; + for (unsigned int idx = 0; idx < count; ++idx) { + sum1 = (uint16_t)((sum1 + GS_PGM_UINT8_BY_PTR(data++)) % 255); + sum2 = (uint16_t)((sum2 + sum1) % 255); + } + } + return (uint16_t)((sum2 << 8) | sum1); +} + +uint16_t gs_fletcher16(const void * data_in, size_t size) +{ + uint16_t sum1 = 0; + uint16_t sum2 = 0; + + if (data_in && size) { + const uint8_t * data = data_in; + for (unsigned int idx = 0; idx < size; ++idx) { + sum1 = (uint16_t)((sum1 + (*data++)) % 255); + sum2 = (uint16_t)((sum2 + sum1) % 255); + } + } + return (uint16_t)((sum2 << 8) | sum1); +} + +void gs_fletcher16_init(gs_fletcher16_t * f16) +{ + f16->sum1 = f16->sum2 = 0; +} + +void gs_fletcher16_update(gs_fletcher16_t * f16, const void * data_in, size_t size) +{ + if (f16 && data_in && size) { + const uint8_t * data = data_in; + for (unsigned int idx = 0; idx < size; ++idx) { + f16->sum1 = (uint16_t)((f16->sum1 + (*data++)) % 255); + f16->sum2 = (uint16_t)((f16->sum2 + f16->sum1) % 255); + } + } +} + +uint16_t gs_fletcher16_finalize(gs_fletcher16_t * f16) +{ + return (uint16_t)((f16->sum2 << 8) | f16->sum1); +} diff --git a/gomspace/libutil/src/function_scheduler.c b/gomspace/libutil/src/function_scheduler.c new file mode 100644 index 00000000..a89c8db2 --- /dev/null +++ b/gomspace/libutil/src/function_scheduler.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +#include +#include +#include +#include +#include +#include + +typedef struct { + // function to call + gs_function_scheduler_function_t function; + // function's user data + void * user_data; + // timeout in mS + uint32_t timeout_ms; + // last execution time in mS + uint32_t last_exec_ms; +} gs_function_scheduler_entry_t; + +struct gs_function_scheduler { + // Max timeout in mS + uint32_t max_timeout_ms; + // allocated entries + unsigned int max_entries; + // entries + gs_function_scheduler_entry_t * entries; +}; + +GS_CHECK_STATIC_ASSERT(sizeof(int) >= 2, int_must_be_at_least_16bit); + +gs_error_t gs_function_scheduler_create(uint32_t max_timeout_ms, unsigned int max_entries, gs_function_scheduler_t ** return_scheduler) +{ + GS_CHECK_ARG(max_timeout_ms <= INT_MAX); + GS_CHECK_ARG(max_entries > 0); + GS_CHECK_ARG(return_scheduler != NULL); + + gs_function_scheduler_entry_t * entries = calloc(max_entries, sizeof(*entries)); + if (entries == NULL) { + return GS_ERROR_ALLOC; + } + + gs_function_scheduler_t * scheduler = calloc(1, sizeof(*scheduler)); + if (scheduler == NULL) { + free (entries); + return GS_ERROR_ALLOC; + } + + scheduler->max_timeout_ms = max_timeout_ms; + scheduler->entries = entries; + scheduler->max_entries = max_entries; + + *return_scheduler = scheduler; + + return GS_OK; +} + +gs_error_t gs_function_scheduler_destroy(gs_function_scheduler_t * scheduler) +{ + GS_CHECK_HANDLE(scheduler); + free(scheduler->entries); + free(scheduler); + return GS_OK; +} + +gs_error_t gs_function_scheduler_register_ms(gs_function_scheduler_t * scheduler, + uint32_t first_timeout_ms, gs_function_scheduler_function_t func, void * user_data) +{ + GS_CHECK_HANDLE(scheduler != NULL); + GS_CHECK_ARG(func != NULL); + + gs_function_scheduler_entry_t * entry = scheduler->entries; + for (unsigned int i = 0; i < scheduler->max_entries; ++i, ++entry) { + if (entry->function == NULL) { + entry->function = func; + entry->user_data = user_data; + entry->timeout_ms = first_timeout_ms; + entry->last_exec_ms = gs_time_rel_ms(); + return GS_OK; + } + } + + return GS_ERROR_FULL; +} + +int gs_function_scheduler_execute_ms(gs_function_scheduler_t * scheduler) +{ + uint32_t timeout_ms = 5000; // max timeout to ensure gs_time_rel_ms() works correctly (wrapping more than once is bad) + + if (scheduler) { + timeout_ms = scheduler->max_timeout_ms; + uint32_t now_ms = gs_time_rel_ms(); + + gs_function_scheduler_entry_t * entry = scheduler->entries; + for (unsigned int i = 0; i < scheduler->max_entries; ++i, ++entry) { + if (entry->function) { + uint32_t elapsed = gs_time_diff_ms(entry->last_exec_ms, now_ms); + if (elapsed >= entry->timeout_ms) { + entry->timeout_ms = (entry->function)(entry->user_data); + entry->last_exec_ms = now_ms = gs_time_rel_ms(); + elapsed = 0; + } + timeout_ms = gs_min(timeout_ms, (entry->timeout_ms - elapsed)); + } + } + } + + return (int)((timeout_ms < INT_MAX) ? timeout_ms : INT_MAX); +} diff --git a/gomspace/libutil/src/gosh/command.c b/gomspace/libutil/src/gosh/command.c new file mode 100644 index 00000000..b68d6c82 --- /dev/null +++ b/gomspace/libutil/src/gosh/command.c @@ -0,0 +1,754 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include "command_local.h" + +#include +#include + +#include // register commands +#include // register commands +#include +#include +#include "../lock.h" + +#define MAX_ARGC 30 + +#ifdef __AVR__ +#include +#define cmd_strcmp strcmp_P +#define cmd_strncmp strncmp_P +#define cmd_strlen strlen_P +#define cmd_strcpy strcpy_P +#define cmd_read_ptr(ptr) ((void *) pgm_read_word(ptr)) +#define cmd_read_int(ptr) pgm_read_word(ptr) +#else +#define cmd_strcmp strcmp +#define cmd_strncmp strncmp +#define cmd_strlen strlen +#define cmd_strcpy strcpy +#define cmd_read_ptr(ptr) *ptr +#define cmd_read_int(ptr) *ptr +#endif + +// define common command log group. +static GS_LOG_GROUP(gs_command_log, "command", GS_LOG_CAT_COMMAND, LOG_DEFAULT_MASK | LOG_INFO_MASK); +#define LOG_DEFAULT gs_command_log + +/** + Compile check that size of gs_command_t is multiplum of 4. +*/ +GS_STATIC_ASSERT((sizeof(gs_command_t) % 4) == 0, gs_command_t_is_not_a_multiplum_of_4); + +// Private context +typedef struct process_context { + // command context - must be first, as it is used to access private context (same address) + gs_command_context_t context; + // process function + gs_error_t (*process)(const gs_command_t * const cmds, int cmd_count, int arg_offset, struct process_context * pc); + // command error + gs_error_t error; + // only exact match (space after last argument) + bool requires_exact_match; + // first command match + const gs_command_t * cmd; + // number of hits when hunting commands, completion etc. + unsigned int hits; + // complete result + struct { + char * line; + size_t token_start; + } complete; +} private_context_t; + +// command block +typedef struct gs_command_block { + //! Pointer to command block. + const gs_command_t * commands; + //! Number of commands in command block. + size_t count; + //! Reference to next command block. + struct gs_command_block * next; +} gs_command_block_t; + +// commands +static gs_command_block_t g_commands; + +// minimum stack size in bytes. +static size_t g_stack_size; + +// command logger callback +static gs_command_log_t g_command_logger = NULL; +static void * g_command_logger_ctx = NULL; + +static gs_error_t command_stdio_set_result(gs_command_context_t *ctx, const char *group, const char *key, const char *value) +{ + static const char* printed_group_header = NULL; + /* Print Group header if Group string is non-empty */ + if ((group != NULL) && (group[0] != '\0')) { + if (printed_group_header != group) { + fprintf(ctx->out, "%s:\r\n", group); + printed_group_header = group; + } + } + /* Print ": " if key string is non-empty */ + if (key != NULL) { + if (key[0] != '\0') { + if ((group != NULL) && (group[0] != '\0')) { + fprintf(ctx->out, " %s: ", key); + } else { + fprintf(ctx->out, "%s: ", key); + } + } + } + fprintf(ctx->out, "%s\r\n", value); + return GS_OK; +} + +gs_error_t gs_command_stdio_flush(gs_command_context_t *ctx) +{ + fflush(ctx->out); + return GS_OK; +} + +gs_error_t gs_command_stdio_wait_for_key(gs_command_context_t *ctx, int *ch, int timeout_ms) +{ + return gs_stdio_getchar_timed(timeout_ms, ch); +} + +static const gs_command_io_functions_t stdio_functions = { + .set_result = command_stdio_set_result, + .flush = gs_command_stdio_flush, + .wait_for_key = gs_command_stdio_wait_for_key +}; + +const char * gs_command_args(gs_command_context_t *ctx) +{ + if (ctx->argc > 1) { + // find first matching argument (= starts with) - this is not 100% and doesn't handle arguments with spaces (quoted) + const char * arg = ctx->command_line; + while (arg && arg[0]) { + if (strncmp(arg, ctx->argv[1], strlen(ctx->argv[1])) == 0) { + return arg; + } + // skip argument + for (; *arg && (*arg != ' '); ++arg); + // skip spaces + // cppcheck-suppress redundantCondition + for (; *arg && (*arg == ' '); ++arg); + } + } + return ""; +} + +bool gs_command_build_argv(char *line, int *argc, char **argv, int max_argc) +{ + // Skip spaces + for (; line && *line && isspace((unsigned int)*line); ++line); + + *argc = 0; + argv[*argc] = line; + + char quote = 0; + + while (*line) { + // check for quote's: ' or " + if ((*line == '\'') || (*line == '\"')) { + if (quote == 0) { + quote = *line; + argv[*argc]++; + } else if (quote == *line) { + quote = 0; + *line = '\0'; + } + } + // check for whitespace and no quotes active + else if (isspace((unsigned int)*line) && quote == 0) { + /* Delete space */ + *line++ = '\0'; + + // skip spaces + for (; *line && isspace((unsigned int)*line); ++line); + + /* If there is more data, we have another argument */ + if (*line) { + (*argc)++; + if (*argc >= max_argc) { + return false; + } + argv[*argc] = line; + } + + continue; + } + + line++; + } + + (*argc)++; + if (*argc >= max_argc) { + return false; + } + + // According to C11 section 5.1.2.2.1, argv[argc] must be NULL + argv[*argc] = NULL; + + // Check for invalid number of quotes + return (quote == 0) ? true : false; +} + +static inline gs_error_t command_logger(const char *cmd_line, gs_error_t ret, gs_error_t cmd_ret, gs_timestamp_t ts, gs_timestamp_t te) +{ + gs_lock_lock(); + gs_command_log_t logger = g_command_logger; + void * log_ctx = g_command_logger_ctx; + gs_lock_unlock(); + + if (logger) { + return logger(cmd_line, ret, cmd_ret, ts, te, log_ctx); + } + return GS_OK; +} + +static gs_error_t command_execute(const gs_command_t * const cmds, int cmd_count, int arg_offset, private_context_t * pc) +{ + for (int i = 0; i < cmd_count; i++) { + const gs_command_t * cmd = &cmds[i]; + + if (cmd_strcmp(pc->context.argv[arg_offset], cmd->name) == 0) { + // check for sub-commands + const gs_command_t * list = (void*) cmd_read_ptr(&cmd->chain.list); + if (list) { + ++arg_offset; + if (arg_offset >= pc->context.argc) { + return GS_ERROR_TYPE; + } + return command_execute(list, cmd_read_int(&cmd->chain.count), arg_offset, pc); + } + + gs_command_handler_t handler = (void *) cmd_read_ptr(&cmd->handler); + if (handler == NULL) { + return GS_ERROR_NOT_IMPLEMENTED; + } + + pc->context.argc -= arg_offset; + pc->context.argv = &pc->context.argv[arg_offset]; + pc->context.command = cmd; + + // check arguments - if specified + if (cmd->mandatory_args || cmd->optional_args) { + const int min_args = (cmd->mandatory_args == GS_COMMAND_NO_ARGS) ? 0 : cmd->mandatory_args; + const int args = (pc->context.argc - 1); + if (args < min_args) { + return GS_ERROR_ARG; + } + if (args > (min_args + cmd->optional_args)) { + return GS_ERROR_ARG; + } + } + + pc->error = handler(&pc->context); + return GS_OK; // command was excecuted + } + } + + return GS_ERROR_NOT_FOUND; +} + +static gs_error_t command_process(private_context_t * pc) +{ + const char * command = gs_string_skip_leading_spaces(pc->context.command_line); + + // Make copy of string, because command parser mangles destroys it + const size_t command_len = strlen(command); + char command_copy[command_len + 1]; + strcpy(command_copy, command); + + if (command_len && command[command_len-1] == ' ') { + pc->requires_exact_match = true; + } + + pc->context.optsp = 1; + pc->context.optind = 1; + pc->context.optopt = '?'; + pc->context.command_line = command; + + // split into arguments + char *argv[MAX_ARGC + 1]; + if (gs_command_build_argv(command_copy, &pc->context.argc, argv, MAX_ARGC + 1) == false) { + return GS_ERROR_ARG; + } + pc->context.argv = argv; + + gs_error_t error = GS_ERROR_NOT_FOUND; + for (const gs_command_block_t * block = &g_commands; block && (error == GS_ERROR_NOT_FOUND); block = block->next) { + if (block->commands) { + error = (pc->process)(block->commands, block->count, 0, pc); + } + } + + return error; +} + +gs_error_t gs_command_run(const char * command, gs_error_t * return_command_result) +{ + return gs_command_execute_stdio(command, return_command_result); +} + +gs_error_t gs_command_execute_stdio(const char * command, gs_error_t * return_command_result) +{ + return gs_command_execute(command, return_command_result, stdout, &stdio_functions, NULL); +} + +gs_error_t gs_command_execute(const char * command, gs_error_t * return_command_result, FILE *out, + const gs_command_io_functions_t *iof, void *iof_ctx) +{ + command = gs_string_skip_leading_spaces(command); + GS_CHECK_ARG(gs_string_empty(command) == false); + + private_context_t pc = { + .process = command_execute, + .error = GS_OK, + .context = { + .command_line = command, + .out = out, + .io_functions = iof, + .io_ctx = iof_ctx, + } + }; + gs_timestamp_t tm_start, tm_end; + gs_clock_get_time(&tm_start); + gs_error_t error = command_process(&pc); + gs_clock_get_time(&tm_end); + command_logger(pc.context.command_line, error, pc.error, tm_start, tm_end); + if ((error == GS_OK) && return_command_result) { + *return_command_result = pc.error; + } + return error; +} + +gs_error_t gs_command_set_output(gs_command_context_t *ctx, const char* group, const char* key, const char* value) +{ + GS_CHECK_ARG(ctx); + + if (ctx->io_functions && ctx->io_functions->set_result) { + return ctx->io_functions->set_result(ctx, group, key, value); + } + + /* If no IO-function set - ignore the data and send Success */ + return GS_OK; +} + +gs_error_t gs_command_set_output_printf(gs_command_context_t *ctx, const char* group, const char* key, const char * format, ...) +{ + GS_CHECK_ARG(ctx); + + if (ctx->io_functions && ctx->io_functions->set_result) + { + va_list args; + va_start(args, format); + char value[256]; + int size = vsnprintf(value, sizeof(value), format, args); + va_end(args); + + /* Don't allow to set truncated results - Return error in this case */ + if (size >= (int)sizeof(value)) { + return GS_ERROR_ALLOC; + } + + return ctx->io_functions->set_result(ctx, group, key, value); + } + + /* If no IO-function set - ignore the data and send Success */ + return GS_OK; +} + +gs_error_t gs_command_flush_output(gs_command_context_t *ctx) +{ + GS_CHECK_ARG(ctx); + + if (ctx->io_functions && ctx->io_functions->flush) { + return ctx->io_functions->flush(ctx); + } + + /* If no IO-function set - ignore the data and send Success */ + return GS_OK; +} + +bool gs_command_wait_any_key(gs_command_context_t *ctx, int timeout_ms) +{ + int ch; + gs_error_t ret = gs_command_wait_key(ctx, &ch, timeout_ms); + + if (ret == GS_ERROR_TIMEOUT) { + return false; + } + + /* Ensure that a commands handler will not stall if IO function if not available etc. + False will only be returned in case of a positive timeout */ + return true; +} + +gs_error_t gs_command_wait_key(gs_command_context_t *ctx, int* ch, int timeout_ms) +{ + if (ctx && ctx->io_functions && ctx->io_functions->wait_for_key) + { + return ctx->io_functions->wait_for_key(ctx, ch, timeout_ms); + } + + /* If no IO-function set set return GS_ERROR_HANDLE */ + return GS_ERROR_HANDLE; +} + +unsigned int gs_command_completer_add_token(gs_command_context_t * ctx, const char * token, bool exact) +{ + private_context_t * pc = (private_context_t *) ctx; + char * line = &pc->complete.line[pc->complete.token_start]; + + if (token == NULL) { + // mark any pending partial token as exact + if ((line[0] == 0) || (pc->hits != 1)) { + return pc->hits; + } + exact = true; + } + + if (exact) { + if (token) { + strcpy(line, token); + } + strcat(line, " "); + pc->complete.token_start = strlen(pc->complete.line); + pc->hits = 1; + } else { + if (pc->hits == 0) { + strcpy(line, token); + } else { + for (; *line && *token && (*line == *token); ++line, ++token); + *line = 0; + } + ++pc->hits; + } + + return pc->hits; +} + +static unsigned int command_complete_add(private_context_t * pc, const gs_command_t * cmd, bool exact) +{ + if (cmd) { + pc->cmd = cmd; + return gs_command_completer_add_token(&pc->context, cmd->name, exact); + } else { + return gs_command_completer_add_token(&pc->context, NULL, exact); + } +} + +static gs_error_t command_complete(const gs_command_t * const cmds, int cmd_count, int arg_offset, private_context_t * pc) +{ + if (arg_offset > 0) { + // command we are looking for must be in this block + pc->hits = 0; + } + pc->cmd = NULL; + bool exact_match = false; + + for (int i = 0; i < cmd_count; i++) { + const gs_command_t * cmd = &cmds[i]; + + if (cmd_read_int(&cmd->mode) & GS_COMMAND_FLAG_HIDDEN) { + continue; + } + + if (gs_string_empty(pc->context.argv[arg_offset])) { + // exceeding known token(s) - partial match + command_complete_add(pc, cmd, false); + continue; + } + + if (pc->requires_exact_match || ((arg_offset+1) < pc->context.argc)) { + // must be an exact match + if (cmd_strcmp(pc->context.argv[arg_offset], cmd->name) == 0) { + command_complete_add(pc, cmd, true); + exact_match = true; + break; + } + } else if (cmd_strncmp(pc->context.argv[arg_offset], cmd->name, + strlen(pc->context.argv[arg_offset])) == 0) { + // partial match + command_complete_add(pc, cmd, false); + } + } + + if (exact_match || ((arg_offset > 0) && (pc->hits == 1))) { + command_complete_add(pc, NULL, true); + + if (strlen(pc->complete.line) > strlen(pc->context.command_line)) { + return GS_OK; + } + + if (pc->cmd->chain.list) { + return command_complete(pc->cmd->chain.list, pc->cmd->chain.count, arg_offset+1, pc); + } + + // command arguments + pc->context.argc -= arg_offset; + pc->context.argv = &pc->context.argv[arg_offset]; + pc->context.command = pc->cmd; + + // add the "already" completed ones + int arg_to_complete = 1; + for (; arg_to_complete < (pc->context.argc - 1); ++arg_to_complete) { + gs_command_completer_add_token(&pc->context, pc->context.argv[arg_to_complete], true); + } + // add the last - if its completed (space after) + if ((arg_to_complete < pc->context.argc) && pc->requires_exact_match) { + // cppcheck-suppress unreadVariable - not used on __AVR__ because it doesn't support 'completer' + gs_command_completer_add_token(&pc->context, pc->context.argv[arg_to_complete], true); + ++arg_to_complete; + } + +#if (__AVR__ == 0) + if (pc->cmd->completer) { + pc->hits = 0; + (pc->cmd->completer)(&pc->context, arg_to_complete); + } else +#endif + { + pc->hits = 1; // no completer - assume single hit + } + + return GS_OK; // only used for breaking loop + } + + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_command_complete(char *line, size_t max_line_length, FILE* out) +{ + const size_t line_len = strlen(line); + char buffer[max_line_length]; + buffer[0] = 0; + private_context_t pc = { + .process = command_complete, + .context = { + .command_line = line, + .out = out, + }, + .complete = { + .line = buffer, + }, + }; + command_process(&pc); + gs_command_completer_add_token(&pc.context, NULL, true); + if (strlen(buffer) > line_len ) { + strcpy(line, buffer); + } + switch (pc.hits) { + case 0: + return GS_ERROR_NOT_FOUND; + case 1: + return GS_OK; + default: + return GS_ERROR_AMBIGUOUS; + } +} + +static void command_help_print(const gs_command_t * const cmd, private_context_t * pc) +{ + if (pc->hits == 1) { + if (cmd->help) { + fprintf(pc->context.out, "%s\r\n", cmd->help); + } + if (cmd->chain.count == 0) { + fprintf(pc->context.out, "usage: %s %s\r\n", cmd->name, cmd->usage ? cmd->usage : ""); + } else { + for (unsigned int i = 0; i < cmd->chain.count; ++i) { + const gs_command_t * scmd = &cmd->chain.list[i]; + + if (scmd->mode & GS_COMMAND_FLAG_HIDDEN) { + continue; + } + fprintf(pc->context.out, " %-19s %s\r\n", scmd->name, scmd->help ? scmd->help : ""); + } + } + } else { + fprintf(pc->context.out, " %-19s %s\r\n", cmd->name, cmd->help ? cmd->help : ""); + } +} + +static void command_help_hit(const gs_command_t * const cmd, private_context_t * pc) +{ + pc->error = GS_OK; + ++pc->hits; + if (pc->hits == 1) { + // single hit so far - hold off printing until we know if we get more + pc->cmd = cmd; + } else { + if (pc->cmd) { + command_help_print(pc->cmd, pc); + pc->cmd = NULL; + } + command_help_print(cmd, pc); + } +} + +static gs_error_t command_help(const gs_command_t * const cmds, int cmd_count, int arg_offset, private_context_t * pc) +{ + for (int i = 0; i < cmd_count; i++) { + const gs_command_t * cmd = &cmds[i]; + + if (cmd_read_int(&cmd->mode) & GS_COMMAND_FLAG_HIDDEN) { + continue; + } + + if (pc->requires_exact_match || ((arg_offset+1) < pc->context.argc)) { + // must be an exact match + if (cmd_strcmp(pc->context.argv[arg_offset], cmd->name) == 0) { + const gs_command_t * list = (void*) cmd_read_ptr(&cmd->chain.list); + if (list && ((arg_offset+1) < pc->context.argc)) { + return command_help(list, cmd_read_int(&cmd->chain.count), arg_offset+1, pc); + } + command_help_hit(cmd, pc); + } + + } else if (cmd_strncmp(pc->context.argv[arg_offset], cmd->name, + strlen(pc->context.argv[arg_offset])) == 0) { + command_help_hit(cmd, pc); + } + } + + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_command_show_help(const char * command, FILE* out) +{ + private_context_t pc = { + .process = command_help, + .error = GS_ERROR_NOT_FOUND, + .context = { + .command_line = command, + .out = out, + } + }; + gs_error_t error = command_process(&pc); + if (pc.cmd) { + command_help_print(pc.cmd, &pc); + error = GS_OK; + } else if ((error == GS_ERROR_NOT_FOUND) && pc.hits) { + error = GS_OK; + } + return error; +} + +gs_error_t gs_command_register(const gs_command_t * commands, size_t count) +{ + GS_CHECK_ARG(commands != NULL); + GS_CHECK_ARG(count > 0); + + gs_error_t error = GS_OK; + + gs_lock_lock(); + { + // check if command block already installed + gs_command_block_t * last_block = NULL; + for (gs_command_block_t * block = &g_commands; block; block = block->next) { + if (block->commands) { + const gs_command_t * cmd = block->commands; + // loop through because it may be in the linked blocks + for (size_t i = 0; i < block->count; ++i, ++cmd) { + if (cmd == commands) { + error = GS_ERROR_EXIST; + break; + } + } + } + last_block = block; + } + + if (error == GS_OK) { + gs_command_block_t * block = calloc(1, sizeof(*block)); + if (block) { + // Insert command last, so lock isn't needed when accessing commands + block->commands = commands; + block->count = count; + block->next = NULL; + last_block->next = block; + } else { + error = GS_ERROR_ALLOC; + } + } + } + gs_lock_unlock(); + + return (error != GS_ERROR_EXIST) ? error : GS_OK; +} + +size_t gs_command_get_stack_size(void) +{ + return g_stack_size; +} + +gs_error_t gs_command_init_no_commands(size_t stack_size) +{ + g_stack_size = stack_size; + + gs_error_t error = gs_lock_init(); + if (error) { + return error; + } + + gs_log_group_register(gs_command_log); + +#if (__linux__ == 0) + // look up static linked commands - only embedded (= none linux) systems + gs_command_block_t * block = &g_commands; + extern volatile unsigned int __command_start __attribute__ ((__weak__)); + extern volatile unsigned int __command_end __attribute__ ((__weak__)); + if (&__command_start) { + block->count = ((ptrdiff_t)&__command_end - (ptrdiff_t)&__command_start) / sizeof(gs_command_t); + block->commands = (gs_command_t *) &__command_start; + } +#endif + + return GS_OK; +} + +gs_error_t gs_command_init(size_t stack_size) +{ + gs_error_t error = gs_command_init_no_commands(stack_size); + if (error == GS_OK) { + // register default commands + gs_command_register_default_commands(); + gs_log_register_commands(); + } + return error; +} + +gs_error_t gs_command_logger_default(const char* cmd_line, gs_error_t ret, gs_error_t cmd_ret, gs_timestamp_t t_start, gs_timestamp_t t_end, void *log_ctx) +{ + (void)log_ctx; + + timestamp_diff(&t_end, &t_start); + if (ret == GS_OK) { + log_info_group(gs_command_log, "'%s' returned '%s' [" + "t: <%04"PRIu32".%06"PRIu32">, dt: <%01"PRIu32".%06"PRIu32">]", + cmd_line, gs_error_string(cmd_ret), + t_start.tv_sec, t_start.tv_nsec/1000, t_end.tv_sec, t_end.tv_nsec/1000); + } else { + log_info_group(gs_command_log, "'%s' could not be run, returned '%s' [" + "t: <%04"PRIu32".%06"PRIu32">]", + cmd_line, gs_error_string(ret), + t_start.tv_sec, t_start.tv_nsec/1000); + } + return GS_OK; +} + +gs_error_t gs_command_register_logger(gs_command_log_t log_cb, void *log_ctx) +{ + gs_lock_lock(); + g_command_logger = log_cb; + g_command_logger_ctx = log_ctx; + gs_lock_unlock(); + + return GS_OK; +} + diff --git a/gomspace/libutil/src/gosh/command_local.h b/gomspace/libutil/src/gosh/command_local.h new file mode 100644 index 00000000..69f715e1 --- /dev/null +++ b/gomspace/libutil/src/gosh/command_local.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +/** + Command I/O function - flush stdout. +*/ +gs_error_t gs_command_stdio_flush(gs_command_context_t *ctx); + +/** + Command I/O function - wait for a key. +*/ +gs_error_t gs_command_stdio_wait_for_key(gs_command_context_t *ctx, int *ch, int timeout_ms); + +/** + Complete command. + @param[in] line command line to complete + @param[in] max \a length (size) + @param[in] out output stream, e.g. stdout +*/ +gs_error_t gs_command_complete(char *line, size_t max_line_length, FILE* out); + +/** + Show help. + @param line command line to show help for. + @param out output stream, e.g. stdout +*/ +gs_error_t gs_command_show_help(const char * command, FILE * out); + +/** + Change console mode. + @param[in] mode console mode, 'cci' + @return_gs_error_t +*/ +int gs_console_change_mode(const char * mode); diff --git a/gomspace/libutil/src/gosh/console.c b/gomspace/libutil/src/gosh/console.c new file mode 100644 index 00000000..99b04aac --- /dev/null +++ b/gomspace/libutil/src/gosh/console.c @@ -0,0 +1,758 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + The console interface provides support for executing commands over stdout (typically a serial port). + + The connection can run in 2 modes: + - normal, standard GOSH interface (Human Machine Interface), echo characters, prompt, etc. + - cci, Computer Computer Interface. Simple text interface, but with tagged output format - easier to parse by a computer. +*/ +#include "console_local.h" +#include "command_local.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include // console defines set through Waf options + +#if (__linux__) +#include +#include +#include +#include +#endif + +/* Max history length (elements) */ +#ifndef GS_CONSOLE_HISTORY_LEN +#define GS_CONSOLE_HISTORY_LEN 10 +#endif + +/* Max input length */ +#ifndef GS_CONSOLE_INPUT_LEN +#define GS_CONSOLE_INPUT_LEN 100 +#endif + +#define CONTROL(X) ((X) - '@') + +typedef enum { + CONSOLE_NORMAL = 0, + CONSOLE_ESCAPE = 1, + CONSOLE_PRE_ESCAPE = 2, +} console_escape_t; + +static const char hash_prompt[] = "\033[1;30m # "; + +static const char backspace_char = '\b'; +static const char space_char = ' '; +static const char cr_char = '\r'; +static const char nl_char = '\n'; + +static const char * user_prompt = "gosh"; + +static console_escape_t escape = CONSOLE_NORMAL; + +#if (GS_CONSOLE_HISTORY_LEN > 0) +static int history_elements; +static int history_cur; +static int history_browse; +static char history[GS_CONSOLE_HISTORY_LEN][GS_CONSOLE_INPUT_LEN+1]; +#endif + +static int size; +static int pos; +static char buf[GS_CONSOLE_INPUT_LEN+1]; +static gs_thread_t console_thread; + +#if (__linux__) +static bool termios_changed; +static struct termios old_stdin; +static struct termios old_stdout; +#endif + +static gs_mutex_t g_cci_lock; // Lock for protecting stdout for async output, e.g. log messages +static gs_error_t command_io_cci_set_result(gs_command_context_t *ctx, const char *group, const char *key, const char *value); +static const gs_command_io_functions_t cci_io_functions = { + .set_result = command_io_cci_set_result, + .flush = gs_command_stdio_flush, + .wait_for_key = gs_command_stdio_wait_for_key, +}; +#define CCI_START_TAG "[X[" +#define CCI_END_TAG "]X]" + +static void gs_console_write(const char *str, int length) +{ + for (int i = 0; i < length; i++) { + putchar(str[i]); + } +} + +static void gs_console_prompt(void) +{ + static const char col_start[] = "\033[1;32m"; + static const char col_end[] = "\033[0m"; + + gs_console_write(col_start, sizeof(col_start) - 1); + gs_console_write(user_prompt, strlen(user_prompt)); + gs_console_write(hash_prompt, sizeof(hash_prompt) - 1); + gs_console_write(col_end, sizeof(col_end) - 1); +} + +void gs_console_set_prompt(const char * _prompt) +{ + if (gs_string_empty(_prompt) == false) { + user_prompt = _prompt; + } +} + +static void gs_console_reset(void) +{ + pos = size = 0; + buf[pos] = 0; + gs_console_prompt(); +} + +static void gs_console_rewind(void) +{ + int plen = strlen(hash_prompt) + strlen(user_prompt); + gs_console_write(&cr_char, 1); + while (size-- + plen) { + gs_console_write(&space_char, 1); + } + pos = size = 0; + gs_console_write(&cr_char, 1); +} + +void gs_console_clear(void) +{ + static const char clear[] = "\033[H\033[2J"; + gs_console_write(clear, sizeof(clear) - 1); + gs_console_rewind(); + gs_console_reset(); +} + +void gs_console_update(void) +{ + gs_console_rewind(); + gs_console_prompt(); + pos = size = strlen(buf); + gs_console_write(buf, size); +} + +#if (GS_CONSOLE_HISTORY_LEN > 0) + +static void gs_console_history_add(void) +{ + strncpy(history[history_cur], buf, GS_CONSOLE_INPUT_LEN); + history[history_cur][GS_CONSOLE_INPUT_LEN] = 0; + + history_browse = 0; + history_cur = (history_cur + 1) % GS_CONSOLE_HISTORY_LEN; + + if (history_elements < GS_CONSOLE_HISTORY_LEN) { + history_elements++; + } +} + +static void gs_console_last_line(void) +{ + if (history_elements < 1) { + return; + } + + if (history_browse >= history_elements) { + return; + } + + gs_console_rewind(); + history_browse++; + strcpy(buf, history[(history_cur - history_browse + GS_CONSOLE_HISTORY_LEN) % GS_CONSOLE_HISTORY_LEN]); + gs_console_update(); +} + +static void gs_console_next_line(void) +{ + if (history_elements < 1) { + return; + } + + if (history_browse < 1) { + return; + } + + gs_console_rewind(); + history_browse--; + if (history_browse > 0) { + strcpy(buf, history[(history_cur - history_browse + GS_CONSOLE_HISTORY_LEN) % GS_CONSOLE_HISTORY_LEN]); + } else { + buf[0] = '\0'; + } + gs_console_update(); +} + +#endif + +static void gs_console_forward_char(void) +{ + if (pos < size) { + gs_console_write(&buf[pos], 1); + pos++; + } +} + +static void gs_console_end_of_line(void) +{ + while (pos < size) { + gs_console_forward_char(); + } +} + +static void gs_console_backward_char(void) +{ + if (pos > 0) { + pos--; + gs_console_write(&backspace_char, 1); + } +} + +static void gs_console_beginning_of_line(void) +{ + while (pos) { + gs_console_backward_char(); + } +} + +static void gs_console_newline(void) +{ + gs_console_write(&cr_char, 1); + gs_console_write(&nl_char, 1); +} + +static bool gs_command_not_empty(const char *ibuf) +{ + while (*ibuf) { + if (!isblank((int) *ibuf++)) { + return true; + } + } + return false; +} + +static void show_help(const char * command) +{ + gs_error_t error = gs_command_show_help(command, stdout); + if (error) { + printf("Could not show help for \'%s\': %s (%d)\r\n", command, gs_error_string(error), error); + } +} + +static void gs_console_execute(void) +{ + gs_console_newline(); + buf[GS_CONSOLE_INPUT_LEN] = 0; // ensure 0 termination + if (size > 0 && gs_command_not_empty(buf)) { +#if (GS_CONSOLE_HISTORY_LEN > 0) + gs_console_history_add(); +#endif + gs_error_t result = GS_OK; + gs_error_t error = gs_command_execute_stdio(buf, &result); + if (error == GS_ERROR_TYPE) { + show_help(buf); + } else if (error == GS_ERROR_NOT_FOUND) { + printf("Unknown command \'%s\'\r\n", buf); + } else if (error == GS_ERROR_ARG) { + show_help(buf); + } else if (error) { + printf("Command \'%s\' did not execute: %s (%d)\r\n", buf, gs_error_string(error), error); + } else if (result == GS_ERROR_ARG) { + show_help(buf); + } else if (result) { + printf("Command \'%s\' executed, but returned error: %s (%d)\r\n", buf, gs_error_string(result), result); + } + } + gs_console_reset(); +} + +static void gs_console_complete(void) +{ + /* We don't expand in the middle of a line */ + if (size != pos) { + return; + } + + const size_t old_buf_len = strlen(buf); + gs_error_t ret = gs_command_complete(buf, sizeof(buf), stdout); + if ((ret == GS_OK) && (old_buf_len == strlen(buf))) { + // completed (again) and no change - show help + ret = GS_ERROR_AMBIGUOUS; + } + switch (ret) { + case GS_ERROR_AMBIGUOUS: + gs_console_newline(); + show_help(buf); + gs_console_update(); + break; + case GS_OK: + gs_console_update(); + break; + default: + case GS_ERROR_NOT_FOUND: + break; + } +} + +static void gs_console_insert(char c) +{ + int i; + int diff = size - pos; + + if (size >= GS_CONSOLE_INPUT_LEN) { + return; + } + + memmove(&buf[pos + 1], &buf[pos], diff); + buf[pos] = c; + + gs_console_write(&buf[pos], diff + 1); + for (i = 0; i < diff; i++) { + gs_console_write(&backspace_char, 1); + } + + size++; + pos++; + buf[size] = '\0'; +} + +static void gs_console_insert_overwrite(char c) +{ + buf[pos++] = c; + + if (pos > size) { + size++; + } + + gs_console_write(&c, 1); +} + +static void gs_console_delete(void) +{ + int i; + int diff = size - pos; + + /* Nothing to delete */ + if (size == pos) { + return; + } + + size--; + memmove(&buf[pos], &buf[pos + 1], diff - 1); + buf[size] = '\0'; + + gs_console_write(&buf[pos], diff - 1); + gs_console_write(&space_char, 1); + for (i = 0; i < diff; i++) { + gs_console_write(&backspace_char, 1); + } +} + +static void gs_console_backspace(void) +{ + if (pos < 1) { + return; + } + + gs_console_backward_char(); + gs_console_delete(); +} + +static void gs_console_kill_line(void) +{ + int i; + int diff; + + diff = size - pos; + + if (diff == 0) { + return; + } + + for (i = 0; i < diff; i++) { + gs_console_write(&space_char, 1); + } + for (i = 0; i < diff; i++) { + gs_console_write(&backspace_char, 1); + } + + memset(&buf[pos], 0, diff); + size = pos; +} + +static void gs_console_kill_line_from_beginning(void) +{ + gs_console_beginning_of_line(); + gs_console_kill_line(); +} + +static void gs_console_backward_kill_word(void) +{ + while (pos > 0 && buf[pos - 1] == ' ') { + gs_console_backspace(); + } + while (pos > 0 && buf[pos - 1] != ' ') { + gs_console_backspace(); + } +} + +static void gs_console_transpose_chars(void) +{ + char c1, c2; + + if (size < 2 || pos < 1) { + return; + } + + if (pos == size) { + c1 = buf[pos - 1]; + c2 = buf[pos - 2]; + + gs_console_backward_char(); + gs_console_backward_char(); + gs_console_insert_overwrite(c1); + gs_console_insert_overwrite(c2); + } else { + c1 = buf[pos]; + c2 = buf[pos - 1]; + + gs_console_backward_char(); + gs_console_insert_overwrite(c1); + gs_console_insert_overwrite(c2); + } +} + +static void gs_console_normal(char c) +{ + switch (c) { + case CONTROL('A'): + gs_console_beginning_of_line(); + break; + case CONTROL('B'): + gs_console_backward_char(); + break; + case CONTROL('C'): + // Either ignored or handled through signals + break; + case CONTROL('D'): + gs_console_delete(); + break; + case CONTROL('E'): + gs_console_end_of_line(); + break; + case CONTROL('F'): + gs_console_forward_char(); + break; + case CONTROL('K'): + gs_console_kill_line(); + break; + case CONTROL('L'): + gs_console_clear(); + break; +#if (GS_CONSOLE_HISTORY_LEN > 0) + case CONTROL('N'): + gs_console_next_line(); + break; + case CONTROL('P'): + gs_console_last_line(); + break; +#endif + case CONTROL('T'): + gs_console_transpose_chars(); + break; + case CONTROL('U'): + gs_console_kill_line_from_beginning(); + break; + case CONTROL('W'): + gs_console_backward_kill_word(); + break; + case CONTROL('Z'): + // We cannot suspend + break; + case CONTROL('H'): + case 0x7f: + gs_console_backspace(); + break; + case '\r': + case '\n': + gs_console_execute(); + break; + case '\t': + gs_console_complete(); + break; + case '\033': + escape = CONSOLE_ESCAPE; + break; + default: + if (escape == CONSOLE_ESCAPE) { + if ((c == '[') || (c == 'O')) { + c = getchar(); + if (c == 'F') + gs_console_end_of_line(); + if (c == 'H') + gs_console_beginning_of_line(); +#if (GS_CONSOLE_HISTORY_LEN > 0) + if (c == 'A') + gs_console_last_line(); + if (c == 'B') + gs_console_next_line(); +#endif + if (c == 'C') + gs_console_forward_char(); + if (c == 'D') + gs_console_backward_char(); + if (c == '1') + if (getchar() == '~') + gs_console_beginning_of_line(); + if (c == '3') + if (getchar() == '~') + gs_console_delete(); + } + escape = CONSOLE_NORMAL; + break; + } + + if (isprint((unsigned char) c)) { + gs_console_insert(c); + } + + break; + } +} + +static gs_error_t command_io_cci_set_result(gs_command_context_t *ctx, const char *group, const char *key, const char *value) +{ + gs_mutex_lock(g_cci_lock); + { + printf(CCI_START_TAG "cmd_res,%s,%s,%s" CCI_END_TAG, group, key, value); + } + gs_mutex_unlock(g_cci_lock); + return GS_OK; +} + +static void gs_console_cci_log(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va) +{ + va_list my_va; + va_copy(my_va, va); + + gs_mutex_lock(g_cci_lock); + { + printf(CCI_START_TAG "log,%04"PRIu32".%06"PRIu32",%c,%s,", ts->tv_sec, ts->tv_nsec / 1000, gs_log_level_to_char(level), group->name); + vprintf(format, my_va); + printf(CCI_END_TAG "\r\n"); + } + gs_mutex_unlock(g_cci_lock); + + va_end(my_va); +} + +static void gs_console_cci(char c) +{ + switch (c) { + case CONTROL('C'): + case CONTROL('L'): + size = 0; + buf[0] = 0; + break; + case '\r': + case '\n': + buf[GS_CONSOLE_INPUT_LEN] = 0; // ensure 0 termination + if (size > 0 && gs_command_not_empty(buf)) { + static unsigned int seq; // simple sequence number keep incrementing + + gs_mutex_lock(g_cci_lock); + ++seq; + printf(CCI_START_TAG "cmd_exec_begin,%u,%s" CCI_END_TAG "\r\n", seq, buf); + gs_mutex_unlock(g_cci_lock); + + gs_error_t result = GS_OK; + gs_error_t error = gs_command_execute(buf, &result, stdout, &cci_io_functions, NULL); + + gs_mutex_lock(g_cci_lock); + printf(CCI_START_TAG "cmd_exec_end,%u,%d,%d" CCI_END_TAG "\r\n", seq, error, result); + gs_mutex_unlock(g_cci_lock); + } + size = 0; + buf[0] = 0; + break; + default: + if (isprint((unsigned char) c) && (size < GS_CONSOLE_INPUT_LEN)) { + buf[size++] = c; + buf[size] = 0; + } + break; + } +} + +// Currrent mode handler, switch by sending command +static void (*console_handler)(char c) = gs_console_normal; + +int gs_console_change_mode(const char * mode) +{ + if (strcasecmp(mode, "cci") == 0) { + gs_error_t error = GS_OK; + if (console_handler != gs_console_cci) { + error = gs_mutex_create(&g_cci_lock); + if (error == GS_OK) { + gs_log_appender_console_set_cb(gs_console_cci_log); + console_handler = gs_console_cci; // change console handler + } + } + return error; + } + return GS_ERROR_NOT_SUPPORTED; +} + +static void * gs_console_thread(void * param) +{ + gs_console_reset(); + while (1) { + char c = getchar(); + console_handler(c); + } + + gs_thread_exit(NULL); +} + +gs_error_t gs_console_exit(void) +{ +#if (__linux__) + if (termios_changed) { + tcsetattr(STDIN_FILENO, TCSANOW, &old_stdin); + tcsetattr(STDOUT_FILENO, TCSANOW, &old_stdout); + } +#endif + return GS_OK; +} + +#if (__linux__) +static inline void exithandler(void) +{ + printf("\n"); + gs_console_exit(); +} +#endif + +static gs_error_t gs_console_init2(uint32_t flags) +{ +#if (__linux__) + // save current stdio setting, for restoring when terminating process + tcgetattr(STDIN_FILENO, &old_stdin); + tcgetattr(STDOUT_FILENO, &old_stdout); + + // change stdin settings + { + struct termios new = old_stdin; + new.c_iflag &= ~(IGNCR | ICRNL); + new.c_lflag &= ~(ECHO | ICANON | IEXTEN); + new.c_cc[VTIME]=0; + new.c_cc[VMIN]=1; + tcsetattr(STDIN_FILENO, TCSANOW, &new); + } + // change stdout settings + { + struct termios new = old_stdout; + new.c_iflag &= ~(IGNCR | ICRNL); + new.c_lflag &= ~(ECHO | ICANON | IEXTEN); + new.c_cc[VTIME]=0; + new.c_cc[VMIN]=1; + tcsetattr(STDOUT_FILENO, TCSANOW, &new); + } + + termios_changed = true; + + // add exit-handler to restore original termianl settings + atexit(exithandler); + + // install signal handlers to ensure terminal settings are restored + if ((flags & GS_CONSOLE_F_NO_SIGNAL_HANDLER) == 0) { + // install signal handler(s) to ensure atexit() is called + gs_signal_catch(SIGTERM, NULL); + + if (gs_command_line_ignore_ctrlc() == false) { + gs_signal_catch(SIGINT, NULL); + } + } +#endif + +#if (__AVR__ == 0) + /** This is very important on AVR32 */ + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); +#endif + return GS_OK; +} + +gs_error_t gs_console_init() +{ + return gs_console_init2(0); +} + +static gs_error_t _console_create_thread(gs_thread_priority_t priority, gs_thread_t * handle, uint32_t thread_create_flags) +{ + gs_error_t error = gs_thread_create("CONSOLE", + gs_console_thread, NULL, + gs_command_get_stack_size(), + priority, + thread_create_flags, + handle); + if (error == GS_OK) { + // give thread a few moments to print prompt + gs_time_sleep_ms(20); + } + return error; +} + +gs_error_t gs_console_create_thread(gs_thread_t * handle) +{ + return _console_create_thread(GS_THREAD_PRIORITY_LOW, handle, 0); +} + +gs_error_t gs_console_create_thread_with_priority(gs_thread_priority_t priority, gs_thread_t * handle) +{ + return _console_create_thread(priority, handle, 0); +} + +gs_error_t gs_console_start(const char * prompt, uint32_t flags) +{ + if (console_thread) { + return GS_ERROR_EXIST; + } + + gs_console_init2(flags); + gs_console_set_prompt(prompt); + + return _console_create_thread(GS_THREAD_PRIORITY_LOW, &console_thread, GS_THREAD_CREATE_JOINABLE); +} + +gs_error_t gs_console_stop(void) +{ + if (console_thread == 0) { + return GS_ERROR_HANDLE; + } +#if (__linux__) + if (pthread_cancel(console_thread) != 0) { + return GS_ERROR_IO; + } + gs_error_t error = gs_thread_join(console_thread, NULL); + if (error == GS_OK) { + console_thread = 0; + } + return error; +#else + return GS_ERROR_NOT_SUPPORTED; +#endif +} diff --git a/gomspace/libutil/src/gosh/console_local.h b/gomspace/libutil/src/gosh/console_local.h new file mode 100644 index 00000000..1332e732 --- /dev/null +++ b/gomspace/libutil/src/gosh/console_local.h @@ -0,0 +1,10 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +/** + Change console mode. + @param[in] mode console mode, 'rgosh', 'normal' + @return_gs_error_t +*/ +int gs_console_change_mode(const char * mode); diff --git a/gomspace/libutil/src/gosh/default_commands.c b/gomspace/libutil/src/gosh/default_commands.c new file mode 100644 index 00000000..fb535318 --- /dev/null +++ b/gomspace/libutil/src/gosh/default_commands.c @@ -0,0 +1,277 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include "command_local.h" +#include "console_local.h" + +#include + +#if defined(__linux__) +#include +#include +#endif + +#include +#include +#include +#include +#include + +static int cmd_help(gs_command_context_t * context) +{ + return gs_command_show_help(gs_command_args(context), context->out); +} + +static int cmd_sleep(gs_command_context_t * context) +{ + uint32_t sleep_ms; + gs_error_t error = gs_string_to_uint32(context->argv[1], &sleep_ms); + if (error) { + return error; + } + + gs_time_sleep_ms(sleep_ms); + + return GS_OK; +} + +static int cmd_watch(gs_command_context_t * context, bool check_error) +{ + uint32_t sleep_ms; + gs_error_t error = gs_string_to_uint32(context->argv[1], &sleep_ms); + if (error) { + return error; + } + + fprintf(context->out, "Execution delay: %" PRIu32 "\r\n", sleep_ms); + + char * new_command = strstr(gs_command_args(context), " "); + if (new_command == NULL) { + return GS_ERROR_ARG; + } else { + new_command = new_command + 1; + } + + fprintf(context->out, "Command: %s\r\n", new_command); + + while(1) { + + gs_error_t cmd_result; + error = gs_command_execute(new_command, &cmd_result, context->out, context->io_functions, context->io_ctx); + if (error) { + return error; + } + if (check_error && cmd_result) { + return cmd_result; + } + + if (gs_stdio_getchar_timed(sleep_ms, NULL) != GS_ERROR_TIMEOUT) { + break; + } + } + + return GS_OK; +} + +static int cmd_watch_nocheck(gs_command_context_t * context) +{ + return cmd_watch(context, false); +} + +static int cmd_watch_check(gs_command_context_t * context) +{ + return cmd_watch(context, true); +} + +#define CONTROL(X) ((X) - '@') + +static int cmd_batch(gs_command_context_t * ctx) +{ + char c; + int quit = 0, execute = 0; + unsigned int batch_size = 100; + unsigned int batch_input = 0; + unsigned int batch_count = 0; + char * batch[20] = {}; + printf("Type each command followed by enter, hit ctrl+e to end typing, ctrl+x to cancel:\r\n"); + + /* Wait for ^q to quit. */ + while (quit == 0) { + + /* Get character */ + c = getchar(); + + switch (c) { + + /* CTRL + X */ + case 0x18: + quit = 1; + break; + + /* CTRL + E */ + case 0x05: + execute = 1; + quit = 1; + break; + + /* Backspace */ + case CONTROL('H'): + case 0x7f: + if (batch_input > 0) { + putchar('\b'); + putchar(' '); + putchar('\b'); + batch_input--; + } + break; + + case '\r': + putchar('\r'); + putchar('\n'); + if ((batch[batch_count] != NULL) && (batch_input < batch_size)) + batch[batch_count][batch_input++] = '\r'; + if ((batch[batch_count] != NULL) && (batch_input < batch_size)) + batch[batch_count][batch_input++] = '\0'; + batch_count++; + batch_input = 0; + if (batch_count == 20) + quit = 1; + break; + + default: + putchar(c); + if (batch[batch_count] == NULL) { + batch[batch_count] = calloc(GS_CONSOLE_INPUT_LEN+1, 1); + } + + if ((batch[batch_count] != NULL) && (batch_input < batch_size)) + batch[batch_count][batch_input++] = c; + break; + } + } + + if (execute) { + printf("\r\n"); + for (unsigned int i = 0; i <= batch_count; i++) { + if (batch[i]) + printf("[%02u] %s\r\n", i, batch[i]); + } + printf("Press ctrl+e to execute, or any key to abort\r\n"); + c = getchar(); + if (c != 0x05) + execute = 0; + } + + /* Run/Free batch job */ + for (unsigned int i = 0; i <= batch_count; i++) { + if (execute && batch[i]) { + printf("EXEC [%02u] %s\r\n", i, batch[i]); + gs_command_run(batch[i], NULL); + } + free(batch[i]); + } + + return GS_OK; +} + +#if defined(__linux__) +static int cmd_exit(gs_command_context_t * context) +{ + gs_console_exit(); + exit(EXIT_SUCCESS); + return GS_OK; +} +#endif + +static int cmd_clock(gs_command_context_t * ctx) +{ + if (ctx->argc > 1) { + gs_timestamp_t ts; + gs_error_t error = gs_clock_from_string(ctx->argv[1], &ts); + if (error) { + return GS_ERROR_ARG; + } + error = gs_clock_set_time(&ts); + if (error) { + fprintf(ctx->out, "Failed to set time, error=%s\r\n", gs_error_string(error)); + return GS_ERROR_DATA; + } + } + + timestamp_t clock; + gs_clock_get_monotonic(&clock); + fprintf(ctx->out, "monotonic: %10"PRIu32".%09"PRIu32" sec\r\n", clock.tv_sec, clock.tv_nsec); + gs_command_set_output_printf(ctx, "", "monotonic", "%10"PRIu32".%09"PRIu32"", clock.tv_sec, clock.tv_nsec); + + char tbuf[25]; + gs_clock_get_time(&clock); + gs_clock_to_iso8601_string(&clock, tbuf, sizeof(tbuf)); + fprintf(ctx->out, "realtime: %10"PRIu32".%09"PRIu32" sec -> %s\r\n", clock.tv_sec, clock.tv_nsec, tbuf); + gs_command_set_output_printf(ctx, "", "realtime", "%10"PRIu32".%09"PRIu32"", clock.tv_sec, clock.tv_nsec); + + return GS_OK; +} + +static int cmd_console_mode(gs_command_context_t * ctx) +{ + return gs_console_change_mode(ctx->argv[1]); +} + +static const gs_command_t GS_COMMAND_ROOT cmd_default[] = { + { + .name = "help", + .help = "Show help", + .usage = "[command[ subcommand[ arg ...]]]", + .handler = cmd_help, + .optional_args = 100, + },{ + .name = "sleep", + .help = "Sleep X ms", + .usage = "", + .handler = cmd_sleep, + .mandatory_args = 1, + },{ + .name = "watch", + .help = "Run commands at intervals (abort with key)", + .usage = " [arg ...]", + .handler = cmd_watch_nocheck, + .mandatory_args = 2, + .optional_args = 100, + },{ + .name = "watch_check", + .help = "Like 'watch', but abort if command fails", + .usage = " ", + .handler = cmd_watch_check, + .mandatory_args = 2, + .optional_args = 100, + },{ + .name = "batch", + .help = "Run multiple commands", + .handler = cmd_batch, + .mode = GS_COMMAND_FLAG_HIDDEN, + },{ + .name = "clock", + .help = "Get/set system clock", + .usage = "[ | ]", + .handler = cmd_clock, + .optional_args = 1, + },{ + .name = "console_mode", + .help = "Console mode(s): cci", + .usage = "", + .handler = cmd_console_mode, + .mode = GS_COMMAND_FLAG_HIDDEN, + .mandatory_args = 1, + }, +#if defined(__linux__) + { + .name = "exit", + .help = "Exit program", + .handler = cmd_exit, + }, +#endif +}; + +gs_error_t gs_command_register_default_commands(void) +{ + return GS_COMMAND_REGISTER(cmd_default); +} diff --git a/gomspace/libutil/src/gosh/getopt.c b/gomspace/libutil/src/gosh/getopt.c new file mode 100644 index 00000000..81055bef --- /dev/null +++ b/gomspace/libutil/src/gosh/getopt.c @@ -0,0 +1,55 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +#include +#include +#include + +int gs_command_getopt(gs_command_context_t *ctx, const char *opts) +{ + int c; + char *cp; + + if (ctx->optsp == 1) { + if (ctx->optind >= ctx->argc || + ctx->argv[ctx->optind][0] != '-' || + ctx->argv[ctx->optind][1] == '\0') { + return EOF; + } else if (!strcmp(ctx->argv[ctx->optind], "--")) { + ctx->optind++; + return EOF; + } + } + + ctx->optopt = c = ctx->argv[ctx->optind][ctx->optsp]; + if (c == ':' || (cp = strchr(opts, c)) == NULL) { + printf("illegal option -- %c\r\n", c); + if (ctx->argv[ctx->optind][++ctx->optsp] == '\0') { + ctx->optind++; + ctx->optsp = 1; + } + return '?'; + } + + if (*++cp == ':') { + if (ctx->argv[ctx->optind][ctx->optsp+1] != '\0') { + ctx->optarg = &ctx->argv[ctx->optind++][ctx->optsp+1]; + } else if(++ctx->optind >= ctx->argc) { + printf("option requires an argument -- %c\r\n", c); + ctx->optsp = 1; + return '?'; + } else { + ctx->optarg = ctx->argv[ctx->optind++]; + } + ctx->optsp = 1; + } else { + if (ctx->argv[ctx->optind][++ctx->optsp] == '\0') { + ctx->optsp = 1; + ctx->optind++; + } + ctx->optarg = NULL; + } + + return c; +} diff --git a/gomspace/libutil/src/hexdump.c b/gomspace/libutil/src/hexdump.c new file mode 100644 index 00000000..7330ef91 --- /dev/null +++ b/gomspace/libutil/src/hexdump.c @@ -0,0 +1,92 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include + +static void print_disp_addr02(FILE * out, uintptr_t disp_addr) +{ + fprintf(out, "0x%02"PRIx32" : ", (uint32_t) disp_addr); +} +static void print_disp_addr04(FILE * out, uintptr_t disp_addr) +{ + fprintf(out, "0x%04"PRIx32" : ", (uint32_t) disp_addr); +} +static void print_disp_addrxx(FILE * out, uintptr_t disp_addr) +{ +#if defined(PRIx64) + fprintf(out, "0x%08"PRIx64" : ", (uint64_t) disp_addr); +#else + fprintf(out, "0x%08"PRIx32" : ", (uint32_t) disp_addr); +#endif +} + +void gs_hexdump_to_stream(const void * in_src, size_t len, const void * in_disp_addr, FILE* out) +{ + volatile const uint8_t * src = in_src; + uintptr_t disp_addr = GS_TYPES_PTR2UINT(in_disp_addr); + const uintptr_t end_disp_addr = disp_addr + len; + + // work-rounds for not printing NIL (if address 0), align addresses, not supporting %zx, %*x or %08p on all platforms + void (*print_addr)(FILE * out, uintptr_t disp_addr); + if (end_disp_addr <= 0xff) { + print_addr = print_disp_addr02; + } else if (end_disp_addr <= 0xffff) { + print_addr = print_disp_addr04; + } else { + print_addr = print_disp_addrxx; + } + + print_addr(out, disp_addr); + + size_t i = 0; + size_t j = 0; + size_t k = 0; + char text[17]; + for(; i < len; ++i) { + const uint8_t ch = *src++; + ++disp_addr; + + // hex + fprintf(out, "%02x ", ch); + ++j; + if (j == 8) { + fprintf(out, " "); + } + + // printable + if ((ch < 32) || (ch > 126)) { + text[k] = '.'; + } else { + text[k] = (char) ch; + } + ++k; + text[k] = 0; + + // newline? + if(j >= 16) { + fprintf(out, "|%-16.16s|\r\n", text); + j = 0; + k = 0; + text[k] = 0; + + if (i < (len - 1)) { + print_addr(out, disp_addr); + } + } + } + if ((i == 0) || (i % 16)) { + if (j) { + // something was printed - show textual + for (; j < 16; j++) { + if (j == 7) { + fprintf(out, " "); + } + fprintf(out, " "); + } + fprintf(out, "|%-16.16s|", text); + } + fprintf(out, "\r\n"); + } +} diff --git a/gomspace/libutil/src/linux/argp.c b/gomspace/libutil/src/linux/argp.c new file mode 100644 index 00000000..e9156595 --- /dev/null +++ b/gomspace/libutil/src/linux/argp.c @@ -0,0 +1,34 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +void gs_argp_parse(const struct argp * argp, + int argc, char ** argv, + unsigned int flags, int * return_arg_index, + const char * revision) +{ + if (gs_string_empty(revision) == false) { + argp_program_version = revision; + } + + int arg_index = 0; + int res = argp_parse(argp, argc, argv, 0, &arg_index, 0); + if (res) { + printf("Failed to parse argument/option (result: %d)\n", res); + exit(GS_EXITCODE_USAGE); + } + + if ((return_arg_index == NULL) && (arg_index < argc)) { + // application doesn't expect unhandled arguments + for (int i = arg_index; i < argc; ++i) { + printf("Unhandled/unknown argument: [%s]\n", argv[i]); + } + exit(GS_EXITCODE_USAGE); + } + + if (return_arg_index) { + *return_arg_index = arg_index; + } +} diff --git a/gomspace/libutil/src/linux/clock.c b/gomspace/libutil/src/linux/clock.c new file mode 100644 index 00000000..191aac25 --- /dev/null +++ b/gomspace/libutil/src/linux/clock.c @@ -0,0 +1,68 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include + +void gs_clock_get_time(gs_timestamp_t * time) +{ + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + + time->tv_sec = (uint32_t) now.tv_sec; + time->tv_nsec = (uint32_t) now.tv_nsec; +} + +gs_error_t gs_clock_set_time(const gs_timestamp_t * time) +{ + struct timespec now; + now.tv_sec = time->tv_sec; + now.tv_nsec = time->tv_nsec; + + int res = clock_settime(CLOCK_REALTIME, &now); + if (res != 0) { + return gs_error(errno); + } + + gs_error_t error = GS_OK; + if (gs_rtc_supported() == GS_OK) { + error = gs_rtc_set_time(time); + } + + return error; +} + +void gs_clock_get_monotonic(gs_timestamp_t * time) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + time->tv_sec = (uint32_t) now.tv_sec; + time->tv_nsec = (uint32_t) now.tv_nsec; +} + +uint64_t gs_clock_get_nsec(void) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + return (((uint64_t)now.tv_sec) * GS_TIMESTAMP_NSEC_PER_SEC) + ((uint64_t)now.tv_nsec); +} + +/** + Required by libcsp. + Proto-typed in ./libcsp/include/csp/arch/csp_clock.h, but with different argumet! + + __attribute__((weak)) extern void clock_get_time(csp_timestamp_t * time); + __attribute__((weak)) extern void clock_set_time(csp_timestamp_t * time); +*/ +void clock_get_time(gs_timestamp_t * time) +{ + gs_clock_get_time(time); +} + +void clock_set_time(const gs_timestamp_t * time) +{ + gs_clock_set_time(time); +} diff --git a/gomspace/libutil/src/linux/command_line.c b/gomspace/libutil/src/linux/command_line.c new file mode 100644 index 00000000..e95cd602 --- /dev/null +++ b/gomspace/libutil/src/linux/command_line.c @@ -0,0 +1,76 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +#define KEY_IGNORE_CTRLC 200 + +static bool ignore_ctrlc; + +static int parser(int key, char *arg, struct argp_state *state) +{ + switch (key) { + case KEY_IGNORE_CTRLC: + ignore_ctrlc = true; + gs_signal_ignore(SIGINT); + break; + + case 'h': + argp_help(state->root_argp, state->out_stream, ARGP_HELP_STD_HELP, state->name); + exit(0); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static const struct argp_option options[] = { + { + .name = "ignore-ctrlc", + .key = KEY_IGNORE_CTRLC, + .doc = "Ignore/disable CTRL-C" + }, + {0} +}; + +static const struct argp argp_console = {.options = options, .parser = parser}; + +const struct argp_child gs_console_command_line_ignore_ctrlc_argp = {.argp = &argp_console}; + +bool gs_command_line_ignore_ctrlc(void) +{ + return ignore_ctrlc; +} + +static const struct argp_option help_options[] = { + { + .name = "help", + .key = 'h', + .doc = "Give this help list" + }, + {0} +}; + +static const struct argp gs_argp_help = {.options = help_options, .parser = parser}; + +const struct argp_child gs_help_command_line_argp = {.argp = &gs_argp_help}; + +const char * gs_command_line_program_name(const char * argv) +{ + if (gs_string_empty(argv) == false) { + const char * name = strrchr(argv, '/'); + if (name) { + // skip slash + ++name; + if (gs_string_empty(name) == false) { + return name; + } + } else { + return argv; + } + } + return ""; +} diff --git a/gomspace/libutil/src/linux/cwd.c b/gomspace/libutil/src/linux/cwd.c new file mode 100644 index 00000000..1cfe373d --- /dev/null +++ b/gomspace/libutil/src/linux/cwd.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +gs_error_t gs_getcwd(char * buf, size_t bufsize) +{ + if (buf && bufsize) { + char * wd = getcwd(buf, bufsize); + if (wd) { + return GS_OK; + } + switch(errno) { + case ENAMETOOLONG: + case ERANGE: + return GS_ERROR_RANGE; + + case EACCES: + case ENOENT: + return GS_ERROR_NOT_FOUND; + + default: + break; + } + } + return GS_ERROR_ARG; +} diff --git a/gomspace/libutil/src/linux/delay.c b/gomspace/libutil/src/linux/delay.c new file mode 100644 index 00000000..f0a39081 --- /dev/null +++ b/gomspace/libutil/src/linux/delay.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +void gs_delay_us(uint32_t time_us) +{ + uint64_t ns = time_us; + ns *= 1000LL; + gs_time_sleep_ns(ns); +} + +uint16_t gs_delay_ts_get(void) +{ + return 0; +} + +void gs_delay_from_ts(uint16_t ts, uint16_t delay) +{ + +} diff --git a/gomspace/libutil/src/linux/drivers/can/can.c b/gomspace/libutil/src/linux/drivers/can/can.c new file mode 100644 index 00000000..40a6b8c8 --- /dev/null +++ b/gomspace/libutil/src/linux/drivers/can/can.c @@ -0,0 +1,308 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + // true if handle is in use + bool inuse; + + // opened socket + int can_socket; + + // receiver thread + gs_thread_t rxthread; + + // received data callback + gs_can_rxdata_callback_t rx_callback; + void * user_data; + +} gs_can_handle_t; + +#define MAX_CAN_HANDLES 10 +static gs_can_handle_t can_handles[MAX_CAN_HANDLES]; + +static int gs_can_alloc_handle(void) +{ + int handle_id; + for (handle_id = 0; (handle_id < MAX_CAN_HANDLES) && (can_handles[handle_id].inuse == true); ++handle_id); + + if (handle_id < MAX_CAN_HANDLES) { + gs_can_handle_t * handle = &can_handles[handle_id]; + memset(handle, 0, sizeof(*handle)); + handle->inuse = true; + handle->can_socket = -1; + } + + return handle_id; +} + +static inline gs_can_handle_t * gs_can_handle(uint8_t hdl) +{ + if (hdl >= MAX_CAN_HANDLES) { + return NULL; + } + if (can_handles[hdl].inuse == false) { + return NULL; + } + return &can_handles[hdl]; +} + +static void * gs_can_rx_thread(void * parameter) +{ + int hdl = (int) GS_TYPES_PTR2INT(parameter); + + log_debug("%s: running, hdl: %d", __FUNCTION__, hdl); + + gs_can_handle_t * handle = gs_can_handle(hdl); + if (handle == NULL) { + log_error("%s: CAN handle: %d is invalid or not opened", __FUNCTION__, hdl); + gs_thread_exit(NULL); + } + + while (1) { + /* Read CAN frame */ + struct can_frame frame; + ssize_t nbytes = read(handle->can_socket, &frame, sizeof(frame)); + if (nbytes < 0) { + log_error("%s: read() on socket failed, error: %s", __FUNCTION__, strerror(errno)); + continue; + } + + if (nbytes != sizeof(frame)) { + log_warning("%s: read() returned incomplete CAN frame of %d bytes - ignoring frame", __FUNCTION__, (int) nbytes); + continue; + } + + /* Frame type */ + if (frame.can_id & (CAN_ERR_FLAG | CAN_RTR_FLAG)) { + /* Drop error and remote frames */ + log_warning("%s: discarding ERR/RTR frame, can_id: 0x%x", __FUNCTION__, frame.can_id); + continue; + } + + const bool extId = (frame.can_id & CAN_EFF_FLAG) ? true : false; + if (extId) { + frame.can_id &= CAN_EFF_MASK; + } else { + frame.can_id &= CAN_SFF_MASK; + } + handle->rx_callback(hdl, frame.can_id, extId, frame.data, frame.can_dlc, gs_time_rel_ms(), handle->user_data, false); + } + + /* We should never reach this point */ + return NULL; +} + +static gs_error_t gs_can_send(uint8_t hdl, uint32_t canMsgId, bool extended, const void * data, size_t data_size, int timeout_ms) +{ + gs_can_handle_t * handle = gs_can_handle(hdl); + if (handle == NULL) { + return GS_ERROR_HANDLE; + } + + if ((data == NULL) || (data_size > 8)) { + log_error("%s: invalid data: %p, data_size: %u", __FUNCTION__, (void*) data, (unsigned int) data_size); + return GS_ERROR_ARG; + } + + struct can_frame frame; + memset(&frame, 0, sizeof(frame)); + frame.can_id = canMsgId; + if (extended) { + frame.can_id |= CAN_EFF_FLAG; + } + + memcpy(frame.data, data, data_size); + + frame.can_dlc = (uint8_t) data_size; + + const int DELAY_MS = 10; + while (write(handle->can_socket, &frame, sizeof(frame)) != sizeof(frame)) { + if ((timeout_ms > 0) && (errno == ENOBUFS)) { + // Wait a bit and try again + gs_thread_sleep_ms(DELAY_MS); + timeout_ms -= DELAY_MS; + } else { + gs_error_t gserror = gs_error(errno); + log_error("%s: write() failed, error: %s", __FUNCTION__, gs_error_string(gserror)); + return gserror; + } + } + + return GS_OK; +} + +gs_error_t gs_can_send_standard(uint8_t hdl, uint32_t canMsgId, const void * data, size_t data_size, int timeout_ms) +{ + return gs_can_send(hdl, canMsgId, false, data, data_size, timeout_ms); +} + +gs_error_t gs_can_send_extended(uint8_t hdl, uint32_t canExtMsgId, const void * data, size_t data_size, int timeout_ms) +{ + return gs_can_send(hdl, canExtMsgId, true, data, data_size, timeout_ms); +} + +static void gs_can_close(gs_can_handle_t * handle) +{ + if (handle->can_socket >= 0) { + close(handle->can_socket); + } + + // free instance - must be the last thing done, no lock needed + handle->inuse = false; +} + +gs_error_t gs_can_open(const char * ifname, int * return_handle) +{ + if ((ifname == NULL) || (ifname[0] == 0) || (return_handle == NULL)) { + log_error("%s: invalid CAN interface name", __FUNCTION__); + return GS_ERROR_ARG; + } + + int handle_id = gs_can_alloc_handle(); + if (handle_id >= MAX_CAN_HANDLES) { + log_error("%s: no free handles", __FUNCTION__); + return GS_ERROR_FULL; + } + gs_can_handle_t * handle = &can_handles[handle_id]; + + /* Create socket */ + if ((handle->can_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + gs_error_t gserror = gs_error(errno); + log_error("%s: socket() failed, error: %s", __FUNCTION__, gs_error_string(gserror)); + gs_can_close(handle); + return gserror; + } + + /* Locate interface */ + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + if (ioctl(handle->can_socket, SIOCGIFINDEX, &ifr) < 0) { + gs_error_t gserror = gs_error(errno); + log_error("%s: ioctl(ifname: [%s]) failed, error: %s", __FUNCTION__, ifr.ifr_name, gs_error_string(gserror)); + gs_can_close(handle); + return gserror; + } + + /* Bind the socket to CAN interface */ + struct sockaddr_can addr; + memset(&addr, 0, sizeof(addr)); + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + if (bind(handle->can_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + gs_error_t gserror = gs_error(errno); + log_error("%s: bind() failed, error: %s", __FUNCTION__, gs_error_string(gserror)); + gs_can_close(handle); + return gserror; + } + + *return_handle = handle_id; + + return GS_OK; +} + +static gs_error_t gs_can_set_filter_mask(uint8_t hdl, bool extended, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data) +{ + gs_can_handle_t * handle = gs_can_handle(hdl); + if (handle == NULL) { + return GS_ERROR_HANDLE; + } + + if (extended) { + if ((canMsgId > CAN_EFF_MASK) || (mask > CAN_EFF_MASK)) { + return GS_ERROR_ARG; + } + } else { + if ((canMsgId > CAN_SFF_MASK) || (mask > CAN_SFF_MASK)) { + return GS_ERROR_ARG; + } + } + + handle->rx_callback = rx_callback; + handle->user_data = user_data; + + struct can_filter filter; + filter.can_id = canMsgId; + filter.can_mask = mask; + if (extended == false) { + filter.can_mask |= (CAN_EFF_MASK & ~CAN_SFF_MASK); + } + + if (setsockopt(handle->can_socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)) < 0) { + gs_error_t gserror = gs_error(errno); + log_error("%s: setsockopt(id: 0x%x, mask: 0x%x) failed, error: %s", __FUNCTION__, canMsgId, mask, gs_error_string(gserror)); + return gserror; + } + + return GS_OK; +} + +gs_error_t gs_can_set_standard_filter_mask(uint8_t hdl, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data) +{ + return gs_can_set_filter_mask(hdl, false, canMsgId, mask, rx_callback, user_data); +} + +gs_error_t gs_can_set_extended_filter_mask(uint8_t hdl, uint32_t canExtMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data) +{ + return gs_can_set_filter_mask(hdl, true, canExtMsgId, mask, rx_callback, user_data); +} + +gs_error_t gs_can_start(uint8_t hdl) +{ + gs_can_handle_t * handle = gs_can_handle(hdl); + if (handle == NULL) { + return GS_ERROR_HANDLE; + } + + if (handle->rxthread) { + return GS_OK; + } + + /* Create receiver thread */ + gs_error_t gserror = gs_thread_create("rxcan", gs_can_rx_thread, GS_TYPES_INT2PTR(hdl), 0, GS_THREAD_PRIORITY_HIGH, 0, &handle->rxthread); + if (gserror) { + log_error("s: gs_thread_create() failed, error: %s", gs_error_string(gserror)); + return gserror; + } + + return GS_OK; +} + +gs_error_t gs_can_stop(uint8_t hdl) +{ + gs_can_handle_t * handle = gs_can_handle(hdl); + if (handle == NULL) { + return GS_ERROR_HANDLE; + } + + return GS_ERROR_NOT_IMPLEMENTED; +} + +gs_error_t gs_can_error_state(uint8_t hdl, bool * restart_required) +{ + gs_can_handle_t * handle = gs_can_handle(hdl); + if (handle == NULL) { + return GS_ERROR_HANDLE; + } + + if (restart_required) { + *restart_required = false; + } + + // missing error state check on CAN layer + + return GS_OK; +} diff --git a/gomspace/libutil/src/linux/drivers/gpio/gpio.c b/gomspace/libutil/src/linux/drivers/gpio/gpio.c new file mode 100644 index 00000000..484c6a58 --- /dev/null +++ b/gomspace/libutil/src/linux/drivers/gpio/gpio.c @@ -0,0 +1,102 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +#define MAX_DRIVERS 20 + +typedef struct { + gs_gpio_driver_entry_t entry; + bool in_use; +} gs_gpio_driver_handle_t; + +static gs_gpio_driver_handle_t gpio_drivers[MAX_DRIVERS]; +static uint8_t max_index_in_use = 0; + + +static inline gs_gpio_driver_entry_t * gs_find_driver_entry(gs_gpio_t * gpio) +{ + gs_gpio_driver_handle_t * handle; + for (int i = max_index_in_use; i >= 0; i--) { + handle = &gpio_drivers[i]; + if (((gpio->pin == handle->entry.pin) || (handle->entry.pin == GS_GPIO_ALL_PINS)) && + ((gpio->port == handle->entry.port) || (handle->entry.port == GS_GPIO_ALL_PORTS)) && + (handle->in_use == true)) { + return &handle->entry; + } + } + return NULL; +} + +gs_error_t gs_gpio_get(gs_gpio_t gpio, bool *value) +{ + gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio); + if (driver_entry) { + if (driver_entry->driver->get_handler) { + return driver_entry->driver->get_handler(gpio, value, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +bool gs_gpio_get_nc(gs_gpio_t gpio) +{ + gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio); + if (driver_entry) { + if (driver_entry->driver->get_nc_handler) { + return driver_entry->driver->get_nc_handler(gpio, driver_entry->driver_data); + } + } + return false; +} + +gs_error_t gs_gpio_set(gs_gpio_t gpio, bool value) +{ + gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio); + if (driver_entry) { + if (driver_entry->driver->set_handler) { + return driver_entry->driver->set_handler(gpio, value, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +void gs_gpio_set_nc(gs_gpio_t gpio, bool value) +{ + gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio); + if (driver_entry) { + if (driver_entry->driver->set_nc_handler) { + driver_entry->driver->set_nc_handler(gpio, value, driver_entry->driver_data); + } + } +} + +gs_error_t gs_gpio_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf) +{ + gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio); + if (driver_entry) { + if (driver_entry->driver->init_as_interrupt_handler) { + return driver_entry->driver->init_as_interrupt_handler(gpio, conf, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_gpio_register_driver(const gs_gpio_driver_entry_t * driver_entry) +{ + GS_CHECK_ARG(driver_entry != NULL); + GS_CHECK_ARG(driver_entry->driver != NULL); + + gs_gpio_driver_handle_t * handle; + for (uint8_t i = 0; i < MAX_DRIVERS; i++) { + handle = &gpio_drivers[i]; + if (handle->in_use == false) { + handle->entry = *driver_entry; + handle->in_use = true; + max_index_in_use = i; + return GS_OK; + } + } + /* Not enough space in buffer */ + return GS_ERROR_FULL; +} diff --git a/gomspace/libutil/src/linux/drivers/gpio/gpio_sysfs.c b/gomspace/libutil/src/linux/drivers/gpio/gpio_sysfs.c new file mode 100644 index 00000000..57efd042 --- /dev/null +++ b/gomspace/libutil/src/linux/drivers/gpio/gpio_sysfs.c @@ -0,0 +1,145 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + @brief GPIO Implementation for Linux of the GPIO API in libutil. + + The GPIO driver provides a simple interface toward driving HW GPIO's. +*/ + +#include + +#include +#include +#include +#include + +#include + +#include + +gs_error_t gs_gpio_sysfs_initialize(gs_gpio_t gpio, bool output,bool init_value, bool active_low) +{ + char gpio_pin_str[6]; + snprintf(gpio_pin_str, sizeof(gpio_pin_str), "%d", gpio.pin); + + /* Try to unexport first */ + gs_sysfs_write_file("/sys/class/gpio/unexport", gpio_pin_str); + + if (gs_sysfs_write_file("/sys/class/gpio/export", gpio_pin_str) != GS_OK) + { + log_warning("failed to export GPIO %s: %s", gpio_pin_str, strerror(errno)); + return GS_ERROR_NOT_SUPPORTED; + } + + char gpio_sys_fname[128]; + snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/active_low", gpio.pin); + const char * active_low_str = active_low ? "1" : "0"; + + if (gs_sysfs_write_file(gpio_sys_fname, active_low_str) != GS_OK) + { + log_warning("failed to set GPIO %d active_low: %s", gpio.pin, strerror(errno)); + return GS_ERROR_NOT_SUPPORTED; + } + + snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/direction", gpio.pin); + + /* Glitch-free output set (high/low makes pin an output and sets value to 1/0 respectively)*/ + const char * dir = output ? (init_value ? "high" : "low") : "in"; + + if (gs_sysfs_write_file(gpio_sys_fname, dir) != GS_OK) + { + log_warning("failed to set GPIO %d direction: %s", gpio.pin, strerror(errno)); + return GS_ERROR_NOT_SUPPORTED; + } + + return GS_OK; +} + +gs_error_t gs_gpio_sysfs_get(gs_gpio_t gpio, bool *value, void * driver_data) +{ + char gpio_sys_fname[128]; + snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin); + + if (access(gpio_sys_fname, R_OK) != 0) + { + log_error("GPIO %d not initialized - Can't read the input.", gpio.pin); + return GS_ERROR_ACCESS; + } + + char value_str[10]; + gs_error_t ret = gs_sysfs_read_file(gpio_sys_fname, value_str, sizeof(value_str)); + if (ret == GS_OK) + { + if (strcmp(value_str, "1") == 0) + *value = true; + else + *value = false; + } + + return ret; +} + +bool gs_gpio_sysfs_get_nc(gs_gpio_t gpio, void * driver_data) +{ + char gpio_sys_fname[128]; + snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin); + + if (access(gpio_sys_fname, R_OK) != 0) + { + log_error("GPIO %d not initialized - Can't read the input.", gpio.pin); + return 0; + } + + char value_str[10]; + gs_sysfs_read_file(gpio_sys_fname, value_str, sizeof(value_str)); + + if (strncmp(value_str, "1", 10) == 0) { + return true; + } else { + return false; + } +} + +gs_error_t gs_gpio_sysfs_set(gs_gpio_t gpio, bool value, void * driver_data) +{ + const char *value_str = value ? "1" : "0"; + + char gpio_sys_fname[128]; + snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin); + if (access(gpio_sys_fname, W_OK) == 0) + { + return gs_sysfs_write_file(gpio_sys_fname, value_str); + } + + log_error("GPIO %d not initialized - Can't set the output.", gpio.pin); + return GS_ERROR_ACCESS; +} + +void gs_gpio_sysfs_set_nc(gs_gpio_t gpio, bool value, void * driver_data) +{ + const char *value_str = value ? "1" : "0"; + + char gpio_sys_fname[128]; + snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin); + if (access(gpio_sys_fname, W_OK) == 0) + { + gs_sysfs_write_file(gpio_sys_fname, value_str); + return; + } + log_error("GPIO %d not initialized - Can't set the output.", gpio.pin); +} + +gs_error_t gs_gpio_sysfs_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data) +{ + return GS_ERROR_NOT_IMPLEMENTED; +} + +const gs_gpio_driver_t gs_gpio_sysfs_driver = { + .get_handler = gs_gpio_sysfs_get, + .get_nc_handler = gs_gpio_sysfs_get_nc, + .set_handler = gs_gpio_sysfs_set, + .set_nc_handler = gs_gpio_sysfs_set_nc, + .init_as_interrupt_handler = gs_gpio_sysfs_init_as_interrupt, +}; + diff --git a/gomspace/libutil/src/linux/drivers/gpio/gpio_virtual.c b/gomspace/libutil/src/linux/drivers/gpio/gpio_virtual.c new file mode 100644 index 00000000..ce20c885 --- /dev/null +++ b/gomspace/libutil/src/linux/drivers/gpio/gpio_virtual.c @@ -0,0 +1,171 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +#define MAX_VPINS 500 + +#define FALLING_EDGE_FLAG 0x1 +#define RISING_EDGE_FLAG 0x2 + +typedef struct { + gs_gpio_t gpio; + bool output; + bool value; + bool in_use; + gs_gpio_isr_t isr; + uint8_t edge_flags; + uint32_t transistions; +} gs_gpio_virtual_t; + +static gs_gpio_virtual_t vpins[MAX_VPINS]; + +gs_error_t gs_gpio_virtual_initialize(gs_gpio_t gpio, bool output, bool value) +{ + gs_gpio_virtual_t * pin; + for (uint16_t i = 0; i < MAX_VPINS; i++) { + pin = &vpins[i]; + if ((!pin->in_use) || ((pin->gpio.pin == gpio.pin) && (pin->gpio.port == gpio.port))) { + pin->gpio = gpio; + pin->output = output; + pin->value = value; + pin->in_use = true; + return GS_OK; + } + } + return GS_ERROR_FULL; +} + +static gs_gpio_virtual_t * find_vpin(gs_gpio_t * gpio) +{ + gs_gpio_virtual_t * pin; + for (uint16_t i = 0; i < MAX_VPINS; i++) { + pin = &vpins[i]; + if (pin->gpio.pin == gpio->pin) { + if (pin->gpio.port == gpio->port) { + if (pin->in_use) { + return pin; + } + } + } + } + return NULL; +} + +gs_error_t gs_gpio_virtual_get(gs_gpio_t gpio, bool *value, void * driver_data) +{ + gs_gpio_virtual_t * pin = find_vpin(&gpio); + if (pin) { + *value = pin->value; + return GS_OK; + } + return GS_ERROR_NOT_FOUND; +} + +bool gs_gpio_virtual_get_nc(gs_gpio_t gpio, void * driver_data) +{ + gs_gpio_virtual_t * pin = find_vpin(&gpio); + if (pin) { + return pin->value; + } + return false; +} + +gs_error_t gs_gpio_virtual_set(gs_gpio_t gpio, bool value, void * driver_data) +{ + gs_gpio_virtual_t * pin = find_vpin(&gpio); + if (pin) { + if (pin->output) { + if (pin->value != value) { + pin->value = value; + pin->transistions++; + } + return GS_OK; + } + return GS_ERROR_PERM; + } + return GS_ERROR_NOT_FOUND; +} + +void gs_gpio_virtual_set_nc(gs_gpio_t gpio, bool value, void * driver_data) +{ + gs_gpio_virtual_t * pin = find_vpin(&gpio); + if (pin) { + if (pin->output) { + if (pin->value != value) { + pin->value = value; + pin->transistions++; + } + } + } +} + +gs_error_t gs_gpio_virtual_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data) +{ + gs_gpio_virtual_t * pin; + for (uint16_t i = 0; i < MAX_VPINS; i++) { + pin = &vpins[i]; + if ((!pin->in_use) || ((pin->gpio.pin == gpio.pin) && (pin->gpio.port == gpio.port))) { + pin->gpio = gpio; + pin->output = false; + pin->value = 0; + pin->in_use = true; + pin->isr = conf->isr; + if (conf->falling_edge) { + pin->edge_flags |= FALLING_EDGE_FLAG; + } + if (conf->rising_edge) { + pin->edge_flags |= RISING_EDGE_FLAG; + } + return GS_OK; + } + } + return GS_ERROR_FULL; +} + +gs_error_t gs_gpio_virtual_force_set(gs_gpio_t gpio, bool value) +{ + gs_gpio_virtual_t * pin = find_vpin(&gpio); + if (pin) { + bool old_value = pin->value; + if (old_value != value) { + pin->value = value; + pin->transistions++; + if (pin->isr) { + if ((old_value == false) && (pin->edge_flags & RISING_EDGE_FLAG)) { + pin->isr(NULL); + } else if ((old_value == true) && (pin->edge_flags & FALLING_EDGE_FLAG)) { + pin->isr(NULL); + } + } + } + return GS_OK; + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_gpio_virtual_get_transistions(gs_gpio_t gpio, uint32_t * transitions) +{ + gs_gpio_virtual_t * pin = find_vpin(&gpio); + if (pin) { + *transitions = pin->transistions; + pin->transistions = 0; + return GS_OK; + } + return GS_ERROR_NOT_FOUND; +} + +const gs_gpio_driver_t gs_gpio_virtual_driver = { + .get_handler = gs_gpio_virtual_get, + .get_nc_handler = gs_gpio_virtual_get_nc, + .set_handler = gs_gpio_virtual_set, + .set_nc_handler = gs_gpio_virtual_set_nc, + .init_as_interrupt_handler = gs_gpio_virtual_init_as_interrupt, +}; + + +const gs_gpio_driver_entry_t gs_gpio_virtual_driver_entry_all = { + .port = GS_GPIO_ALL_PORTS, + .pin = GS_GPIO_ALL_PINS, + .driver = &gs_gpio_virtual_driver, + .driver_data = NULL, +}; diff --git a/gomspace/libutil/src/linux/drivers/i2c/i2c.c b/gomspace/libutil/src/linux/drivers/i2c/i2c.c new file mode 100644 index 00000000..679ae3f7 --- /dev/null +++ b/gomspace/libutil/src/linux/drivers/i2c/i2c.c @@ -0,0 +1,144 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +#define MAX_DRIVERS 20 +#define HIGHEST_I2C_ADDR 127 + +typedef struct { + gs_i2c_master_driver_entry_t entry; + bool in_use; +} gs_i2c_master_driver_handle_t; + +typedef struct { + gs_i2c_slave_driver_entry_t entry; + bool in_use; +} gs_i2c_slave_driver_handle_t; + +static gs_i2c_master_driver_handle_t master_drivers[MAX_DRIVERS]; +static gs_i2c_slave_driver_handle_t slave_drivers[MAX_DRIVERS]; + +static uint8_t max_index_master_in_use = 0; +static uint8_t max_index_slave_in_use = 0; + +gs_error_t gs_i2c_master_transaction(uint8_t device, uint8_t addr, const void * tx, + size_t txlen, + void * rx, + size_t rxlen, + int timeout_ms) +{ + GS_CHECK_RANGE(addr <= HIGHEST_I2C_ADDR); + gs_i2c_master_driver_handle_t * handle; + for (int i = max_index_master_in_use; i >= 0; i--) { + handle = &master_drivers[i]; + if (((device == handle->entry.device) || (handle->entry.device == GS_I2C_ALL_DEVICES)) + && ((addr == handle->entry.addr) || (handle->entry.addr == GS_I2C_ALL_ADDR)) + && (handle->in_use == true)) { + if (handle->entry.driver->master_transaction_handler) { + return handle->entry.driver->master_transaction_handler(device, addr, tx, txlen, rx, rxlen, timeout_ms, handle->entry.driver_data); + } + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_i2c_master_register_driver(const gs_i2c_master_driver_entry_t * driver_entry) +{ + GS_CHECK_ARG(driver_entry != NULL); + GS_CHECK_ARG(driver_entry->driver != NULL); + + GS_CHECK_RANGE((driver_entry->addr == GS_I2C_ALL_ADDR) || (driver_entry->addr <= HIGHEST_I2C_ADDR)); + + gs_i2c_master_driver_handle_t * handle; + for (uint8_t i = 0; i < MAX_DRIVERS; i++) { + handle = &master_drivers[i]; + if (handle->in_use == false) { + handle->entry = *driver_entry; + handle->in_use = true; + max_index_master_in_use = i; + return GS_OK; + } + } + + /* Not enough space in buffer */ + return GS_ERROR_FULL; +} + +static inline gs_i2c_slave_driver_entry_t * gs_slave_find_driver_entry(uint8_t device) +{ + gs_i2c_slave_driver_handle_t * handle; + for (int i = max_index_slave_in_use; i >= 0; i--) { + handle = &slave_drivers[i]; + if (((device == handle->entry.device) || (handle->entry.device == GS_I2C_ALL_DEVICES)) + && (handle->in_use == true)) { + return &handle->entry; + } + } + return NULL; +} + +gs_error_t gs_i2c_slave_start(uint8_t device) +{ + gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device); + if (driver_entry) { + if (driver_entry->driver->start_handler) { + return driver_entry->driver->start_handler(device, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_i2c_slave_set_rx(uint8_t device, gs_i2c_slave_receive_t rx) +{ + gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device); + if (driver_entry) { + if (driver_entry->driver->set_rx_handler) { + return driver_entry->driver->set_rx_handler(device, rx, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_i2c_slave_set_get_rx_buf(uint8_t device, gs_i2c_slave_get_rx_buf_t get_rx_buf, size_t buf_length) +{ + gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device); + if (driver_entry) { + if (driver_entry->driver->set_get_rx_buf_handler) { + return driver_entry->driver->set_get_rx_buf_handler(device, get_rx_buf, buf_length, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_i2c_slave_set_response(uint8_t device, const uint8_t * tx, size_t tx_length) +{ + gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device); + if (driver_entry) { + if (driver_entry->driver->set_response_handler) { + return driver_entry->driver->set_response_handler(device, tx, tx_length, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_i2c_slave_register_driver(const gs_i2c_slave_driver_entry_t * driver_entry) +{ + GS_CHECK_ARG(driver_entry != NULL); + GS_CHECK_ARG(driver_entry->driver != NULL); + + gs_i2c_slave_driver_handle_t * handle; + for (uint8_t i = 0; i < MAX_DRIVERS; i++) { + handle = &slave_drivers[i]; + if (handle->in_use == false) { + handle->entry = *driver_entry; + handle->in_use = true; + max_index_slave_in_use = i; + return GS_OK; + } + } + + /* Not enough space in buffer */ + return GS_ERROR_FULL; +} diff --git a/gomspace/libutil/src/linux/drivers/spi/spi.c b/gomspace/libutil/src/linux/drivers/spi/spi.c new file mode 100644 index 00000000..6756482c --- /dev/null +++ b/gomspace/libutil/src/linux/drivers/spi/spi.c @@ -0,0 +1,137 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +#define MAX_DRIVERS 20 + +typedef struct { + gs_spi_master_driver_entry_t entry; + bool in_use; +} gs_spi_master_driver_handle_t; + +typedef struct { + gs_spi_slave_driver_entry_t entry; + bool in_use; +} gs_spi_slave_driver_handle_t; + +static gs_spi_master_driver_handle_t master_drivers[MAX_DRIVERS]; +static gs_spi_slave_driver_handle_t slave_drivers[MAX_DRIVERS]; + +static uint8_t max_index_master_in_use = 0; +static uint8_t max_index_slave_in_use = 0; + +static inline gs_spi_master_driver_entry_t * gs_master_find_driver_entry(uint8_t slave) +{ + gs_spi_master_driver_handle_t * handle; + for (int i = max_index_master_in_use; i >= 0; i--) { + handle = &master_drivers[i]; + if (((slave == handle->entry.slave) || (handle->entry.slave == GS_SPI_ALL_SLAVES)) && (handle->in_use == true)) { + return &handle->entry; + } + } + return NULL; +} + +gs_error_t gs_spi_master_transaction(uint8_t slave, const void * tx, void * rx, size_t size, int timeout_ms) +{ + gs_spi_master_trans_t trans = {.tx = tx, .rx = rx, .size = size}; + return gs_spi_master_transactions(slave, &trans, 1, timeout_ms); +} + +gs_error_t gs_spi_master_transactions(uint8_t slave, gs_spi_master_trans_t *trans, size_t count, int timeout_ms) +{ + gs_spi_master_driver_entry_t * driver_entry = gs_master_find_driver_entry(slave); + if (driver_entry) { + if (driver_entry->driver->master_transactions_handler) { + return driver_entry->driver->master_transactions_handler(slave, trans, count, timeout_ms, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_spi_master_register_driver(const gs_spi_master_driver_entry_t * driver_entry) +{ + GS_CHECK_ARG(driver_entry != NULL); + GS_CHECK_ARG(driver_entry->driver != NULL); + + gs_spi_master_driver_handle_t * handle; + for (uint8_t i = 0; i < MAX_DRIVERS; i++) { + handle = &master_drivers[i]; + if (handle->in_use == false) { + handle->entry = *driver_entry; + handle->in_use = true; + max_index_master_in_use = i; + return GS_OK; + } + } + + /* Not enough space in buffer */ + return GS_ERROR_FULL; +} + +static inline gs_spi_slave_driver_entry_t * gs_slave_find_driver_entry(uint8_t device) +{ + gs_spi_slave_driver_handle_t * handle; + for (int i = max_index_slave_in_use; i >= 0; i--) { + handle = &slave_drivers[i]; + if (((device == handle->entry.device) || (handle->entry.device == GS_SPI_ALL_DEVICES)) + && (handle->in_use == true)) { + return &handle->entry; + } + } + return NULL; +} + +gs_error_t gs_spi_slave_start(uint8_t device) +{ + gs_spi_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device); + if (driver_entry) { + if (driver_entry->driver->start_handler) { + return driver_entry->driver->start_handler(device, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_spi_slave_set_rx(uint8_t device, gs_spi_slave_receive_t rx) +{ + gs_spi_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device); + if (driver_entry) { + if (driver_entry->driver->set_rx_handler) { + return driver_entry->driver->set_rx_handler(device, rx, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_spi_slave_set_response(uint8_t device, size_t offset, const uint8_t * tx, size_t size) +{ + gs_spi_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device); + if (driver_entry) { + if (driver_entry->driver->set_response_handler) { + return driver_entry->driver->set_response_handler(device, offset, tx, size, driver_entry->driver_data); + } + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_spi_slave_register_driver(const gs_spi_slave_driver_entry_t * driver_entry) +{ + GS_CHECK_ARG(driver_entry != NULL); + GS_CHECK_ARG(driver_entry->driver != NULL); + + gs_spi_slave_driver_handle_t * handle; + for (uint8_t i = 0; i < MAX_DRIVERS; i++) { + handle = &slave_drivers[i]; + if (handle->in_use == false) { + handle->entry = *driver_entry; + handle->in_use = true; + max_index_slave_in_use = i; + return GS_OK; + } + } + /* Not enough space in buffer */ + return GS_ERROR_FULL; +} diff --git a/gomspace/libutil/src/linux/drivers/sys/memory.c b/gomspace/libutil/src/linux/drivers/sys/memory.c new file mode 100644 index 00000000..8def9988 --- /dev/null +++ b/gomspace/libutil/src/linux/drivers/sys/memory.c @@ -0,0 +1,30 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +gs_error_t gs_mem_get_int_ram_stat(gs_mem_ram_stat_t * ram_stat) +{ + return GS_ERROR_NOT_SUPPORTED; +} + +gs_error_t gs_mem_get_ext_ram_stat(gs_mem_ram_stat_t * ram_stat) +{ + struct sysinfo info; + int res = sysinfo(&info); + if (res != GS_OK) { + return res; + } + + ram_stat->total = info.totalram; + ram_stat->max_available = -1; + ram_stat->min_available = -1; + ram_stat->available = info.freeram; + + return GS_OK; +} + +gs_mem_ram_type_t gs_mem_get_ram_default() +{ + return GS_MEM_RAM_TYPE_EXTERNAL; +} diff --git a/gomspace/libutil/src/linux/function.c b/gomspace/libutil/src/linux/function.c new file mode 100644 index 00000000..9e0f7c0f --- /dev/null +++ b/gomspace/libutil/src/linux/function.c @@ -0,0 +1,41 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +typedef struct { + const char * short_name; + const char * long_name; + gs_function_t function; +} gs_function_register_t; + +static gs_function_register_t registry[10]; + +gs_error_t gs_function_register(const char * short_name, const char * long_name, gs_function_t function) +{ + for (unsigned int i = 0; i < GS_ARRAY_SIZE(registry); ++i) { + gs_function_register_t * cb = ®istry[i]; + if ((cb->short_name == NULL) && (cb->long_name == NULL)) { + cb->short_name = short_name; + cb->long_name = long_name; + cb->function = function; + return GS_OK; + } + } + return GS_ERROR_FULL; +} + +gs_error_t gs_function_invoke(const char * name, void * arg) +{ + for (unsigned int i = 0; i < GS_ARRAY_SIZE(registry); ++i) { + gs_function_register_t * cb = ®istry[i]; + if ((gs_string_empty(cb->short_name) == false) && (strcasecmp(cb->short_name, name) == 0)) { + return (cb->function)(arg); + } + if ((gs_string_empty(cb->long_name) == false) && (strcasecmp(cb->long_name, name) == 0)) { + return (cb->function)(arg); + } + } + + return GS_ERROR_NOT_IMPLEMENTED; +} diff --git a/gomspace/libutil/src/linux/mutex.c b/gomspace/libutil/src/linux/mutex.c new file mode 100644 index 00000000..00336510 --- /dev/null +++ b/gomspace/libutil/src/linux/mutex.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +gs_error_t gs_mutex_create(gs_mutex_t * mutex) +{ + if (mutex == NULL) { + return GS_ERROR_ARG; + } + + *mutex = malloc(sizeof(pthread_mutex_t)); + if (*mutex == NULL) { + return GS_ERROR_ALLOC; + } + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + int res = pthread_mutex_init(*mutex, &attr); + if (res < 0) { + res = gs_error(errno); + free(*mutex); + } + + return res; +} + +gs_error_t gs_mutex_destroy(gs_mutex_t mutex) +{ + int res = GS_OK; + if (mutex) { + res = pthread_mutex_destroy(mutex); + if (res < 0) { + res = gs_error(errno); + } + free(mutex); + } + return res; +} + +gs_error_t gs_mutex_lock(gs_mutex_t mutex) +{ + int res = pthread_mutex_lock(mutex); + if (res < 0) { + res = gs_error(errno); + } + return res; +} + +gs_error_t gs_mutex_unlock(gs_mutex_t mutex) +{ + int res = pthread_mutex_unlock(mutex); + if (res < 0) { + res = gs_error(errno); + } + return res; +} diff --git a/gomspace/libutil/src/linux/queue.c b/gomspace/libutil/src/linux/queue.c new file mode 100644 index 00000000..cb477f70 --- /dev/null +++ b/gomspace/libutil/src/linux/queue.c @@ -0,0 +1,217 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + * Inspired by c-pthread-queue by Matthew Dickinson + * http://code.google.com/p/c-pthread-queue/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PTHREAD_QUEUE_ARG GS_ERROR_ARG +#define PTHREAD_QUEUE_EMPTY GS_ERROR_NOT_FOUND +#define PTHREAD_QUEUE_FULL GS_ERROR_FULL +#define PTHREAD_QUEUE_TIMEOUT GS_ERROR_TIMEOUT +#define PTHREAD_QUEUE_OK GS_OK + +typedef struct gs_pthread_queue { + uint8_t * buffer; + size_t size; + size_t item_size; + size_t items; + size_t in; + size_t out; + pthread_mutex_t mutex; + pthread_cond_t cond_full; + pthread_cond_t cond_empty; +} pthread_queue_t; + +static pthread_queue_t * pthread_queue_create(size_t length, size_t item_size) +{ + pthread_queue_t * q = malloc(sizeof(pthread_queue_t)); + + if (q != NULL) { + q->buffer = malloc(length*item_size); + if (q->buffer != NULL) { + q->size = length; + q->item_size = item_size; + q->items = 0; + q->in = 0; + q->out = 0; + if (pthread_mutex_init(&(q->mutex), NULL) || pthread_cond_init(&(q->cond_full), NULL) || pthread_cond_init(&(q->cond_empty), NULL)) { + free(q->buffer); + free(q); + q = NULL; + } + } else { + free(q); + q = NULL; + } + } + + return q; +} + +static void pthread_queue_delete(pthread_queue_t * q) +{ + if (q) { + free(q->buffer); + free(q); + } +} + +static int pthread_queue_enqueue(pthread_queue_t * queue, const void * value, uint32_t timeout) +{ + /* Calculate timeout */ + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts)) { + return PTHREAD_QUEUE_ARG; + } + + uint32_t sec = timeout / 1000; + uint32_t nsec = (timeout - 1000 * sec) * 1000000; + + ts.tv_sec += sec; + + if (ts.tv_nsec + nsec > 1000000000) + ts.tv_sec++; + + ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000; + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + + while (queue->items == queue->size) { + int ret = -1; + if (timeout) { + ret = pthread_cond_timedwait(&(queue->cond_full), &(queue->mutex), &ts); + } + if (ret) { + pthread_mutex_unlock(&(queue->mutex)); + return PTHREAD_QUEUE_TIMEOUT; + } + } + + /* Coby object from input buffer */ + memcpy(queue->buffer+(queue->in * queue->item_size), value, queue->item_size); + queue->items++; + queue->in = (queue->in + 1) % queue->size; + pthread_mutex_unlock(&(queue->mutex)); + + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_empty)); + + return PTHREAD_QUEUE_OK; +} + +static int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) +{ + /* Calculate timeout */ + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts)) { + return PTHREAD_QUEUE_ARG; + } + + uint32_t sec = timeout / 1000; + uint32_t nsec = (timeout - 1000 * sec) * 1000000; + + ts.tv_sec += sec; + + if (ts.tv_nsec + nsec > 1000000000) + ts.tv_sec++; + + ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000; + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + while (queue->items == 0) { + int ret = -1; + if (timeout) { + ret = pthread_cond_timedwait(&(queue->cond_empty), &(queue->mutex), &ts); + } + if (ret) { + pthread_mutex_unlock(&(queue->mutex)); + return PTHREAD_QUEUE_TIMEOUT; + } + } + + /* Coby object to output buffer */ + memcpy(buf, queue->buffer+(queue->out * queue->item_size), queue->item_size); + queue->items--; + queue->out = (queue->out + 1) % queue->size; + pthread_mutex_unlock(&(queue->mutex)); + + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_full)); + + return PTHREAD_QUEUE_OK; +} + +static size_t pthread_queue_items(pthread_queue_t * queue) +{ + pthread_mutex_lock(&(queue->mutex)); + size_t items = queue->items; + pthread_mutex_unlock(&(queue->mutex)); + return items; +} + +gs_error_t gs_queue_create(size_t items, size_t item_size, gs_queue_t * queue) +{ + if (queue == NULL) { + return GS_ERROR_ARG; + } + pthread_queue_t * q = pthread_queue_create(items, item_size); + if (q == NULL) { + return GS_ERROR_ALLOC; + } + *queue = q; + return GS_OK; +} + +gs_error_t gs_queue_destroy(gs_queue_t queue) +{ + pthread_queue_delete(queue); + return GS_OK; +} + +gs_error_t gs_queue_enqueue(gs_queue_t queue, const void *value, int timeout_ms) +{ + return pthread_queue_enqueue(queue, value, (timeout_ms >= 0) ? timeout_ms : INT_MAX); +} + +gs_error_t gs_queue_enqueue_isr(gs_queue_t queue, const void * value, gs_context_switch_t * cswitch) +{ + (void) cswitch; + gs_error_t error = gs_queue_enqueue(queue, value, 0); + return (error != GS_ERROR_TIMEOUT) ? error : GS_ERROR_FULL; +} + +gs_error_t gs_queue_dequeue(gs_queue_t queue, int timeout_ms, void *buf) +{ + return pthread_queue_dequeue(queue, buf, (timeout_ms >= 0) ? timeout_ms : INT_MAX); +} + +gs_error_t gs_queue_dequeue_isr(gs_queue_t queue, gs_context_switch_t * cswitch, void *buf) +{ + (void) cswitch; + gs_error_t error = gs_queue_dequeue(queue, 0, buf); + return (error != GS_ERROR_TIMEOUT) ? error : GS_ERROR_NOT_FOUND; +} + +unsigned int gs_queue_size(gs_queue_t queue) +{ + if (queue) { + return pthread_queue_items(queue); + } + return 0; +} + +unsigned int gs_queue_size_isr(gs_queue_t queue) +{ + return gs_queue_size(queue); +} diff --git a/gomspace/libutil/src/linux/rtc.c b/gomspace/libutil/src/linux/rtc.c new file mode 100644 index 00000000..ff241d58 --- /dev/null +++ b/gomspace/libutil/src/linux/rtc.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +static gs_error_t gs_rtc_get(void * driver_data, gs_timestamp_t * return_time) +{ + if (return_time == NULL) { + return GS_ERROR_ARG; + } + + return_time->tv_sec = 0; + return_time->tv_nsec = 0; + + int fd = open("/dev/rtc", O_RDONLY | O_CLOEXEC); + if (fd < 0) { + return gs_error(errno); + } + + struct tm tm; + memset(&tm, 0, sizeof(tm)); + int res = ioctl(fd, RTC_RD_TIME, &tm); + close(fd); + if (res < 0) { + return gs_error(errno); + } + + time_t time = mktime(&tm); + if (time < 0) { + return GS_ERROR_DATA; + } + + return_time->tv_sec = (uint32_t) time; + + return GS_OK; +} + +static gs_error_t gs_rtc_set(void * driver_data, const gs_timestamp_t * set_time) +{ + if (set_time == NULL) { + return GS_ERROR_ARG; + } + + int fd = open("/dev/rtc", O_RDONLY | O_CLOEXEC); + if (fd < 0) { + return gs_error(errno); + } + + const time_t now = set_time->tv_sec; + struct tm tm; + gmtime_r(&now, &tm); + int res = ioctl(fd, RTC_SET_TIME, &tm); + close(fd); + if (res < 0) { + return gs_error(errno); + } + + return GS_OK; +} + +gs_error_t gs_rtc_register_linux(bool get, bool set) +{ + static gs_rtc_driver_t rtc_driver; + if (get) { + rtc_driver.get_time = gs_rtc_get; + } + if (set) { + rtc_driver.set_time = gs_rtc_set; + } + return gs_rtc_register(&rtc_driver, NULL); +} diff --git a/gomspace/libutil/src/linux/sem.c b/gomspace/libutil/src/linux/sem.c new file mode 100644 index 00000000..b4d2c09d --- /dev/null +++ b/gomspace/libutil/src/linux/sem.c @@ -0,0 +1,89 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include + +gs_error_t gs_sem_create(unsigned int initialValue, gs_sem_t * sem) +{ + if (sem == NULL) { + return GS_ERROR_ARG; + } + + *sem = malloc(sizeof(sem_t)); + if (*sem == NULL) { + return GS_ERROR_ALLOC; + } + + int res = sem_init(*sem, 0, initialValue); + if (res < 0) { + res = gs_error(errno); + free(*sem); + } + + return res; +} + +gs_error_t gs_sem_destroy(gs_sem_t sem) +{ + int res = GS_OK; + if (sem) { + res = sem_destroy(sem); + if (res < 0) { + res = gs_error(errno); + } + free(sem); + } + return res; +} + +gs_error_t gs_sem_wait(gs_sem_t sem, int timeout_ms) +{ + int res; + + if (timeout_ms < 0) { + res = sem_wait(sem); + } else { + struct timespec ts; + res = clock_gettime(CLOCK_REALTIME, &ts); + if (res == 0) { + const uint32_t ms = (uint32_t)timeout_ms; + uint32_t sec = ms / 1000; + uint32_t nsec = (ms - (1000 * sec)) * 1000000; + + ts.tv_sec += sec; + + if (ts.tv_nsec + nsec >= 1000000000) { + ts.tv_sec++; + } + + ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000; + + res = sem_timedwait(sem, &ts); + } + } + if (res < 0) { + res = gs_error(errno); + } + return res; +} + +gs_error_t gs_sem_post(gs_sem_t sem) +{ + int res = sem_post(sem); + if (res < 0) { + res = gs_error(errno); + } + return res; +} + +gs_error_t gs_sem_post_isr(gs_sem_t sem, gs_context_switch_t * cswitch) +{ + (void) cswitch; + int res = sem_post(sem); + if (res < 0) { + res = gs_error(errno); + } + return res; +} diff --git a/gomspace/libutil/src/linux/signal.c b/gomspace/libutil/src/linux/signal.c new file mode 100644 index 00000000..826bc325 --- /dev/null +++ b/gomspace/libutil/src/linux/signal.c @@ -0,0 +1,38 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +static void gs_signal_default_handler(int signo, siginfo_t *si, void *context) +{ + exit(GS_EXITCODE_SIGNAL(signo)); // ensure atexit are invoked +} + +gs_error_t gs_signal_catch(int signal, gs_signal_handler handler) +{ + if (handler == NULL) { + handler = gs_signal_default_handler; + } + struct sigaction sa = { .sa_flags = SA_SIGINFO, + .sa_sigaction = handler}; + if (sigemptyset(&sa.sa_mask)) { + return GS_ERROR_UNKNOWN; + } + if (sigaction(signal, &sa, NULL)) { + return GS_ERROR_UNKNOWN; + } + return GS_OK; +} + +gs_error_t gs_signal_ignore(int signal) +{ + struct sigaction sa = { .sa_flags = 0, + .sa_handler = SIG_IGN}; // handle signal by ignoring + if (sigemptyset(&sa.sa_mask)) { + return GS_ERROR_UNKNOWN; + } + if (sigaction(signal, &sa, NULL)) { + return GS_ERROR_UNKNOWN; + } + return GS_OK; +} diff --git a/gomspace/libutil/src/linux/stdio.c b/gomspace/libutil/src/linux/stdio.c new file mode 100644 index 00000000..0fa052b7 --- /dev/null +++ b/gomspace/libutil/src/linux/stdio.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +gs_error_t gs_stdio_putchar(int ch) +{ + const int res = putchar(ch); + if (res < 0) { + return GS_ERROR_IO; + } + return GS_OK; +} + +gs_error_t gs_stdio_getchar_timed(int timeout_ms, int * ch) +{ + struct pollfd fds = {STDIN_FILENO, POLLIN, 0}; + const int res = poll(&fds, 1, timeout_ms); + + if (res == 0) { + return GS_ERROR_TIMEOUT; + } + + if ((res > 0) && (fds.revents & POLLIN)) { + int tmp = getchar(); + if (tmp >= 0) { + if (ch) { + *ch = tmp; + } + return GS_OK; + } + } + + return GS_ERROR_IO; +} diff --git a/gomspace/libutil/src/linux/sysfs_helper.c b/gomspace/libutil/src/linux/sysfs_helper.c new file mode 100644 index 00000000..2cdb390a --- /dev/null +++ b/gomspace/libutil/src/linux/sysfs_helper.c @@ -0,0 +1,48 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +gs_error_t gs_sysfs_write_file(const char *path, const char *value) +{ + log_trace("sysfs: write %s to %s", value, path); + + int fd = open(path, O_WRONLY); + if (fd < 0) { + return GS_ERROR_HANDLE; + } + + size_t len = strlen(value); + ssize_t bytes = write(fd, value, len); + close(fd); + if (bytes < 0) { + return GS_ERROR_NO_DATA; + } + + return (len == (size_t)bytes) ? GS_OK : GS_ERROR_NO_DATA; +} + +gs_error_t gs_sysfs_read_file(const char *path, char *value, size_t len) +{ + log_trace("sysfs: read %s", path); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + return GS_ERROR_HANDLE; + } + + ssize_t bytes = read(fd, value, len); + close(fd); + if (bytes < 0) { + return GS_ERROR_DATA; + } + + return GS_OK; +} diff --git a/gomspace/libutil/src/linux/thread.c b/gomspace/libutil/src/linux/thread.c new file mode 100644 index 00000000..43de1815 --- /dev/null +++ b/gomspace/libutil/src/linux/thread.c @@ -0,0 +1,89 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +gs_error_t gs_thread_create(const char * const name, + gs_thread_func_t func, + void * parameter, + size_t stack_size, + gs_thread_priority_t priority, + uint32_t flags, + gs_thread_t * return_handle) +{ + gs_time_uptime(); // force initialize of static offset + + pthread_attr_t attr; + int res = pthread_attr_init(&attr); + if (res) { + return GS_ERROR_ALLOC; + } + + if (flags & GS_THREAD_CREATE_JOINABLE) { + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + } else { + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + + gs_thread_t handle; + res = pthread_create(&handle, &attr, func, parameter); + pthread_attr_destroy(&attr); + if (res) { + return GS_ERROR_ALLOC; + } + + if (return_handle) { + *return_handle = handle; + } + + return GS_OK; +} + +gs_error_t gs_thread_create_with_stack(const char * const name, + gs_thread_func_t func, + void * parameter, + size_t stack_size, + gs_stack_type_t *stack, + gs_thread_priority_t priority, + uint32_t flags, + gs_thread_t * return_handle) +{ + return gs_thread_create(name, func, parameter, stack_size, priority, flags, return_handle); +} + +void gs_thread_exit(void * exitValue) +{ + pthread_exit(exitValue); +} + +void gs_thread_sleep_ms(uint32_t time_ms) +{ + gs_time_sleep_ms(time_ms); +} + +gs_error_t gs_thread_join(gs_thread_t thread, void ** return_retval) +{ + gs_error_t error = GS_ERROR_ARG; + void * retval = 0; + if (thread) { + int res = pthread_join(thread, &retval); + if (res == 0) { + error = GS_OK; + } else { + retval = 0; + } + } + if (return_retval) { + *return_retval = retval; + } + return error; +} + +void gs_thread_block(void) +{ + /* Wait here forever */ + for (;;) { + gs_time_sleep_ms(10000); + } +} diff --git a/gomspace/libutil/src/linux/time.c b/gomspace/libutil/src/linux/time.c new file mode 100644 index 00000000..46377feb --- /dev/null +++ b/gomspace/libutil/src/linux/time.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +uint32_t gs_time_rel_ms(void) +{ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + + return (uint32_t)((ts.tv_sec * 1000) + (ts.tv_nsec/1000000)); +} + +uint32_t gs_time_rel_ms_isr(void) +{ + return gs_time_rel_ms(); +} + +static uint32_t uptime_offset = 0; +uint32_t gs_time_uptime(void) +{ + uint32_t seconds; + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + seconds = 0; + } else { + seconds = (uint32_t) ts.tv_sec; + } + if (uptime_offset == 0) { + uptime_offset = seconds; + } + return (seconds - uptime_offset); +} + +void gs_time_sleep_ns(uint64_t time_ns) +{ + struct timespec ts; + ts.tv_sec = (time_ns / GS_TIMESTAMP_NSEC_PER_SEC); + ts.tv_nsec = (time_ns % GS_TIMESTAMP_NSEC_PER_SEC); + + // improvement: check return code (INTR) and use remaining. + nanosleep(&ts, NULL); +} + +void gs_time_sleep_ms(uint32_t time_ms) +{ + uint64_t ns = time_ms; + ns *= 1000000LL; + gs_time_sleep_ns( ns); +} diff --git a/gomspace/libutil/src/lock.c b/gomspace/libutil/src/lock.c new file mode 100644 index 00000000..76be91bd --- /dev/null +++ b/gomspace/libutil/src/lock.c @@ -0,0 +1,30 @@ +/* Copyright (c) 2013-2019 GomSpace A/S. All rights reserved. */ + +#include "lock.h" +#include + +static gs_mutex_t gs_lock; + +gs_error_t gs_lock_init(void) +{ + if (gs_lock == NULL) { + return gs_mutex_create(&gs_lock); + } + return GS_OK; +} + +gs_error_t gs_lock_lock(void) +{ + if (gs_lock == NULL) { + gs_error_t error = gs_lock_init(); + if (error) { + return error; + } + } + return gs_mutex_lock(gs_lock); +} + +gs_error_t gs_lock_unlock(void) +{ + return gs_mutex_unlock(gs_lock); +} diff --git a/gomspace/libutil/src/lock.h b/gomspace/libutil/src/lock.h new file mode 100644 index 00000000..b22841e8 --- /dev/null +++ b/gomspace/libutil/src/lock.h @@ -0,0 +1,14 @@ +/* Copyright (c) 2013-2019 GomSpace A/S. All rights reserved. */ +/** + @file + + Basic/core locking. + + Use for rare read/write locking, e.g. protecting register/de-regsiter functions. +*/ + +#include + +gs_error_t gs_lock_init(void); +gs_error_t gs_lock_lock(void); +gs_error_t gs_lock_unlock(void); diff --git a/gomspace/libutil/src/log/appender/console.c b/gomspace/libutil/src/log/appender/console.c new file mode 100644 index 00000000..818248b3 --- /dev/null +++ b/gomspace/libutil/src/log/appender/console.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +// generated by waf configure -> GS_LOG_ENABLE_ISR_LOGS +#include + +static gs_log_appender_console_cb_t g_console_log_cb = NULL; +static gs_mutex_t g_log_console_mutex = NULL; + +gs_error_t gs_log_console_append_init(gs_log_appender_t *appender) +{ + gs_error_t ret = GS_OK; + if (g_log_console_mutex == NULL) { + ret = gs_mutex_create(&g_log_console_mutex); + if (ret != GS_OK) { + g_log_console_mutex = NULL; + } + } + return ret; +} + +static void gs_log_console_append_isr(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va) +{ + va_list my_va; + va_copy(my_va, va); + + const char * color = gs_log_level_to_color_begin(level); + const char * end_color = gs_log_level_to_color_end(); + const char clevel = gs_log_level_to_char(level); + + // print log + printf("%s%04"PRIu32".%06"PRIu32" %c %s: ", color, ts->tv_sec, ts->tv_nsec / 1000, clevel, group->name); + GS_PGM_VPRINTF(format, my_va); + printf("%s\r\n", end_color); + + va_end(my_va); +} + +static void gs_log_console_append(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va) +{ + if (g_console_log_cb) + return g_console_log_cb(appender, level, group, ts, format, va); + + if (g_log_console_mutex) { + gs_mutex_lock(g_log_console_mutex); + } + + gs_log_console_append_isr(appender, level, group, ts, format, va); + + if (g_log_console_mutex) { + gs_mutex_unlock(g_log_console_mutex); + } +} + +static void gs_log_console_append_get_info(gs_log_appender_t *appender, char *info_str, uint8_t str_size) +{ + if (!info_str) { + return; + } + + snprintf(info_str, str_size, "Prints on stdout"); +} + +gs_error_t gs_log_appender_console_set_cb(gs_log_appender_console_cb_t cb) +{ + g_console_log_cb = cb; + return GS_OK; +} + +static const gs_log_appender_driver_t console_appender_driver = { + .init = gs_log_console_append_init, + .append = gs_log_console_append, +#ifdef GS_LOG_ENABLE_ISR_LOGS + .append_isr = gs_log_console_append_isr, +#else + .append_isr = 0, +#endif + .info = gs_log_console_append_get_info, +}; + +gs_log_appender_t gs_log_appender_console = { + .name = "console", + .drv = &console_appender_driver, + .drv_config = 0, + .mask = LOG_ALL_MASK, +}; diff --git a/gomspace/libutil/src/log/appender/simple_file.c b/gomspace/libutil/src/log/appender/simple_file.c new file mode 100644 index 00000000..f74a19e7 --- /dev/null +++ b/gomspace/libutil/src/log/appender/simple_file.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#ifndef __AVR__ +#include +#include +#include + +#include +#include + +typedef struct simple_file_drv_data { + FILE *fp; + gs_mutex_t mutex; +} simple_file_drv_data_t; + +static gs_error_t gs_log_simple_file_init(gs_log_appender_t *appender) +{ + const gs_log_appender_simple_file_config_t *config = appender->drv_config; + + if (config == NULL || gs_string_empty(config->filename)) { + return GS_ERROR_ARG; + } + + simple_file_drv_data_t *drv_data = appender->drv_data; + if (drv_data == NULL) { + drv_data = calloc(1, sizeof(*drv_data)); + if (drv_data == NULL) { + return GS_ERROR_ALLOC; + } + } + + /* If file is already open - Close it first */ + if (drv_data->fp) { + gs_mutex_lock(drv_data->mutex); + fclose(drv_data->fp); + drv_data->fp = NULL; + gs_mutex_unlock(drv_data->mutex); + gs_mutex_destroy(drv_data->mutex); + } + + const char * mode = config->truncate ? "w" : "a"; + + drv_data->fp = fopen(config->filename, mode); + if (drv_data->fp == NULL) { + log_error("%s: failed to open log-file: [%s], mode: %s", __FUNCTION__, config->filename, mode); + free(drv_data); + drv_data = 0; + return GS_ERROR_IO; + } + + gs_mutex_create(&drv_data->mutex); + appender->drv_data = drv_data; /* Set driver data on appender */ + return GS_OK; +} + +static void gs_log_simple_file_append(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va) +{ + va_list my_va; + va_copy(my_va, va); + + const gs_log_appender_simple_file_config_t *config = appender->drv_config; + simple_file_drv_data_t *drv_data = appender->drv_data; + if (drv_data == 0) { + va_end(my_va); + return; + } + + const char clevel = gs_log_level_to_char(level); + + const time_t t = ts->tv_sec; + struct tm result; + const char * tzone; + if (config->use_local_time) { + localtime_r(&t, &result); + tzone = ""; + } else { + gmtime_r(&t, &result); + tzone = "Z"; + } + + if (drv_data->mutex) { + gs_mutex_lock(drv_data->mutex); + } + { + fprintf(drv_data->fp, "%04d-%02d-%02d %02d:%02d:%02d.%06"PRIu32"%s %c %s: ", + result.tm_year + 1900, result.tm_mon + 1, result.tm_mday, + result.tm_hour, result.tm_min, result.tm_sec, + ts->tv_nsec / 1000, tzone, clevel, group->name); + vfprintf(drv_data->fp, format, my_va); + fprintf(drv_data->fp, "\r\n"); + fflush(drv_data->fp); + } + if (drv_data->mutex) { + gs_mutex_unlock(drv_data->mutex); + } + + va_end(my_va); +} + +static void gs_log_simple_file_append_info(gs_log_appender_t *appender, char *info_str, uint8_t str_size) +{ + if (!info_str) { + return; + } + + const gs_log_appender_simple_file_config_t *config = appender->drv_config; + snprintf(info_str, str_size, "Writes to file \"%s\"", config->filename); +} + +const gs_log_appender_driver_t gs_log_appender_simple_file_driver = { + .init = gs_log_simple_file_init, + .append = gs_log_simple_file_append, + .append_isr = 0, + .info = gs_log_simple_file_append_info, +}; + +#endif diff --git a/gomspace/libutil/src/log/commands.c b/gomspace/libutil/src/log/commands.c new file mode 100644 index 00000000..85ac7ae5 --- /dev/null +++ b/gomspace/libutil/src/log/commands.c @@ -0,0 +1,392 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include "local.h" +#include + + +// iterator context +typedef struct { + gs_command_context_t * ctx; + gs_log_group_t * first; + gs_log_appender_t * first_appender; + bool completer; + bool detailed; +} iter_group_t; + +#define FORMAT_BUF_SIZE 10 +static const char * format_mask(uint8_t mask, char * buf) +{ + snprintf(buf, FORMAT_BUF_SIZE, "%c%c%c%c%c%c", + (mask & LOG_ERROR_MASK) ? 'E' : '.', + (mask & LOG_WARNING_MASK) ? 'W' : '.', + (mask & LOG_NOTICE_MASK) ? 'N' : '.', + (mask & LOG_INFO_MASK) ? 'I' : '.', + (mask & LOG_DEBUG_MASK) ? 'D' : '.', + (mask & LOG_TRACE_MASK) ? 'T' : '.'); + return buf; +} + +static bool iter_print_group_appenders(void * ctx_in, gs_log_appender_t * appender) +{ + gs_bytebuffer_t *bb = ctx_in; + gs_bytebuffer_printf(bb, "%s,", appender->name); + return true; +} + +static bool iter_print_group(void * ctx_in, gs_log_group_t * group) +{ + iter_group_t * ctx = ctx_in; + char level_mask[FORMAT_BUF_SIZE]; + if (!ctx->completer) { + char appender_str[128] = "\0"; + gs_bytebuffer_t bb; + gs_bytebuffer_init(&bb, appender_str, sizeof(appender_str)); + gs_log_group_appender_iterate(group, &bb, iter_print_group_appenders); + if (ctx->detailed) { + gs_command_set_output_printf(ctx->ctx, group->name, "category", "0x%08x", group->category); + gs_command_set_output_printf(ctx->ctx, group->name, "mask", "%-6s (0x%02x)", format_mask(group->mask, level_mask), group->mask); + gs_command_set_output_printf(ctx->ctx, group->name, "appenders", appender_str); + } else { + gs_command_set_output_printf(ctx->ctx, NULL, NULL, "%-15s %-6s %s", group->name, format_mask(group->mask, level_mask), appender_str); + } + } else { + fprintf(ctx->ctx->out, " %-15s %-6s\r\n", + group->name, + format_mask(group->mask, level_mask)); + } + return true; +} + +static int cmd_log_group_list(gs_command_context_t * ctx) +{ + iter_group_t iter = {.ctx = ctx, .completer = false}; + + if (ctx->argc > 1) { + iter.detailed = true; + gs_log_group_iterate(ctx->argv[1], &iter, iter_print_group); + } else { + fprintf(ctx->out, "Group Mask Appenders\r\n"); + gs_log_group_iterate("*", &iter, iter_print_group); + } + return GS_OK; +} + +static bool iter_print_appender(void * ctx_in, gs_log_appender_t * appender) +{ + iter_group_t * ctx = ctx_in; + char level_mask[FORMAT_BUF_SIZE]; + if (!ctx->completer) { + if (ctx->detailed) { + gs_command_set_output_printf(ctx->ctx, appender->name, "mask", "%-6s (0x%02x)", format_mask(appender->mask, level_mask), appender->mask); + + if (appender->drv->info) { + char info_str[100]; + appender->drv->info(appender, info_str, sizeof(info_str)); + gs_command_set_output(ctx->ctx, appender->name, "info", info_str); + } + } else { + gs_command_set_output_printf(ctx->ctx, NULL, NULL, "%-15s %-6s", appender->name, format_mask(appender->mask, level_mask)); + } + } else { + fprintf(ctx->ctx->out, " %-15s %-6s\r\n", + appender->name, + format_mask(appender->mask, level_mask)); + } + return true; +} + +static int cmd_log_appender_list(gs_command_context_t * ctx) +{ + iter_group_t iter = {.ctx = ctx, .completer = false}; + + if (ctx->argc > 1) { + iter.detailed = true; + gs_log_appender_iterate(ctx->argv[1], &iter, iter_print_appender); + } else { + fprintf(ctx->out, "Appender Mask\r\n"); + gs_log_appender_iterate("*", &iter, iter_print_appender); + } + return GS_OK; +} + +typedef gs_error_t (*log_get_mask_t)(const char *name, uint8_t* mask); +typedef gs_error_t (*log_set_mask_t)(const char *name, uint8_t mask); + +static int cmd_log_mask_handler(gs_command_context_t * ctx, log_get_mask_t get_mask, log_set_mask_t set_mask) +{ + /* strtok writes to the string, so we need to duplicate it to avoid writing to read-only memory */ + char strbuf[100]; + GS_STRNCPY(strbuf, ctx->argv[1]); + + char * saveptr = NULL; + char * token = strtok_r(strbuf, ",", &saveptr); + gs_error_t error = GS_OK; + while (token && (error == GS_OK)) { + + uint8_t old_mask = 0; + if (gs_log_is_group_all(token) == false) { + error = get_mask(token, &old_mask); + } + if (error == GS_OK) { + uint8_t new_mask = 0; + error = gs_log_string_to_mask(ctx->argv[2], old_mask, &new_mask); + if (error == GS_OK) { + error = set_mask(token, new_mask); + } + } + + token = strtok_r(NULL, ",", &saveptr); + } + + return error; +} + +static int cmd_log_group_mask(gs_command_context_t * ctx) +{ + return cmd_log_mask_handler(ctx, gs_log_group_get_level_mask, gs_log_group_set_level_mask); +} + +static int cmd_log_appender_mask(gs_command_context_t * ctx) +{ + return cmd_log_mask_handler(ctx, gs_log_appender_get_level_mask, gs_log_appender_set_level_mask); +} + + +#ifndef __AVR__ +static bool iter_log_completer(void *ctx_in, gs_log_group_t * group) +{ + iter_group_t * ctx = ctx_in; + unsigned int hits = gs_command_completer_add_token(ctx->ctx, group->name, false); + if (hits == 1) { + ctx->first = group; + } else { + if (hits == 2) { + fprintf(ctx->ctx->out, "\r\n"); + iter_print_group(ctx, ctx->first); + } + iter_print_group(ctx, group); + } + return true; +} + +static gs_error_t cmd_log_group_completer(gs_command_context_t * ctx, int arg_to_complete) +{ + if (arg_to_complete == 1) { + iter_group_t iter = {.ctx = ctx, .completer = true}; + char name[50]; + snprintf(name, sizeof(name), "%s*", (ctx->argc > 1) ? ctx->argv[1] : ""); + gs_log_group_iterate(name, &iter, iter_log_completer); + return GS_OK; + } + return GS_ERROR_AMBIGUOUS; +} + +static bool iter_log_appender_completer(void *ctx_in, gs_log_appender_t * appender) +{ + iter_group_t * ctx = ctx_in; + unsigned int hits = gs_command_completer_add_token(ctx->ctx, appender->name, false); + if (hits == 1) { + ctx->first_appender = appender; + } else { + if (hits == 2) { + fprintf(ctx->ctx->out, "\r\n"); + iter_print_appender(ctx, ctx->first_appender); + } + iter_print_appender(ctx, appender); + } + return true; +} + +static gs_error_t cmd_log_appender_completer(gs_command_context_t * ctx, int arg_to_complete) +{ + if (arg_to_complete == 1) { + iter_group_t iter = {.ctx = ctx, .completer = true}; + char name[50]; + snprintf(name, sizeof(name), "%s*", (ctx->argc > 1) ? ctx->argv[1] : ""); + gs_log_appender_iterate(name, &iter, iter_log_appender_completer); + return GS_OK; + } + return GS_ERROR_AMBIGUOUS; +} +#endif + +typedef struct { + gs_command_context_t *cmd_ctx; + unsigned int count; +} hist_ctx_t; + +static bool appender_history_iter(void *ctx, gs_log_level_t level, const gs_timestamp_t *ts, const char *group, const char *msg) +{ + hist_ctx_t * hist_ctx = ctx; + + /* Break iteration if history record count is reached. */ + if (hist_ctx->count-- == 0) { + return false; + } + + gs_command_set_output_printf(hist_ctx->cmd_ctx, NULL, NULL, + "%s%04"PRIu32".%06"PRIu32" %c %s: %s%s", + gs_log_level_to_color_begin(level), + ts->tv_sec, ts->tv_nsec/1000, + gs_log_level_to_char(level), + group, + msg, + gs_log_level_to_color_end()); + + return true; +} + +static int cmd_log_appender_hist(gs_command_context_t * ctx) +{ + hist_ctx_t hist_ctx = {.cmd_ctx = ctx, .count = 20}; + if (ctx->argc == 3) { + hist_ctx.count = atoi(ctx->argv[2]); + } + + return gs_log_appender_history_iterate(ctx->argv[1], &hist_ctx, appender_history_iter); +} + + +static bool iter_log_group_find(void* ctx_in, gs_log_group_t *group) +{ + gs_log_group_t **grp = ctx_in; + *grp = group; + return false; +} + +static int cmd_log_insert(gs_command_context_t * ctx) +{ + gs_log_group_t *log_group = NULL; + gs_error_t error = gs_log_group_iterate(ctx->argv[1], &log_group, iter_log_group_find); + if (error != GS_OK) { + return error; + } + + gs_log_level_t level; + error = gs_log_string_to_level(ctx->argv[2], &level); + if (error == GS_OK) { + gs_log(level, log_group, GS_PGM_STR("%s"), ctx->argv[3]); + } + + return error; +} + +static int cmd_log_color(gs_command_context_t * ctx) +{ + bool color; + gs_error_t error = gs_string_to_bool(ctx->argv[1], &color); + if (error == GS_OK) { + gs_log_set_print_color(color); + } + + return error; +} + +static const gs_command_t GS_COMMAND_SUB cmd_log_group_cmds[] = { + { + .name = "list", + .help = "list log groups", + .usage = "[group]", +#ifndef __AVR__ + .completer = cmd_log_group_completer, +#endif + .handler = cmd_log_group_list, + .mandatory_args = GS_COMMAND_NO_ARGS, + .optional_args = 1, + },{ + .name = "mask", + .help = "Set log group mask(s): e|w|i|d|t|stand|all|non", + .usage = "[,group] <[+-]level>[,level]", +#ifndef __AVR__ + .completer = cmd_log_group_completer, +#endif + .handler = cmd_log_group_mask, + .mandatory_args = 2, + },{ + .name = "insert", + .help = "Log message", + .usage = " ", +#ifndef __AVR__ + .completer = cmd_log_group_completer, +#endif + .handler = cmd_log_insert, + .mandatory_args = 3, + },{ + .name = "color", + .help = "Enable/disable color logs (stdout)", + .usage = "", + .handler = cmd_log_color, + .mandatory_args = 1, + } +}; + +static const gs_command_t GS_COMMAND_SUB cmd_log_appender_cmds[] = { + { + .name = "list", + .help = "list log appenders", + .usage = "[appender]", +#ifndef __AVR__ + .completer = cmd_log_appender_completer, +#endif + .handler = cmd_log_appender_list, + .mandatory_args = GS_COMMAND_NO_ARGS, + .optional_args = 1, + }, { + .name = "mask", + .help = "Set log appender mask(s): e|w|i|d|t|stand|all|non", + .usage = "[,appender] <[+-]level>[,level]", +#ifndef __AVR__ + .completer = cmd_log_appender_completer, +#endif + .handler = cmd_log_appender_mask, + .mandatory_args = 2, + }, { + .name = "hist", + .help = "Show log appender history", + .usage = " [cnt]", +#ifndef __AVR__ + .completer = cmd_log_appender_completer, +#endif + .handler = cmd_log_appender_hist, + .mandatory_args = 1, + .optional_args = 1, + } +}; + +static const gs_command_t GS_COMMAND_SUB cmd_log_cmds[] = { + { + .name = "group", + .help = "log group commands", + .chain = GS_COMMAND_INIT_CHAIN(cmd_log_group_cmds), + }, { + .name = "appender", + .help = "log appender commands", + .chain = GS_COMMAND_INIT_CHAIN(cmd_log_appender_cmds), + } +}; + +static const gs_command_t GS_COMMAND_ROOT cmd_log[] = { + { + .name = "log", + .help = "log: Log system", + .chain = GS_COMMAND_INIT_CHAIN(cmd_log_cmds) + },{ + .name = "debug", + .help = "Set Log group mask(s): e|w|n|i|d|t|stand|all|off", + .usage = "[,group] <[+-]level>[,level]", +#ifndef __AVR__ + .completer = cmd_log_group_completer, +#endif + .handler = cmd_log_group_mask, + .mandatory_args = 2, + }, +}; + +gs_error_t gs_log_register_commands(void) +{ + return GS_COMMAND_REGISTER(cmd_log); +} diff --git a/gomspace/libutil/src/log/local.h b/gomspace/libutil/src/log/local.h new file mode 100644 index 00000000..654577c8 --- /dev/null +++ b/gomspace/libutil/src/log/local.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +struct gs_log_list { + union { + gs_log_group_t * group; + gs_log_appender_t *appender; + } data; + struct gs_log_list * next; +}; + +/** + De-register appender for the given log group. + + @note The de-register function is not safe when logging is active, this function + is mostly for test and should only be used in product code with extreme caution. + If logging is still not active, this function can be used safely. + + @param[in] group_name Name of the group. + @param[in] appender_name Name of appender to de-register for this group. + @return gs_error_t +*/ +gs_error_t gs_log_group_deregister_appender(const char * group_name, const char * appender_name); + +bool gs_log_is_group_all(const char * name); diff --git a/gomspace/libutil/src/log/log.c b/gomspace/libutil/src/log/log.c new file mode 100644 index 00000000..16865900 --- /dev/null +++ b/gomspace/libutil/src/log/log.c @@ -0,0 +1,705 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include "local.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "../lock.h" +#include + +#define MASK_SET 0 +#define MASK_AND 1 +#define MASK_OR 2 + +// use color in log print +static bool g_print_no_color; + +// Log Group list +GS_STATIC gs_log_list_t g_log_groups = { .data = { .group = 0} }; + +// Log Appender list +GS_STATIC gs_log_list_t g_log_appenders = { .data = { .appender = 0} }; + +// Root Log Appenders - used for holding a appender list +GS_STATIC gs_log_group_t g_log_group_root = {.name = GS_LOG_GROUP_ROOT}; + +// Default log group - always present. +GS_LOG_GROUP(LOG_DEFAULT, "default", GS_LOG_CAT_DEFAULT, LOG_ERROR_MASK | LOG_WARNING_MASK | LOG_NOTICE_MASK | LOG_INFO_MASK); + +bool gs_log_is_group_all(const char * name) +{ + return (name && ((strcasecmp(name, "*") == 0) || (strcasecmp(name, "all") == 0))); +} + +static bool iter_set_level_mask(void * ctx_in, gs_log_group_t * group) +{ + uint8_t * level_mask = ctx_in; + group->mask = *level_mask; + return true; +} + +gs_error_t gs_log_group_set_level_mask(const char * group_name, uint8_t mask) +{ + if (gs_string_empty(group_name)) { + return GS_ERROR_HANDLE; + } + + return gs_log_group_iterate(group_name, &mask, iter_set_level_mask); +} + +static bool iter_get_level_mask(void * ctx_in, gs_log_group_t * group) +{ + uint8_t * level_mask = ctx_in; + *level_mask = group->mask; + return true; +} + +gs_error_t gs_log_group_get_level_mask(const char * group_name, uint8_t *mask) +{ + if (gs_string_empty(group_name)) { + return GS_ERROR_HANDLE; + } + + gs_error_t error = gs_log_group_iterate(group_name, mask, iter_get_level_mask); + return error; +} + +gs_error_t gs_log_string_to_level(const char * str, gs_log_level_t * return_level) +{ + if (gs_string_empty(str)) { + return GS_ERROR_ARG; + } + + const size_t len = strlen(str); + gs_log_level_t level; + + if (strncasecmp(str, "trace", len) == 0) { + level = LOG_TRACE; + } else if (strncasecmp(str, "debug", len) == 0) { + level = LOG_DEBUG; + } else if (strncasecmp(str, "informational", len) == 0) { + level = LOG_INFO; + } else if (strncasecmp(str, "notice", len) == 0) { + level = LOG_NOTICE; + } else if (strncasecmp(str, "warning", len) == 0) { + level = LOG_WARNING; + } else if (strncasecmp(str, "error", len) == 0) { + level = LOG_ERROR; + } else { + return GS_ERROR_DATA; + } + + if (return_level) { + *return_level = level; + } + + return GS_OK; +} + +char gs_log_level_to_char(gs_log_level_t level) +{ + switch (level) { + case LOG_TRACE: return 'T'; + case LOG_DEBUG: return 'D'; + case LOG_INFO: return 'I'; + case LOG_NOTICE: return 'N'; + case LOG_WARNING: return 'W'; + case LOG_ERROR: return 'E'; + default: return '?'; + } +} + +gs_error_t gs_log_string_to_mask(const char *str, uint8_t old, uint8_t * return_mask) +{ + GS_CHECK_ARG(gs_string_empty(str) == false); + + char strbuf[50]; // copy buf, coz strtok will mess it up + GS_STRNCPY(strbuf, str); + + char *saveptr = NULL; + char *token = strtok_r(strbuf, ",", &saveptr); + while (token) { + // check for +xxx (add), -xxxx (remove), xxxx (set) + int op = MASK_SET; + if (*token == '+') { + op = MASK_OR; + token++; + } else if (*token == '-') { + op = MASK_AND; + token++; + } + + const unsigned int token_length = strlen(token); + if (token_length < 1) { + return GS_ERROR_DATA; + } + + /* Check mask */ + uint8_t mask; + gs_log_level_t level; + if (gs_log_string_to_level(token, &level) == GS_OK) { + // actual level + if (op == MASK_SET) { + // set all level bits equal or lover + mask = LOG_ALL_MASK & ~((1 << level) - 1); + } else { + mask = (1 << level); + } + } else if (!strncasecmp(token, "default", token_length)) { // legacy - conflicts with 'de(bug)' + mask = LOG_DEFAULT_MASK; + op = MASK_SET; + } else if (!strncasecmp(token, "standard", token_length)) { + mask = LOG_DEFAULT_MASK; + op = MASK_SET; + } else if (!strncasecmp(token, "all", token_length)) { + mask = LOG_ALL_MASK; + op = MASK_SET; + } else if (!strncasecmp(token, "off", token_length)) { + mask = 0; + op = MASK_SET; + } else if (!strncasecmp(token, "none", token_length)) { // legacy - conflicts with 'no(tice)' + mask = 0; + op = MASK_SET; + } else if (gs_string_to_uint8(token, &mask) == GS_OK) { + op = MASK_SET; + } else { + return GS_ERROR_DATA; + } + + /* Apply operation */ + if (op == MASK_OR) { + old |= mask; + } else if (op == MASK_AND) { + old &= ~mask; + } else if (op == MASK_SET) { + old = mask; + } + + token = strtok_r(NULL, ",", &saveptr); + } + + if (return_mask) { + *return_mask = old; + } + + return GS_OK; +} + +/** + All functions must call this initialization function, to ensure log is initialized. +*/ +static gs_error_t gs_log_init_internal(void) +{ + if (g_log_groups.data.group == NULL) { + return gs_log_init(true); + } + return GS_OK; +} + +gs_error_t gs_log_group_iterate(const char * group_name, void * ctx, gs_log_group_iterator_t iter) +{ + const bool all = (gs_string_empty(group_name) || gs_log_is_group_all(group_name)); + bool found = false; + + for (gs_log_list_t *node = &g_log_groups; node; node = node->next) { + if (node->data.group) { + if (all || gs_string_match(group_name, node->data.group->name)) { + found = true; + bool cont = iter(ctx, node->data.group); + if (cont == false) { + return GS_OK; + } + } + } + } + + return found ? GS_OK : GS_ERROR_NOT_FOUND; +} + +static gs_error_t gs_log_group_register_internal(gs_log_group_t *group) +{ + // check if appender is already in the list and find last node + gs_log_list_t * parent = &g_log_groups; // there will always be at least 1 group -> default + for (; parent; parent = parent->next) { + if ((parent->data.group == group) || (strcasecmp(group->name, parent->data.group->name) == 0)) { + return GS_ERROR_EXIST; + } + if (parent->next == NULL) { + break; + } + } + + gs_log_list_t * new_group_node = calloc(1, sizeof(*new_group_node)); + if (new_group_node == NULL) { + return GS_ERROR_ALLOC; + } + + new_group_node->data.group = group; + + // add to list - must be done last, iterating list can be done without locking + parent->next = new_group_node; + + return GS_OK; +} + +gs_error_t gs_log_group_register(gs_log_group_t *group) +{ + GS_CHECK_ARG(group != NULL); + GS_CHECK_ARG(gs_string_empty(group->name) == false); + + gs_log_init_internal(); + + gs_lock_lock(); + gs_error_t error = gs_log_group_register_internal(group); + gs_lock_unlock(); + + return error; +} + + +bool gs_log_group_is_level_enabled(gs_log_group_t *group, gs_log_level_t level) +{ + return ((group->mask & level) > 0); +} + +gs_error_t gs_log_appender_iterate(const char * name, void * ctx, gs_log_appender_iterator_t iter) +{ + const bool all = (gs_string_empty(name) || gs_log_is_group_all(name)); + bool found = false; + + /* Iterate the dynamically registered log appenders: */ + for (gs_log_list_t *node = &g_log_appenders; node; node = node->next) { + if (node->data.appender) { + if (all || gs_string_match(name, node->data.appender->name)) { + found = true; + bool cont = iter(ctx, node->data.appender); + if (cont == false) { + return GS_OK; + } + } + } + } + + return found ? GS_OK : GS_ERROR_NOT_FOUND; +} + +struct gs_log_history_ctx { + gs_log_record_iterator_t iter; + void *ctx; +}; + +static bool gs_log_history_iterator(void* ctx, gs_log_appender_t *appender) +{ + struct gs_log_history_ctx *hist_ctx = ctx; + if (appender->drv->hist) { + appender->drv->hist(appender, hist_ctx->ctx, hist_ctx->iter); + } + return true; +} + +gs_error_t gs_log_appender_history_iterate(const char * name, void * ctx, gs_log_record_iterator_t iter) +{ + struct gs_log_history_ctx hist_ctx = {.iter=iter, .ctx = ctx}; + + return gs_log_appender_iterate(name, &hist_ctx, gs_log_history_iterator); +} + +static gs_error_t gs_log_appender_register_internal(gs_log_appender_t *appender) +{ + if (g_log_appenders.data.appender == NULL) { + // first appender + g_log_appenders.data.appender = appender; + + if (appender->drv->init) { + gs_error_t error = appender->drv->init(appender); + if (error) { + g_log_appenders.data.appender = NULL; + return error; + } + } + + return GS_OK; + } + + // check if appender is already in the list and find last node + gs_log_list_t * parent = &g_log_appenders; + for (; parent; parent = parent->next) { + if ((parent->data.appender == appender) || (strcasecmp(parent->data.appender->name, appender->name) == 0)) { + return GS_ERROR_EXIST; + } + if (parent->next == NULL) { + break; + } + } + + gs_log_list_t *new_appender = calloc(1, sizeof(*new_appender)); + if (new_appender == NULL) { + return GS_ERROR_ALLOC; + } + + new_appender->data.appender = appender; + + if (appender->drv->init) { + gs_error_t error = appender->drv->init(appender); + if (error) { + free(new_appender); + return error; + } + } + + // add to list - must be done last, iterating list can be done without locking + parent->next = new_appender; + + return GS_OK; +} + +gs_error_t gs_log_appender_register(gs_log_appender_t *appender) +{ + GS_CHECK_ARG(appender != NULL); + GS_CHECK_ARG(gs_string_empty(appender->name) == false); + GS_CHECK_ARG(appender->drv != NULL); + GS_CHECK_ARG(appender->drv->append != NULL); + + gs_log_init_internal(); + + gs_lock_lock(); + gs_error_t error = gs_log_appender_register_internal(appender); + gs_lock_unlock(); + + return error; +} + +gs_error_t gs_log_appender_add(gs_log_appender_t *appender, uint16_t count) +{ + GS_CHECK_ARG(appender != NULL); + GS_CHECK_ARG(count != 0); + + gs_error_t error = GS_OK; + for (uint16_t i = 0; i < count; i++) { + gs_error_t tmp_error = gs_log_appender_register(&appender[i]); + if ((error == GS_OK) && tmp_error) { + error = tmp_error; + } + } + + return error; +} + +static bool iter_set_appender_level_mask(void * ctx_in, gs_log_appender_t * appender) +{ + uint8_t * level_mask = ctx_in; + appender->mask = *level_mask; + return true; +} + +gs_error_t gs_log_appender_set_level_mask(const char * appender_name, uint8_t mask) +{ + if (gs_string_empty(appender_name)) { + return GS_ERROR_HANDLE; + } + + return gs_log_appender_iterate(appender_name, &mask, iter_set_appender_level_mask); +} + +static bool iter_get_appender_level_mask(void * ctx_in, gs_log_appender_t * appender) +{ + uint8_t * level_mask = ctx_in; + *level_mask = appender->mask; + return true; +} + +gs_error_t gs_log_appender_get_level_mask(const char * appender_name, uint8_t *mask) +{ + if (gs_string_empty(appender_name)) { + return GS_ERROR_HANDLE; + } + + return gs_log_appender_iterate(appender_name, mask, iter_get_appender_level_mask); +} + +// Appender register/de-register iterator context +typedef struct { + gs_log_appender_t *appender; + gs_error_t ret; +} iter_group_appender_t; + +static bool iter_log_appender_add(void *ctx, gs_log_group_t *group) +{ + iter_group_appender_t* in = ctx; + + gs_log_list_t * last_elem = group->appenders; + for (gs_log_list_t *elem = group->appenders; elem; elem = elem->next) { + last_elem = elem; + if (elem->data.appender == in->appender) { + in->ret = GS_ERROR_EXIST; + return true; + } + } + + in->ret = GS_ERROR_ALLOC; + gs_log_list_t *new_appender = calloc(1, sizeof(*new_appender)); + if (new_appender) { + new_appender->data.appender = in->appender; + new_appender->next = 0; + if (last_elem != NULL) { + last_elem->next = new_appender; + } else { + group->appenders = new_appender; + } + in->ret = GS_OK; + } + + return true; +} + +gs_error_t gs_log_group_register_appender(const char * group_name, const char * appender_name) +{ + gs_log_appender_t *appender = NULL; + for (gs_log_list_t *elem = &g_log_appenders; elem; elem = elem->next) { + if (elem->data.appender) { + if (strcasecmp(elem->data.appender->name, appender_name) == 0) { + appender = elem->data.appender; + break; + } + } + } + if (NULL == appender) { + return GS_ERROR_NOT_FOUND; + } + + iter_group_appender_t ctx = {.appender = appender, .ret = GS_OK}; + + gs_error_t ret = GS_OK; + if (strcasecmp(group_name, GS_LOG_GROUP_ROOT) == 0) { + iter_log_appender_add(&ctx, &g_log_group_root); + } else { + ret = gs_log_group_iterate(group_name, &ctx, iter_log_appender_add); + } + + if (ret == GS_OK) { + ret = ctx.ret; + } + + return ret; +} + +static bool iter_log_appender_remove(void *ctx, gs_log_group_t *group) +{ + iter_group_appender_t* in = ctx; + in->ret = GS_ERROR_NOT_FOUND; + + gs_log_list_t * last_elem = group->appenders; + for (gs_log_list_t *elem = group->appenders; elem; elem = elem->next) { + if (elem->data.appender == in->appender) { + if (elem == group->appenders) { + group->appenders = elem->next; + } + last_elem->next = elem->next; + free(elem); + in->ret = GS_OK; + break; + } + last_elem = elem; + } + + return true; +} + +gs_error_t gs_log_group_deregister_appender(const char * group_name, const char * appender_name) +{ + gs_log_appender_t *appender = NULL; + for (gs_log_list_t *elem = &g_log_appenders; elem; elem = elem->next) { + if (elem->data.appender) { + if (strcasecmp(elem->data.appender->name, appender_name) == 0) { + appender = elem->data.appender; + break; + } + } + } + if (NULL == appender) { + return GS_ERROR_NOT_FOUND; + } + + iter_group_appender_t ctx = {.appender = appender, .ret = GS_OK}; + + gs_error_t ret; + if (strcasecmp(group_name, GS_LOG_GROUP_ROOT) == 0) { + ret = iter_log_appender_remove(&ctx, &g_log_group_root); + } else { + ret = gs_log_group_iterate(group_name, &ctx, iter_log_appender_remove); + } + + if (ret == GS_OK) { + ret = ctx.ret; + } + + return ret; +} + +gs_error_t gs_log_group_appender_iterate(gs_log_group_t * group, void * ctx, gs_log_appender_iterator_t iter) +{ + GS_CHECK_ARG(group != NULL); + + bool found = false; + for (gs_log_list_t *elem = group->appenders; elem; elem = elem->next) { + found = true; + iter(ctx, elem->data.appender); + } + + /* Iterate root appenders */ + for (gs_log_list_t *elem = g_log_group_root.appenders; elem; elem = elem->next) { + found = true; + iter(ctx, elem->data.appender); + } + + return found ? GS_OK : GS_ERROR_NOT_FOUND; +} + +static inline void gs_log_process_appenders(const gs_log_list_t * it, gs_log_level_t level, + const gs_log_group_t * group, gs_timestamp_t* ts, bool from_isr, const char * format, va_list va) +{ + for (; it; it = it->next) { + gs_log_appender_t* appender = it->data.appender; + + if ((appender->mask & (1 << level)) == 0) { + continue; + } + + if (from_isr == false) { + // log from none ISR context + appender->drv->append(appender, level, group, ts, format, va); + + } else if (appender->drv->append_isr) { + // log from ISR (Interrupt Service Routine) context + appender->drv->append_isr(appender, level, group, ts, format, va); + } + } +} + +static inline void gs_log_common_va(gs_log_level_t level, gs_log_group_t * group, bool from_isr, const char * format, va_list va) +{ + // get time as soon as possible + gs_timestamp_t ts; + gs_clock_get_time(&ts); + + // only needed if someone call function directly - otherwise the log macro has set it to a valid group + if (group == NULL) { + group = LOG_DEFAULT; + } + + // check level mask for current group (this will nearly always be true, because the log macro has done the checking + if (group->mask & (1 << level)) { + + // legacy - if log hasn't been initialized, this will initialize with console output enabled. + gs_log_init_internal(); + + if (group->appenders) { + gs_log_process_appenders(group->appenders, level, group, &ts, from_isr, format, va); + } + + if (group->additivity) { + /* Call root appenders */ + gs_log_process_appenders(g_log_group_root.appenders, level, group, &ts, from_isr, format, va); + } + } +} + +void gs_log(gs_log_level_t level, gs_log_group_t * group, const char * format, ...) +{ + va_list va_args; + va_start(va_args, format); + gs_log_common_va(level, group, false, format, va_args); + va_end(va_args); +} + +void gs_log_isr(gs_log_level_t level, gs_log_group_t * group, const char * format, ...) +{ + va_list va_args; + va_start(va_args, format); + gs_log_common_va(level, group, true, format, va_args); + va_end(va_args); +} + +void gs_log_va(gs_log_level_t level, gs_log_group_t * group, const char * format, va_list args) +{ + gs_log_common_va(level, group, false, format, args); +} + +void gs_log_set_print_color(bool color) +{ + g_print_no_color = (color == false); +} + +const char * gs_log_level_to_color_begin(gs_log_level_t level) +{ + if (g_print_no_color) { + return ""; + } + + switch (level) { + case LOG_ERROR: return "\E[1;31m"; // Red + case LOG_WARNING: return "\E[0;33m"; // Yellow + case LOG_NOTICE: + case LOG_INFO: return "\E[0;32m"; // Green + case LOG_DEBUG: return "\E[0;34m"; // Blue + default: + case LOG_TRACE: return "\E[0;35m"; // Magenta + } +} + +const char * gs_log_level_to_color_end(void) +{ + if (g_print_no_color) { + return ""; + } + + return "\E[0m"; +} + +uint8_t gs_log_level_to_mask(gs_log_level_t level) +{ + /* Enable all levels with priority above the set level */ + uint8_t level_mask = (0xFF << level) & LOG_ALL_MASK; + return level_mask; +} + +static bool iter_flush_appender(void * ctx_in, gs_log_appender_t * appender) +{ + if (appender->drv->flush) { + appender->drv->flush(appender); + } + return true; +} + +gs_error_t gs_log_appender_flush_all() +{ + return gs_log_appender_iterate("", NULL, iter_flush_appender); +} + +gs_error_t gs_log_init(bool with_console_appender) +{ + gs_error_t error = GS_OK; + + gs_lock_init(); // ignore result, this is the log system + + if (g_log_groups.data.group == NULL) { + + // default log group -> mark log as initialized + g_log_groups.data.group = LOG_DEFAULT; + + // register console log appender + if (with_console_appender) { + error = gs_log_appender_register(&gs_log_appender_console); + if (error == GS_OK) { + error = gs_log_group_register_appender(GS_LOG_GROUP_ROOT, gs_log_appender_console.name); + } + } + } + + return error; +} diff --git a/gomspace/libutil/src/rtc.c b/gomspace/libutil/src/rtc.c new file mode 100644 index 00000000..160df778 --- /dev/null +++ b/gomspace/libutil/src/rtc.c @@ -0,0 +1,42 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +static const gs_rtc_driver_t * rtc_driver; +static void * rtc_driver_data; + +gs_error_t gs_rtc_register(const gs_rtc_driver_t * driver, void * driver_data) +{ + rtc_driver = driver; + rtc_driver_data = driver_data; + return GS_OK; +} + +gs_error_t gs_rtc_supported(void) +{ + return rtc_driver ? GS_OK : GS_ERROR_NOT_SUPPORTED; +} + +gs_error_t gs_rtc_get_time(gs_timestamp_t * time) +{ + if (time == NULL) { + return GS_ERROR_ARG; + } + + if (rtc_driver && rtc_driver->get_time) { + return rtc_driver->get_time(rtc_driver_data, time); + } + return GS_ERROR_NOT_SUPPORTED; +} + +gs_error_t gs_rtc_set_time(const gs_timestamp_t * time) +{ + if (time == NULL) { + return GS_ERROR_ARG; + } + + if (rtc_driver && rtc_driver->set_time) { + return rtc_driver->set_time(rtc_driver_data, time); + } + return GS_ERROR_NOT_SUPPORTED; +} diff --git a/gomspace/libutil/src/stdio.c b/gomspace/libutil/src/stdio.c new file mode 100644 index 00000000..c723f8fe --- /dev/null +++ b/gomspace/libutil/src/stdio.c @@ -0,0 +1,81 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include + +gs_error_t gs_stdio_get(char * buf, size_t len) +{ + while (len > 0) { + int ch; + gs_error_t error = gs_stdio_getchar(&ch); + if (error) { + return error; + } + *buf++ = ch; + --len; + } + + return GS_OK; +} + +gs_error_t gs_stdio_put(const char * buf, size_t len, bool text) +{ + while (len > 0) { + if ((*buf == '\n') && text) { + gs_stdio_putchar('\r'); + } + gs_stdio_putchar(*buf++); + --len; + } + + return GS_OK; +} + +void gs_color_printf(gs_color_printf_t color_arg, const char * format, ...) +{ + va_list args; + va_start(args, format); + + if ((color_arg & GS_COLOR_ATTRS) == GS_COLOR_BOLD) { + printf("\033[1;"); + } else { + printf("\033[0;"); + } + + switch(color_arg & GS_COLOR_COLORS) { + case GS_COLOR_NONE: + printf("0m"); + break; + case GS_COLOR_BLACK: + printf("30m"); + break; + case GS_COLOR_RED: + printf("31m"); + break; + case GS_COLOR_GREEN: + printf("32m"); + break; + case GS_COLOR_YELLOW: + printf("33m"); + break; + case GS_COLOR_BLUE: + printf("34m"); + break; + case GS_COLOR_MAGENTA: + printf("35m"); + break; + case GS_COLOR_CYAN: + printf("36m"); + break; + case GS_COLOR_WHITE: + printf("37m"); + break; + default: + break; + } + + vprintf(format, args); + printf("\033[0m"); + + va_end(args); +} diff --git a/gomspace/libutil/src/string.c b/gomspace/libutil/src/string.c new file mode 100644 index 00000000..31419fd0 --- /dev/null +++ b/gomspace/libutil/src/string.c @@ -0,0 +1,746 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#if (__AVR__ == 0) +#include +#endif + +#ifndef GS_STRING_GET_SUBOPTION_UNIT_TEST +#define GS_STRING_GET_SUBOPTION_UNIT_TEST 0 +#endif + +const char * gs_string_skip_leading_spaces(const char * string) +{ + if (string) { + for (; *string == ' '; ++string); + } + return string; +} + +gs_error_t gs_string_to_int32(const char * string, int32_t * return_value) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (!(isdigit((int)string[0]) || (string[0] == '-'))) { + return GS_ERROR_DATA; + } + + int32_t tmp; + uint8_t base = 10; + + // check for hexadecimal notation + if ((string[0] == '0') && ((string[1] == 'x') || (string[1] == 'X'))) + { + base = 16; + } + + const char *desired_end = string + strlen(string); + // The desired end should point to that last non-space char in the string + while ((isspace((int)desired_end[0]) || (desired_end[0] == '\0')) && (desired_end > string)) + { + desired_end--; + } + char *end; + gs_error_t err = GS_OK; + tmp = gs_string_strto32int(string, &end, base, &err); + if (err != GS_OK) + { + return err; + } + if (desired_end != end-1) + { + return GS_ERROR_DATA; + } + + if (return_value) + { + *return_value = tmp; + } + + return GS_OK; +} + +gs_error_t gs_string_to_int8(const char * string, int8_t * return_value) +{ + int32_t value; + gs_error_t error = gs_string_to_int32(string, &value); + if (error == GS_OK) { + if ((value >= INT8_MIN) && (value <= INT8_MAX)) { + if (return_value) { + *return_value = (int8_t) value; + } + } else { + error = GS_ERROR_OVERFLOW; + } + } + return error; +} + +gs_error_t gs_string_to_uint8(const char * string, uint8_t * return_value) +{ + uint32_t value; + gs_error_t error = gs_string_to_uint32(string, &value); + if (error == GS_OK) { + if (value <= UINT8_MAX) { + if (return_value) { + *return_value = (uint8_t) value; + } + } else { + error = GS_ERROR_OVERFLOW; + } + } + return error; +} + +gs_error_t gs_string_to_int16(const char * string, int16_t * return_value) +{ + int32_t value; + gs_error_t error = gs_string_to_int32(string, &value); + if (error == GS_OK) { + if ((value >= INT16_MIN) && (value <= INT16_MAX)) { + if (return_value) { + *return_value = (int16_t) value; + } + } else { + error = GS_ERROR_OVERFLOW; + } + } + return error; +} + +gs_error_t gs_string_to_uint16(const char * string, uint16_t * return_value) +{ + uint32_t value; + gs_error_t error = gs_string_to_uint32(string, &value); + if (error == GS_OK) { + if (value <= UINT16_MAX) { + if (return_value) { + *return_value = (uint16_t) value; + } + } else { + error = GS_ERROR_OVERFLOW; + } + } + return error; +} + +gs_error_t gs_string_to_uint32(const char * string, uint32_t * return_value) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (!isdigit((int)string[0])) { + return GS_ERROR_DATA; + } + + uint32_t tmp; + uint8_t base = 10; + + // check for hexadecimal notation + if ((string[0] == '0') && ((string[1] == 'x') || (string[1] == 'X'))) + { + base = 16; + } + const char *desired_end = string + strlen(string); + // The desired end should point to that last non-space char in the string + while ((isspace((int)desired_end[0]) || desired_end[0] == 0) && (desired_end > string)) + { + desired_end--; + } + char *end; + gs_error_t err = GS_OK; + tmp = gs_string_strto32uint(string, &end, base, &err); + + if (err != GS_OK) + { + return err; + } + if (desired_end != end-1) + { + return GS_ERROR_DATA; + } + + + if (return_value) { + *return_value = tmp; + } + + return GS_OK; +} + +gs_error_t gs_string_to_uint64(const char * string, uint64_t * return_value) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (!isdigit((int)string[0])) + { + return GS_ERROR_DATA; + } + + uint64_t tmp; + uint8_t base = 10; + + // check for hexadecimal notation + if ((string[0] == '0') && ((string[1] == 'x') || (string[1] == 'X'))) { + base = 16; + } + const char *desired_end = string + strlen(string); + // The desired end should point to that last non-space char in the string + while ((isspace((int)desired_end[0]) || desired_end[0] == 0) && (desired_end > string)) + { + desired_end--; + } + char *end; + gs_error_t err = GS_OK; + tmp = gs_string_strto64uint(string, &end, base, &err); + if (err != GS_OK) + { + return err; + } + + if (desired_end != end-1) + { + return GS_ERROR_DATA; + } + + + if (return_value) { + *return_value = tmp; + } + + return GS_OK; +} + +gs_error_t gs_string_to_int64(const char * string, int64_t * return_value) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (!(isdigit((int)string[0]) || (string[0] == '-'))) + { + return GS_ERROR_DATA; + } + + int64_t tmp; + uint8_t base = 10; + + // check for hexadecimal notation + if ((string[0] == '0') && ((string[1] == 'x') || (string[1] == 'X'))) { + base = 16; + } + const char *desired_end = string + strlen(string); + // The desired end should point to that last non-space char in the string + while ((isspace((int)desired_end[0]) || desired_end[0] == 0) && (desired_end > string)) + { + desired_end--; + } + char *end; + gs_error_t err = GS_OK; + tmp = gs_string_strto64int(string, &end, base, &err); + if (err != GS_OK) + { + return err; + } + + if (desired_end != end-1) + { + return GS_ERROR_DATA; + } + + + if (return_value) { + *return_value = tmp; + } + + return GS_OK; +} + +gs_error_t gs_string_hex_to_uint32(const char * string, uint32_t * return_value) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (!isxdigit((int)string[0])) { + return GS_ERROR_DATA; + } + + const char *desired_end = string + strlen(string); + // The desired end should point to that last non-space char in the string + while ((isspace((int)desired_end[0]) || desired_end[0] == 0) && (desired_end > string)) + { + desired_end--; + } + char *end; + gs_error_t err = GS_OK; + uint32_t tmp = gs_string_strto32uint(string, &end, 16, &err); + + if (err != GS_OK) + { + return err; + } + if (desired_end != end-1) + { + return GS_ERROR_DATA; + } + + if (return_value) { + *return_value = tmp; + } + + return GS_OK; +} + +gs_error_t gs_string_hex_to_uint64(const char * string, uint64_t * return_value) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (!isxdigit((int)string[0])) + { + return GS_ERROR_DATA; + } + + const char *desired_end = string + strlen(string); + // The desired end should point to that last non-space char in the string + while ((isspace((int)desired_end[0]) || desired_end[0] == 0) && (desired_end > string)) + { + desired_end--; + } + char *end; + gs_error_t err = GS_OK; + uint64_t tmp = gs_string_strto64uint(string, &end, 16, &err); + if (err != GS_OK) + { + return err; + } + + if (desired_end != end-1) + { + return GS_ERROR_DATA; + } + + if (return_value) { + *return_value = tmp; + } + + return GS_OK; +} + +#if (__AVR__ == 0) +gs_error_t gs_string_to_float(const char * string, float * pvalue) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (gs_string_empty(string)) { + return GS_ERROR_DATA; + } + + // float strtof(const char *nptr, char **endptr); + char * endp = NULL; + float tmp = strtof(string, &endp); + //if ((endp == NULL) || (gs_string_empty(gs_string_skip_leading_spaces(endp)) == false) || (string == endp)) { + if ((endp == NULL) || (gs_string_empty(gs_string_skip_leading_spaces(endp)) == false) || (string == endp) || isinf(tmp)) { + return GS_ERROR_DATA; + } + + if (pvalue) { + *pvalue = tmp; + } + + return GS_OK; +} + +gs_error_t gs_string_to_double(const char * string, double * pvalue) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + + if (gs_string_empty(string)) { + return GS_ERROR_DATA; + } + + // double strtod(const char *nptr, char **endptr); + char * endp = NULL; + double tmp = strtod(string, &endp); + if ((endp == NULL) || (gs_string_empty(gs_string_skip_leading_spaces(endp)) == false) || isinf(tmp)) { + return GS_ERROR_DATA; + } + + if (pvalue) { + *pvalue = tmp; + } + + return GS_OK; +} +#endif + +#define GS_STRING_BOOL_TRUE "true" +#define GS_STRING_BOOL_FALSE "false" + +const char * gs_string_from_bool(bool value) +{ + if (value) { + return GS_STRING_BOOL_TRUE; + } else { + return GS_STRING_BOOL_FALSE; + } +} + +gs_error_t gs_string_to_bool(const char * string, bool * return_value) +{ + string = gs_string_skip_leading_spaces(string); + if (string == NULL) { + return GS_ERROR_ARG; + } + if (string[0] == 0) { + return GS_ERROR_DATA; + } + + bool value = false; + if (strcasecmp(string, GS_STRING_BOOL_TRUE) == 0) { + value = true; + } else if (strcasecmp(string, GS_STRING_BOOL_FALSE) == 0) { + value = false; + } else if (strcasecmp(string, "on") == 0) { + value = true; + } else if (strcasecmp(string, "off") == 0) { + value = false; + } else if (strcasecmp(string, "1") == 0) { + value = true; + } else if (strcasecmp(string, "0") == 0) { + value = false; + } else if (strcasecmp(string, "yes") == 0) { + value = true; + } else if (strcasecmp(string, "no") == 0) { + value = false; + } else { + return GS_ERROR_DATA; + } + + if (return_value) { + *return_value = value; + } + + return GS_OK; +} + +char * gs_string_bytesize(long n, char *buf, size_t buf_size) +{ + char postfix = 'B'; + double size = (double) n; + if (n >= 1048576) { + size /= 1048576.0; + postfix = 'M'; + } else if (n >= 1024) { + size /= 1024.0; + postfix = 'K'; + } + snprintf(buf, buf_size, "%.1f%c", size, postfix); + return buf; +} + +gs_error_t gs_string_to_pointer(const char * string, void ** value) +{ +#if __LP64__ + uint64_t tmp; + gs_error_t error = gs_string_to_uint64(string, &tmp); + if ((error == GS_OK) && value) { + *value = GS_TYPES_UINT2PTR(tmp); + } + return error; +#else + uint32_t tmp; + gs_error_t error = gs_string_to_uint32(string, &tmp); + if ((error == GS_OK) && value) { + *value = GS_TYPES_UINT2PTR(tmp); + } + return error; +#endif +} + +bool gs_string_empty(const char * string) +{ + if ((string == NULL) || (string[0] == 0)) { + return true; + } + return false; +} + +bool gs_string_match(const char * pattern, const char * string) +{ + if (string && pattern) { + while (*string || *pattern) { + int p = tolower((int)*pattern); + int s = tolower((int)*string); + + if (*pattern == '*') { + ++pattern; + p = tolower((int)*pattern); + for (; *string && (tolower((int)*string) != p); ++string); + s = tolower((int)*string); + } + + if (s != p) { + return false; + } + + if (s) { + ++string; + } + if (p) { + ++pattern; + } + } + if ((*string == 0) && (*pattern == 0)) { + return true; + } + } + return false; +} + +bool gs_string_has_wildcards(const char * string) +{ + if (strchr(string, '*')) { + return true; + } + // future wildcard + //if (strchr(str, '?')) { + // return true; + //} + return false; +} + +void gs_string_trim(char * buffer, size_t buffer_size) +{ + // remove trailing stuff + int len = strnlen(buffer, buffer_size); + if (len) { + for (int i = (len - 1); i >= 0; --i) { + if (isspace((int)buffer[i])) { + buffer[i] = 0; + } else { + break; + } + } + } + + char * start; + for (start = buffer; *start && isspace((int)*start); ++start); + if (*start && (start != buffer)) { + // move chars up + for (; *start; ++start) { + *buffer++ = *start; + } + *buffer = 0; + } +} + +bool gs_string_endswith(const char * string, const char * endswith) +{ + if (string == NULL || endswith == NULL) { + return false; + } + + int str_len = strlen(string); + int endswith_len = strlen(endswith); + + return (str_len >= endswith_len) && + (0 == strcmp(string + (str_len-endswith_len), endswith)); +} + +static size_t suboption_len(const char * ref, const char * end) +{ + if (ref) { + size_t len = (end) ? ((size_t)(end - ref)) : strlen(ref); + for (; len && (ref[len - 1] == ' '); --len); + return len; + } + return 0; +} + +static gs_error_t suboption_copy(const char * data, size_t len, char * buf, size_t buf_size, gs_error_t error) +{ + if (len >= buf_size) { + error = GS_ERROR_OVERFLOW; + len = (buf_size - 1); + } + + if (data == NULL) { + len = 0; + } else { + strncpy(buf, data, len); + } + + buf[len] = 0; + + return error; +} + +gs_error_t gs_string_get_suboption(const char * options, const char * suboption, char * buf, size_t buf_size) +{ + GS_CHECK_ARG(options != NULL); + GS_CHECK_ARG((buf != NULL) && (buf_size > 0)); + + const char * next = options; + for (;next;) { + const char * key = next; + if (*key == ',') { + key = NULL; // no key-value + } + next = strchr(next, ','); + + const char * value = NULL; + if (key) { + for (; *key == ' '; ++key); + + value = strchr(key, '='); + if (value == NULL) { + // no value + } else if (next && (value >= next)) { + // no value + value = NULL; + } + } + + const unsigned int key_len = suboption_len(key, value ? value : next); + + if (value) { + if (*value == '=') { + ++value; + } + for (; *value == ' '; ++value); + } + + const unsigned int value_len = suboption_len(value, next); + + if (GS_STRING_GET_SUBOPTION_UNIT_TEST) { // -> #define + printf(" key=[%.*s], len=%u, value=[%.*s], len=%u, next=[%s]\n", + key_len, key ? key : "", key_len, + value_len, value ? value : "", value_len, + next ? next : ""); + } + + // if suboption is empty, it means get value of first element - ignoring any key + if (gs_string_empty(suboption)) { + if (value) { + return suboption_copy(value, value_len, buf, buf_size, GS_OK); + } + if (key) { + return suboption_copy(key, key_len, buf, buf_size, GS_OK); + } + return suboption_copy(NULL, 0, buf, buf_size, GS_OK); // empty + } + + if ((key_len == strlen(suboption)) && (strncasecmp(key, suboption, key_len) == 0)) { + return suboption_copy(value, value_len, buf, buf_size, GS_OK); + } + + if (next) { + ++next; + if (next[0] == 0) { + next = NULL; + } + } + } + + // not found - return default + return suboption_copy(NULL, 0, buf, buf_size, GS_ERROR_NOT_FOUND); +} + +static const char * _suboption_name(const char * suboption) +{ + if (gs_string_empty(suboption)) { + return "first suboption"; + } + return suboption; +} + +#define _get_suboption(_type) \ + char buf[20]; \ + gs_error_t error = gs_string_get_suboption(options, suboption, buf, sizeof(buf)); \ + if (error == GS_OK) { \ + error = gs_string_to_##_type(buf, value); \ + } \ + if (error) { \ + if (error == GS_ERROR_NOT_FOUND) { \ + error = GS_OK; \ + } else { \ + log_error("Failed to extract suboption [%s] from [%s], error: %d", _suboption_name(suboption), options, error); \ + } \ + *value = def; \ + } \ + return error; \ + +gs_error_t gs_string_get_suboption_uint8(const char * options, const char * suboption, uint8_t def, uint8_t * value) +{ + _get_suboption(uint8) +} + +gs_error_t gs_string_get_suboption_uint16(const char * options, const char * suboption, uint16_t def, uint16_t * value) +{ + _get_suboption(uint16) +} + +gs_error_t gs_string_get_suboption_uint32(const char * options, const char * suboption, uint32_t def, uint32_t * value) +{ + _get_suboption(uint32) +} + +gs_error_t gs_string_get_suboption_string(const char * options, const char * suboption, const char * def, char * buf, size_t buf_size) +{ + gs_error_t error = gs_string_get_suboption(options, suboption, buf, buf_size); + if (error) { + if (error == GS_ERROR_NOT_FOUND) { + error = GS_OK; + } + error = suboption_copy(def, def ? strlen(def) : 0, buf, buf_size, error); + } + return error; +} + +gs_error_t gs_string_get_suboption_bool(const char * options, const char * suboption, bool def, bool * value) +{ + char buf[20]; + gs_error_t error = gs_string_get_suboption(options, suboption, buf, sizeof(buf)); + if (error == GS_OK) { + if (gs_string_empty(buf) || (suboption && (strcasecmp(suboption, buf) == 0))) { + // this means 'true', a=21,active,a=22 + *value = true; + } else { + error = gs_string_to_bool(buf, value); + } + } + if (error) { + if (error == GS_ERROR_NOT_FOUND) { + error = GS_OK; + } else { + log_error("Failed to extract suboption [%s] from [%s], error: %d", _suboption_name(suboption), options, error); + } + *value = def; + } + return error; +} diff --git a/gomspace/libutil/src/strtoint.c b/gomspace/libutil/src/strtoint.c new file mode 100644 index 00000000..152eff39 --- /dev/null +++ b/gomspace/libutil/src/strtoint.c @@ -0,0 +1,399 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. [rescinded 22 July 1999] + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + +@deftypefn Supplemental {long int} strtol (const char *@var{string}, @ + char **@var{endptr}, int @var{base}) +@deftypefnx Supplemental {unsigned long int} strtoul (const char *@var{string}, @ + char **@var{endptr}, int @var{base}) + +The @code{strtol} function converts the string in @var{string} to a +long integer value according to the given @var{base}, which must be +between 2 and 36 inclusive, or be the special value 0. If @var{base} +is 0, @code{strtol} will look for the prefixes @code{0} and @code{0x} +to indicate bases 8 and 16, respectively, else default to base 10. +When the base is 16 (either explicitly or implicitly), a prefix of +@code{0x} is allowed. The handling of @var{endptr} is as that of +@code{strtod} above. The @code{strtoul} function is the same, except +that the converted value is unsigned. + +@end deftypefn + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#include +#include +#include + +/* + * Convert a string to a long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +int32_t gs_string_strto32int(const char *nptr, char **endptr, uint8_t base, gs_error_t * err) +{ + register const char *s = nptr; + register uint32_t acc; + register int c; + register uint32_t cutoff; + register int neg = 0, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do + { + c = *s++; + } while (isspace(c)); + if (c == '-') + { + neg = 1; + c = *s++; + } else if (c == '+') + { + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) + { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + { + base = c == '0' ? 8 : 10; + } + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(uint32_t)INT32_MIN : INT32_MAX; + cutlim = cutoff % (uint32_t)base; + cutoff /= (uint32_t)base; + for (acc = 0, any = 0;; c = *s++) + { + if (isdigit(c)) + { + c -= '0'; + } + else if (isalpha(c)) + { + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + } + else + { + break; + } + if (c >= base) + { + break; + } + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + } + else + { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) + { + acc = neg ? INT32_MIN : INT32_MAX; + *err = GS_ERROR_OVERFLOW; + } else if (neg) + { + acc = -acc; + } + if (endptr != 0) + { + *endptr = (char *) (any ? s - 1 : nptr); + } + return (acc); +} + + +/* + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +uint32_t gs_string_strto32uint(const char *nptr, char **endptr, uint8_t base, gs_error_t * err) +{ + register const char *s = nptr; + register uint32_t acc; + register int32_t c; + register uint32_t cutoff; + register int32_t neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do + { + c = *s++; + } while (isspace(c)); + if (c == '-') + { + neg = 1; + c = *s++; + } else if (c == '+') + { + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) + { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + { + base = c == '0' ? 8 : 10; + } + cutoff = (uint32_t)UINT32_MAX / (uint32_t)base; + cutlim = (uint32_t)UINT32_MAX % (uint32_t)base; + for (acc = 0, any = 0;; c = *s++) + { + if (isdigit(c)) + { + c -= '0'; + } + else if (isalpha(c)) + { + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + } + else + { + break; + } + if (c >= base) + { + break; + } + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + } + else + { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) + { + acc = UINT32_MAX; + *err = GS_ERROR_OVERFLOW; + } else if (neg) + { + acc = -acc; + } + if (endptr != 0) + { + *endptr = (char *) (any ? s - 1 : nptr); + } + return (acc); +} + + +int64_t gs_string_strto64int(const char *nptr, char **endptr, uint8_t base, gs_error_t * err) +{ + register const char *s = nptr; + register uint64_t acc; + register int32_t c; + register uint64_t cutoff; + register int32_t neg = 0, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(uint64_t)INT64_MIN : INT64_MAX; + cutlim = cutoff % (uint64_t)base; + cutoff /= (uint64_t)base; + for (acc = 0, any = 0;; c = *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? INT64_MIN : INT64_MAX; + *err = GS_ERROR_OVERFLOW; + } else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return (acc); +} + + +/* + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ + +uint64_t gs_string_strto64uint(const char *nptr, char **endptr, uint8_t base, gs_error_t * err) +{ + register const char *s = nptr; + register uint64_t acc; + register int32_t c; + register uint64_t cutoff; + register int32_t neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (uint64_t)UINT64_MAX / (uint64_t)base; + cutlim = (uint64_t)UINT64_MAX % (uint64_t)base; + for (acc = 0, any = 0;; c = *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = UINT64_MAX; + *err = GS_ERROR_OVERFLOW; + } else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return (acc); +} diff --git a/gomspace/libutil/src/test/cmocka.c b/gomspace/libutil/src/test/cmocka.c new file mode 100644 index 00000000..c3c5d171 --- /dev/null +++ b/gomspace/libutil/src/test/cmocka.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +void cm_print_error(const char * const format, ...) CMOCKA_PRINTF_ATTRIBUTE(1, 2); + +#define EQUAL_TO_STRING(equal) (equal ? "!=" : "==") + +void _gs_assert_int_equal(const intptr_t left, const intptr_t right, bool equal, const char * const file, const int line) +{ + const bool cmp_equal = left == right; + if (cmp_equal != equal) { + cm_print_error("%ld %s %ld\n", left, EQUAL_TO_STRING(equal), right); + + _fail(file, line); + } +} + +void _gs_assert_uint_equal(const uintptr_t left, const uintptr_t right, bool equal, bool hex, const char * const file, const int line) +{ + const bool cmp_equal = left == right; + if (cmp_equal != equal) { + if(hex) { + cm_print_error("0x%lx %s 0x%lx\n", left, EQUAL_TO_STRING(equal), right); + } else { + cm_print_error("%lu %s %lu\n", left, EQUAL_TO_STRING(equal), right); + } + + _fail(file, line); + } +} + +void _gs_assert_error_equal(const int left, const int right, bool equal, const char * const file, const int line) +{ + const bool cmp_equal = left == right; + if (cmp_equal != equal) { + cm_print_error("%d(%s) %s %d(%s)\n", left, gs_error_string(left), EQUAL_TO_STRING(equal), right, gs_error_string(right)); + + _fail(file, line); + } +} + +void _gs_assert_float_equal(const float left, const float right, const float diff, bool equal, const char * const file, const int line) +{ + const bool cmp_equal = (fabsf(left - right) < diff); + if (cmp_equal != equal) { + cm_print_error("%e %s %e\n", left, EQUAL_TO_STRING(equal), right); + + _fail(file, line); + } +} + +void _gs_assert_double_equal(const double left, const double right, const double diff, bool equal, const char * const file, const int line) +{ + const bool cmp_equal = (fabsf(left - right) < diff); + if (cmp_equal != equal) { + cm_print_error("%e %s %e\n", left, EQUAL_TO_STRING(equal), right); + + _fail(file, line); + } +} diff --git a/gomspace/libutil/src/test/command.c b/gomspace/libutil/src/test/command.c new file mode 100644 index 00000000..e9678523 --- /dev/null +++ b/gomspace/libutil/src/test/command.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include + +void cm_print_error(const char * const format, ...) CMOCKA_PRINTF_ATTRIBUTE(1, 2); + +struct results { + char group[32]; + char key[32]; + char value[128]; +}; + +static FILE* g_output_file = 0; +static char g_stdout_buf[10000]; +static char g_stdin_buf[1000]; +static uint16_t g_stdin_idx; + +static struct results g_results[100]; +static uint32_t g_results_count = 0; + + +static gs_error_t test_set_result(gs_command_context_t *ctx, const char *group, const char *key, const char *value) +{ + if (g_results_count >= GS_ARRAY_SIZE(g_results)) { + return GS_ERROR_ALLOC; + } + + if (group) { + strncpy(g_results[g_results_count].group, group, sizeof(g_results[0].group)); + } + if (key) { + strncpy(g_results[g_results_count].key, key, sizeof(g_results[0].key)); + } + if (value) { + strncpy(g_results[g_results_count].value, value, sizeof(g_results[0].value)); + } + g_results_count++; + return GS_OK; +} + +static gs_error_t test_flush(gs_command_context_t *ctx) +{ + fflush(ctx->out); + return GS_OK; +} + +static gs_error_t test_wait_for_key(gs_command_context_t *ctx, int *ch, int timeout_ms) +{ + if (g_stdin_buf[g_stdin_idx] == 0) { + gs_time_sleep_ms(timeout_ms); + return GS_ERROR_TIMEOUT; + } + + *ch = g_stdin_buf[g_stdin_idx]; + g_stdin_idx++; + return GS_OK; +} + +static gs_command_io_functions_t g_test_io_functions = { + .set_result = test_set_result, + .flush = test_flush, + .wait_for_key = test_wait_for_key, +}; + + +static gs_error_t prepare_validation(const char *input) +{ + g_results_count = 0; + memset(g_stdout_buf, 0, sizeof(g_stdout_buf)); + memset(g_stdin_buf, 0, sizeof(g_stdin_buf)); + g_stdin_idx = 0; + + if (input != NULL) { + memcpy(g_stdin_buf, input, strlen(input)); + } + + g_output_file = fmemopen(g_stdout_buf, sizeof(g_stdout_buf) - 1, "w"); + if (!g_output_file) { + return GS_ERROR_ALLOC; + } + + return GS_OK; +} + +static bool match(const char *first, const char * second) +{ + return (fnmatch(first, second, 0) == 0); +} + +static gs_error_t do_validation(const char *expected) +{ + fclose(g_output_file); + + if (expected == NULL) + return GS_OK; + + if (!match(expected, g_stdout_buf)) + { + return GS_ERROR_DATA; + } + return GS_OK; +} + + +void _gs_assert_command_validate(const char *cmd, gs_error_t ret, gs_error_t cmd_ret, const char *std_in, const char *std_out, + const char * const file, const int line) +{ + if (prepare_validation(std_in) != GS_OK) + { + cm_print_error("Validation function failed to allocate it's resources\n"); + _fail(file, line); + } + + gs_error_t _ret; + gs_error_t _command_ret = GS_OK; + _ret = gs_command_execute(cmd, &_command_ret, g_output_file, &g_test_io_functions, NULL); + + if (_ret != ret) { + cm_print_error("Return: %d(%s) != %d(%s)\n", _ret, gs_error_string(_ret), ret, gs_error_string(ret)); + _fail(file, line); + } + + if (_ret == GS_OK) /* Only check CMD return if command execution succeeded */ + { + if (_command_ret != cmd_ret) { + cm_print_error("Command return: %d(%s) != %d(%s)\n", _command_ret, gs_error_string(_command_ret), + cmd_ret, gs_error_string(cmd_ret)); + _fail(file, line); + } + } + + if (do_validation(std_out) != GS_OK) + { + //cm_print_error("Stdout != : \n%s\n!=\n%s\n", g_stdout_buf, std_out); + printf("Stdout != : \n%s\n!=\n%s\n", g_stdout_buf, std_out); + _fail(file, line); + } + + return; +} + +void _gs_assert_command_validate_last_result(unsigned int no, const char *group, const char *key, const char *value, + const char * const file, const int line) +{ + if (no >= g_results_count) + { + cm_print_error("Result no: %d not available. Only %d results returned from command\n", no, g_results_count); + _fail(file, line); + } + + if (group) { + if (!match(group, g_results[no].group)) { + cm_print_error("group: %s != %s\n", group, g_results[no].group); + _fail(file, line); + } + } + if (key) { + if (!match(key, g_results[no].key)) { + cm_print_error("key: %s != %s\n", key, g_results[no].key); + _fail(file, line); + } + } + if (value) { + if (!match(value, g_results[no].value)) { + cm_print_error("value: %s != %s\n", value, g_results[no].value); + _fail(file, line); + } + } + + return; +} diff --git a/gomspace/libutil/src/test/log.c b/gomspace/libutil/src/test/log.c new file mode 100644 index 00000000..ba7671e1 --- /dev/null +++ b/gomspace/libutil/src/test/log.c @@ -0,0 +1,165 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// cmocka +void cm_print_error(const char * const format, ...) CMOCKA_PRINTF_ATTRIBUTE(1, 2); + +static gs_mutex_t g_lock; + +#define GS_TEST_LOG_MAX_LEVELS 6 +#define GS_TEST_LOG_MAX_LOG_MESSAGES 200 + +typedef struct { + // Overall count per log level. + unsigned int count[GS_TEST_LOG_MAX_LEVELS]; + // Log messages. + struct { + // Level. + gs_log_level_t level; + // Format log message. + char msg[150]; + } logs[GS_TEST_LOG_MAX_LOG_MESSAGES]; + // Index of next entry in \a logs. + unsigned int next_log; +} gs_test_log_stats_t; + +static gs_test_log_stats_t gs_test_log_stats; + +static void log_callback(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t * group, const gs_timestamp_t * ts, const char * format, va_list va) +{ + gs_mutex_lock(g_lock); + gs_test_log_stats.count[level]++; + + { + gs_bytebuffer_t bb; + gs_bytebuffer_init(&bb, + gs_test_log_stats.logs[gs_test_log_stats.next_log].msg, + sizeof(gs_test_log_stats.logs[gs_test_log_stats.next_log].msg)); + + va_list my_va; + va_copy(my_va, va); + gs_bytebuffer_printf(&bb, "%s: ", group->name); + gs_bytebuffer_vprintf(&bb, format, my_va); + va_end(my_va); + gs_bytebuffer_get_as_string(&bb, NULL); // ensure NUL termination + gs_test_log_stats.logs[gs_test_log_stats.next_log].level = level; + + ++gs_test_log_stats.next_log; + if (gs_test_log_stats.next_log >= GS_TEST_LOG_MAX_LOG_MESSAGES) { + gs_test_log_stats.next_log = 0; + } + gs_test_log_stats.logs[gs_test_log_stats.next_log].msg[0] = 0; // clear next entry + gs_test_log_stats.logs[gs_test_log_stats.next_log].level = GS_TEST_LOG_MAX_LEVELS; + } + + gs_mutex_unlock(g_lock); +} + +static gs_log_appender_driver_t g_log_test_appender_driver = { + .init = 0, + .append = log_callback, + .append_isr = log_callback, +}; + +static gs_log_appender_t g_log_test_appender = { + .name = "test_appender", + .drv = &g_log_test_appender_driver, + .drv_config = NULL, + .drv_data = NULL, + .mask = LOG_ALL_MASK, +}; + +void gs_test_log_init(bool verbose) +{ + gs_log_init(true); + + if (g_lock == NULL) { + GS_ASSERT_ERROR_EQUAL(gs_mutex_create(&g_lock), GS_OK); + + /* Add/Register appender - Only first time that init is called. */ + gs_log_appender_add(&g_log_test_appender, 1); + gs_log_group_register_appender("root", "test_appender"); + } + if (verbose) { + gs_log_appender_set_level_mask("console", LOG_ALL_MASK); + } else { + gs_log_appender_set_level_mask("console", 0); + } + + gs_test_log_clear(); +} + +void gs_test_log_clear(void) +{ + gs_mutex_lock(g_lock); + memset(&gs_test_log_stats, 0, sizeof(gs_test_log_stats)); + gs_mutex_unlock(g_lock); +} + +void gs_assert_log_count(int level, unsigned int count, const char * file, int line) +{ + if (level < 0) { + unsigned int tot = 0; + for (int i = 0; i < GS_TEST_LOG_MAX_LEVELS; ++i) { + tot += gs_test_log_stats.count[i]; + } + if (tot != count) { + cm_print_error("Unexpected total log count: %u != %u\n", tot, count); + _fail(file, line); + } + } else if (level >= GS_TEST_LOG_MAX_LEVELS) { + cm_print_error("Unknown log level: %d - valid levels 0 - %d\n", level, GS_TEST_LOG_MAX_LEVELS - 1); + _fail(file, line); + } else if (gs_test_log_stats.count[level] != count) { + cm_print_error("Unexpected log count: %u != %u\n", gs_test_log_stats.count[level], count); + _fail(file, line); + } +} + +void gs_assert_log(unsigned int stack_index, unsigned int count, gs_log_level_t level, const char * pattern, const char * file, int line) +{ + if (stack_index == UINT32_MAX) { + // loop through all logs + unsigned int next = gs_test_log_stats.next_log; + unsigned int hits = 0; + for (unsigned int i = next - 1; i != next; --i) { + if (i >= GS_TEST_LOG_MAX_LOG_MESSAGES) { + i = (GS_TEST_LOG_MAX_LOG_MESSAGES - 1); + } + if ((gs_test_log_stats.logs[i].level == level) && (gs_test_log_stats.logs[i].msg[0])) { + if (fnmatch(pattern, gs_test_log_stats.logs[i].msg, 0) == 0) { + ++hits; + } + } + } + if (hits != count) { + cm_print_error("Unexpected log count: %u != %u\n", hits, count); + _fail(file, line); + } + } else if (stack_index >= GS_TEST_LOG_MAX_LOG_MESSAGES) { + cm_print_error("Invalid stack_index: %u - valid 0 - %d\n", stack_index, GS_TEST_LOG_MAX_LOG_MESSAGES - 1); + _fail(file, line); + } else { + unsigned int i = (((gs_test_log_stats.next_log + GS_TEST_LOG_MAX_LOG_MESSAGES) - 1 - stack_index) % GS_TEST_LOG_MAX_LOG_MESSAGES); + if ((gs_test_log_stats.logs[i].level == level) && + (gs_test_log_stats.logs[i].msg[0]) && + (fnmatch(pattern, gs_test_log_stats.logs[i].msg, 0) == 0)) { + // match + } else { + cm_print_error("[%c][%s] != [%c][%s]\n", + gs_log_level_to_char(gs_test_log_stats.logs[i].level), gs_test_log_stats.logs[i].msg, + gs_log_level_to_char(level), pattern); + _fail(file, line); + } + } +} diff --git a/gomspace/libutil/src/time.c b/gomspace/libutil/src/time.c new file mode 100644 index 00000000..123f5994 --- /dev/null +++ b/gomspace/libutil/src/time.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +uint32_t gs_time_diff_ms(uint32_t ref_ms, uint32_t now_ms) +{ + if (now_ms >= ref_ms) { + return (now_ms - ref_ms); + } + + // assuming time wrapped at max uint32_t + return ((UINT32_MAX - ref_ms) + now_ms); +} + +bool gs_time_sleep_until_ms(uint32_t * ref_ms, uint32_t sleep_ms) +{ + const uint32_t now = gs_time_rel_ms(); + *ref_ms += sleep_ms; // this is expected to be in the future + uint32_t ms = gs_time_diff_ms(now, *ref_ms); + if (ms > sleep_ms) { + // we are behind - catch up, could be bad seed or too long processing + *ref_ms = now; + gs_time_sleep_ms(0); // yield - let others have a go + return true; + } + gs_time_sleep_ms(ms); + return false; +} diff --git a/gomspace/libutil/src/timestamp.c b/gomspace/libutil/src/timestamp.c new file mode 100644 index 00000000..2f7ee913 --- /dev/null +++ b/gomspace/libutil/src/timestamp.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +int timestamp_add(gs_timestamp_t * base, const gs_timestamp_t * add) +{ + if (!base || !add) + return -1; + + base->tv_sec += add->tv_sec; + if (base->tv_nsec + add->tv_nsec >= GS_TIMESTAMP_NSEC_PER_SEC) { + base->tv_sec++; + base->tv_nsec = (base->tv_nsec + add->tv_nsec) % GS_TIMESTAMP_NSEC_PER_SEC; + } else { + base->tv_nsec += add->tv_nsec; + } + + return 0; +} + +int timestamp_diff(gs_timestamp_t * base, const gs_timestamp_t * diff) +{ + if (!base || !diff) + return -1; + + base->tv_sec -= diff->tv_sec; + if (base->tv_nsec >= diff->tv_nsec) { + base->tv_nsec -= diff->tv_nsec; + } else { + base->tv_sec--; + base->tv_nsec = (base->tv_nsec + GS_TIMESTAMP_NSEC_PER_SEC) - diff->tv_nsec; + } + + return 0; +} + +/* Test is timestamp is greater or equal */ +int timestamp_ge(const gs_timestamp_t * base, const gs_timestamp_t * test) +{ + if (!base || !test) + return -1; + + if (test->tv_sec > base->tv_sec || + (test->tv_sec == base->tv_sec && + test->tv_nsec > base->tv_nsec)) { + return 1; + } + + return 0; +} + +int timestamp_copy(const gs_timestamp_t * from, gs_timestamp_t * to) +{ + if (!from || !to) + return -1; + + to->tv_sec = from->tv_sec; + to->tv_nsec = from->tv_nsec; + + return 0; +} diff --git a/gomspace/libutil/src/vmem/commands.c b/gomspace/libutil/src/vmem/commands.c new file mode 100644 index 00000000..06a37af4 --- /dev/null +++ b/gomspace/libutil/src/vmem/commands.c @@ -0,0 +1,123 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +static int cmd_vmem_read(gs_command_context_t * ctx) +{ + if (gs_vmem_get_map() == NULL) { + return GS_ERROR_NOT_FOUND; + } + + void * addr; + if (gs_string_to_pointer(ctx->argv[1], &addr)) { + return GS_ERROR_ARG; + } + + uint32_t length; + if (gs_string_to_uint32(ctx->argv[2], &length)) { + return GS_ERROR_ARG; + } + + char data[length]; + void* to = gs_vmem_cpy(data, addr, length); + if (to == NULL) { + return GS_ERROR_ARG; + } + + gs_hexdump_to_stream(data, length, addr, ctx->out); + + return GS_OK; +} + +static unsigned int to_int(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + return -1; +} + +static int cmd_vmem_write(gs_command_context_t * ctx) +{ + if (gs_vmem_get_map() == NULL) { + return GS_ERROR_NOT_FOUND; + } + + void * addr; + if (gs_string_to_pointer(ctx->argv[1], &addr)) { + return GS_ERROR_ARG; + } + + int len = strlen(ctx->argv[2]) / 2; + char data[len]; + + for (int i = 0; (i < len); ++i) { + data[i] = 16 * to_int(ctx->argv[2][2*i]) + to_int(ctx->argv[2][2*i+1]); + } + + gs_vmem_cpy(addr, data, len); + return GS_OK; +} + +static int cmd_vmem_list(gs_command_context_t * ctx) +{ + return gs_vmem_list(ctx->out); +} + +static int cmd_vmem_lock(gs_command_context_t * context) +{ + return gs_vmem_lock_by_name(context->argv[1], true); +} + +static int cmd_vmem_unlock(gs_command_context_t * context) +{ + return gs_vmem_lock_by_name(context->argv[1], false); +} + +static const gs_command_t GS_COMMAND_SUB cmd_vmem_sub[] = { + { + .name = "read", + .help = "Read from virtual memory", + .usage = " ", + .handler = cmd_vmem_read, + .mandatory_args = 2, + },{ + .name = "write", + .help = "Write to virtual memory", + .usage = " ", + .handler = cmd_vmem_write, + .mandatory_args = 2, + },{ + .name = "lock", + .help = "Lock the virtual memory", + .usage = "", + .handler = cmd_vmem_lock, + .mandatory_args = 1, + },{ + .name = "unlock", + .help = "Unlock the virtual memory", + .usage = "", + .handler = cmd_vmem_unlock, + .mandatory_args = 1, + },{ + .name = "list", + .help = "Show virtual memory mappings", + .handler = cmd_vmem_list, + .mandatory_args = GS_COMMAND_NO_ARGS, + } +}; + +static const gs_command_t GS_COMMAND_ROOT cmd_vmem[] = { + { + .name = "vmem", + .help = "Virtual memory", + .chain = GS_COMMAND_INIT_CHAIN(cmd_vmem_sub), + }, +}; + +gs_error_t gs_vmem_register_commands(void) +{ + return GS_COMMAND_REGISTER(cmd_vmem); +} diff --git a/gomspace/libutil/src/vmem/vmem.c b/gomspace/libutil/src/vmem/vmem.c new file mode 100644 index 00000000..30068a01 --- /dev/null +++ b/gomspace/libutil/src/vmem/vmem.c @@ -0,0 +1,143 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include + +static const gs_vmem_t * g_vmem_map; + +gs_error_t gs_vmem_set_map(const gs_vmem_t * map) +{ + g_vmem_map = map; + return GS_OK; +} + +const gs_vmem_t * gs_vmem_get_map(void) +{ + return g_vmem_map; +} + +gs_error_t gs_vmem_list(FILE * out) +{ + const gs_vmem_t * mem = g_vmem_map; + if (mem) { + unsigned int found = 0; + for (; mem->name; ++mem) { + if (found == 0) { + fprintf(out, "%-20s %-10s %-6s %-6s %s\r\n", "name", "virt.", "phys.", "size", "size"); + } + fprintf(out, "%-20s %p 0x%04x 0x%04x %5u\r\n", mem->name, mem->virtmem.p, (unsigned int) mem->physmem.u, (unsigned int) mem->size, (unsigned int) mem->size); + ++found; + } + if (found) { + return GS_OK; + } + } + return GS_ERROR_NOT_FOUND; +} + +const gs_vmem_t * gs_vmem_get_by_name(const char * name) +{ + if (name) { + const gs_vmem_t * mem = g_vmem_map; + if (mem) { + for (; mem->name; ++mem) { + if (strcasecmp(name, mem->name) == 0) { + return mem; + } + } + } + } + return NULL; +} + +gs_error_t gs_vmem_lock_by_name(const char * name, bool on) +{ + const gs_vmem_t * mem = gs_vmem_get_by_name(name); + if (mem) { + if (mem->drv && mem->drv->lock) { + return (mem->drv->lock)(mem, on); + } + return GS_ERROR_NOT_SUPPORTED; + } + return GS_ERROR_NOT_FOUND; +} + +gs_error_t gs_vmem_lock_all(bool on) +{ + const gs_vmem_t * mem = g_vmem_map; + if (mem) { + unsigned int locked = 0; + for (; mem->name; ++mem) { + if (mem->drv && mem->drv->lock) { + (mem->drv->lock)(mem, on); + ++locked; + } + } + if (locked) { + return GS_OK; + } + } + return GS_ERROR_NOT_FOUND; +} + +/** + @note NO LOGGING - currently the log system uses this interface, and logging can therefor create circular/forever loops. +*/ +void * gs_vmem_cpy(void* to, const void* from, size_t size) +{ + /* Search memories */ + const gs_vmem_t *vmem_from = NULL; + const gs_vmem_t *vmem_to = NULL; + const gs_vmem_t *mem = g_vmem_map; + const gs_address_t _to = {.p = to}; + const gs_address_t _from = {.p = (void*) from}; + + if (mem) { + while(mem->size != 0) { + //printf("0x%lx 0x%lx %"PRIu32" %lu %lu\r\n", mem->start, mem->physmem_start, mem->size, to, from); + if ((_to.u >= mem->virtmem.u) && (_to.u < mem->virtmem.u + mem->size)) { + vmem_to = mem; + } + if ((_from.u >= mem->virtmem.u) && (_from.u < mem->virtmem.u + mem->size)) { + vmem_from = mem; + } + mem++; + } + } + + // VMEM -> VMEM + if (vmem_to && vmem_from) { + printf("%s: VMEM to VMEM is not supported\r\n", __FUNCTION__); + return NULL; + } + + // RAM -> VMEM + if (vmem_to) { + if ((vmem_to->drv == NULL) || (vmem_to->drv->write == NULL)) { + printf("%s: Writting to VMEM %p is not supported\r\n", __FUNCTION__, to); + return NULL; + } + gs_address_t physaddr = {.u = (_to.u - vmem_to->virtmem.u) + vmem_to->physmem.u}; + //printf("Copying from ram 0x%lx to physaddr 0x%lx %u\r\n", from, physaddr, (unsigned int) size); + vmem_to->drv->write(vmem_to, physaddr.p, from, size); + return to; + } + + // VMEM -> RAM + if (vmem_from) { + if (vmem_from->drv == NULL || (vmem_from->drv->read == NULL)) { + printf("%s: Reading from VMEM %p is not supported\r\n", __FUNCTION__, from); + return NULL; + } + gs_address_t physaddr = {.u = (_from.u - vmem_from->virtmem.u) + vmem_from->physmem.u}; + //printf("Copying from mem physaddr 0x%lx to 0x%lx %u\r\n", physaddr, to, (unsigned int) size); + vmem_from->drv->read(vmem_from, to, physaddr.p, size); + return to; + } + + // RAM -> RAM (no VMEM mapping found) + return memcpy(to, from, size); +} diff --git a/gomspace/libutil/src/watchdog/local.h b/gomspace/libutil/src/watchdog/local.h new file mode 100644 index 00000000..1dc2e0de --- /dev/null +++ b/gomspace/libutil/src/watchdog/local.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include + +typedef struct { + gs_thread_t task; + bool is_running; + bool do_stop; +} gs_swwd_monitor_task_t; + +// Return monitor task instance +gs_swwd_monitor_task_t * gs_swwd_get_monitor_task(void); + +GS_LOG_GROUP_EXTERN(gs_swwd_log); +#define LOG_DEFAULT gs_swwd_log diff --git a/gomspace/libutil/src/watchdog/monitor_task.c b/gomspace/libutil/src/watchdog/monitor_task.c new file mode 100644 index 00000000..54d7e668 --- /dev/null +++ b/gomspace/libutil/src/watchdog/monitor_task.c @@ -0,0 +1,68 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include "local.h" +#include +#include +#include + +#define GS_SWWD_CHECK_INTERVAL_MS 1000 /* Check every 1 sec. */ + +static void * gs_swwd_monitor_task(void* parameter) +{ + gs_swwd_monitor_task_t * monitor = parameter; + + log_info("monitor task started"); + + monitor->is_running = true; + + while(!monitor->do_stop) { + + gs_time_sleep_ms(GS_SWWD_CHECK_INTERVAL_MS); + + gs_swwd_check_expired_clients(NULL); + } + + monitor->is_running = false; + + log_info("monitor task exiting"); + + return NULL; +} + +gs_error_t gs_swwd_monitor_task_start(void) +{ + gs_swwd_monitor_task_t * monitor = gs_swwd_get_monitor_task(); + GS_CHECK_SUPPORTED(monitor != NULL); /* SWWD must be initialized */ + GS_CHECK_SUPPORTED(monitor->is_running == false); /* SWWD task must not already be running */ + + /* Start the monitor task */ + gs_error_t error = gs_thread_create("SWWD", gs_swwd_monitor_task, monitor, 4000, GS_THREAD_PRIORITY_HIGH, 0, &monitor->task); + if (error) { + log_error("%s: gs_thread_create() failed, error: %s", __FUNCTION__, gs_error_string(error)); + } + return error; +} + +gs_error_t gs_swwd_monitor_task_stop(uint32_t timeout_s) +{ + gs_swwd_monitor_task_t * monitor = gs_swwd_get_monitor_task(); + GS_CHECK_SUPPORTED(monitor != NULL); /* SWWD must be initialized */ + + /* Signal the task to stop */ + monitor->do_stop = true; + + /* Wait for the task to stop */ + const uint32_t timeout = GS_SWWD_CHECK_INTERVAL_MS + (timeout_s * 1000); + uint32_t tm = 0; + while(monitor->is_running && (tm < timeout)) { + gs_thread_sleep_ms(100); + tm += 100; + } + + if (monitor->is_running) { + return GS_ERROR_BUSY; + } + + return GS_OK; +} + diff --git a/gomspace/libutil/src/watchdog/watchdog.c b/gomspace/libutil/src/watchdog/watchdog.c new file mode 100644 index 00000000..867601b1 --- /dev/null +++ b/gomspace/libutil/src/watchdog/watchdog.c @@ -0,0 +1,292 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include "local.h" +#include +#include +#include +#include +#include + +#define GS_SWWD_DEFAULT_TIMEOUT 30 /* 30 second timeout */ + +// define log group and make it default +GS_LOG_GROUP(gs_swwd_log, "swwd", GS_LOG_CAT_DEFAULT, GS_LOG_DEFAULT_MASK); + +// watchdog state +typedef enum { + GS_SWWD_STATE_FREE = 0, + GS_SWWD_STATE_ACTIVE = 1, + GS_SWWD_STATE_EXPIRED = 2, +} gs_swwd_state_t; + +// watchdog client instance +struct gs_swwd_hdl { + // State + gs_swwd_state_t state; + // Last 'user' touch value - used for detecting if watchdog has been touched (avoid race condition) + uint32_t last_touch; + // Last detected touch time + uint32_t last_touch_ms; + + // User 'set' attributes + struct { + // Name + const char* name; + // Timeout - converted from seconds (given to API) to mS + uint32_t timeout_ms; + // Action when/if timeout occurs + gs_swwd_timeout_action_t action; + // callback + gs_swwd_callback_function_t cb; + // user data (for callback) + void * cb_userdata; + // Touch - incremented on each touch + uint32_t touch; + } user; +}; + +typedef struct gs_swwd { + gs_watchdog_device_t *wdev; + gs_mutex_t lock; + gs_swwd_monitor_task_t monitor; + uint32_t num_clients; + gs_swwd_hdl_t clients[0]; +} gs_swwd_t; + +static gs_swwd_t* g_swwd = NULL; + +gs_swwd_monitor_task_t * gs_swwd_get_monitor_task(void) +{ + if (g_swwd) { + return &g_swwd->monitor; + } + return NULL; +} + +gs_error_t gs_swwd_create(uint32_t max_clients, gs_watchdog_device_t * wdev) +{ + GS_CHECK_SUPPORTED(g_swwd == NULL); /* SWWD must not be initialized more than once */ + GS_CHECK_ARG(max_clients > 0); + if (wdev) { + // ping is the only mandatory + GS_CHECK_ARG((wdev->ops != NULL) && (wdev->ops->ping != NULL)); + } + + gs_log_group_register(LOG_DEFAULT); + + gs_swwd_t *swwd_obj = calloc(1, sizeof(*swwd_obj) + (sizeof(swwd_obj->clients[0]) * max_clients)); + if (swwd_obj == NULL) { + return GS_ERROR_ALLOC; + } + + if (gs_mutex_create(&(swwd_obj->lock))) { + free(swwd_obj); + return GS_ERROR_ALLOC; + } + + swwd_obj->num_clients = max_clients; + swwd_obj->wdev = wdev; + + if (wdev) { + if (wdev->ops->set_timeout) { + wdev->ops->set_timeout(wdev, (wdev->timeout > 0) ? wdev->timeout : GS_SWWD_DEFAULT_TIMEOUT); + } + if (wdev->ops->set_pretimeout) { + wdev->ops->set_pretimeout(wdev, (wdev->pretimeout > 0) ? wdev->pretimeout : (GS_SWWD_DEFAULT_TIMEOUT - (GS_SWWD_DEFAULT_TIMEOUT/10))); + } + if (wdev->ops->start) { + wdev->ops->start(wdev); + } + wdev->ops->ping(wdev); + + } else { + log_warning("%s: no watchdog device specifed - cannot reset system!", __FUNCTION__); + } + + g_swwd = swwd_obj; /* Set the task handle as the last operation */ + return GS_OK; +} + +gs_error_t gs_swwd_destroy(uint32_t timeout_s) +{ + GS_CHECK_SUPPORTED(g_swwd != NULL); + + if (gs_swwd_monitor_task_stop(timeout_s) != GS_OK) { + return GS_ERROR_BUSY; + } + + if (g_swwd->wdev && g_swwd->wdev->ops->stop) { + g_swwd->wdev->ops->stop(g_swwd->wdev); + } + + gs_mutex_destroy(g_swwd->lock); + free(g_swwd); + g_swwd = NULL; + + return GS_OK; +} + +static const char * get_action(gs_swwd_timeout_action_t action) +{ + switch (action) { + case GS_SWWD_TIMEOUT_ACTION_RESET: + return "reset"; + case GS_SWWD_TIMEOUT_ACTION_LOG: + return "log"; + } + return "unknown"; +} + +gs_error_t gs_swwd_check_expired_clients(uint32_t *num_expired) +{ + GS_CHECK_SUPPORTED(g_swwd != NULL); /* SWWD must be initialized */ + + uint32_t expired_clients_reset = 0; + uint32_t expired_clients_log = 0; + + gs_mutex_lock(g_swwd->lock); + for (uint32_t idx = 0; idx < g_swwd->num_clients; idx++) { + gs_swwd_hdl_t * wd = &g_swwd->clients[idx]; + + uint32_t now_ms = gs_time_rel_ms(); + if (wd->state == GS_SWWD_STATE_ACTIVE) { + if (wd->last_touch != wd->user.touch) { + // watchdog has been touched since last we checked - update touch time + wd->last_touch = wd->user.touch; + wd->last_touch_ms = now_ms; + } else { + const uint32_t elapsed_ms = gs_time_diff_ms(wd->last_touch_ms, now_ms); + if (elapsed_ms >= wd->user.timeout_ms) { + wd->state = GS_SWWD_STATE_EXPIRED; + + char logbuf[100]; + snprintf(logbuf, sizeof(logbuf), + "[%s] expired -> %s (elapsed %"PRIu32" mS, timeout %"PRIu32" mS)", + wd->user.name, + get_action(wd->user.action), + elapsed_ms, + wd->user.timeout_ms); + + gs_swwd_callback_function_t cb = wd->user.cb; + void * cb_userdata = wd->user.cb_userdata; + + // Unlock while doing log and callback + // - we accept the tiny risk, that client has deregistered and will be called with invalid data + gs_mutex_unlock(g_swwd->lock); + { + log_error("%s", logbuf); + if (cb) { + (cb)(cb_userdata); + } + } + gs_mutex_lock(g_swwd->lock); + } + } + } + if (wd->state == GS_SWWD_STATE_EXPIRED) { + switch (wd->user.action) { + case GS_SWWD_TIMEOUT_ACTION_RESET: + ++expired_clients_reset; + break; + case GS_SWWD_TIMEOUT_ACTION_LOG: + if (wd->last_touch != wd->user.touch) { + // its alive - reactive watchdog + wd->state = GS_SWWD_STATE_ACTIVE; + wd->last_touch = wd->user.touch; + wd->last_touch_ms = now_ms; + } else { + ++expired_clients_log; + } + break; + } + } + } + gs_mutex_unlock(g_swwd->lock); + + if ((expired_clients_reset == 0) && g_swwd->wdev) { + g_swwd->wdev->ops->ping(g_swwd->wdev); + } + + if (num_expired) { + *num_expired = (expired_clients_reset + expired_clients_log); + } + + return GS_OK; +} + +gs_error_t gs_swwd_register_with_action(gs_swwd_hdl_t ** user_wd, + uint32_t timeout_seconds, + gs_swwd_callback_function_t callback, void * userdata, + const char * name, + gs_swwd_timeout_action_t action) +{ + GS_CHECK_ARG(gs_string_empty(name) == false); + GS_CHECK_ARG(timeout_seconds > 0); + GS_CHECK_ARG(user_wd != NULL); + GS_CHECK_SUPPORTED(g_swwd != NULL); /* SWWD must be initialized */ + + gs_swwd_hdl_t * wd = NULL; + gs_mutex_lock(g_swwd->lock); + { + for (unsigned int idx = 0; idx < g_swwd->num_clients; idx++) { + if (g_swwd->clients[idx].state == GS_SWWD_STATE_FREE) { + wd = &g_swwd->clients[idx]; + memset(wd, 0, sizeof(*wd)); + + // set user stuff + wd->user.name = name; + wd->user.timeout_ms = (timeout_seconds * 1000); + wd->user.cb = callback; + wd->user.cb_userdata = userdata; + wd->user.action = action; + + // set internal stuff + wd->state = GS_SWWD_STATE_ACTIVE; + wd->last_touch_ms = gs_time_rel_ms(); + break; + } + } + } + gs_mutex_unlock(g_swwd->lock); + + *user_wd = wd; + + if (wd == NULL) { + log_error("[%s] cannot create instance due to no available handles", name); + return GS_ERROR_FULL; + } + + return GS_OK; +} + +gs_error_t gs_swwd_deregister(gs_swwd_hdl_t ** wd) +{ + GS_CHECK_SUPPORTED(g_swwd != NULL); /* SWWD must be initialized */ + GS_CHECK_ARG(wd != NULL); + GS_CHECK_HANDLE(*wd != NULL); + + gs_mutex_lock(g_swwd->lock); + memset((*wd), 0, sizeof(**wd)); + gs_mutex_unlock(g_swwd->lock); + *wd = NULL; + + return GS_OK; +} + +gs_error_t gs_swwd_touch(gs_swwd_hdl_t * wd) +{ + GS_CHECK_HANDLE(wd != NULL); + + ++wd->user.touch; + return GS_OK; +} + +gs_error_t gs_swwd_set_timeout(gs_swwd_hdl_t * wd, uint32_t timeout_seconds) +{ + GS_CHECK_ARG(timeout_seconds > 0); + GS_CHECK_HANDLE(wd != NULL); + + ++wd->user.touch; + wd->user.timeout_ms = (timeout_seconds * 1000); + return GS_OK; +} diff --git a/gomspace/libutil/src/zip/cppcheck-suppress.txt b/gomspace/libutil/src/zip/cppcheck-suppress.txt new file mode 100644 index 00000000..b23b687d --- /dev/null +++ b/gomspace/libutil/src/zip/cppcheck-suppress.txt @@ -0,0 +1,5 @@ +// we don't wanna change 3rd part code for none-critical issue +localtimeCalled:src/zip/miniz/miniz.c +utimeCalled:src/zip/miniz/miniz.c +assignIfError:src/zip/miniz/miniz.c +unreadVariable:src/zip/miniz/miniz.c diff --git a/gomspace/libutil/src/zip/miniz/LIST_OF_CHANGES b/gomspace/libutil/src/zip/miniz/LIST_OF_CHANGES new file mode 100644 index 00000000..cb7adb22 --- /dev/null +++ b/gomspace/libutil/src/zip/miniz/LIST_OF_CHANGES @@ -0,0 +1,9429 @@ +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +diff --git a/src/zip/miniz/miniz.h b/src/zip/miniz/miniz.h +index 86fac4c..f92f14e 100644 +--- a/src/zip/miniz/miniz.h ++++ b/src/zip/miniz/miniz.h +@@ -447,7 +447,7 @@ typedef void *const voidpc; + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress +-#define crc32 mz_crc32 ++// #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +diff --git a/src/zip/miniz/miniz.c b/src/zip/miniz/miniz.c +index 67318cc..960f07c 100644 +--- a/src/zip/miniz/miniz.c ++++ b/src/zip/miniz/miniz.c +@@ -1936,6 +1936,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; ++ memset(d->m_dict, 0, sizeof(d->m_dict)); // Initialize array to 0's + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +@@ -2464,7 +2465,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex + } + r->m_table_sizes[2] = 19; + } +- for (; (int)r->m_type >= 0; r->m_type--) ++ for (; ((int)r->m_type) >= 0; r->m_type--) + { + int tree_next, tree_cur; + tinfl_huff_table *pTable; +@@ -3025,7 +3026,7 @@ static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) + #define MZ_DELETE_FILE remove + + #else +-#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") ++// #pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") + #ifndef MINIZ_NO_TIME + #include + #endif +@@ -3267,12 +3268,12 @@ static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) + } + + #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +-static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) ++static void mz_zip_time_t_to_dos_time(MZ_TIME_T time_, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) + { + #ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; +- errno_t err = localtime_s(tm, &time); ++ errno_t err = localtime_s(tm, &time_); + if (err) + { + *pDOS_date = 0; +@@ -3280,7 +3281,7 @@ static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_u + return; + } + #else +- struct tm *tm = localtime(&time); ++ struct tm *tm = localtime(&time_); + #endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); +@@ -3874,7 +3875,10 @@ mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) ++ { ++ MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); ++ } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { +@@ -4134,7 +4138,7 @@ static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_inde + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); +- field_data_remaining -= sizeof(mz_uint64); ++ // field_data_remaining -= sizeof(mz_uint64); + } + + break; +@@ -4219,11 +4223,11 @@ static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) + { +- mz_uint32 index; +- if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) ++ mz_uint32 index_; ++ if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index_)) + return -1; + else +- return (int)index; ++ return (int)index_; + } + + mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +@@ -5332,12 +5336,12 @@ mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; +- mz_zip_archive_file_stat stat; ++ mz_zip_archive_file_stat stat_; + +- if (!mz_zip_reader_file_stat(pZip, i, &stat)) ++ if (!mz_zip_reader_file_stat(pZip, i, &stat_)) + return MZ_FALSE; + +- if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) ++ if (!mz_zip_reader_locate_file_v2(pZip, stat_.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ +@@ -6011,6 +6015,11 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) + { ++ if(!pZip) ++ { ++ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++ } ++ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; +@@ -6035,7 +6044,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + +- if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) ++ if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; +@@ -6296,6 +6305,11 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n + mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) + { ++ if(!pZip) ++ { ++ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++ } ++ + mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; +@@ -6315,7 +6329,7 @@ mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, + level = level_and_flags & 0xF; + + /* Sanity checks */ +- if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) ++ if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; +@@ -6828,7 +6842,7 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive * + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { +- const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); ++ // const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { +@@ -6836,8 +6850,8 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive * + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + +- local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); +- local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ ++ // local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); ++ // local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; +@@ -6966,7 +6980,7 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive * + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + +- cur_src_file_ofs += n; ++ // cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +diff --git a/src/zip/miniz/miniz.h b/src/zip/miniz/miniz.h +index 68f903c..e517263 100644 +--- a/src/zip/miniz/miniz.h ++++ b/src/zip/miniz/miniz.h +@@ -203,7 +203,7 @@ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + + #define MZ_CRC32_INIT (0) + /* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +-mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); ++// mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + + /* Compression strategies. */ + enum +@@ -301,7 +301,7 @@ typedef struct mz_stream_s + typedef mz_stream *mz_streamp; + + /* Returns the version string of miniz.c. */ +-const char *mz_version(void); ++// const char *mz_version(void); + + /* mz_deflateInit() initializes a compressor with default options: */ + /* Parameters: */ +@@ -324,7 +324,7 @@ int mz_deflateInit(mz_streamp pStream, int level); + int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + + /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +-int mz_deflateReset(mz_streamp pStream); ++// int mz_deflateReset(mz_streamp pStream); + + /* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ + /* Parameters: */ +@@ -345,7 +345,7 @@ int mz_deflate(mz_streamp pStream, int flush); + int mz_deflateEnd(mz_streamp pStream); + + /* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +-mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); ++// mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + + /* Single-call compression functions mz_compress() and mz_compress2(): */ + /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +@@ -353,7 +353,7 @@ int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char * + int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + + /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +-mz_ulong mz_compressBound(mz_ulong source_len); ++// mz_ulong mz_compressBound(mz_ulong source_len); + + /* Initializes a decompressor. */ + int mz_inflateInit(mz_streamp pStream); +@@ -386,7 +386,7 @@ int mz_inflateEnd(mz_streamp pStream); + int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + + /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +-const char *mz_error(int err); ++// const char *mz_error(int err); + + /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ + /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +@@ -436,13 +436,13 @@ typedef void *const voidpc; + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 +-#define deflateReset mz_deflateReset ++// #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd +-#define deflateBound mz_deflateBound ++// #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 +-#define compressBound mz_compressBound ++// #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate +@@ -452,15 +452,15 @@ typedef void *const voidpc; + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 +-#define zError mz_error ++// #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +-#define zlibVersion mz_version +-#define zlib_version mz_version() ++// #define zlibVersion mz_version ++// #define zlib_version mz_version() + #endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + + #endif /* MINIZ_NO_ZLIB_APIS */ +@@ -501,15 +501,15 @@ typedef int mz_bool; + #define MZ_FILE FILE + #endif /* #ifdef MINIZ_NO_STDIO */ + +-#ifdef MINIZ_NO_TIME +-typedef struct mz_dummy_time_t_tag +-{ +- int m_dummy; +-} mz_dummy_time_t; +-#define MZ_TIME_T mz_dummy_time_t +-#else +-#define MZ_TIME_T time_t +-#endif ++// #ifdef MINIZ_NO_TIME ++// typedef struct mz_dummy_time_t_tag ++// { ++// int m_dummy; ++// } mz_dummy_time_t; ++// #define MZ_TIME_T mz_dummy_time_t ++// #else ++// #define MZ_TIME_T time_t ++// #endif + + #define MZ_ASSERT(x) assert(x) + +@@ -551,7 +551,7 @@ extern "C" { + + extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); + extern void miniz_def_free_func(void *opaque, void *address); +-extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); ++// extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + + #define MZ_UINT16_MAX (0xFFFFU) + #define MZ_UINT32_MAX (0xFFFFFFFFU) +@@ -609,11 +609,11 @@ enum + /* Function returns a pointer to the compressed data, or NULL on failure. */ + /* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ + /* The caller must free() the returned block when it's no longer needed. */ +-void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); ++// void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + + /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ + /* Returns 0 on failure. */ +-size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); ++// size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + + /* Compresses an image to a compressed PNG file in memory. */ + /* On entry: */ +@@ -625,14 +625,14 @@ size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void + /* Function returns a pointer to the compressed data, or NULL on failure. */ + /* *pLen_out will be set to the size of the PNG image file. */ + /* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +-void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +-void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); ++// void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); ++// void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + + /* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ + typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + + /* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +-mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); ++// mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + + enum + { +@@ -727,9 +727,9 @@ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pI + + /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ + /* tdefl_compress_buffer() always consumes the entire input buffer. */ +-tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); ++// tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +-tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); ++// tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); + mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + + /* Create tdefl_compress() flags given zlib-style compression parameters. */ +@@ -741,8 +741,8 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int + /* Allocate the tdefl_compressor structure in C so that */ + /* non-C language bindings to tdefl_ API don't need to worry about */ + /* structure size and allocation mechanism. */ +-tdefl_compressor *tdefl_compressor_alloc(); +-void tdefl_compressor_free(tdefl_compressor *pComp); ++// tdefl_compressor *tdefl_compressor_alloc(); ++// void tdefl_compressor_free(tdefl_compressor *pComp); + + #ifdef __cplusplus + } +@@ -775,17 +775,17 @@ enum + /* Function returns a pointer to the decompressed data, or NULL on failure. */ + /* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ + /* The caller must call mz_free() on the returned block when it's no longer needed. */ +-void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); ++// void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + + /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ + /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ + #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +-size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); ++// size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + + /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ + /* Returns 1 on success or 0 on failure. */ + typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +-int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); ++// int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + + struct tinfl_decompressor_tag; + typedef struct tinfl_decompressor_tag tinfl_decompressor; +@@ -794,8 +794,8 @@ typedef struct tinfl_decompressor_tag tinfl_decompressor; + /* non-C language bindings to tinfl_ API don't need to worry about */ + /* structure size and allocation mechanism. */ + +-tinfl_decompressor *tinfl_decompressor_alloc(); +-void tinfl_decompressor_free(tinfl_decompressor *pDecomp); ++// tinfl_decompressor *tinfl_decompressor_alloc(); ++// void tinfl_decompressor_free(tinfl_decompressor *pDecomp); + + /* Max size of LZ dictionary. */ + #define TINFL_LZ_DICT_SIZE 32768 +@@ -896,319 +896,319 @@ struct tinfl_decompressor_tag + + /* ------------------- ZIP archive reading/writing */ + +-#ifndef MINIZ_NO_ARCHIVE_APIS ++// #ifndef MINIZ_NO_ARCHIVE_APIS + +-#ifdef __cplusplus +-extern "C" { +-#endif ++// #ifdef __cplusplus ++// extern "C" { ++// #endif + +-enum +-{ ++// enum ++// { + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ +- MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, +- MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, +- MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +-}; +- +-typedef struct +-{ +- /* Central directory file index. */ +- mz_uint32 m_file_index; ++// MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, ++// MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, ++// MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 ++// }; ++ ++// typedef struct ++// { ++// /* Central directory file index. */ ++// mz_uint32 m_file_index; ++ ++// /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ ++// mz_uint64 m_central_dir_ofs; ++ ++// /* These fields are copied directly from the zip's central dir. */ ++// mz_uint16 m_version_made_by; ++// mz_uint16 m_version_needed; ++// mz_uint16 m_bit_flag; ++// mz_uint16 m_method; ++ ++// #ifndef MINIZ_NO_TIME ++// MZ_TIME_T m_time; ++// #endif + +- /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ +- mz_uint64 m_central_dir_ofs; ++// /* CRC-32 of uncompressed data. */ ++// mz_uint32 m_crc32; + +- /* These fields are copied directly from the zip's central dir. */ +- mz_uint16 m_version_made_by; +- mz_uint16 m_version_needed; +- mz_uint16 m_bit_flag; +- mz_uint16 m_method; ++// /* File's compressed size. */ ++// mz_uint64 m_comp_size; + +-#ifndef MINIZ_NO_TIME +- MZ_TIME_T m_time; +-#endif ++// /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ ++// mz_uint64 m_uncomp_size; + +- /* CRC-32 of uncompressed data. */ +- mz_uint32 m_crc32; ++// /* Zip internal and external file attributes. */ ++// mz_uint16 m_internal_attr; ++// mz_uint32 m_external_attr; + +- /* File's compressed size. */ +- mz_uint64 m_comp_size; ++// /* Entry's local header file offset in bytes. */ ++// mz_uint64 m_local_header_ofs; + +- /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ +- mz_uint64 m_uncomp_size; ++// /* Size of comment in bytes. */ ++// mz_uint32 m_comment_size; + +- /* Zip internal and external file attributes. */ +- mz_uint16 m_internal_attr; +- mz_uint32 m_external_attr; ++// /* MZ_TRUE if the entry appears to be a directory. */ ++// mz_bool m_is_directory; + +- /* Entry's local header file offset in bytes. */ +- mz_uint64 m_local_header_ofs; ++// /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ ++// mz_bool m_is_encrypted; + +- /* Size of comment in bytes. */ +- mz_uint32 m_comment_size; ++// /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ ++// mz_bool m_is_supported; + +- /* MZ_TRUE if the entry appears to be a directory. */ +- mz_bool m_is_directory; ++// /* Filename. If string ends in '/' it's a subdirectory entry. */ ++// /* Guaranteed to be zero terminated, may be truncated to fit. */ ++// char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + +- /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ +- mz_bool m_is_encrypted; ++// /* Comment field. */ ++// /* Guaranteed to be zero terminated, may be truncated to fit. */ ++// char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +- /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ +- mz_bool m_is_supported; ++// } mz_zip_archive_file_stat; + +- /* Filename. If string ends in '/' it's a subdirectory entry. */ +- /* Guaranteed to be zero terminated, may be truncated to fit. */ +- char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; ++// typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); ++// typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); ++// typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +- /* Comment field. */ +- /* Guaranteed to be zero terminated, may be truncated to fit. */ +- char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; ++// struct mz_zip_internal_state_tag; ++// typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +-} mz_zip_archive_file_stat; ++// typedef enum { ++// MZ_ZIP_MODE_INVALID = 0, ++// MZ_ZIP_MODE_READING = 1, ++// MZ_ZIP_MODE_WRITING = 2, ++// MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 ++// } mz_zip_mode; + +-typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +-typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +-typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); ++// typedef enum { ++// MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, ++// MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, ++// MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, ++// MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, ++ // MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ ++ // MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ ++ // MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ ++// MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, ++// MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 ++// } mz_zip_flags; + +-struct mz_zip_internal_state_tag; +-typedef struct mz_zip_internal_state_tag mz_zip_internal_state; +- +-typedef enum { +- MZ_ZIP_MODE_INVALID = 0, +- MZ_ZIP_MODE_READING = 1, +- MZ_ZIP_MODE_WRITING = 2, +- MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +-} mz_zip_mode; +- +-typedef enum { +- MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, +- MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, +- MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, +- MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, +- MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ +- MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ +- MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ +- MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, +- MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 +-} mz_zip_flags; +- +-typedef enum { +- MZ_ZIP_TYPE_INVALID = 0, +- MZ_ZIP_TYPE_USER, +- MZ_ZIP_TYPE_MEMORY, +- MZ_ZIP_TYPE_HEAP, +- MZ_ZIP_TYPE_FILE, +- MZ_ZIP_TYPE_CFILE, +- MZ_ZIP_TOTAL_TYPES +-} mz_zip_type; ++// typedef enum { ++// MZ_ZIP_TYPE_INVALID = 0, ++// MZ_ZIP_TYPE_USER, ++// MZ_ZIP_TYPE_MEMORY, ++// MZ_ZIP_TYPE_HEAP, ++// MZ_ZIP_TYPE_FILE, ++// MZ_ZIP_TYPE_CFILE, ++// MZ_ZIP_TOTAL_TYPES ++// } mz_zip_type; + + /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +-typedef enum { +- MZ_ZIP_NO_ERROR = 0, +- MZ_ZIP_UNDEFINED_ERROR, +- MZ_ZIP_TOO_MANY_FILES, +- MZ_ZIP_FILE_TOO_LARGE, +- MZ_ZIP_UNSUPPORTED_METHOD, +- MZ_ZIP_UNSUPPORTED_ENCRYPTION, +- MZ_ZIP_UNSUPPORTED_FEATURE, +- MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, +- MZ_ZIP_NOT_AN_ARCHIVE, +- MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, +- MZ_ZIP_UNSUPPORTED_MULTIDISK, +- MZ_ZIP_DECOMPRESSION_FAILED, +- MZ_ZIP_COMPRESSION_FAILED, +- MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, +- MZ_ZIP_CRC_CHECK_FAILED, +- MZ_ZIP_UNSUPPORTED_CDIR_SIZE, +- MZ_ZIP_ALLOC_FAILED, +- MZ_ZIP_FILE_OPEN_FAILED, +- MZ_ZIP_FILE_CREATE_FAILED, +- MZ_ZIP_FILE_WRITE_FAILED, +- MZ_ZIP_FILE_READ_FAILED, +- MZ_ZIP_FILE_CLOSE_FAILED, +- MZ_ZIP_FILE_SEEK_FAILED, +- MZ_ZIP_FILE_STAT_FAILED, +- MZ_ZIP_INVALID_PARAMETER, +- MZ_ZIP_INVALID_FILENAME, +- MZ_ZIP_BUF_TOO_SMALL, +- MZ_ZIP_INTERNAL_ERROR, +- MZ_ZIP_FILE_NOT_FOUND, +- MZ_ZIP_ARCHIVE_TOO_LARGE, +- MZ_ZIP_VALIDATION_FAILED, +- MZ_ZIP_WRITE_CALLBACK_FAILED, +- MZ_ZIP_TOTAL_ERRORS +-} mz_zip_error; +- +-typedef struct +-{ +- mz_uint64 m_archive_size; +- mz_uint64 m_central_directory_file_ofs; +- +- /* We only support up to UINT32_MAX files in zip64 mode. */ +- mz_uint32 m_total_files; +- mz_zip_mode m_zip_mode; +- mz_zip_type m_zip_type; +- mz_zip_error m_last_error; +- +- mz_uint64 m_file_offset_alignment; +- +- mz_alloc_func m_pAlloc; +- mz_free_func m_pFree; +- mz_realloc_func m_pRealloc; +- void *m_pAlloc_opaque; +- +- mz_file_read_func m_pRead; +- mz_file_write_func m_pWrite; +- mz_file_needs_keepalive m_pNeeds_keepalive; +- void *m_pIO_opaque; +- +- mz_zip_internal_state *m_pState; +- +-} mz_zip_archive; +- +-typedef struct +-{ +- mz_zip_archive *pZip; +- mz_uint flags; +- +- int status; +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- mz_uint file_crc32; +-#endif +- mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; +- mz_zip_archive_file_stat file_stat; +- void *pRead_buf; +- void *pWrite_buf; ++// typedef enum { ++// MZ_ZIP_NO_ERROR = 0, ++// MZ_ZIP_UNDEFINED_ERROR, ++// MZ_ZIP_TOO_MANY_FILES, ++// MZ_ZIP_FILE_TOO_LARGE, ++// MZ_ZIP_UNSUPPORTED_METHOD, ++// MZ_ZIP_UNSUPPORTED_ENCRYPTION, ++// MZ_ZIP_UNSUPPORTED_FEATURE, ++// MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, ++// MZ_ZIP_NOT_AN_ARCHIVE, ++// MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, ++// MZ_ZIP_UNSUPPORTED_MULTIDISK, ++// MZ_ZIP_DECOMPRESSION_FAILED, ++// MZ_ZIP_COMPRESSION_FAILED, ++// MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, ++// MZ_ZIP_CRC_CHECK_FAILED, ++// MZ_ZIP_UNSUPPORTED_CDIR_SIZE, ++// MZ_ZIP_ALLOC_FAILED, ++// MZ_ZIP_FILE_OPEN_FAILED, ++// MZ_ZIP_FILE_CREATE_FAILED, ++// MZ_ZIP_FILE_WRITE_FAILED, ++// MZ_ZIP_FILE_READ_FAILED, ++// MZ_ZIP_FILE_CLOSE_FAILED, ++// MZ_ZIP_FILE_SEEK_FAILED, ++// MZ_ZIP_FILE_STAT_FAILED, ++// MZ_ZIP_INVALID_PARAMETER, ++// MZ_ZIP_INVALID_FILENAME, ++// MZ_ZIP_BUF_TOO_SMALL, ++// MZ_ZIP_INTERNAL_ERROR, ++// MZ_ZIP_FILE_NOT_FOUND, ++// MZ_ZIP_ARCHIVE_TOO_LARGE, ++// MZ_ZIP_VALIDATION_FAILED, ++// MZ_ZIP_WRITE_CALLBACK_FAILED, ++// MZ_ZIP_TOTAL_ERRORS ++// } mz_zip_error; ++ ++// typedef struct ++// { ++// mz_uint64 m_archive_size; ++// mz_uint64 m_central_directory_file_ofs; ++ ++// /* We only support up to UINT32_MAX files in zip64 mode. */ ++// mz_uint32 m_total_files; ++// mz_zip_mode m_zip_mode; ++// mz_zip_type m_zip_type; ++// mz_zip_error m_last_error; ++ ++// mz_uint64 m_file_offset_alignment; ++ ++// mz_alloc_func m_pAlloc; ++// mz_free_func m_pFree; ++// mz_realloc_func m_pRealloc; ++// void *m_pAlloc_opaque; ++ ++// mz_file_read_func m_pRead; ++// mz_file_write_func m_pWrite; ++// mz_file_needs_keepalive m_pNeeds_keepalive; ++// void *m_pIO_opaque; ++ ++// mz_zip_internal_state *m_pState; ++ ++// } mz_zip_archive; ++ ++// typedef struct ++// { ++// mz_zip_archive *pZip; ++// mz_uint flags; ++ ++// int status; ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// mz_uint file_crc32; ++// #endif ++// mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; ++// mz_zip_archive_file_stat file_stat; ++// void *pRead_buf; ++// void *pWrite_buf; + +- size_t out_blk_remain; ++// size_t out_blk_remain; + +- tinfl_decompressor inflator; ++// tinfl_decompressor inflator; + +-} mz_zip_reader_extract_iter_state; ++// } mz_zip_reader_extract_iter_state; + + /* -------- ZIP reading */ + + /* Inits a ZIP archive reader. */ + /* These functions read and validate the archive's central directory. */ +-mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); ++// mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +-mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); ++// mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +-#ifndef MINIZ_NO_STDIO ++// #ifndef MINIZ_NO_STDIO + /* Read a archive from a disk file. */ + /* file_start_ofs is the file offset where the archive actually begins, or 0. */ + /* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +-mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +-mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); ++// mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); ++// mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + + /* Read an archive from an already opened FILE, beginning at the current file position. */ + /* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ + /* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +-mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +-#endif ++// mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); ++// #endif + + /* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +-mz_bool mz_zip_reader_end(mz_zip_archive *pZip); ++// mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + + /* -------- ZIP reading or writing */ + + /* Clears a mz_zip_archive struct to all zeros. */ + /* Important: This must be done before passing the struct to any mz_zip functions. */ +-void mz_zip_zero_struct(mz_zip_archive *pZip); ++// void mz_zip_zero_struct(mz_zip_archive *pZip); + +-mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +-mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); ++// mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); ++// mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + + /* Returns the total number of files in the archive. */ +-mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); ++// mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +-mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +-mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +-MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); ++// mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); ++// mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); ++// MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + + /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +-size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); ++// size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + + /* Attempts to locates a file in the archive's central directory. */ + /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ + /* Returns -1 if the file cannot be found. */ +-int mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); ++// int mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + /* Returns MZ_FALSE if the file cannot be found. */ +-mz_bool mz_zip_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex); ++// mz_bool mz_zip_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex); + + /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ + /* Note that the m_last_error functionality is not thread safe. */ +-mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +-mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +-mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +-mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +-const char *mz_zip_get_error_string(mz_zip_error mz_err); ++// mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); ++// mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); ++// mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); ++// mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); ++// const char *mz_zip_get_error_string(mz_zip_error mz_err); + + /* MZ_TRUE if the archive file entry is a directory entry. */ +-mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); ++// mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + + /* MZ_TRUE if the file is encrypted/strong encrypted. */ +-mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); ++// mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + + /* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +-mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); ++// mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + + /* Retrieves the filename of an archive file entry. */ + /* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +-mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); ++// mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + + /* Attempts to locates a file in the archive's central directory. */ + /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ + /* Returns -1 if the file cannot be found. */ +-int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +-int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); ++// int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); ++// int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + + /* Returns detailed information about an archive file entry. */ +-mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); ++// mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + + /* MZ_TRUE if the file is in zip64 format. */ + /* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +-mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); ++// mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + + /* Returns the total central directory size in bytes. */ + /* The current max supported size is <= MZ_UINT32_MAX. */ +-size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); ++// size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + + /* Extracts a archive file to a memory buffer using no memory allocation. */ + /* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +-mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +-mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); ++// mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); ++// mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + + /* Extracts a archive file to a memory buffer. */ +-mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +-mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); ++// mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); ++// mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + + /* Extracts a archive file to a dynamically allocated heap buffer. */ + /* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ + /* Returns NULL and sets the last error on failure. */ +-void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +-void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); ++// void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); ++// void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + + /* Extracts a archive file using a callback function to output the file's data. */ +-mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +-mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); ++// mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); ++// mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + + /* Extract a file iteratively */ +-mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +-mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +-size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +-mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); ++// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); ++// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); ++// size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); ++// mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +-#ifndef MINIZ_NO_STDIO ++// #ifndef MINIZ_NO_STDIO + /* Extracts a archive file to a disk file and sets its last accessed and modified times. */ + /* This function only extracts files, not archive directory records. */ +-mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +-mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); ++// mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); ++// mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + + /* Extracts a archive file starting at the current position in the destination FILE stream. */ +-mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +-mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +-#endif ++// mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); ++// mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); ++// #endif + + #if 0 + /* TODO */ +@@ -1233,26 +1233,26 @@ mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pA + // mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); + + /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +-mz_bool mz_zip_end(mz_zip_archive *pZip); ++// mz_bool mz_zip_end(mz_zip_archive *pZip); + + /* -------- ZIP writing */ + +-#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS ++// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + + /* Inits a ZIP archive writer. */ + /*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ + /*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +-mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +-mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); ++// mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); ++// mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +-mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +-mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); ++// mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); ++// mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +-#ifndef MINIZ_NO_STDIO +-mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +-mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +-mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +-#endif ++// #ifndef MINIZ_NO_STDIO ++// mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); ++// mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); ++// mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); ++// #endif + + /* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ + /* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +@@ -1260,33 +1260,33 @@ mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint f + /* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ + /* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ + /* the archive is finalized the file's central directory will be hosed. */ +-mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +-mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); ++// mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); ++// mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + + /* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ + /* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ + /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +-mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); ++// mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + + /* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ + /* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +-mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +- mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); ++// mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, ++// mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +-mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +- mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, +- const char *user_extra_data_central, mz_uint user_extra_data_central_len); ++// mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, ++// mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, ++// const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +-#ifndef MINIZ_NO_STDIO ++// #ifndef MINIZ_NO_STDIO + /* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ + /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +-mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); ++// mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + /* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +-mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, +- const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, +- const char *user_extra_data_central, mz_uint user_extra_data_central_len); +-#endif ++// mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, ++// const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, ++// const char *user_extra_data_central, mz_uint user_extra_data_central_len); ++// #endif + + /* Adds a file to an archive by fully cloning the data from another archive. */ + /* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +@@ -1295,15 +1295,15 @@ mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, + /* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ + /* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ + /* An archive must be manually finalized by calling this function for it to be valid. */ +-mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); ++// mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + + /* Finalizes a heap archive, returning a poiner to the heap block and its size. */ + /* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +-mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); ++// mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + + /* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ + /* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +-mz_bool mz_zip_writer_end(mz_zip_archive *pZip); ++// mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + + /* -------- Misc. high-level helper functions: */ + +@@ -1311,19 +1311,19 @@ mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + /* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ + /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ + /* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +-mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +-mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); ++// mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); ++// mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + + /* Reads a single file from an archive into a heap block. */ + /* If pComment is not NULL, only the file with the specified comment will be extracted. */ + /* Returns NULL on failure. */ +-void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +-void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); ++// void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); ++// void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); + +-#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ ++// #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +-#ifdef __cplusplus +-} +-#endif ++// #ifdef __cplusplus ++// } ++// #endif + +-#endif /* MINIZ_NO_ARCHIVE_APIS */ ++// #endif /* MINIZ_NO_ARCHIVE_APIS */ + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +diff --git a/src/zip/miniz/miniz.c b/src/zip/miniz/miniz.c +index 9ee7635..910d4b1 100644 +--- a/src/zip/miniz/miniz.c ++++ b/src/zip/miniz/miniz.c +@@ -65,92 +65,92 @@ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) + } + + /* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +-#if 0 +- mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +- { +- static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, +- 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; +- mz_uint32 crcu32 = (mz_uint32)crc; +- if (!ptr) +- return MZ_CRC32_INIT; +- crcu32 = ~crcu32; +- while (buf_len--) +- { +- mz_uint8 b = *ptr++; +- crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; +- crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; +- } +- return ~crcu32; +- } +-#else ++// #if 0 ++// mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) ++// { ++// static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, ++// 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; ++// mz_uint32 crcu32 = (mz_uint32)crc; ++// if (!ptr) ++// return MZ_CRC32_INIT; ++// crcu32 = ~crcu32; ++// while (buf_len--) ++// { ++// mz_uint8 b = *ptr++; ++// crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; ++// crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; ++// } ++// return ~crcu32; ++// } ++// #else + /* Faster, but larger CPU cache footprint. + */ +-mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +-{ +- static const mz_uint32 s_crc_table[256] = +- { +- 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, +- 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, +- 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, +- 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, +- 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, +- 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, +- 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, +- 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, +- 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, +- 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, +- 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, +- 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, +- 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, +- 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, +- 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, +- 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, +- 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, +- 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, +- 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, +- 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, +- 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, +- 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, +- 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, +- 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, +- 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, +- 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, +- 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, +- 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, +- 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, +- 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, +- 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, +- 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, +- 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, +- 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, +- 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, +- 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, +- 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +- }; +- +- mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; +- const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; +- +- while (buf_len >= 4) +- { +- crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; +- crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; +- crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; +- crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; +- pByte_buf += 4; +- buf_len -= 4; +- } ++// mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) ++// { ++// static const mz_uint32 s_crc_table[256] = ++// { ++// 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, ++// 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, ++// 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, ++// 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, ++// 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, ++// 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, ++// 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, ++// 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, ++// 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, ++// 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, ++// 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, ++// 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, ++// 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, ++// 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, ++// 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, ++// 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, ++// 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, ++// 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, ++// 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, ++// 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, ++// 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, ++// 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, ++// 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, ++// 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, ++// 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, ++// 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, ++// 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, ++// 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, ++// 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, ++// 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, ++// 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, ++// 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, ++// 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, ++// 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, ++// 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, ++// 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, ++// 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D ++// }; ++ ++// mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; ++// const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; ++ ++// while (buf_len >= 4) ++// { ++// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; ++// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; ++// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; ++// pByte_buf += 4; ++// buf_len -= 4; ++// } + +- while (buf_len) +- { +- crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; +- ++pByte_buf; +- --buf_len; +- } ++// while (buf_len) ++// { ++// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++// ++pByte_buf; ++// --buf_len; ++// } + +- return ~crc32; +-} +-#endif ++// return ~crc32; ++// } ++// #endif + + void mz_free(void *p) + { +@@ -167,16 +167,16 @@ void miniz_def_free_func(void *opaque, void *address) + (void)opaque, (void)address; + MZ_FREE(address); + } +-void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +-{ +- (void)opaque, (void)address, (void)items, (void)size; +- return MZ_REALLOC(address, items * size); +-} ++// void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) ++// { ++// (void)opaque, (void)address, (void)items, (void)size; ++// return MZ_REALLOC(address, items * size); ++// } + +-const char *mz_version(void) +-{ +- return MZ_VERSION; +-} ++// const char *mz_version(void) ++// { ++// return MZ_VERSION; ++// } + + #ifndef MINIZ_NO_ZLIB_APIS + +@@ -221,14 +221,14 @@ int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, + return MZ_OK; + } + +-int mz_deflateReset(mz_streamp pStream) +-{ +- if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) +- return MZ_STREAM_ERROR; +- pStream->total_in = pStream->total_out = 0; +- tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); +- return MZ_OK; +-} ++// int mz_deflateReset(mz_streamp pStream) ++// { ++// if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) ++// return MZ_STREAM_ERROR; ++// pStream->total_in = pStream->total_out = 0; ++// tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); ++// return MZ_OK; ++// } + + int mz_deflate(mz_streamp pStream, int flush) + { +@@ -300,12 +300,12 @@ int mz_deflateEnd(mz_streamp pStream) + return MZ_OK; + } + +-mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +-{ +- (void)pStream; +- /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ +- return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +-} ++// mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) ++// { ++// (void)pStream; ++// This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) ++// return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); ++// } + + int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) + { +@@ -342,10 +342,10 @@ int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char * + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); + } + +-mz_ulong mz_compressBound(mz_ulong source_len) +-{ +- return mz_deflateBound(NULL, source_len); +-} ++// mz_ulong mz_compressBound(mz_ulong source_len) ++// { ++// return mz_deflateBound(NULL, source_len); ++// } + + typedef struct + { +@@ -551,22 +551,22 @@ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char + return mz_inflateEnd(&stream); + } + +-const char *mz_error(int err) +-{ +- static struct +- { +- int m_err; +- const char *m_pDesc; +- } s_error_descs[] = +- { +- { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } +- }; +- mz_uint i; +- for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) +- if (s_error_descs[i].m_err == err) +- return s_error_descs[i].m_pDesc; +- return NULL; +-} ++// const char *mz_error(int err) ++// { ++// static struct ++// { ++// int m_err; ++// const char *m_pDesc; ++// } s_error_descs[] = ++// { ++// { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } ++// }; ++// mz_uint i; ++// for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) ++// if (s_error_descs[i].m_err == err) ++// return s_error_descs[i].m_pDesc; ++// return NULL; ++// } + + #endif /*MINIZ_NO_ZLIB_APIS */ + +@@ -1049,7 +1049,7 @@ static void tdefl_start_static_block(tdefl_compressor *d) + TDEFL_PUT_BITS(1, 2); + } + +-static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; ++static const mz_uint16 mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + + #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS + static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +@@ -1358,7 +1358,6 @@ static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) + #endif + static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) + { +- printf("\n--------------------------------------------------- DEBUG ---------------------------------------\n"); + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; +@@ -1456,176 +1455,176 @@ static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahe + } + #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +-static mz_bool tdefl_compress_fast(tdefl_compressor *d) +-{ +- /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ +- mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; +- mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; +- mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; ++// #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN ++// static mz_bool tdefl_compress_fast(tdefl_compressor *d) ++// { ++// /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ ++// mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; ++// mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; ++// mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + +- while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) +- { +- const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; +- mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; +- mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); +- d->m_src_buf_left -= num_bytes_to_process; +- lookahead_size += num_bytes_to_process; ++// while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) ++// { ++// const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; ++// mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; ++// mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); ++// d->m_src_buf_left -= num_bytes_to_process; ++// lookahead_size += num_bytes_to_process; + +- while (num_bytes_to_process) +- { +- mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); +- memcpy(d->m_dict + dst_pos, d->m_pSrc, n); +- if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) +- memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); +- d->m_pSrc += n; +- dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; +- num_bytes_to_process -= n; +- } ++// while (num_bytes_to_process) ++// { ++// mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); ++// memcpy(d->m_dict + dst_pos, d->m_pSrc, n); ++// if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) ++// memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); ++// d->m_pSrc += n; ++// dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; ++// num_bytes_to_process -= n; ++// } + +- dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); +- if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) +- break; ++// dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); ++// if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) ++// break; + +- while (lookahead_size >= 4) +- { +- mz_uint cur_match_dist, cur_match_len = 1; +- mz_uint8 *pCur_dict = d->m_dict + cur_pos; +- mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; +- mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; +- mz_uint probe_pos = d->m_hash[hash]; +- d->m_hash[hash] = (mz_uint16)lookahead_pos; +- +- if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) +- { +- const mz_uint16 *p = (const mz_uint16 *)pCur_dict; +- const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); +- mz_uint32 probe_len = 32; +- do +- { +- } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && +- (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); +- cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); +- if (!probe_len) +- cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; ++// while (lookahead_size >= 4) ++// { ++// mz_uint cur_match_dist, cur_match_len = 1; ++// mz_uint8 *pCur_dict = d->m_dict + cur_pos; ++// mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; ++// mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; ++// mz_uint probe_pos = d->m_hash[hash]; ++// d->m_hash[hash] = (mz_uint16)lookahead_pos; ++ ++// if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) ++// { ++// const mz_uint16 *p = (const mz_uint16 *)pCur_dict; ++// const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); ++// mz_uint32 probe_len = 32; ++// do ++// { ++// } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && ++// (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); ++// cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); ++// if (!probe_len) ++// cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + +- if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) +- { +- cur_match_len = 1; +- *pLZ_code_buf++ = (mz_uint8)first_trigram; +- *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); +- d->m_huff_count[0][(mz_uint8)first_trigram]++; +- } +- else +- { +- mz_uint32 s0, s1; +- cur_match_len = MZ_MIN(cur_match_len, lookahead_size); ++// if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) ++// { ++// cur_match_len = 1; ++// *pLZ_code_buf++ = (mz_uint8)first_trigram; ++// *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); ++// d->m_huff_count[0][(mz_uint8)first_trigram]++; ++// } ++// else ++// { ++// mz_uint32 s0, s1; ++// cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + +- MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); ++// MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + +- cur_match_dist--; ++// cur_match_dist--; + +- pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +- *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +- pLZ_code_buf += 3; +- *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); ++// pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); ++// *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; ++// pLZ_code_buf += 3; ++// *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + +- s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; +- s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; +- d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; ++// s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; ++// s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; ++// d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + +- d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; +- } +- } +- else +- { +- *pLZ_code_buf++ = (mz_uint8)first_trigram; +- *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); +- d->m_huff_count[0][(mz_uint8)first_trigram]++; +- } ++// d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; ++// } ++// } ++// else ++// { ++// *pLZ_code_buf++ = (mz_uint8)first_trigram; ++// *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); ++// d->m_huff_count[0][(mz_uint8)first_trigram]++; ++// } + +- if (--num_flags_left == 0) +- { +- num_flags_left = 8; +- pLZ_flags = pLZ_code_buf++; +- } ++// if (--num_flags_left == 0) ++// { ++// num_flags_left = 8; ++// pLZ_flags = pLZ_code_buf++; ++// } + +- total_lz_bytes += cur_match_len; +- lookahead_pos += cur_match_len; +- dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); +- cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; +- MZ_ASSERT(lookahead_size >= cur_match_len); +- lookahead_size -= cur_match_len; ++// total_lz_bytes += cur_match_len; ++// lookahead_pos += cur_match_len; ++// dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); ++// cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; ++// MZ_ASSERT(lookahead_size >= cur_match_len); ++// lookahead_size -= cur_match_len; + +- if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) +- { +- int n; +- d->m_lookahead_pos = lookahead_pos; +- d->m_lookahead_size = lookahead_size; +- d->m_dict_size = dict_size; +- d->m_total_lz_bytes = total_lz_bytes; +- d->m_pLZ_code_buf = pLZ_code_buf; +- d->m_pLZ_flags = pLZ_flags; +- d->m_num_flags_left = num_flags_left; +- if ((n = tdefl_flush_block(d, 0)) != 0) +- return (n < 0) ? MZ_FALSE : MZ_TRUE; +- total_lz_bytes = d->m_total_lz_bytes; +- pLZ_code_buf = d->m_pLZ_code_buf; +- pLZ_flags = d->m_pLZ_flags; +- num_flags_left = d->m_num_flags_left; +- } +- } ++// if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ++// { ++// int n; ++// d->m_lookahead_pos = lookahead_pos; ++// d->m_lookahead_size = lookahead_size; ++// d->m_dict_size = dict_size; ++// d->m_total_lz_bytes = total_lz_bytes; ++// d->m_pLZ_code_buf = pLZ_code_buf; ++// d->m_pLZ_flags = pLZ_flags; ++// d->m_num_flags_left = num_flags_left; ++// if ((n = tdefl_flush_block(d, 0)) != 0) ++// return (n < 0) ? MZ_FALSE : MZ_TRUE; ++// total_lz_bytes = d->m_total_lz_bytes; ++// pLZ_code_buf = d->m_pLZ_code_buf; ++// pLZ_flags = d->m_pLZ_flags; ++// num_flags_left = d->m_num_flags_left; ++// } ++// } + +- while (lookahead_size) +- { +- mz_uint8 lit = d->m_dict[cur_pos]; ++// while (lookahead_size) ++// { ++// mz_uint8 lit = d->m_dict[cur_pos]; + +- total_lz_bytes++; +- *pLZ_code_buf++ = lit; +- *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); +- if (--num_flags_left == 0) +- { +- num_flags_left = 8; +- pLZ_flags = pLZ_code_buf++; +- } ++// total_lz_bytes++; ++// *pLZ_code_buf++ = lit; ++// *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); ++// if (--num_flags_left == 0) ++// { ++// num_flags_left = 8; ++// pLZ_flags = pLZ_code_buf++; ++// } + +- d->m_huff_count[0][lit]++; ++// d->m_huff_count[0][lit]++; + +- lookahead_pos++; +- dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); +- cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; +- lookahead_size--; ++// lookahead_pos++; ++// dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); ++// cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ++// lookahead_size--; + +- if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) +- { +- int n; +- d->m_lookahead_pos = lookahead_pos; +- d->m_lookahead_size = lookahead_size; +- d->m_dict_size = dict_size; +- d->m_total_lz_bytes = total_lz_bytes; +- d->m_pLZ_code_buf = pLZ_code_buf; +- d->m_pLZ_flags = pLZ_flags; +- d->m_num_flags_left = num_flags_left; +- if ((n = tdefl_flush_block(d, 0)) != 0) +- return (n < 0) ? MZ_FALSE : MZ_TRUE; +- total_lz_bytes = d->m_total_lz_bytes; +- pLZ_code_buf = d->m_pLZ_code_buf; +- pLZ_flags = d->m_pLZ_flags; +- num_flags_left = d->m_num_flags_left; +- } +- } +- } ++// if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ++// { ++// int n; ++// d->m_lookahead_pos = lookahead_pos; ++// d->m_lookahead_size = lookahead_size; ++// d->m_dict_size = dict_size; ++// d->m_total_lz_bytes = total_lz_bytes; ++// d->m_pLZ_code_buf = pLZ_code_buf; ++// d->m_pLZ_flags = pLZ_flags; ++// d->m_num_flags_left = num_flags_left; ++// if ((n = tdefl_flush_block(d, 0)) != 0) ++// return (n < 0) ? MZ_FALSE : MZ_TRUE; ++// total_lz_bytes = d->m_total_lz_bytes; ++// pLZ_code_buf = d->m_pLZ_code_buf; ++// pLZ_flags = d->m_pLZ_flags; ++// num_flags_left = d->m_num_flags_left; ++// } ++// } ++// } + +- d->m_lookahead_pos = lookahead_pos; +- d->m_lookahead_size = lookahead_size; +- d->m_dict_size = dict_size; +- d->m_total_lz_bytes = total_lz_bytes; +- d->m_pLZ_code_buf = pLZ_code_buf; +- d->m_pLZ_flags = pLZ_flags; +- d->m_num_flags_left = num_flags_left; +- return MZ_TRUE; +-} +-#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ ++// d->m_lookahead_pos = lookahead_pos; ++// d->m_lookahead_size = lookahead_size; ++// d->m_dict_size = dict_size; ++// d->m_total_lz_bytes = total_lz_bytes; ++// d->m_pLZ_code_buf = pLZ_code_buf; ++// d->m_pLZ_flags = pLZ_flags; ++// d->m_num_flags_left = num_flags_left; ++// return MZ_TRUE; ++// } ++// #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + + static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) + { +@@ -1870,16 +1869,16 @@ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pI + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +- if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && +- ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && +- ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) +- { +- if (!tdefl_compress_fast(d)) +- return d->m_prev_return_status; +- } +- else +-#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ ++// #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN ++// if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ++// ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ++// ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) ++// { ++// if (!tdefl_compress_fast(d)) ++// return d->m_prev_return_status; ++// } ++// else ++// #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; +@@ -1904,11 +1903,11 @@ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pI + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + } + +-tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +-{ +- MZ_ASSERT(d->m_pPut_buf_func); +- return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +-} ++// tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) ++// { ++// MZ_ASSERT(d->m_pPut_buf_func); ++// return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); ++// } + + tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) + { +@@ -1944,92 +1943,92 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun + return TDEFL_STATUS_OKAY; + } + +-tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +-{ +- return d->m_prev_return_status; +-} ++// tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) ++// { ++// return d->m_prev_return_status; ++// } + + mz_uint32 tdefl_get_adler32(tdefl_compressor *d) + { + return d->m_adler32; + } + +-mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +-{ +- tdefl_compressor *pComp; +- mz_bool succeeded; +- if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) +- return MZ_FALSE; +- pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +- if (!pComp) +- return MZ_FALSE; +- succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); +- succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); +- MZ_FREE(pComp); +- return succeeded; +-} ++// mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) ++// { ++// tdefl_compressor *pComp; ++// mz_bool succeeded; ++// if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) ++// return MZ_FALSE; ++// pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); ++// if (!pComp) ++// return MZ_FALSE; ++// succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); ++// succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); ++// MZ_FREE(pComp); ++// return succeeded; ++// } + +-typedef struct +-{ +- size_t m_size, m_capacity; +- mz_uint8 *m_pBuf; +- mz_bool m_expandable; +-} tdefl_output_buffer; ++// typedef struct ++// { ++// size_t m_size, m_capacity; ++// mz_uint8 *m_pBuf; ++// mz_bool m_expandable; ++// } tdefl_output_buffer; + +-static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +-{ +- tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; +- size_t new_size = p->m_size + len; +- if (new_size > p->m_capacity) +- { +- size_t new_capacity = p->m_capacity; +- mz_uint8 *pNew_buf; +- if (!p->m_expandable) +- return MZ_FALSE; +- do +- { +- new_capacity = MZ_MAX(128U, new_capacity << 1U); +- } while (new_size > new_capacity); +- pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); +- if (!pNew_buf) +- return MZ_FALSE; +- p->m_pBuf = pNew_buf; +- p->m_capacity = new_capacity; +- } +- memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); +- p->m_size = new_size; +- return MZ_TRUE; +-} ++// static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) ++// { ++// tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; ++// size_t new_size = p->m_size + len; ++// if (new_size > p->m_capacity) ++// { ++// size_t new_capacity = p->m_capacity; ++// mz_uint8 *pNew_buf; ++// if (!p->m_expandable) ++// return MZ_FALSE; ++// do ++// { ++// new_capacity = MZ_MAX(128U, new_capacity << 1U); ++// } while (new_size > new_capacity); ++// pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); ++// if (!pNew_buf) ++// return MZ_FALSE; ++// p->m_pBuf = pNew_buf; ++// p->m_capacity = new_capacity; ++// } ++// memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); ++// p->m_size = new_size; ++// return MZ_TRUE; ++// } + +-void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +-{ +- tdefl_output_buffer out_buf; +- MZ_CLEAR_OBJ(out_buf); +- if (!pOut_len) +- return MZ_FALSE; +- else +- *pOut_len = 0; +- out_buf.m_expandable = MZ_TRUE; +- if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) +- return NULL; +- *pOut_len = out_buf.m_size; +- return out_buf.m_pBuf; +-} ++// void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) ++// { ++// tdefl_output_buffer out_buf; ++// MZ_CLEAR_OBJ(out_buf); ++// if (!pOut_len) ++// return MZ_FALSE; ++// else ++// *pOut_len = 0; ++// out_buf.m_expandable = MZ_TRUE; ++// if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) ++// return NULL; ++// *pOut_len = out_buf.m_size; ++// return out_buf.m_pBuf; ++// } + +-size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +-{ +- tdefl_output_buffer out_buf; +- MZ_CLEAR_OBJ(out_buf); +- if (!pOut_buf) +- return 0; +- out_buf.m_pBuf = (mz_uint8 *)pOut_buf; +- out_buf.m_capacity = out_buf_len; +- if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) +- return 0; +- return out_buf.m_size; +-} ++// size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) ++// { ++// tdefl_output_buffer out_buf; ++// MZ_CLEAR_OBJ(out_buf); ++// if (!pOut_buf) ++// return 0; ++// out_buf.m_pBuf = (mz_uint8 *)pOut_buf; ++// out_buf.m_capacity = out_buf_len; ++// if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) ++// return 0; ++// return out_buf.m_size; ++// } + +-static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; ++static const mz_uint16 s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + + /* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ + mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +@@ -2060,102 +2059,102 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int + /* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +-void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +-{ +- /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ +- static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; +- tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +- tdefl_output_buffer out_buf; +- int i, bpl = w * num_chans, y, z; +- mz_uint32 c; +- *pLen_out = 0; +- if (!pComp) +- return NULL; +- MZ_CLEAR_OBJ(out_buf); +- out_buf.m_expandable = MZ_TRUE; +- out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); +- if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) +- { +- MZ_FREE(pComp); +- return NULL; +- } +- /* write dummy header */ +- for (z = 41; z; --z) +- tdefl_output_buffer_putter(&z, 1, &out_buf); +- /* compress image data */ +- tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); +- for (y = 0; y < h; ++y) +- { +- tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); +- tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); +- } +- if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) +- { +- MZ_FREE(pComp); +- MZ_FREE(out_buf.m_pBuf); +- return NULL; +- } +- /* write real header */ +- *pLen_out = out_buf.m_size - 41; +- { +- static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; +- mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, +- 0x0a, 0x1a, 0x0a, 0x00, 0x00, +- 0x00, 0x0d, 0x49, 0x48, 0x44, +- 0x52, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x08, +- 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x49, 0x44, 0x41, +- 0x54 }; +- pnghdr[18] = (mz_uint8)(w >> 8); +- pnghdr[19] = (mz_uint8)w; +- pnghdr[22] = (mz_uint8)(h >> 8); +- pnghdr[23] = (mz_uint8)h; +- pnghdr[25] = chans[num_chans]; +- pnghdr[33] = (mz_uint8)(*pLen_out >> 24); +- pnghdr[34] = (mz_uint8)(*pLen_out >> 16); +- pnghdr[35] = (mz_uint8)(*pLen_out >> 8); +- pnghdr[36] = (mz_uint8)*pLen_out; +- c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); +- for (i = 0; i < 4; ++i, c <<= 8) +- ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); +- memcpy(out_buf.m_pBuf, pnghdr, 41); +- } +- /* write footer (IDAT CRC-32, followed by IEND chunk) */ +- if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) +- { +- *pLen_out = 0; +- MZ_FREE(pComp); +- MZ_FREE(out_buf.m_pBuf); +- return NULL; +- } +- c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); +- for (i = 0; i < 4; ++i, c <<= 8) +- (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); +- /* compute final size of file, grab compressed data buffer and return */ +- *pLen_out += 57; +- MZ_FREE(pComp); +- return out_buf.m_pBuf; +-} +-void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +-{ ++// void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) ++// { ++// /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ ++// static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; ++// tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); ++// tdefl_output_buffer out_buf; ++// int i, bpl = w * num_chans, y, z; ++// mz_uint32 c; ++// *pLen_out = 0; ++// if (!pComp) ++// return NULL; ++// MZ_CLEAR_OBJ(out_buf); ++// out_buf.m_expandable = MZ_TRUE; ++// out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); ++// if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) ++// { ++// MZ_FREE(pComp); ++// return NULL; ++// } ++// /* write dummy header */ ++// for (z = 41; z; --z) ++// tdefl_output_buffer_putter(&z, 1, &out_buf); ++// /* compress image data */ ++// tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); ++// for (y = 0; y < h; ++y) ++// { ++// tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); ++// tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); ++// } ++// if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) ++// { ++// MZ_FREE(pComp); ++// MZ_FREE(out_buf.m_pBuf); ++// return NULL; ++// } ++// /* write real header */ ++// *pLen_out = out_buf.m_size - 41; ++// { ++// static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; ++// mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, ++// 0x0a, 0x1a, 0x0a, 0x00, 0x00, ++// 0x00, 0x0d, 0x49, 0x48, 0x44, ++// 0x52, 0x00, 0x00, 0x00, 0x00, ++// 0x00, 0x00, 0x00, 0x00, 0x08, ++// 0x00, 0x00, 0x00, 0x00, 0x00, ++// 0x00, 0x00, 0x00, 0x00, 0x00, ++// 0x00, 0x00, 0x49, 0x44, 0x41, ++// 0x54 }; ++// pnghdr[18] = (mz_uint8)(w >> 8); ++// pnghdr[19] = (mz_uint8)w; ++// pnghdr[22] = (mz_uint8)(h >> 8); ++// pnghdr[23] = (mz_uint8)h; ++// pnghdr[25] = chans[num_chans]; ++// pnghdr[33] = (mz_uint8)(*pLen_out >> 24); ++// pnghdr[34] = (mz_uint8)(*pLen_out >> 16); ++// pnghdr[35] = (mz_uint8)(*pLen_out >> 8); ++// pnghdr[36] = (mz_uint8)*pLen_out; ++// c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); ++// for (i = 0; i < 4; ++i, c <<= 8) ++// ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); ++// memcpy(out_buf.m_pBuf, pnghdr, 41); ++// } ++// /* write footer (IDAT CRC-32, followed by IEND chunk) */ ++// if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) ++// { ++// *pLen_out = 0; ++// MZ_FREE(pComp); ++// MZ_FREE(out_buf.m_pBuf); ++// return NULL; ++// } ++// c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); ++// for (i = 0; i < 4; ++i, c <<= 8) ++// (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); ++// /* compute final size of file, grab compressed data buffer and return */ ++// *pLen_out += 57; ++// MZ_FREE(pComp); ++// return out_buf.m_pBuf; ++// } ++// void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) ++// { + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ +- return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +-} ++// return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); ++// } + + /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ + /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ + /* structure size and allocation mechanism. */ +-tdefl_compressor *tdefl_compressor_alloc() +-{ +- return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +-} ++// tdefl_compressor *tdefl_compressor_alloc() ++// { ++// return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); ++// } + +-void tdefl_compressor_free(tdefl_compressor *pComp) +-{ +- MZ_FREE(pComp); +-} ++// void tdefl_compressor_free(tdefl_compressor *pComp) ++// { ++// MZ_FREE(pComp); ++// } + + #ifdef _MSC_VER + #pragma warning(pop) +@@ -2339,12 +2338,12 @@ extern "C" { + + 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_uint16 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 mz_uint8 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 mz_uint16 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 mz_uint8 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 }; ++ static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; +@@ -2805,94 +2804,94 @@ common_exit: + } + + /* Higher level helper functions. */ +-void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +-{ +- tinfl_decompressor decomp; +- void *pBuf = NULL, *pNew_buf; +- size_t src_buf_ofs = 0, out_buf_capacity = 0; +- *pOut_len = 0; +- tinfl_init(&decomp); +- for (;;) +- { +- size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; +- tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, +- (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); +- if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) +- { +- MZ_FREE(pBuf); +- *pOut_len = 0; +- return NULL; +- } +- src_buf_ofs += src_buf_size; +- *pOut_len += dst_buf_size; +- if (status == TINFL_STATUS_DONE) +- break; +- new_out_buf_capacity = out_buf_capacity * 2; +- if (new_out_buf_capacity < 128) +- new_out_buf_capacity = 128; +- pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); +- if (!pNew_buf) +- { +- MZ_FREE(pBuf); +- *pOut_len = 0; +- return NULL; +- } +- pBuf = pNew_buf; +- out_buf_capacity = new_out_buf_capacity; +- } +- return pBuf; +-} ++// void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) ++// { ++// tinfl_decompressor decomp; ++// void *pBuf = NULL, *pNew_buf; ++// size_t src_buf_ofs = 0, out_buf_capacity = 0; ++// *pOut_len = 0; ++// tinfl_init(&decomp); ++// for (;;) ++// { ++// size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; ++// tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, ++// (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); ++// if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) ++// { ++// MZ_FREE(pBuf); ++// *pOut_len = 0; ++// return NULL; ++// } ++// src_buf_ofs += src_buf_size; ++// *pOut_len += dst_buf_size; ++// if (status == TINFL_STATUS_DONE) ++// break; ++// new_out_buf_capacity = out_buf_capacity * 2; ++// if (new_out_buf_capacity < 128) ++// new_out_buf_capacity = 128; ++// pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); ++// if (!pNew_buf) ++// { ++// MZ_FREE(pBuf); ++// *pOut_len = 0; ++// return NULL; ++// } ++// pBuf = pNew_buf; ++// out_buf_capacity = new_out_buf_capacity; ++// } ++// return pBuf; ++// } + +-size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +-{ +- tinfl_decompressor decomp; +- tinfl_status status; +- tinfl_init(&decomp); +- status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); +- return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +-} ++// size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) ++// { ++// tinfl_decompressor decomp; ++// tinfl_status status; ++// tinfl_init(&decomp); ++// status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); ++// return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; ++// } + +-int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +-{ +- int result = 0; +- tinfl_decompressor decomp; +- mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); +- size_t in_buf_ofs = 0, dict_ofs = 0; +- if (!pDict) +- return TINFL_STATUS_FAILED; +- tinfl_init(&decomp); +- for (;;) +- { +- size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; +- tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, +- (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); +- in_buf_ofs += in_buf_size; +- if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) +- break; +- if (status != TINFL_STATUS_HAS_MORE_OUTPUT) +- { +- result = (status == TINFL_STATUS_DONE); +- break; +- } +- dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); +- } +- MZ_FREE(pDict); +- *pIn_buf_size = in_buf_ofs; +- return result; +-} ++// int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) ++// { ++// int result = 0; ++// tinfl_decompressor decomp; ++// mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); ++// size_t in_buf_ofs = 0, dict_ofs = 0; ++// if (!pDict) ++// return TINFL_STATUS_FAILED; ++// tinfl_init(&decomp); ++// for (;;) ++// { ++// size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; ++// tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, ++// (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); ++// in_buf_ofs += in_buf_size; ++// if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) ++// break; ++// if (status != TINFL_STATUS_HAS_MORE_OUTPUT) ++// { ++// result = (status == TINFL_STATUS_DONE); ++// break; ++// } ++// dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); ++// } ++// MZ_FREE(pDict); ++// *pIn_buf_size = in_buf_ofs; ++// return result; ++// } + +-tinfl_decompressor *tinfl_decompressor_alloc() +-{ +- tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); +- if (pDecomp) +- tinfl_init(pDecomp); +- return pDecomp; +-} ++// tinfl_decompressor *tinfl_decompressor_alloc() ++// { ++// tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); ++// if (pDecomp) ++// tinfl_init(pDecomp); ++// return pDecomp; ++// } + +-void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +-{ +- MZ_FREE(pDecomp); +-} ++// void tinfl_decompressor_free(tinfl_decompressor *pDecomp) ++// { ++// MZ_FREE(pDecomp); ++// } + + #ifdef __cplusplus + } +@@ -2925,461 +2924,461 @@ void tinfl_decompressor_free(tinfl_decompressor *pDecomp) + **************************************************************************/ + + +-#ifndef MINIZ_NO_ARCHIVE_APIS ++// #ifndef MINIZ_NO_ARCHIVE_APIS + +-#ifdef __cplusplus +-extern "C" { +-#endif ++// #ifdef __cplusplus ++// extern "C" { ++// #endif + + /* ------------------- .ZIP archive reading */ + +-#ifdef MINIZ_NO_STDIO +-#define MZ_FILE void * +-#else +-#include ++// #ifdef MINIZ_NO_STDIO ++// #define MZ_FILE void * ++// #else ++// #include + +-#if defined(_MSC_VER) || defined(__MINGW64__) +-static FILE *mz_fopen(const char *pFilename, const char *pMode) +-{ +- FILE *pFile = NULL; +- fopen_s(&pFile, pFilename, pMode); +- return pFile; +-} +-static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +-{ +- FILE *pFile = NULL; +- if (freopen_s(&pFile, pPath, pMode, pStream)) +- return NULL; +- return pFile; +-} +-#ifndef MINIZ_NO_TIME +-#include +-#endif +-#define MZ_FOPEN mz_fopen +-#define MZ_FCLOSE fclose +-#define MZ_FREAD fread +-#define MZ_FWRITE fwrite +-#define MZ_FTELL64 _ftelli64 +-#define MZ_FSEEK64 _fseeki64 +-#define MZ_FILE_STAT_STRUCT _stat +-#define MZ_FILE_STAT _stat +-#define MZ_FFLUSH fflush +-#define MZ_FREOPEN mz_freopen +-#define MZ_DELETE_FILE remove +-#elif defined(__MINGW32__) +-#ifndef MINIZ_NO_TIME +-#include +-#endif +-#define MZ_FOPEN(f, m) fopen(f, m) +-#define MZ_FCLOSE fclose +-#define MZ_FREAD fread +-#define MZ_FWRITE fwrite +-#define MZ_FTELL64 ftello64 +-#define MZ_FSEEK64 fseeko64 +-#define MZ_FILE_STAT_STRUCT _stat +-#define MZ_FILE_STAT _stat +-#define MZ_FFLUSH fflush +-#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +-#define MZ_DELETE_FILE remove +-#elif defined(__TINYC__) +-#ifndef MINIZ_NO_TIME +-#include +-#endif +-#define MZ_FOPEN(f, m) fopen(f, m) +-#define MZ_FCLOSE fclose +-#define MZ_FREAD fread +-#define MZ_FWRITE fwrite +-#define MZ_FTELL64 ftell +-#define MZ_FSEEK64 fseek +-#define MZ_FILE_STAT_STRUCT stat +-#define MZ_FILE_STAT stat +-#define MZ_FFLUSH fflush +-#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +-#define MZ_DELETE_FILE remove +-#elif defined(__GNUC__) && _LARGEFILE64_SOURCE +-#ifndef MINIZ_NO_TIME +-#include +-#endif +-#define MZ_FOPEN(f, m) fopen64(f, m) +-#define MZ_FCLOSE fclose +-#define MZ_FREAD fread +-#define MZ_FWRITE fwrite +-#define MZ_FTELL64 ftello64 +-#define MZ_FSEEK64 fseeko64 +-#define MZ_FILE_STAT_STRUCT stat64 +-#define MZ_FILE_STAT stat64 +-#define MZ_FFLUSH fflush +-#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +-#define MZ_DELETE_FILE remove +-#elif defined(__APPLE__) && _LARGEFILE64_SOURCE +-#ifndef MINIZ_NO_TIME +-#include +-#endif +-#define MZ_FOPEN(f, m) fopen(f, m) +-#define MZ_FCLOSE fclose +-#define MZ_FREAD fread +-#define MZ_FWRITE fwrite +-#define MZ_FTELL64 ftello +-#define MZ_FSEEK64 fseeko +-#define MZ_FILE_STAT_STRUCT stat +-#define MZ_FILE_STAT stat +-#define MZ_FFLUSH fflush +-#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +-#define MZ_DELETE_FILE remove ++// #if defined(_MSC_VER) || defined(__MINGW64__) ++// static FILE *mz_fopen(const char *pFilename, const char *pMode) ++// { ++// FILE *pFile = NULL; ++// fopen_s(&pFile, pFilename, pMode); ++// return pFile; ++// } ++// static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) ++// { ++// FILE *pFile = NULL; ++// if (freopen_s(&pFile, pPath, pMode, pStream)) ++// return NULL; ++// return pFile; ++// } ++// #ifndef MINIZ_NO_TIME ++// #include ++// #endif ++// #define MZ_FOPEN mz_fopen ++// #define MZ_FCLOSE fclose ++// #define MZ_FREAD fread ++// #define MZ_FWRITE fwrite ++// #define MZ_FTELL64 _ftelli64 ++// #define MZ_FSEEK64 _fseeki64 ++// #define MZ_FILE_STAT_STRUCT _stat ++// #define MZ_FILE_STAT _stat ++// #define MZ_FFLUSH fflush ++// #define MZ_FREOPEN mz_freopen ++// #define MZ_DELETE_FILE remove ++// #elif defined(__MINGW32__) ++// #ifndef MINIZ_NO_TIME ++// #include ++// #endif ++// #define MZ_FOPEN(f, m) fopen(f, m) ++// #define MZ_FCLOSE fclose ++// #define MZ_FREAD fread ++// #define MZ_FWRITE fwrite ++// #define MZ_FTELL64 ftello64 ++// #define MZ_FSEEK64 fseeko64 ++// #define MZ_FILE_STAT_STRUCT _stat ++// #define MZ_FILE_STAT _stat ++// #define MZ_FFLUSH fflush ++// #define MZ_FREOPEN(f, m, s) freopen(f, m, s) ++// #define MZ_DELETE_FILE remove ++// #elif defined(__TINYC__) ++// #ifndef MINIZ_NO_TIME ++// #include ++// #endif ++// #define MZ_FOPEN(f, m) fopen(f, m) ++// #define MZ_FCLOSE fclose ++// #define MZ_FREAD fread ++// #define MZ_FWRITE fwrite ++// #define MZ_FTELL64 ftell ++// #define MZ_FSEEK64 fseek ++// #define MZ_FILE_STAT_STRUCT stat ++// #define MZ_FILE_STAT stat ++// #define MZ_FFLUSH fflush ++// #define MZ_FREOPEN(f, m, s) freopen(f, m, s) ++// #define MZ_DELETE_FILE remove ++// #elif defined(__GNUC__) && _LARGEFILE64_SOURCE ++// #ifndef MINIZ_NO_TIME ++// #include ++// #endif ++// #define MZ_FOPEN(f, m) fopen64(f, m) ++// #define MZ_FCLOSE fclose ++// #define MZ_FREAD fread ++// #define MZ_FWRITE fwrite ++// #define MZ_FTELL64 ftello64 ++// #define MZ_FSEEK64 fseeko64 ++// #define MZ_FILE_STAT_STRUCT stat64 ++// #define MZ_FILE_STAT stat64 ++// #define MZ_FFLUSH fflush ++// #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) ++// #define MZ_DELETE_FILE remove ++// #elif defined(__APPLE__) && _LARGEFILE64_SOURCE ++// #ifndef MINIZ_NO_TIME ++// #include ++// #endif ++// #define MZ_FOPEN(f, m) fopen(f, m) ++// #define MZ_FCLOSE fclose ++// #define MZ_FREAD fread ++// #define MZ_FWRITE fwrite ++// #define MZ_FTELL64 ftello ++// #define MZ_FSEEK64 fseeko ++// #define MZ_FILE_STAT_STRUCT stat ++// #define MZ_FILE_STAT stat ++// #define MZ_FFLUSH fflush ++// #define MZ_FREOPEN(p, m, s) freopen(p, m, s) ++// #define MZ_DELETE_FILE remove ++ ++// #else ++// // #pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") ++// #ifndef MINIZ_NO_TIME ++// #include ++// #endif ++// #define MZ_FOPEN(f, m) fopen(f, m) ++// #define MZ_FCLOSE fclose ++// #define MZ_FREAD fread ++// #define MZ_FWRITE fwrite ++// #ifdef __STRICT_ANSI__ ++// #define MZ_FTELL64 ftell ++// #define MZ_FSEEK64 fseek ++// #else ++// #define MZ_FTELL64 ftello ++// #define MZ_FSEEK64 fseeko ++// #endif ++// #define MZ_FILE_STAT_STRUCT stat ++// #define MZ_FILE_STAT stat ++// #define MZ_FFLUSH fflush ++// #define MZ_FREOPEN(f, m, s) freopen(f, m, s) ++// #define MZ_DELETE_FILE remove ++// #endif /* #ifdef _MSC_VER */ ++// #endif /* #ifdef MINIZ_NO_STDIO */ ++ ++// #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +-#else +-// #pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +-#ifndef MINIZ_NO_TIME +-#include +-#endif +-#define MZ_FOPEN(f, m) fopen(f, m) +-#define MZ_FCLOSE fclose +-#define MZ_FREAD fread +-#define MZ_FWRITE fwrite +-#ifdef __STRICT_ANSI__ +-#define MZ_FTELL64 ftell +-#define MZ_FSEEK64 fseek +-#else +-#define MZ_FTELL64 ftello +-#define MZ_FSEEK64 fseeko +-#endif +-#define MZ_FILE_STAT_STRUCT stat +-#define MZ_FILE_STAT stat +-#define MZ_FFLUSH fflush +-#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +-#define MZ_DELETE_FILE remove +-#endif /* #ifdef _MSC_VER */ +-#endif /* #ifdef MINIZ_NO_STDIO */ ++/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ ++// enum ++// { ++// /* ZIP archive identifiers and record sizes */ ++// MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, ++// MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, ++// MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, ++// MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, ++// MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, ++// MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, ++ ++// /* ZIP64 archive identifier and record sizes */ ++// MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, ++// MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, ++// MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, ++// MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, ++// MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, ++// MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, ++// MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, ++// MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, ++ ++// /* Central directory header record offsets */ ++// MZ_ZIP_CDH_SIG_OFS = 0, ++// MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, ++// MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, ++// MZ_ZIP_CDH_BIT_FLAG_OFS = 8, ++// MZ_ZIP_CDH_METHOD_OFS = 10, ++// MZ_ZIP_CDH_FILE_TIME_OFS = 12, ++// MZ_ZIP_CDH_FILE_DATE_OFS = 14, ++// MZ_ZIP_CDH_CRC32_OFS = 16, ++// MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, ++// MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, ++// MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, ++// MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, ++// MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, ++// MZ_ZIP_CDH_DISK_START_OFS = 34, ++// MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, ++// MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, ++// MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, ++ ++// /* Local directory header offsets */ ++// MZ_ZIP_LDH_SIG_OFS = 0, ++// MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, ++// MZ_ZIP_LDH_BIT_FLAG_OFS = 6, ++// MZ_ZIP_LDH_METHOD_OFS = 8, ++// MZ_ZIP_LDH_FILE_TIME_OFS = 10, ++// MZ_ZIP_LDH_FILE_DATE_OFS = 12, ++// MZ_ZIP_LDH_CRC32_OFS = 14, ++// MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, ++// MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, ++// MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, ++// MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, ++// MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, ++ ++// /* End of central directory offsets */ ++// MZ_ZIP_ECDH_SIG_OFS = 0, ++// MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, ++// MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, ++// MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, ++// MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, ++// MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, ++// MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, ++// MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, ++ ++// /* ZIP64 End of central directory locator offsets */ ++// MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ ++// MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ ++// MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ ++// MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ ++ ++// /* ZIP64 End of central directory header offsets */ ++// MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ ++// MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ ++// MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ ++// MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ ++// MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ ++// MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ ++// MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ ++// MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ ++// MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ ++// MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ ++// MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, ++// MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, ++// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, ++// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, ++// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, ++// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, ++// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 ++// }; ++ ++// typedef struct ++// { ++// void *m_p; ++// size_t m_size, m_capacity; ++// mz_uint m_element_size; ++// } mz_zip_array; + +-#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) ++// struct mz_zip_internal_state_tag ++// { ++// mz_zip_array m_central_dir; ++// mz_zip_array m_central_dir_offsets; ++// mz_zip_array m_sorted_central_dir_offsets; + +-/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +-enum +-{ +- /* ZIP archive identifiers and record sizes */ +- MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, +- MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, +- MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, +- MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, +- MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, +- MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, +- +- /* ZIP64 archive identifier and record sizes */ +- MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, +- MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, +- MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, +- MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, +- MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, +- MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, +- MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, +- MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, +- +- /* Central directory header record offsets */ +- MZ_ZIP_CDH_SIG_OFS = 0, +- MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, +- MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, +- MZ_ZIP_CDH_BIT_FLAG_OFS = 8, +- MZ_ZIP_CDH_METHOD_OFS = 10, +- MZ_ZIP_CDH_FILE_TIME_OFS = 12, +- MZ_ZIP_CDH_FILE_DATE_OFS = 14, +- MZ_ZIP_CDH_CRC32_OFS = 16, +- MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, +- MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, +- MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, +- MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, +- MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, +- MZ_ZIP_CDH_DISK_START_OFS = 34, +- MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, +- MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, +- MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, +- +- /* Local directory header offsets */ +- MZ_ZIP_LDH_SIG_OFS = 0, +- MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, +- MZ_ZIP_LDH_BIT_FLAG_OFS = 6, +- MZ_ZIP_LDH_METHOD_OFS = 8, +- MZ_ZIP_LDH_FILE_TIME_OFS = 10, +- MZ_ZIP_LDH_FILE_DATE_OFS = 12, +- MZ_ZIP_LDH_CRC32_OFS = 14, +- MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, +- MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, +- MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, +- MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, +- MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, +- +- /* End of central directory offsets */ +- MZ_ZIP_ECDH_SIG_OFS = 0, +- MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, +- MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, +- MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, +- MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, +- MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, +- MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, +- MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, +- +- /* ZIP64 End of central directory locator offsets */ +- MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ +- MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ +- MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ +- MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ +- +- /* ZIP64 End of central directory header offsets */ +- MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ +- MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ +- MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ +- MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ +- MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ +- MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ +- MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ +- MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ +- MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ +- MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ +- MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, +- MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, +- MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, +- MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, +- MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, +- MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, +- MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +-}; ++// /* The flags passed in when the archive is initially opened. */ ++// uint32_t m_init_flags; + +-typedef struct +-{ +- void *m_p; +- size_t m_size, m_capacity; +- mz_uint m_element_size; +-} mz_zip_array; ++// /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ ++// mz_bool m_zip64; + +-struct mz_zip_internal_state_tag +-{ +- mz_zip_array m_central_dir; +- mz_zip_array m_central_dir_offsets; +- mz_zip_array m_sorted_central_dir_offsets; ++// /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ ++// mz_bool m_zip64_has_extended_info_fields; + +- /* The flags passed in when the archive is initially opened. */ +- uint32_t m_init_flags; ++// /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ ++// MZ_FILE *m_pFile; ++// mz_uint64 m_file_archive_start_ofs; + +- /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ +- mz_bool m_zip64; ++// void *m_pMem; ++// size_t m_mem_size; ++// size_t m_mem_capacity; ++// }; + +- /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ +- mz_bool m_zip64_has_extended_info_fields; ++// #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +- /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ +- MZ_FILE *m_pFile; +- mz_uint64 m_file_archive_start_ofs; ++// #if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) ++// static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) ++// { ++// MZ_ASSERT(index < pArray->m_size); ++// return index; ++// } ++// #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] ++// #else ++// #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] ++// #endif + +- void *m_pMem; +- size_t m_mem_size; +- size_t m_mem_capacity; +-}; ++// static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) ++// { ++// memset(pArray, 0, sizeof(mz_zip_array)); ++// pArray->m_element_size = element_size; ++// } + +-#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size ++// static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); ++// memset(pArray, 0, sizeof(mz_zip_array)); ++// } + +-#if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) +-static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +-{ +- MZ_ASSERT(index < pArray->m_size); +- return index; +-} +-#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +-#else +-#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +-#endif ++// static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) ++// { ++// void *pNew_p; ++// size_t new_capacity = min_new_capacity; ++// MZ_ASSERT(pArray->m_element_size); ++// if (pArray->m_capacity >= min_new_capacity) ++// return MZ_TRUE; ++// if (growing) ++// { ++// new_capacity = MZ_MAX(1, pArray->m_capacity); ++// while (new_capacity < min_new_capacity) ++// new_capacity *= 2; ++// } ++// if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) ++// return MZ_FALSE; ++// pArray->m_p = pNew_p; ++// pArray->m_capacity = new_capacity; ++// return MZ_TRUE; ++// } + +-static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +-{ +- memset(pArray, 0, sizeof(mz_zip_array)); +- pArray->m_element_size = element_size; +-} ++// static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) ++// { ++// if (new_capacity > pArray->m_capacity) ++// { ++// if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) ++// return MZ_FALSE; ++// } ++// return MZ_TRUE; ++// } + +-static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +-{ +- pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); +- memset(pArray, 0, sizeof(mz_zip_array)); +-} ++// static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) ++// { ++// if (new_size > pArray->m_capacity) ++// { ++// if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) ++// return MZ_FALSE; ++// } ++// pArray->m_size = new_size; ++// return MZ_TRUE; ++// } + +-static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +-{ +- void *pNew_p; +- size_t new_capacity = min_new_capacity; +- MZ_ASSERT(pArray->m_element_size); +- if (pArray->m_capacity >= min_new_capacity) +- return MZ_TRUE; +- if (growing) +- { +- new_capacity = MZ_MAX(1, pArray->m_capacity); +- while (new_capacity < min_new_capacity) +- new_capacity *= 2; +- } +- if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) +- return MZ_FALSE; +- pArray->m_p = pNew_p; +- pArray->m_capacity = new_capacity; +- return MZ_TRUE; +-} ++// static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) ++// { ++// return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); ++// } + +-static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +-{ +- if (new_capacity > pArray->m_capacity) +- { +- if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) +- return MZ_FALSE; +- } +- return MZ_TRUE; +-} ++// static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) ++// { ++// size_t orig_size = pArray->m_size; ++// if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) ++// return MZ_FALSE; ++// memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); ++// return MZ_TRUE; ++// } + +-static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +-{ +- if (new_size > pArray->m_capacity) +- { +- if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) +- return MZ_FALSE; +- } +- pArray->m_size = new_size; +- return MZ_TRUE; +-} ++// #ifndef MINIZ_NO_TIME ++// static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) ++// { ++// struct tm tm; ++// memset(&tm, 0, sizeof(tm)); ++// tm.tm_isdst = -1; ++// tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; ++// tm.tm_mon = ((dos_date >> 5) & 15) - 1; ++// tm.tm_mday = dos_date & 31; ++// tm.tm_hour = (dos_time >> 11) & 31; ++// tm.tm_min = (dos_time >> 5) & 63; ++// tm.tm_sec = (dos_time << 1) & 62; ++// return mktime(&tm); ++// } + +-static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +-{ +- return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +-} ++// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS ++// static void mz_zip_time_t_to_dos_time(MZ_TIME_T time_, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) ++// { ++// #ifdef _MSC_VER ++// struct tm tm_struct; ++// struct tm *tm = &tm_struct; ++// errno_t err = localtime_s(tm, &time_); ++// if (err) ++// { ++// *pDOS_date = 0; ++// *pDOS_time = 0; ++// return; ++// } ++// #else ++// struct tm *tm = localtime(&time_); ++// #endif /* #ifdef _MSC_VER */ + +-static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +-{ +- size_t orig_size = pArray->m_size; +- if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) +- return MZ_FALSE; +- memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); +- return MZ_TRUE; +-} ++// *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); ++// *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); ++// } ++// #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +-#ifndef MINIZ_NO_TIME +-static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +-{ +- struct tm tm; +- memset(&tm, 0, sizeof(tm)); +- tm.tm_isdst = -1; +- tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; +- tm.tm_mon = ((dos_date >> 5) & 15) - 1; +- tm.tm_mday = dos_date & 31; +- tm.tm_hour = (dos_time >> 11) & 31; +- tm.tm_min = (dos_time >> 5) & 63; +- tm.tm_sec = (dos_time << 1) & 62; +- return mktime(&tm); +-} ++// #ifndef MINIZ_NO_STDIO ++// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS ++// static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) ++// { ++// struct MZ_FILE_STAT_STRUCT file_stat; + +-#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +-static void mz_zip_time_t_to_dos_time(MZ_TIME_T time_, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +-{ +-#ifdef _MSC_VER +- struct tm tm_struct; +- struct tm *tm = &tm_struct; +- errno_t err = localtime_s(tm, &time_); +- if (err) +- { +- *pDOS_date = 0; +- *pDOS_time = 0; +- return; +- } +-#else +- struct tm *tm = localtime(&time_); +-#endif /* #ifdef _MSC_VER */ ++// /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ ++// if (MZ_FILE_STAT(pFilename, &file_stat) != 0) ++// return MZ_FALSE; + +- *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); +- *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +-} +-#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ ++// *pTime = file_stat.st_mtime; + +-#ifndef MINIZ_NO_STDIO +-#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +-static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +-{ +- struct MZ_FILE_STAT_STRUCT file_stat; ++// return MZ_TRUE; ++// } ++// #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +- /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ +- if (MZ_FILE_STAT(pFilename, &file_stat) != 0) +- return MZ_FALSE; ++// static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) ++// { ++// struct utimbuf t; + +- *pTime = file_stat.st_mtime; ++// memset(&t, 0, sizeof(t)); ++// t.actime = access_time; ++// t.modtime = modified_time; + +- return MZ_TRUE; +-} +-#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ ++// return !utime(pFilename, &t); ++// } ++// #endif /* #ifndef MINIZ_NO_STDIO */ ++// #endif /* #ifndef MINIZ_NO_TIME */ + +-static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +-{ +- struct utimbuf t; ++// static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) ++// { ++// if (pZip) ++// pZip->m_last_error = err_num; ++// return MZ_FALSE; ++// } + +- memset(&t, 0, sizeof(t)); +- t.actime = access_time; +- t.modtime = modified_time; ++// static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) ++// { ++// (void)flags; ++// if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- return !utime(pFilename, &t); +-} +-#endif /* #ifndef MINIZ_NO_STDIO */ +-#endif /* #ifndef MINIZ_NO_TIME */ ++// if (!pZip->m_pAlloc) ++// pZip->m_pAlloc = miniz_def_alloc_func; ++// if (!pZip->m_pFree) ++// pZip->m_pFree = miniz_def_free_func; ++// if (!pZip->m_pRealloc) ++// pZip->m_pRealloc = miniz_def_realloc_func; + +-static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +-{ +- if (pZip) +- pZip->m_last_error = err_num; +- return MZ_FALSE; +-} ++// pZip->m_archive_size = 0; ++// pZip->m_central_directory_file_ofs = 0; ++// pZip->m_total_files = 0; ++// pZip->m_last_error = MZ_ZIP_NO_ERROR; + +-static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +-{ +- (void)flags; +- if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- +- if (!pZip->m_pAlloc) +- pZip->m_pAlloc = miniz_def_alloc_func; +- if (!pZip->m_pFree) +- pZip->m_pFree = miniz_def_free_func; +- if (!pZip->m_pRealloc) +- pZip->m_pRealloc = miniz_def_realloc_func; +- +- pZip->m_archive_size = 0; +- pZip->m_central_directory_file_ofs = 0; +- pZip->m_total_files = 0; +- pZip->m_last_error = MZ_ZIP_NO_ERROR; +- +- if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- +- memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); +- MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); +- MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); +- MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); +- pZip->m_pState->m_init_flags = flags; +- pZip->m_pState->m_zip64 = MZ_FALSE; +- pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; +- +- pZip->m_zip_mode = MZ_ZIP_MODE_READING; ++// if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +- return MZ_TRUE; +-} ++// memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); ++// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); ++// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); ++// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); ++// pZip->m_pState->m_init_flags = flags; ++// pZip->m_pState->m_zip64 = MZ_FALSE; ++// pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + +-static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +-{ +- const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; +- const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); +- mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); +- mz_uint8 l = 0, r = 0; +- pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +- pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +- pE = pL + MZ_MIN(l_len, r_len); +- while (pL < pE) +- { +- if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) +- break; +- pL++; +- pR++; +- } +- return (pL == pE) ? (l_len < r_len) : (l < r); +-} ++// pZip->m_zip_mode = MZ_ZIP_MODE_READING; ++ ++// return MZ_TRUE; ++// } + ++// static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) ++// { ++// const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; ++// const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); ++// mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// mz_uint8 l = 0, r = 0; ++// pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; ++// pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; ++// pE = pL + MZ_MIN(l_len, r_len); ++// while (pL < pE) ++// { ++// if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) ++// break; ++// pL++; ++// pR++; ++// } ++// return (pL == pE) ? (l_len < r_len) : (l < r); ++// } ++/* + #define MZ_SWAP_UINT32(a, b) \ + do \ + { \ +@@ -3388,1708 +3387,1708 @@ static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pC + b = t; \ + } \ + MZ_MACRO_END +- ++*/ + /* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +-static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +-{ +- mz_zip_internal_state *pState = pZip->m_pState; +- const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; +- const mz_zip_array *pCentral_dir = &pState->m_central_dir; +- mz_uint32 *pIndices; +- mz_uint32 start, end; +- const mz_uint32 size = pZip->m_total_files; +- +- if (size <= 1U) +- return; ++// static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) ++// { ++// mz_zip_internal_state *pState = pZip->m_pState; ++// const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; ++// const mz_zip_array *pCentral_dir = &pState->m_central_dir; ++// mz_uint32 *pIndices; ++// mz_uint32 start, end; ++// const mz_uint32 size = pZip->m_total_files; + +- pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); ++// if (size <= 1U) ++// return; + +- start = (size - 2U) >> 1U; +- for (;;) +- { +- mz_uint64 child, root = start; +- for (;;) +- { +- if ((child = (root << 1U) + 1U) >= size) +- break; +- child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); +- if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) +- break; +- MZ_SWAP_UINT32(pIndices[root], pIndices[child]); +- root = child; +- } +- if (!start) +- break; +- start--; +- } ++// pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + +- end = size - 1; +- while (end > 0) +- { +- mz_uint64 child, root = 0; +- MZ_SWAP_UINT32(pIndices[end], pIndices[0]); +- for (;;) +- { +- if ((child = (root << 1U) + 1U) >= end) +- break; +- child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); +- if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) +- break; +- MZ_SWAP_UINT32(pIndices[root], pIndices[child]); +- root = child; +- } +- end--; +- } +-} ++// start = (size - 2U) >> 1U; ++// for (;;) ++// { ++// mz_uint64 child, root = start; ++// for (;;) ++// { ++// if ((child = (root << 1U) + 1U) >= size) ++// break; ++// child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); ++// if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) ++// break; ++// MZ_SWAP_UINT32(pIndices[root], pIndices[child]); ++// root = child; ++// } ++// if (!start) ++// break; ++// start--; ++// } + +-static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +-{ +- mz_int64 cur_file_ofs; +- mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; +- mz_uint8 *pBuf = (mz_uint8 *)buf_u32; ++// end = size - 1; ++// while (end > 0) ++// { ++// mz_uint64 child, root = 0; ++// MZ_SWAP_UINT32(pIndices[end], pIndices[0]); ++// for (;;) ++// { ++// if ((child = (root << 1U) + 1U) >= end) ++// break; ++// child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); ++// if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) ++// break; ++// MZ_SWAP_UINT32(pIndices[root], pIndices[child]); ++// root = child; ++// } ++// end--; ++// } ++// } + +- /* Basic sanity checks - reject files which are too small */ +- if (pZip->m_archive_size < record_size) +- return MZ_FALSE; ++// static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) ++// { ++// mz_int64 cur_file_ofs; ++// mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; ++// mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + +- /* Find the record by scanning the file from the end towards the beginning. */ +- cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); +- for (;;) +- { +- int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); ++// /* Basic sanity checks - reject files which are too small */ ++// if (pZip->m_archive_size < record_size) ++// return MZ_FALSE; + +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) +- return MZ_FALSE; ++// /* Find the record by scanning the file from the end towards the beginning. */ ++// cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); ++// for (;;) ++// { ++// int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + +- for (i = n - 4; i >= 0; --i) +- { +- mz_uint s = MZ_READ_LE32(pBuf + i); +- if (s == record_sig) +- { +- if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) +- break; +- } +- } ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) ++// return MZ_FALSE; + +- if (i >= 0) +- { +- cur_file_ofs += i; +- break; +- } ++// for (i = n - 4; i >= 0; --i) ++// { ++// mz_uint s = MZ_READ_LE32(pBuf + i); ++// if (s == record_sig) ++// { ++// if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) ++// break; ++// } ++// } + +- /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ +- if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) +- return MZ_FALSE; ++// if (i >= 0) ++// { ++// cur_file_ofs += i; ++// break; ++// } + +- cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); +- } ++// /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ ++// if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) ++// return MZ_FALSE; + +- *pOfs = cur_file_ofs; +- return MZ_TRUE; +-} ++// cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); ++// } + +-static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +-{ +- mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; +- mz_uint64 cdir_ofs = 0; +- mz_int64 cur_file_ofs = 0; +- const mz_uint8 *p; ++// *pOfs = cur_file_ofs; ++// return MZ_TRUE; ++// } + +- mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; +- mz_uint8 *pBuf = (mz_uint8 *)buf_u32; +- mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); +- mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +- mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; ++// static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) ++// { ++// mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; ++// mz_uint64 cdir_ofs = 0; ++// mz_int64 cur_file_ofs = 0; ++// const mz_uint8 *p; + +- mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +- mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; ++// mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; ++// mz_uint8 *pBuf = (mz_uint8 *)buf_u32; ++// mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); ++// mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; ++// mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + +- mz_uint64 zip64_end_of_central_dir_ofs = 0; ++// mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; ++// mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + +- /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ +- if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); ++// mz_uint64 zip64_end_of_central_dir_ofs = 0; + +- if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) +- return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); ++// /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ ++// if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +- /* Read and verify the end of central directory record. */ +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + +- if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) +- return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); ++// /* Read and verify the end of central directory record. */ ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +- if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) +- { +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) +- { +- if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) +- { +- zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); +- if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) +- return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); ++// if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ++// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +- if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) +- { +- if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) +- { +- pZip->m_pState->m_zip64 = MZ_TRUE; +- } +- } +- } +- } +- } ++// if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) ++// { ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) ++// { ++// if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) ++// { ++// zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); ++// if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) ++// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +- pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); +- cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); +- num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); +- cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); +- cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); +- cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); ++// if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// { ++// if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) ++// { ++// pZip->m_pState->m_zip64 = MZ_TRUE; ++// } ++// } ++// } ++// } ++// } + +- if (pZip->m_pState->m_zip64) +- { +- mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); +- mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); +- mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); +- mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); +- mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); ++// pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); ++// cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); ++// num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); ++// cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); ++// cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); ++// cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + +- if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// if (pZip->m_pState->m_zip64) ++// { ++// mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); ++// mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); ++// mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); ++// mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); ++// mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + +- if (zip64_total_num_of_disks != 1U) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); ++// if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- /* Check for miniz's practical limits */ +- if (zip64_cdir_total_entries > MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// if (zip64_total_num_of_disks != 1U) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +- pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; ++// /* Check for miniz's practical limits */ ++// if (zip64_cdir_total_entries > MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + +- if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + +- cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; ++// if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + +- /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ +- if (zip64_size_of_central_directory > MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); ++// cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + +- cdir_size = (mz_uint32)zip64_size_of_central_directory; ++// /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ ++// if (zip64_size_of_central_directory > MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +- num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); ++// cdir_size = (mz_uint32)zip64_size_of_central_directory; + +- cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); ++// num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + +- cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); +- } ++// cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + +- if (pZip->m_total_files != cdir_entries_on_this_disk) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); ++// cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); ++// } + +- if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); ++// if (pZip->m_total_files != cdir_entries_on_this_disk) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +- if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +- if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- pZip->m_central_directory_file_ofs = cdir_ofs; ++// if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (pZip->m_total_files) +- { +- mz_uint i, n; +- /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ +- if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || +- (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// pZip->m_central_directory_file_ofs = cdir_ofs; + +- if (sort_central_dir) +- { +- if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- } ++// if (pZip->m_total_files) ++// { ++// mz_uint i, n; ++// /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ ++// if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || ++// (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +- if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// if (sort_central_dir) ++// { ++// if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// } + +- /* Now create an index into the central directory file records, do some basic sanity checking on each record */ +- p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; +- for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) +- { +- mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; +- mz_uint64 comp_size, decomp_size, local_header_ofs; ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +- if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// /* Now create an index into the central directory file records, do some basic sanity checking on each record */ ++// p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; ++// for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) ++// { ++// mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; ++// mz_uint64 comp_size, decomp_size, local_header_ofs; + +- MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); ++// if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (sort_central_dir) +- MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; ++// MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + +- comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); +- decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); +- local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); +- filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +- ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); ++// if (sort_central_dir) ++// MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + +- if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && +- (ext_data_size) && +- (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) +- { +- /* Attempt to find zip64 extended information field in the entry's extra data */ +- mz_uint32 extra_size_remaining = ext_data_size; ++// comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); ++// decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); ++// local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); ++// filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + +- if (extra_size_remaining) +- { +- const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; ++// if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && ++// (ext_data_size) && ++// (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) ++// { ++// /* Attempt to find zip64 extended information field in the entry's extra data */ ++// mz_uint32 extra_size_remaining = ext_data_size; + +- do +- { +- mz_uint32 field_id; +- mz_uint32 field_data_size; ++// if (extra_size_remaining) ++// { ++// const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + +- if (extra_size_remaining < (sizeof(mz_uint16) * 2)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// do ++// { ++// mz_uint32 field_id; ++// mz_uint32 field_data_size; + +- field_id = MZ_READ_LE16(pExtra_data); +- field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); ++// if (extra_size_remaining < (sizeof(mz_uint16) * 2)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// field_id = MZ_READ_LE16(pExtra_data); ++// field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + +- if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) +- { +- /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ +- pZip->m_pState->m_zip64 = MZ_TRUE; +- pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; +- break; +- } ++// if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; +- extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; +- } while (extra_size_remaining); +- } +- } ++// if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) ++// { ++// /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ ++// pZip->m_pState->m_zip64 = MZ_TRUE; ++// pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; ++// break; ++// } + +- /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ +- if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) +- { +- if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- } ++// pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; ++// extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; ++// } while (extra_size_remaining); ++// } ++// } + +- disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); +- if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); ++// /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ ++// if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) ++// { ++// if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// } + +- if (comp_size != MZ_UINT32_MAX) +- { +- if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- } ++// disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); ++// if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +- bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); +- if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); ++// if (comp_size != MZ_UINT32_MAX) ++// { ++// if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// } + +- if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); ++// if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + +- n -= total_header_size; +- p += total_header_size; +- } +- } ++// if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (sort_central_dir) +- mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); ++// n -= total_header_size; ++// p += total_header_size; ++// } ++// } + +- return MZ_TRUE; +-} ++// if (sort_central_dir) ++// mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + +-void mz_zip_zero_struct(mz_zip_archive *pZip) +-{ +- if (pZip) +- MZ_CLEAR_OBJ(*pZip); +-} ++// return MZ_TRUE; ++// } + +-static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +-{ +- mz_bool status = MZ_TRUE; ++// void mz_zip_zero_struct(mz_zip_archive *pZip) ++// { ++// if (pZip) ++// MZ_CLEAR_OBJ(*pZip); ++// } + +- if (!pZip) +- return MZ_FALSE; ++// static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) ++// { ++// mz_bool status = MZ_TRUE; + +- if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) +- { +- if (set_last_error) +- pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; ++// if (!pZip) ++// return MZ_FALSE; + +- return MZ_FALSE; +- } ++// if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) ++// { ++// if (set_last_error) ++// pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + +- if (pZip->m_pState) +- { +- mz_zip_internal_state *pState = pZip->m_pState; +- pZip->m_pState = NULL; ++// return MZ_FALSE; ++// } + +- mz_zip_array_clear(pZip, &pState->m_central_dir); +- mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); +- mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); ++// if (pZip->m_pState) ++// { ++// mz_zip_internal_state *pState = pZip->m_pState; ++// pZip->m_pState = NULL; + +-#ifndef MINIZ_NO_STDIO +- if (pState->m_pFile) +- { +- if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) +- { +- if (MZ_FCLOSE(pState->m_pFile) == EOF) +- { +- if (set_last_error) +- pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; +- status = MZ_FALSE; +- } +- } +- pState->m_pFile = NULL; +- } +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// mz_zip_array_clear(pZip, &pState->m_central_dir); ++// mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); ++// mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- } +- pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; ++// #ifndef MINIZ_NO_STDIO ++// if (pState->m_pFile) ++// { ++// if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) ++// { ++// if (MZ_FCLOSE(pState->m_pFile) == EOF) ++// { ++// if (set_last_error) ++// pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; ++// status = MZ_FALSE; ++// } ++// } ++// pState->m_pFile = NULL; ++// } ++// #endif /* #ifndef MINIZ_NO_STDIO */ + +- return status; +-} ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// } ++// pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + +-mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +-{ +- return mz_zip_reader_end_internal(pZip, MZ_TRUE); +-} +-mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +-{ +- if ((!pZip) || (!pZip->m_pRead)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return status; ++// } + +- if (!mz_zip_reader_init_internal(pZip, flags)) +- return MZ_FALSE; ++// mz_bool mz_zip_reader_end(mz_zip_archive *pZip) ++// { ++// return mz_zip_reader_end_internal(pZip, MZ_TRUE); ++// } ++// mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) ++// { ++// if ((!pZip) || (!pZip->m_pRead)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- pZip->m_zip_type = MZ_ZIP_TYPE_USER; +- pZip->m_archive_size = size; ++// if (!mz_zip_reader_init_internal(pZip, flags)) ++// return MZ_FALSE; + +- if (!mz_zip_reader_read_central_dir(pZip, flags)) +- { +- mz_zip_reader_end_internal(pZip, MZ_FALSE); +- return MZ_FALSE; +- } ++// pZip->m_zip_type = MZ_ZIP_TYPE_USER; ++// pZip->m_archive_size = size; + +- return MZ_TRUE; +-} ++// if (!mz_zip_reader_read_central_dir(pZip, flags)) ++// { ++// mz_zip_reader_end_internal(pZip, MZ_FALSE); ++// return MZ_FALSE; ++// } + +-static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +-{ +- mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +- size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); +- memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); +- return s; +-} ++// return MZ_TRUE; ++// } + +-mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +-{ +- if (!pMem) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) ++// { ++// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; ++// size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); ++// memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); ++// return s; ++// } + +- if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); ++// mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) ++// { ++// if (!pMem) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if (!mz_zip_reader_init_internal(pZip, flags)) +- return MZ_FALSE; ++// if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +- pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; +- pZip->m_archive_size = size; +- pZip->m_pRead = mz_zip_mem_read_func; +- pZip->m_pIO_opaque = pZip; +- pZip->m_pNeeds_keepalive = NULL; ++// if (!mz_zip_reader_init_internal(pZip, flags)) ++// return MZ_FALSE; + +-#ifdef __cplusplus +- pZip->m_pState->m_pMem = const_cast(pMem); +-#else +- pZip->m_pState->m_pMem = (void *)pMem; +-#endif ++// pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; ++// pZip->m_archive_size = size; ++// pZip->m_pRead = mz_zip_mem_read_func; ++// pZip->m_pIO_opaque = pZip; ++// pZip->m_pNeeds_keepalive = NULL; + +- pZip->m_pState->m_mem_size = size; ++// #ifdef __cplusplus ++// pZip->m_pState->m_pMem = const_cast(pMem); ++// #else ++// pZip->m_pState->m_pMem = (void *)pMem; ++// #endif + +- if (!mz_zip_reader_read_central_dir(pZip, flags)) +- { +- mz_zip_reader_end_internal(pZip, MZ_FALSE); +- return MZ_FALSE; +- } ++// pZip->m_pState->m_mem_size = size; + +- return MZ_TRUE; +-} ++// if (!mz_zip_reader_read_central_dir(pZip, flags)) ++// { ++// mz_zip_reader_end_internal(pZip, MZ_FALSE); ++// return MZ_FALSE; ++// } + +-#ifndef MINIZ_NO_STDIO +-static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +-{ +- mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +- mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); ++// return MZ_TRUE; ++// } + +- file_ofs += pZip->m_pState->m_file_archive_start_ofs; ++// #ifndef MINIZ_NO_STDIO ++// static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) ++// { ++// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; ++// mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + +- if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) +- return 0; ++// file_ofs += pZip->m_pState->m_file_archive_start_ofs; + +- return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +-} ++// if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) ++// return 0; + +-mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +-{ +- return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +-} ++// return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); ++// } + +-mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +-{ +- mz_uint64 file_size; +- MZ_FILE *pFile; ++// mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) ++// { ++// return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); ++// } + +- if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) ++// { ++// mz_uint64 file_size; ++// MZ_FILE *pFile; + +- pFile = MZ_FOPEN(pFilename, "rb"); +- if (!pFile) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); ++// if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- file_size = archive_size; +- if (!file_size) +- { +- if (MZ_FSEEK64(pFile, 0, SEEK_END)) +- { +- MZ_FCLOSE(pFile); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); +- } ++// pFile = MZ_FOPEN(pFilename, "rb"); ++// if (!pFile) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +- file_size = MZ_FTELL64(pFile); +- } ++// file_size = archive_size; ++// if (!file_size) ++// { ++// if (MZ_FSEEK64(pFile, 0, SEEK_END)) ++// { ++// MZ_FCLOSE(pFile); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); ++// } + +- /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ ++// file_size = MZ_FTELL64(pFile); ++// } + +- if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +- { +- MZ_FCLOSE(pFile); +- return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); +- } ++// /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + +- if (!mz_zip_reader_init_internal(pZip, flags)) +- { +- MZ_FCLOSE(pFile); +- return MZ_FALSE; +- } ++// if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// { ++// MZ_FCLOSE(pFile); ++// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); ++// } + +- pZip->m_zip_type = MZ_ZIP_TYPE_FILE; +- pZip->m_pRead = mz_zip_file_read_func; +- pZip->m_pIO_opaque = pZip; +- pZip->m_pState->m_pFile = pFile; +- pZip->m_archive_size = file_size; +- pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; ++// if (!mz_zip_reader_init_internal(pZip, flags)) ++// { ++// MZ_FCLOSE(pFile); ++// return MZ_FALSE; ++// } + +- if (!mz_zip_reader_read_central_dir(pZip, flags)) +- { +- mz_zip_reader_end_internal(pZip, MZ_FALSE); +- return MZ_FALSE; +- } ++// pZip->m_zip_type = MZ_ZIP_TYPE_FILE; ++// pZip->m_pRead = mz_zip_file_read_func; ++// pZip->m_pIO_opaque = pZip; ++// pZip->m_pState->m_pFile = pFile; ++// pZip->m_archive_size = file_size; ++// pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + +- return MZ_TRUE; +-} ++// if (!mz_zip_reader_read_central_dir(pZip, flags)) ++// { ++// mz_zip_reader_end_internal(pZip, MZ_FALSE); ++// return MZ_FALSE; ++// } + +-mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +-{ +- mz_uint64 cur_file_ofs; ++// return MZ_TRUE; ++// } + +- if ((!pZip) || (!pFile)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); ++// mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) ++// { ++// mz_uint64 cur_file_ofs; + +- cur_file_ofs = MZ_FTELL64(pFile); ++// if ((!pZip) || (!pFile)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +- if (!archive_size) +- { +- if (MZ_FSEEK64(pFile, 0, SEEK_END)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); ++// cur_file_ofs = MZ_FTELL64(pFile); + +- archive_size = MZ_FTELL64(pFile) - cur_file_ofs; ++// if (!archive_size) ++// { ++// if (MZ_FSEEK64(pFile, 0, SEEK_END)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + +- if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); +- } ++// archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + +- if (!mz_zip_reader_init_internal(pZip, flags)) +- return MZ_FALSE; ++// if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); ++// } + +- pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; +- pZip->m_pRead = mz_zip_file_read_func; ++// if (!mz_zip_reader_init_internal(pZip, flags)) ++// return MZ_FALSE; + +- pZip->m_pIO_opaque = pZip; +- pZip->m_pState->m_pFile = pFile; +- pZip->m_archive_size = archive_size; +- pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; ++// pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; ++// pZip->m_pRead = mz_zip_file_read_func; + +- if (!mz_zip_reader_read_central_dir(pZip, flags)) +- { +- mz_zip_reader_end_internal(pZip, MZ_FALSE); +- return MZ_FALSE; +- } ++// pZip->m_pIO_opaque = pZip; ++// pZip->m_pState->m_pFile = pFile; ++// pZip->m_archive_size = archive_size; ++// pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + +- return MZ_TRUE; +-} ++// if (!mz_zip_reader_read_central_dir(pZip, flags)) ++// { ++// mz_zip_reader_end_internal(pZip, MZ_FALSE); ++// return MZ_FALSE; ++// } + +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// return MZ_TRUE; ++// } + +-static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +-{ +- if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) +- return NULL; +- return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +-} ++// #endif /* #ifndef MINIZ_NO_STDIO */ + +-mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +-{ +- mz_uint m_bit_flag; +- const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +- if (!p) +- { +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- return MZ_FALSE; +- } ++// static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) ++// { ++// if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) ++// return NULL; ++// return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); ++// } + +- m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); +- return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +-} ++// mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) ++// { ++// mz_uint m_bit_flag; ++// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); ++// if (!p) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return MZ_FALSE; ++// } + +-mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +-{ +- mz_uint bit_flag; +- mz_uint method; ++// m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); ++// return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; ++// } + +- const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +- if (!p) +- { +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- return MZ_FALSE; +- } ++// mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) ++// { ++// mz_uint bit_flag; ++// mz_uint method; + +- method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +- bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); ++// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); ++// if (!p) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return MZ_FALSE; ++// } + +- if ((method != 0) && (method != MZ_DEFLATED)) +- { +- mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); +- return MZ_FALSE; +- } ++// method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); ++// bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + +- if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) +- { +- mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); +- return MZ_FALSE; +- } ++// if ((method != 0) && (method != MZ_DEFLATED)) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); ++// return MZ_FALSE; ++// } + +- if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) +- { +- mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); +- return MZ_FALSE; +- } ++// if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); ++// return MZ_FALSE; ++// } + +- return MZ_TRUE; +-} ++// if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); ++// return MZ_FALSE; ++// } + +-mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +-{ +- mz_uint filename_len, attribute_mapping_id, external_attr; +- const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +- if (!p) +- { +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- return MZ_FALSE; +- } ++// return MZ_TRUE; ++// } + +- filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +- if (filename_len) +- { +- if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') +- return MZ_TRUE; +- } ++// mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) ++// { ++// mz_uint filename_len, attribute_mapping_id, external_attr; ++// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); ++// if (!p) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return MZ_FALSE; ++// } ++ ++// filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// if (filename_len) ++// { ++// if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') ++// return MZ_TRUE; ++// } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ +- attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; +- (void)attribute_mapping_id; ++// attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; ++// (void)attribute_mapping_id; + +- external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); +- if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) +- { +- return MZ_TRUE; +- } ++// external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); ++// if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) ++// { ++// return MZ_TRUE; ++// } + +- return MZ_FALSE; +-} ++// return MZ_FALSE; ++// } + +-static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +-{ +- mz_uint n; +- const mz_uint8 *p = pCentral_dir_header; +- +- if (pFound_zip64_extra_data) +- *pFound_zip64_extra_data = MZ_FALSE; +- +- if ((!p) || (!pStat)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- +- /* Extract fields from the central directory record. */ +- pStat->m_file_index = file_index; +- pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); +- pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); +- pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); +- pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); +- pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +-#ifndef MINIZ_NO_TIME +- pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +-#endif +- pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); +- pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); +- pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); +- pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); +- pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); +- pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); +- +- /* Copy as much of the filename and comment as possible. */ +- n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +- n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); +- memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); +- pStat->m_filename[n] = '\0'; +- +- n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); +- n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); +- pStat->m_comment_size = n; +- memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); +- pStat->m_comment[n] = '\0'; +- +- /* Set some flags for convienance */ +- pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); +- pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); +- pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); +- +- /* See if we need to read any zip64 extended information fields. */ +- /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ +- if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) +- { +- /* Attempt to find zip64 extended information field in the entry's extra data */ +- mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); ++// static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) ++// { ++// mz_uint n; ++// const mz_uint8 *p = pCentral_dir_header; + +- if (extra_size_remaining) +- { +- const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// if (pFound_zip64_extra_data) ++// *pFound_zip64_extra_data = MZ_FALSE; + +- do +- { +- mz_uint32 field_id; +- mz_uint32 field_data_size; ++// if ((!p) || (!pStat)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if (extra_size_remaining < (sizeof(mz_uint16) * 2)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// /* Extract fields from the central directory record. */ ++// pStat->m_file_index = file_index; ++// pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); ++// pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); ++// pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); ++// pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); ++// pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); ++// #ifndef MINIZ_NO_TIME ++// pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); ++// #endif ++// pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); ++// pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); ++// pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); ++// pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); ++// pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); ++// pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); ++ ++// /* Copy as much of the filename and comment as possible. */ ++// n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); ++// memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); ++// pStat->m_filename[n] = '\0'; ++ ++// n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); ++// n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); ++// pStat->m_comment_size = n; ++// memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); ++// pStat->m_comment[n] = '\0'; ++ ++// /* Set some flags for convienance */ ++// pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); ++// pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); ++// pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); ++ ++// /* See if we need to read any zip64 extended information fields. */ ++// /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ ++// if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) ++// { ++// /* Attempt to find zip64 extended information field in the entry's extra data */ ++// mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + +- field_id = MZ_READ_LE16(pExtra_data); +- field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); ++// if (extra_size_remaining) ++// { ++// const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + +- if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// do ++// { ++// mz_uint32 field_id; ++// mz_uint32 field_data_size; + +- if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) +- { +- const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; +- mz_uint32 field_data_remaining = field_data_size; ++// if (extra_size_remaining < (sizeof(mz_uint16) * 2)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (pFound_zip64_extra_data) +- *pFound_zip64_extra_data = MZ_TRUE; ++// field_id = MZ_READ_LE16(pExtra_data); ++// field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + +- if (pStat->m_uncomp_size == MZ_UINT32_MAX) +- { +- if (field_data_remaining < sizeof(mz_uint64)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- pStat->m_uncomp_size = MZ_READ_LE64(pField_data); +- pField_data += sizeof(mz_uint64); +- field_data_remaining -= sizeof(mz_uint64); +- } ++// if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) ++// { ++// const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; ++// mz_uint32 field_data_remaining = field_data_size; ++ ++// if (pFound_zip64_extra_data) ++// *pFound_zip64_extra_data = MZ_TRUE; ++ ++// if (pStat->m_uncomp_size == MZ_UINT32_MAX) ++// { ++// if (field_data_remaining < sizeof(mz_uint64)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++ ++// pStat->m_uncomp_size = MZ_READ_LE64(pField_data); ++// pField_data += sizeof(mz_uint64); ++// field_data_remaining -= sizeof(mz_uint64); ++// } ++ ++// if (pStat->m_comp_size == MZ_UINT32_MAX) ++// { ++// if (field_data_remaining < sizeof(mz_uint64)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++ ++// pStat->m_comp_size = MZ_READ_LE64(pField_data); ++// pField_data += sizeof(mz_uint64); ++// field_data_remaining -= sizeof(mz_uint64); ++// } ++ ++// if (pStat->m_local_header_ofs == MZ_UINT32_MAX) ++// { ++// if (field_data_remaining < sizeof(mz_uint64)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++ ++// pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); ++// pField_data += sizeof(mz_uint64); ++// // field_data_remaining -= sizeof(mz_uint64); ++// } ++ ++// break; ++// } + +- if (pStat->m_comp_size == MZ_UINT32_MAX) +- { +- if (field_data_remaining < sizeof(mz_uint64)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; ++// extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; ++// } while (extra_size_remaining); ++// } ++// } + +- pStat->m_comp_size = MZ_READ_LE64(pField_data); +- pField_data += sizeof(mz_uint64); +- field_data_remaining -= sizeof(mz_uint64); +- } ++// return MZ_TRUE; ++// } + +- if (pStat->m_local_header_ofs == MZ_UINT32_MAX) +- { +- if (field_data_remaining < sizeof(mz_uint64)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) ++// { ++// mz_uint i; ++// if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) ++// return 0 == memcmp(pA, pB, len); ++// for (i = 0; i < len; ++i) ++// if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) ++// return MZ_FALSE; ++// return MZ_TRUE; ++// } + +- pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); +- pField_data += sizeof(mz_uint64); +- // field_data_remaining -= sizeof(mz_uint64); +- } ++// static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) ++// { ++// const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; ++// mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// mz_uint8 l = 0, r = 0; ++// pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; ++// pE = pL + MZ_MIN(l_len, r_len); ++// while (pL < pE) ++// { ++// if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) ++// break; ++// pL++; ++// pR++; ++// } ++// return (pL == pE) ? (int)(l_len - r_len) : (l - r); ++// } + +- break; +- } ++// static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) ++// { ++// mz_zip_internal_state *pState = pZip->m_pState; ++// const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; ++// const mz_zip_array *pCentral_dir = &pState->m_central_dir; ++// mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); ++// const uint32_t size = pZip->m_total_files; ++// const mz_uint filename_len = (mz_uint)strlen(pFilename); + +- pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; +- extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; +- } while (extra_size_remaining); +- } +- } ++// if (pIndex) ++// *pIndex = 0; + +- return MZ_TRUE; +-} ++// if (size) ++// { ++// /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ ++// /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ ++// mz_int64 l = 0, h = (mz_int64)size - 1; + +-static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +-{ +- mz_uint i; +- if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) +- return 0 == memcmp(pA, pB, len); +- for (i = 0; i < len; ++i) +- if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) +- return MZ_FALSE; +- return MZ_TRUE; +-} ++// while (l <= h) ++// { ++// mz_int64 m = l + ((h - l) >> 1); ++// uint32_t file_index = pIndices[(uint32_t)m]; + +-static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +-{ +- const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; +- mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); +- mz_uint8 l = 0, r = 0; +- pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +- pE = pL + MZ_MIN(l_len, r_len); +- while (pL < pE) +- { +- if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) +- break; +- pL++; +- pR++; +- } +- return (pL == pE) ? (int)(l_len - r_len) : (l - r); +-} ++// int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); ++// if (!comp) ++// { ++// if (pIndex) ++// *pIndex = file_index; ++// return MZ_TRUE; ++// } ++// else if (comp < 0) ++// l = m + 1; ++// else ++// h = m - 1; ++// } ++// } + +-static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +-{ +- mz_zip_internal_state *pState = pZip->m_pState; +- const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; +- const mz_zip_array *pCentral_dir = &pState->m_central_dir; +- mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); +- const uint32_t size = pZip->m_total_files; +- const mz_uint filename_len = (mz_uint)strlen(pFilename); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); ++// } + +- if (pIndex) +- *pIndex = 0; ++// int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) ++// { ++// mz_uint32 index_; ++// if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index_)) ++// return -1; ++// else ++// return (int)index_; ++// } + +- if (size) +- { +- /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ +- /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ +- mz_int64 l = 0, h = (mz_int64)size - 1; ++// mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) ++// { ++// mz_uint file_index; ++// size_t name_len, comment_len; + +- while (l <= h) +- { +- mz_int64 m = l + ((h - l) >> 1); +- uint32_t file_index = pIndices[(uint32_t)m]; ++// if (pIndex) ++// *pIndex = 0; + +- int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); +- if (!comp) +- { +- if (pIndex) +- *pIndex = file_index; +- return MZ_TRUE; +- } +- else if (comp < 0) +- l = m + 1; +- else +- h = m - 1; +- } +- } ++// if ((!pZip) || (!pZip->m_pState) || (!pName)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +-} ++// /* See if we can use a binary search */ ++// if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && ++// (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && ++// ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) ++// { ++// return mz_zip_locate_file_binary_search(pZip, pName, pIndex); ++// } + +-int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +-{ +- mz_uint32 index_; +- if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index_)) +- return -1; +- else +- return (int)index_; +-} ++// /* Locate the entry by scanning the entire central directory */ ++// name_len = strlen(pName); ++// if (name_len > MZ_UINT16_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +-mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +-{ +- mz_uint file_index; +- size_t name_len, comment_len; ++// comment_len = pComment ? strlen(pComment) : 0; ++// if (comment_len > MZ_UINT16_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if (pIndex) +- *pIndex = 0; ++// for (file_index = 0; file_index < pZip->m_total_files; file_index++) ++// { ++// const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); ++// mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; ++// if (filename_len < name_len) ++// continue; ++// if (comment_len) ++// { ++// mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); ++// const char *pFile_comment = pFilename + filename_len + file_extra_len; ++// if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) ++// continue; ++// } ++// if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) ++// { ++// int ofs = filename_len - 1; ++// do ++// { ++// if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) ++// break; ++// } while (--ofs >= 0); ++// ofs++; ++// pFilename += ofs; ++// filename_len -= ofs; ++// } ++// if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) ++// { ++// if (pIndex) ++// *pIndex = file_index; ++// return MZ_TRUE; ++// } ++// } + +- if ((!pZip) || (!pZip->m_pState) || (!pName)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); ++// } + +- /* See if we can use a binary search */ +- if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && +- (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && +- ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) +- { +- return mz_zip_locate_file_binary_search(pZip, pName, pIndex); +- } ++// mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) ++// { ++// int status = TINFL_STATUS_DONE; ++// mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; ++// mz_zip_archive_file_stat file_stat; ++// void *pRead_buf; ++// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; ++// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; ++// tinfl_decompressor inflator; + +- /* Locate the entry by scanning the entire central directory */ +- name_len = strlen(pName); +- if (name_len > MZ_UINT16_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- comment_len = pComment ? strlen(pComment) : 0; +- if (comment_len > MZ_UINT16_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) ++// return MZ_FALSE; + +- for (file_index = 0; file_index < pZip->m_total_files; file_index++) +- { +- const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +- mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); +- const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +- if (filename_len < name_len) +- continue; +- if (comment_len) +- { +- mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); +- const char *pFile_comment = pFilename + filename_len + file_extra_len; +- if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) +- continue; +- } +- if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) +- { +- int ofs = filename_len - 1; +- do +- { +- if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) +- break; +- } while (--ofs >= 0); +- ofs++; +- pFilename += ofs; +- filename_len -= ofs; +- } +- if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) +- { +- if (pIndex) +- *pIndex = file_index; +- return MZ_TRUE; +- } +- } ++// /* A directory or zero length file */ ++// if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) ++// return MZ_TRUE; + +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +-} ++// /* Encryption and patch files are not supported. */ ++// if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + +-mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +-{ +- int status = TINFL_STATUS_DONE; +- mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; +- mz_zip_archive_file_stat file_stat; +- void *pRead_buf; +- mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +- mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; +- tinfl_decompressor inflator; +- +- if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- +- if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +- return MZ_FALSE; +- +- /* A directory or zero length file */ +- if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) +- return MZ_TRUE; +- +- /* Encryption and patch files are not supported. */ +- if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); +- +- /* This function only supports decompressing stored and deflate. */ +- if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); +- +- /* Ensure supplied output buffer is large enough. */ +- needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; +- if (buf_size < needed_size) +- return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); +- +- /* Read and parse the local directory entry. */ +- cur_file_ofs = file_stat.m_local_header_ofs; +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +- +- if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- +- cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +- if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- +- if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) +- { +- /* The file is stored or the caller has requested the compressed data. */ +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// /* This function only supports decompressing stored and deflate. */ ++// if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) +- { +- if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) +- return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); +- } +-#endif ++// /* Ensure supplied output buffer is large enough. */ ++// needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; ++// if (buf_size < needed_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + +- return MZ_TRUE; +- } ++// /* Read and parse the local directory entry. */ ++// cur_file_ofs = file_stat.m_local_header_ofs; ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +- /* Decompress the file either directly from memory or from a file input buffer. */ +- tinfl_init(&inflator); ++// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (pZip->m_pState->m_pMem) +- { +- /* Read directly from the archive in memory. */ +- pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; +- read_buf_size = read_buf_avail = file_stat.m_comp_size; +- comp_remaining = 0; +- } +- else if (pUser_read_buf) +- { +- /* Use a user provided read buffer. */ +- if (!user_read_buf_size) +- return MZ_FALSE; +- pRead_buf = (mz_uint8 *)pUser_read_buf; +- read_buf_size = user_read_buf_size; +- read_buf_avail = 0; +- comp_remaining = file_stat.m_comp_size; +- } +- else +- { +- /* Temporarily allocate a read buffer. */ +- read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); +- if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); ++// if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) ++// { ++// /* The file is stored or the caller has requested the compressed data. */ ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +- read_buf_avail = 0; +- comp_remaining = file_stat.m_comp_size; +- } ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) ++// { ++// if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) ++// return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); ++// } ++// #endif + +- do +- { +- /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ +- size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); +- if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) +- { +- read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +- { +- status = TINFL_STATUS_FAILED; +- mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); +- break; +- } +- cur_file_ofs += read_buf_avail; +- comp_remaining -= read_buf_avail; +- read_buf_ofs = 0; +- } +- in_buf_size = (size_t)read_buf_avail; +- status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); +- read_buf_avail -= in_buf_size; +- read_buf_ofs += in_buf_size; +- out_buf_ofs += out_buf_size; +- } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); +- +- if (status == TINFL_STATUS_DONE) +- { +- /* Make sure the entire file was decompressed, and check its CRC. */ +- if (out_buf_ofs != file_stat.m_uncomp_size) +- { +- mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); +- status = TINFL_STATUS_FAILED; +- } +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) +- { +- mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); +- status = TINFL_STATUS_FAILED; +- } +-#endif +- } ++// return MZ_TRUE; ++// } + +- if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) +- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); ++// /* Decompress the file either directly from memory or from a file input buffer. */ ++// tinfl_init(&inflator); + +- return status == TINFL_STATUS_DONE; +-} ++// if (pZip->m_pState->m_pMem) ++// { ++// /* Read directly from the archive in memory. */ ++// pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; ++// read_buf_size = read_buf_avail = file_stat.m_comp_size; ++// comp_remaining = 0; ++// } ++// else if (pUser_read_buf) ++// { ++// /* Use a user provided read buffer. */ ++// if (!user_read_buf_size) ++// return MZ_FALSE; ++// pRead_buf = (mz_uint8 *)pUser_read_buf; ++// read_buf_size = user_read_buf_size; ++// read_buf_avail = 0; ++// comp_remaining = file_stat.m_comp_size; ++// } ++// else ++// { ++// /* Temporarily allocate a read buffer. */ ++// read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); ++// if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +-mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +-{ +- mz_uint32 file_index; +- if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +- return MZ_FALSE; +- return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +-} ++// if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +-mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +-{ +- return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +-} ++// read_buf_avail = 0; ++// comp_remaining = file_stat.m_comp_size; ++// } + +-mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +-{ +- return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +-} ++// do ++// { ++// /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ ++// size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); ++// if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) ++// { ++// read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) ++// { ++// status = TINFL_STATUS_FAILED; ++// mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); ++// break; ++// } ++// cur_file_ofs += read_buf_avail; ++// comp_remaining -= read_buf_avail; ++// read_buf_ofs = 0; ++// } ++// in_buf_size = (size_t)read_buf_avail; ++// status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); ++// read_buf_avail -= in_buf_size; ++// read_buf_ofs += in_buf_size; ++// out_buf_ofs += out_buf_size; ++// } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); ++ ++// if (status == TINFL_STATUS_DONE) ++// { ++// /* Make sure the entire file was decompressed, and check its CRC. */ ++// if (out_buf_ofs != file_stat.m_uncomp_size) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); ++// status = TINFL_STATUS_FAILED; ++// } ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); ++// status = TINFL_STATUS_FAILED; ++// } ++// #endif ++// } + +-void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +-{ +- mz_uint64 comp_size, uncomp_size, alloc_size; +- const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +- void *pBuf; ++// if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + +- if (pSize) +- *pSize = 0; ++// return status == TINFL_STATUS_DONE; ++// } + +- if (!p) +- { +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- return NULL; +- } ++// mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) ++// { ++// mz_uint32 file_index; ++// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) ++// return MZ_FALSE; ++// return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); ++// } + +- comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); +- uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); ++// mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) ++// { ++// return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); ++// } + +- alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +- if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +- { +- mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); +- return NULL; +- } ++// mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) ++// { ++// return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); ++// } + +- if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) +- { +- mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- return NULL; +- } ++// void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) ++// { ++// mz_uint64 comp_size, uncomp_size, alloc_size; ++// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); ++// void *pBuf; + +- if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); +- return NULL; +- } ++// if (pSize) ++// *pSize = 0; + +- if (pSize) +- *pSize = (size_t)alloc_size; +- return pBuf; +-} ++// if (!p) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return NULL; ++// } + +-void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +-{ +- mz_uint32 file_index; +- if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +- { +- if (pSize) +- *pSize = 0; +- return MZ_FALSE; +- } +- return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +-} ++// comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); ++// uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + +-mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +-{ +- int status = TINFL_STATUS_DONE; +- mz_uint file_crc32 = MZ_CRC32_INIT; +- mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; +- mz_zip_archive_file_stat file_stat; +- void *pRead_buf = NULL; +- void *pWrite_buf = NULL; +- mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +- mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; +- +- if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- +- if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +- return MZ_FALSE; +- +- /* A directory or zero length file */ +- if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) +- return MZ_TRUE; +- +- /* Encryption and patch files are not supported. */ +- if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); +- +- /* This function only supports decompressing stored and deflate. */ +- if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); +- +- /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ +- cur_file_ofs = file_stat.m_local_header_ofs; +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +- +- if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- +- cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +- if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- +- /* Decompress the file either directly from memory or from a file input buffer. */ +- if (pZip->m_pState->m_pMem) +- { +- pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; +- read_buf_size = read_buf_avail = file_stat.m_comp_size; +- comp_remaining = 0; +- } +- else +- { +- read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); +- if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; ++// if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// return NULL; ++// } + +- read_buf_avail = 0; +- comp_remaining = file_stat.m_comp_size; +- } ++// if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// return NULL; ++// } + +- if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) +- { +- /* The file is stored or the caller has requested the compressed data. */ +- if (pZip->m_pState->m_pMem) +- { +- if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); ++// return NULL; ++// } + +- if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) +- { +- mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); +- status = TINFL_STATUS_FAILED; +- } +- else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +- { +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +-#endif +- } ++// if (pSize) ++// *pSize = (size_t)alloc_size; ++// return pBuf; ++// } + +- cur_file_ofs += file_stat.m_comp_size; +- out_buf_ofs += file_stat.m_comp_size; +- comp_remaining = 0; +- } +- else +- { +- while (comp_remaining) +- { +- read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +- { +- mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +- status = TINFL_STATUS_FAILED; +- break; +- } ++// void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) ++// { ++// mz_uint32 file_index; ++// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) ++// { ++// if (pSize) ++// *pSize = 0; ++// return MZ_FALSE; ++// } ++// return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); ++// } + +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +- { +- file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); +- } +-#endif ++// mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) ++// { ++// int status = TINFL_STATUS_DONE; ++// mz_uint file_crc32 = MZ_CRC32_INIT; ++// mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; ++// mz_zip_archive_file_stat file_stat; ++// void *pRead_buf = NULL; ++// void *pWrite_buf = NULL; ++// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; ++// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + +- if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +- { +- mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); +- status = TINFL_STATUS_FAILED; +- break; +- } ++// if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- cur_file_ofs += read_buf_avail; +- out_buf_ofs += read_buf_avail; +- comp_remaining -= read_buf_avail; +- } +- } +- } +- else +- { +- tinfl_decompressor inflator; +- tinfl_init(&inflator); ++// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) ++// return MZ_FALSE; + +- if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) +- { +- mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- status = TINFL_STATUS_FAILED; +- } +- else +- { +- do +- { +- mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); +- size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); +- if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) +- { +- read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); +- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +- { +- mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +- status = TINFL_STATUS_FAILED; +- break; +- } +- cur_file_ofs += read_buf_avail; +- comp_remaining -= read_buf_avail; +- read_buf_ofs = 0; +- } ++// /* A directory or zero length file */ ++// if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) ++// return MZ_TRUE; + +- in_buf_size = (size_t)read_buf_avail; +- status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); +- read_buf_avail -= in_buf_size; +- read_buf_ofs += in_buf_size; ++// /* Encryption and patch files are not supported. */ ++// if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + +- if (out_buf_size) +- { +- if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) +- { +- mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); +- status = TINFL_STATUS_FAILED; +- break; +- } ++// /* This function only supports decompressing stored and deflate. */ ++// if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +-#endif +- if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) +- { +- mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); +- status = TINFL_STATUS_FAILED; +- break; +- } +- } +- } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); +- } +- } ++// /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ ++// cur_file_ofs = file_stat.m_local_header_ofs; ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +- if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) +- { +- /* Make sure the entire file was decompressed, and check its CRC. */ +- if (out_buf_ofs != file_stat.m_uncomp_size) +- { +- mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); +- status = TINFL_STATUS_FAILED; +- } +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- else if (file_crc32 != file_stat.m_crc32) +- { +- mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); +- status = TINFL_STATUS_FAILED; +- } +-#endif +- } ++// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (!pZip->m_pState->m_pMem) +- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); ++// cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); ++// if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +- if (pWrite_buf) +- pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); ++// /* Decompress the file either directly from memory or from a file input buffer. */ ++// if (pZip->m_pState->m_pMem) ++// { ++// pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; ++// read_buf_size = read_buf_avail = file_stat.m_comp_size; ++// comp_remaining = 0; ++// } ++// else ++// { ++// read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); ++// if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +- return status == TINFL_STATUS_DONE; +-} ++// read_buf_avail = 0; ++// comp_remaining = file_stat.m_comp_size; ++// } + +-mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +-{ +- mz_uint32 file_index; +- if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +- return MZ_FALSE; ++// if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) ++// { ++// /* The file is stored or the caller has requested the compressed data. */ ++// if (pZip->m_pState->m_pMem) ++// { ++// if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +- return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +-} ++// if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); ++// status = TINFL_STATUS_FAILED; ++// } ++// else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) ++// { ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); ++// #endif ++// } + +-mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +-{ +- mz_zip_reader_extract_iter_state *pState; +- mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +- mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; ++// cur_file_ofs += file_stat.m_comp_size; ++// out_buf_ofs += file_stat.m_comp_size; ++// comp_remaining = 0; ++// } ++// else ++// { ++// while (comp_remaining) ++// { ++// read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// status = TINFL_STATUS_FAILED; ++// break; ++// } + +- /* Argument sanity check */ +- if ((!pZip) || (!pZip->m_pState)) +- return NULL; ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) ++// { ++// file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); ++// } ++// #endif + +- /* Allocate an iterator status structure */ +- pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); +- if (!pState) +- { +- mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- return NULL; +- } ++// if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); ++// status = TINFL_STATUS_FAILED; ++// break; ++// } + +- /* Fetch file details */ +- if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } ++// cur_file_ofs += read_buf_avail; ++// out_buf_ofs += read_buf_avail; ++// comp_remaining -= read_buf_avail; ++// } ++// } ++// } ++// else ++// { ++// tinfl_decompressor inflator; ++// tinfl_init(&inflator); + +- /* Encryption and patch files are not supported. */ +- if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) +- { +- mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } ++// if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// status = TINFL_STATUS_FAILED; ++// } ++// else ++// { ++// do ++// { ++// mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); ++// size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); ++// if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) ++// { ++// read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); ++// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// status = TINFL_STATUS_FAILED; ++// break; ++// } ++// cur_file_ofs += read_buf_avail; ++// comp_remaining -= read_buf_avail; ++// read_buf_ofs = 0; ++// } + +- /* This function only supports decompressing stored and deflate. */ +- if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) +- { +- mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } ++// in_buf_size = (size_t)read_buf_avail; ++// status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); ++// read_buf_avail -= in_buf_size; ++// read_buf_ofs += in_buf_size; + +- /* Init state - save args */ +- pState->pZip = pZip; +- pState->flags = flags; ++// if (out_buf_size) ++// { ++// if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); ++// status = TINFL_STATUS_FAILED; ++// break; ++// } ++ ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); ++// #endif ++// if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); ++// status = TINFL_STATUS_FAILED; ++// break; ++// } ++// } ++// } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); ++// } ++// } + +- /* Init state - reset variables to defaults */ +- pState->status = TINFL_STATUS_DONE; +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- pState->file_crc32 = MZ_CRC32_INIT; +-#endif +- pState->read_buf_ofs = 0; +- pState->out_buf_ofs = 0; +- pState->pRead_buf = NULL; +- pState->pWrite_buf = NULL; +- pState->out_blk_remain = 0; +- +- /* Read and parse the local directory entry. */ +- pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; +- if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +- { +- mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } ++// if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) ++// { ++// /* Make sure the entire file was decompressed, and check its CRC. */ ++// if (out_buf_ofs != file_stat.m_uncomp_size) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); ++// status = TINFL_STATUS_FAILED; ++// } ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// else if (file_crc32 != file_stat.m_crc32) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); ++// status = TINFL_STATUS_FAILED; ++// } ++// #endif ++// } + +- if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +- { +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } ++// if (!pZip->m_pState->m_pMem) ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + +- pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +- if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) +- { +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } ++// if (pWrite_buf) ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + +- /* Decompress the file either directly from memory or from a file input buffer. */ +- if (pZip->m_pState->m_pMem) +- { +- pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; +- pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; +- pState->comp_remaining = pState->file_stat.m_comp_size; +- } +- else +- { +- if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) +- { +- /* Decompression required, therefore intermediate read buffer required */ +- pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +- if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) +- { +- mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } +- } +- else +- { +- /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ +- pState->read_buf_size = 0; +- } +- pState->read_buf_avail = 0; +- pState->comp_remaining = pState->file_stat.m_comp_size; +- } ++// return status == TINFL_STATUS_DONE; ++// } ++ ++// mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) ++// { ++// mz_uint32 file_index; ++// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) ++// return MZ_FALSE; ++ ++// return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); ++// } ++ ++// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) ++// { ++// mz_zip_reader_extract_iter_state *pState; ++// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; ++// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; ++ ++// /* Argument sanity check */ ++// if ((!pZip) || (!pZip->m_pState)) ++// return NULL; ++ ++// /* Allocate an iterator status structure */ ++// pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); ++// if (!pState) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// return NULL; ++// } ++ ++// /* Fetch file details */ ++// if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } ++ ++// /* Encryption and patch files are not supported. */ ++// if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } ++ ++// /* This function only supports decompressing stored and deflate. */ ++// if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } ++ ++// /* Init state - save args */ ++// pState->pZip = pZip; ++// pState->flags = flags; ++ ++// /* Init state - reset variables to defaults */ ++// pState->status = TINFL_STATUS_DONE; ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// pState->file_crc32 = MZ_CRC32_INIT; ++// #endif ++// pState->read_buf_ofs = 0; ++// pState->out_buf_ofs = 0; ++// pState->pRead_buf = NULL; ++// pState->pWrite_buf = NULL; ++// pState->out_blk_remain = 0; ++ ++// /* Read and parse the local directory entry. */ ++// pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; ++// if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } ++ ++// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } ++ ++// pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); ++// if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } + +- if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) +- { +- /* Decompression required, init decompressor */ +- tinfl_init( &pState->inflator ); ++// /* Decompress the file either directly from memory or from a file input buffer. */ ++// if (pZip->m_pState->m_pMem) ++// { ++// pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; ++// pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; ++// pState->comp_remaining = pState->file_stat.m_comp_size; ++// } ++// else ++// { ++// if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) ++// { ++// /* Decompression required, therefore intermediate read buffer required */ ++// pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); ++// if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } ++// } ++// else ++// { ++// /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ ++// pState->read_buf_size = 0; ++// } ++// pState->read_buf_avail = 0; ++// pState->comp_remaining = pState->file_stat.m_comp_size; ++// } + +- /* Allocate write buffer */ +- if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) +- { +- mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- if (pState->pRead_buf) +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- return NULL; +- } +- } ++// if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) ++// { ++// /* Decompression required, init decompressor */ ++// tinfl_init( &pState->inflator ); + +- return pState; +-} ++// /* Allocate write buffer */ ++// if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// if (pState->pRead_buf) ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// return NULL; ++// } ++// } + +-mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +-{ +- mz_uint32 file_index; ++// return pState; ++// } + +- /* Locate file index by name */ +- if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +- return NULL; ++// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) ++// { ++// mz_uint32 file_index; + +- /* Construct iterator */ +- return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +-} ++// /* Locate file index by name */ ++// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) ++// return NULL; + +-size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +-{ +- size_t copied_to_caller = 0; ++// /* Construct iterator */ ++// return mz_zip_reader_extract_iter_new(pZip, file_index, flags); ++// } + +- /* Argument sanity check */ +- if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) +- return 0; ++// size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) ++// { ++// size_t copied_to_caller = 0; + +- if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) +- { +- /* The file is stored or the caller has requested the compressed data, calc amount to return. */ +- copied_to_caller = MZ_MIN( buf_size, pState->comp_remaining ); ++// /* Argument sanity check */ ++// if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) ++// return 0; + +- /* Zip is in memory....or requires reading from a file? */ +- if (pState->pZip->m_pState->m_pMem) +- { +- /* Copy data to caller's buffer */ +- memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); +- pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; +- } +- else +- { +- /* Read directly into caller's buffer */ +- if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) +- { +- /* Failed to read all that was asked for, flag failure and alert user */ +- mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); +- pState->status = TINFL_STATUS_FAILED; +- copied_to_caller = 0; +- } +- } ++// if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) ++// { ++// /* The file is stored or the caller has requested the compressed data, calc amount to return. */ ++// copied_to_caller = MZ_MIN( buf_size, pState->comp_remaining ); + +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- /* Compute CRC if not returning compressed data only */ +- if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +- pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +-#endif ++// /* Zip is in memory....or requires reading from a file? */ ++// if (pState->pZip->m_pState->m_pMem) ++// { ++// /* Copy data to caller's buffer */ ++// memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); ++// pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; ++// } ++// else ++// { ++// /* Read directly into caller's buffer */ ++// if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) ++// { ++// /* Failed to read all that was asked for, flag failure and alert user */ ++// mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); ++// pState->status = TINFL_STATUS_FAILED; ++// copied_to_caller = 0; ++// } ++// } + +- /* Advance offsets, dec counters */ +- pState->cur_file_ofs += copied_to_caller; +- pState->out_buf_ofs += copied_to_caller; +- pState->comp_remaining -= copied_to_caller; +- } +- else +- { +- do +- { +- /* Calc ptr to write buffer - given current output pos and block size */ +- mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// /* Compute CRC if not returning compressed data only */ ++// if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) ++// pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); ++// #endif + +- /* Calc max output size - given current output pos and block size */ +- size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); ++// /* Advance offsets, dec counters */ ++// pState->cur_file_ofs += copied_to_caller; ++// pState->out_buf_ofs += copied_to_caller; ++// pState->comp_remaining -= copied_to_caller; ++// } ++// else ++// { ++// do ++// { ++// /* Calc ptr to write buffer - given current output pos and block size */ ++// mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + +- if (!pState->out_blk_remain) +- { +- /* Read more data from file if none available (and reading from file) */ +- if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) +- { +- /* Calc read size */ +- pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); +- if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) +- { +- mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); +- pState->status = TINFL_STATUS_FAILED; +- break; +- } ++// /* Calc max output size - given current output pos and block size */ ++// size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + +- /* Advance offsets, dec counters */ +- pState->cur_file_ofs += pState->read_buf_avail; +- pState->comp_remaining -= pState->read_buf_avail; +- pState->read_buf_ofs = 0; +- } ++// if (!pState->out_blk_remain) ++// { ++// /* Read more data from file if none available (and reading from file) */ ++// if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) ++// { ++// /* Calc read size */ ++// pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); ++// if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) ++// { ++// mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); ++// pState->status = TINFL_STATUS_FAILED; ++// break; ++// } ++ ++// /* Advance offsets, dec counters */ ++// pState->cur_file_ofs += pState->read_buf_avail; ++// pState->comp_remaining -= pState->read_buf_avail; ++// pState->read_buf_ofs = 0; ++// } + +- /* Perform decompression */ +- in_buf_size = (size_t)pState->read_buf_avail; +- pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); +- pState->read_buf_avail -= in_buf_size; +- pState->read_buf_ofs += in_buf_size; ++// /* Perform decompression */ ++// in_buf_size = (size_t)pState->read_buf_avail; ++// pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); ++// pState->read_buf_avail -= in_buf_size; ++// pState->read_buf_ofs += in_buf_size; + +- /* Update current output block size remaining */ +- pState->out_blk_remain = out_buf_size; +- } ++// /* Update current output block size remaining */ ++// pState->out_blk_remain = out_buf_size; ++// } + +- if (pState->out_blk_remain) +- { +- /* Calc amount to return. */ +- size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); ++// if (pState->out_blk_remain) ++// { ++// /* Calc amount to return. */ ++// size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + +- /* Copy data to caller's buffer */ +- memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); ++// /* Copy data to caller's buffer */ ++// memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- /* Perform CRC */ +- pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +-#endif ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// /* Perform CRC */ ++// pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); ++// #endif + +- /* Decrement data consumed from block */ +- pState->out_blk_remain -= to_copy; ++// /* Decrement data consumed from block */ ++// pState->out_blk_remain -= to_copy; + +- /* Inc output offset, while performing sanity check */ +- if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) +- { +- mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); +- pState->status = TINFL_STATUS_FAILED; +- break; +- } ++// /* Inc output offset, while performing sanity check */ ++// if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) ++// { ++// mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); ++// pState->status = TINFL_STATUS_FAILED; ++// break; ++// } + +- /* Increment counter of data copied to caller */ +- copied_to_caller += to_copy; +- } +- } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); +- } ++// /* Increment counter of data copied to caller */ ++// copied_to_caller += to_copy; ++// } ++// } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); ++// } + +- /* Return how many bytes were copied into user buffer */ +- return copied_to_caller; +-} ++// /* Return how many bytes were copied into user buffer */ ++// return copied_to_caller; ++// } + +-mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +-{ +- int status; ++// mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) ++// { ++// int status; + +- /* Argument sanity check */ +- if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) +- return MZ_FALSE; ++// /* Argument sanity check */ ++// if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) ++// return MZ_FALSE; + +- /* Was decompression completed and requested? */ +- if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) +- { +- /* Make sure the entire file was decompressed, and check its CRC. */ +- if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) +- { +- mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); +- pState->status = TINFL_STATUS_FAILED; +- } +-#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +- else if (pState->file_crc32 != pState->file_stat.m_crc32) +- { +- mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); +- pState->status = TINFL_STATUS_FAILED; +- } +-#endif +- } ++// /* Was decompression completed and requested? */ ++// if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) ++// { ++// /* Make sure the entire file was decompressed, and check its CRC. */ ++// if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) ++// { ++// mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); ++// pState->status = TINFL_STATUS_FAILED; ++// } ++// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS ++// else if (pState->file_crc32 != pState->file_stat.m_crc32) ++// { ++// mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); ++// pState->status = TINFL_STATUS_FAILED; ++// } ++// #endif ++// } + +- /* Free buffers */ +- if (!pState->pZip->m_pState->m_pMem) +- pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); +- if (pState->pWrite_buf) +- pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); ++// /* Free buffers */ ++// if (!pState->pZip->m_pState->m_pMem) ++// pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); ++// if (pState->pWrite_buf) ++// pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + +- /* Save status */ +- status = pState->status; ++// /* Save status */ ++// status = pState->status; + +- /* Free context */ +- pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); ++// /* Free context */ ++// pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + +- return status == TINFL_STATUS_DONE; +-} ++// return status == TINFL_STATUS_DONE; ++// } + +-#ifndef MINIZ_NO_STDIO +-static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +-{ +- (void)ofs; ++// #ifndef MINIZ_NO_STDIO ++// static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) ++// { ++// (void)ofs; + +- return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +-} ++// return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); ++// } + +-mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +-{ +- mz_bool status; +- mz_zip_archive_file_stat file_stat; +- MZ_FILE *pFile; ++// mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) ++// { ++// mz_bool status; ++// mz_zip_archive_file_stat file_stat; ++// MZ_FILE *pFile; + +- if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +- return MZ_FALSE; ++// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) ++// return MZ_FALSE; + +- if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); ++// if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + +- pFile = MZ_FOPEN(pDst_filename, "wb"); +- if (!pFile) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); ++// pFile = MZ_FOPEN(pDst_filename, "wb"); ++// if (!pFile) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +- status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); ++// status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + +- if (MZ_FCLOSE(pFile) == EOF) +- { +- if (status) +- mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); ++// if (MZ_FCLOSE(pFile) == EOF) ++// { ++// if (status) ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + +- status = MZ_FALSE; +- } ++// status = MZ_FALSE; ++// } + +-#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) +- if (status) +- mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +-#endif ++// #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) ++// if (status) ++// mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); ++// #endif + +- return status; +-} ++// return status; ++// } + +-mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +-{ +- mz_uint32 file_index; +- if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) +- return MZ_FALSE; ++// mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) ++// { ++// mz_uint32 file_index; ++// if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) ++// return MZ_FALSE; + +- return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +-} ++// return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); ++// } + +-mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +-{ +- mz_zip_archive_file_stat file_stat; ++// mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) ++// { ++// mz_zip_archive_file_stat file_stat; + +- if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +- return MZ_FALSE; ++// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) ++// return MZ_FALSE; + +- if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); ++// if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + +- return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +-} ++// return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); ++// } + +-mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +-{ +- mz_uint32 file_index; +- if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) +- return MZ_FALSE; ++// mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) ++// { ++// mz_uint32 file_index; ++// if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) ++// return MZ_FALSE; + +- return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +-} +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); ++// } ++// #endif /* #ifndef MINIZ_NO_STDIO */ + + // static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) + // { +@@ -5444,1202 +5443,1202 @@ mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pA + + /* ------------------- .ZIP archive writing */ + +-#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS ++// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +-static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +-{ +- p[0] = (mz_uint8)v; +- p[1] = (mz_uint8)(v >> 8); +-} +-static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +-{ +- p[0] = (mz_uint8)v; +- p[1] = (mz_uint8)(v >> 8); +- p[2] = (mz_uint8)(v >> 16); +- p[3] = (mz_uint8)(v >> 24); +-} +-static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +-{ +- mz_write_le32(p, (mz_uint32)v); +- mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +-} ++// static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) ++// { ++// p[0] = (mz_uint8)v; ++// p[1] = (mz_uint8)(v >> 8); ++// } ++// static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) ++// { ++// p[0] = (mz_uint8)v; ++// p[1] = (mz_uint8)(v >> 8); ++// p[2] = (mz_uint8)(v >> 16); ++// p[3] = (mz_uint8)(v >> 24); ++// } ++// static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) ++// { ++// mz_write_le32(p, (mz_uint32)v); ++// mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); ++// } + +-#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +-#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +-#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) ++// #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) ++// #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) ++// #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +-static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +-{ +- mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +- mz_zip_internal_state *pState = pZip->m_pState; +- mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); ++// static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) ++// { ++// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; ++// mz_zip_internal_state *pState = pZip->m_pState; ++// mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + +- if (!n) +- return 0; ++// if (!n) ++// return 0; + +- /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ +- if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) +- { +- mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); +- return 0; +- } ++// /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ ++// if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); ++// return 0; ++// } + +- if (new_size > pState->m_mem_capacity) +- { +- void *pNew_block; +- size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); ++// if (new_size > pState->m_mem_capacity) ++// { ++// void *pNew_block; ++// size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + +- while (new_capacity < new_size) +- new_capacity *= 2; ++// while (new_capacity < new_size) ++// new_capacity *= 2; + +- if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) +- { +- mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- return 0; +- } ++// if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// return 0; ++// } + +- pState->m_pMem = pNew_block; +- pState->m_mem_capacity = new_capacity; +- } +- memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); +- pState->m_mem_size = (size_t)new_size; +- return n; +-} ++// pState->m_pMem = pNew_block; ++// pState->m_mem_capacity = new_capacity; ++// } ++// memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); ++// pState->m_mem_size = (size_t)new_size; ++// return n; ++// } + +-static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +-{ +- mz_zip_internal_state *pState; +- mz_bool status = MZ_TRUE; ++// static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) ++// { ++// mz_zip_internal_state *pState; ++// mz_bool status = MZ_TRUE; + +- if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) +- { +- if (set_last_error) +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- return MZ_FALSE; +- } ++// if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) ++// { ++// if (set_last_error) ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return MZ_FALSE; ++// } + +- pState = pZip->m_pState; +- pZip->m_pState = NULL; +- mz_zip_array_clear(pZip, &pState->m_central_dir); +- mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); +- mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); ++// pState = pZip->m_pState; ++// pZip->m_pState = NULL; ++// mz_zip_array_clear(pZip, &pState->m_central_dir); ++// mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); ++// mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +-#ifndef MINIZ_NO_STDIO +- if (pState->m_pFile) +- { +- if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) +- { +- if (MZ_FCLOSE(pState->m_pFile) == EOF) +- { +- if (set_last_error) +- mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +- status = MZ_FALSE; +- } +- } ++// #ifndef MINIZ_NO_STDIO ++// if (pState->m_pFile) ++// { ++// if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) ++// { ++// if (MZ_FCLOSE(pState->m_pFile) == EOF) ++// { ++// if (set_last_error) ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); ++// status = MZ_FALSE; ++// } ++// } + +- pState->m_pFile = NULL; +- } +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// pState->m_pFile = NULL; ++// } ++// #endif /* #ifndef MINIZ_NO_STDIO */ + +- if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); +- pState->m_pMem = NULL; +- } ++// if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); ++// pState->m_pMem = NULL; ++// } + +- pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +- pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; +- return status; +-} ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); ++// pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; ++// return status; ++// } + +-mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +-{ +- mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; ++// mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) ++// { ++// mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + +- if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +- { +- if (!pZip->m_pRead) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- } ++// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ++// { ++// if (!pZip->m_pRead) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// } + +- if (pZip->m_file_offset_alignment) +- { +- /* Ensure user specified file offset alignment is a power of 2. */ +- if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- } ++// if (pZip->m_file_offset_alignment) ++// { ++// /* Ensure user specified file offset alignment is a power of 2. */ ++// if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// } + +- if (!pZip->m_pAlloc) +- pZip->m_pAlloc = miniz_def_alloc_func; +- if (!pZip->m_pFree) +- pZip->m_pFree = miniz_def_free_func; +- if (!pZip->m_pRealloc) +- pZip->m_pRealloc = miniz_def_realloc_func; ++// if (!pZip->m_pAlloc) ++// pZip->m_pAlloc = miniz_def_alloc_func; ++// if (!pZip->m_pFree) ++// pZip->m_pFree = miniz_def_free_func; ++// if (!pZip->m_pRealloc) ++// pZip->m_pRealloc = miniz_def_realloc_func; + +- pZip->m_archive_size = existing_size; +- pZip->m_central_directory_file_ofs = 0; +- pZip->m_total_files = 0; ++// pZip->m_archive_size = existing_size; ++// pZip->m_central_directory_file_ofs = 0; ++// pZip->m_total_files = 0; + +- if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +- memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); ++// memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + +- MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); +- MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); +- MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); ++// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); ++// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); ++// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + +- pZip->m_pState->m_zip64 = zip64; +- pZip->m_pState->m_zip64_has_extended_info_fields = zip64; ++// pZip->m_pState->m_zip64 = zip64; ++// pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + +- pZip->m_zip_type = MZ_ZIP_TYPE_USER; +- pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; ++// pZip->m_zip_type = MZ_ZIP_TYPE_USER; ++// pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + +- return MZ_TRUE; +-} ++// return MZ_TRUE; ++// } + +-mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +-{ +- return mz_zip_writer_init_v2(pZip, existing_size, 0); +-} ++// mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) ++// { ++// return mz_zip_writer_init_v2(pZip, existing_size, 0); ++// } + +-mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +-{ +- pZip->m_pWrite = mz_zip_heap_write_func; +- pZip->m_pNeeds_keepalive = NULL; ++// mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) ++// { ++// pZip->m_pWrite = mz_zip_heap_write_func; ++// pZip->m_pNeeds_keepalive = NULL; + +- if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +- pZip->m_pRead = mz_zip_mem_read_func; ++// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ++// pZip->m_pRead = mz_zip_mem_read_func; + +- pZip->m_pIO_opaque = pZip; ++// pZip->m_pIO_opaque = pZip; + +- if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) +- return MZ_FALSE; ++// if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) ++// return MZ_FALSE; + +- pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; ++// pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + +- if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) +- { +- if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) +- { +- mz_zip_writer_end_internal(pZip, MZ_FALSE); +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- } +- pZip->m_pState->m_mem_capacity = initial_allocation_size; +- } ++// if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) ++// { ++// if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) ++// { ++// mz_zip_writer_end_internal(pZip, MZ_FALSE); ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// } ++// pZip->m_pState->m_mem_capacity = initial_allocation_size; ++// } + +- return MZ_TRUE; +-} ++// return MZ_TRUE; ++// } + +-mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +-{ +- return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +-} ++// mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) ++// { ++// return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); ++// } + +-#ifndef MINIZ_NO_STDIO +-static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +-{ +- mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +- mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); ++// #ifndef MINIZ_NO_STDIO ++// static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) ++// { ++// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; ++// mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + +- file_ofs += pZip->m_pState->m_file_archive_start_ofs; ++// file_ofs += pZip->m_pState->m_file_archive_start_ofs; + +- if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) +- { +- mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); +- return 0; +- } ++// if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); ++// return 0; ++// } + +- return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +-} ++// return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); ++// } + +-mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +-{ +- return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +-} ++// mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) ++// { ++// return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); ++// } + +-mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +-{ +- MZ_FILE *pFile; ++// mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) ++// { ++// MZ_FILE *pFile; + +- pZip->m_pWrite = mz_zip_file_write_func; +- pZip->m_pNeeds_keepalive = NULL; ++// pZip->m_pWrite = mz_zip_file_write_func; ++// pZip->m_pNeeds_keepalive = NULL; + +- if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +- pZip->m_pRead = mz_zip_file_read_func; ++// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ++// pZip->m_pRead = mz_zip_file_read_func; + +- pZip->m_pIO_opaque = pZip; ++// pZip->m_pIO_opaque = pZip; ++ ++// if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) ++// return MZ_FALSE; ++ ++// if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) ++// { ++// mz_zip_writer_end(pZip); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); ++// } + +- if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) +- return MZ_FALSE; ++// pZip->m_pState->m_pFile = pFile; ++// pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + +- if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) +- { +- mz_zip_writer_end(pZip); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); +- } ++// if (size_to_reserve_at_beginning) ++// { ++// mz_uint64 cur_ofs = 0; ++// char buf[4096]; + +- pZip->m_pState->m_pFile = pFile; +- pZip->m_zip_type = MZ_ZIP_TYPE_FILE; ++// MZ_CLEAR_OBJ(buf); + +- if (size_to_reserve_at_beginning) +- { +- mz_uint64 cur_ofs = 0; +- char buf[4096]; ++// do ++// { ++// size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) ++// { ++// mz_zip_writer_end(pZip); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// } ++// cur_ofs += n; ++// size_to_reserve_at_beginning -= n; ++// } while (size_to_reserve_at_beginning); ++// } + +- MZ_CLEAR_OBJ(buf); ++// return MZ_TRUE; ++// } + +- do +- { +- size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) +- { +- mz_zip_writer_end(pZip); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- } +- cur_ofs += n; +- size_to_reserve_at_beginning -= n; +- } while (size_to_reserve_at_beginning); +- } ++// mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) ++// { ++// pZip->m_pWrite = mz_zip_file_write_func; ++// pZip->m_pNeeds_keepalive = NULL; + +- return MZ_TRUE; +-} ++// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ++// pZip->m_pRead = mz_zip_file_read_func; + +-mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +-{ +- pZip->m_pWrite = mz_zip_file_write_func; +- pZip->m_pNeeds_keepalive = NULL; ++// pZip->m_pIO_opaque = pZip; + +- if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +- pZip->m_pRead = mz_zip_file_read_func; ++// if (!mz_zip_writer_init_v2(pZip, 0, flags)) ++// return MZ_FALSE; + +- pZip->m_pIO_opaque = pZip; ++// pZip->m_pState->m_pFile = pFile; ++// pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); ++// pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + +- if (!mz_zip_writer_init_v2(pZip, 0, flags)) +- return MZ_FALSE; ++// return MZ_TRUE; ++// } ++// #endif /* #ifndef MINIZ_NO_STDIO */ + +- pZip->m_pState->m_pFile = pFile; +- pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); +- pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; ++// mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) ++// { ++// mz_zip_internal_state *pState; + +- return MZ_TRUE; +-} +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +-mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +-{ +- mz_zip_internal_state *pState; ++// if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) ++// { ++// /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ ++// if (!pZip->m_pState->m_zip64) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// } + +- if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// /* No sense in trying to write to an archive that's already at the support max size */ ++// if (pZip->m_pState->m_zip64) ++// { ++// if (pZip->m_total_files == MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// } ++// else ++// { ++// if (pZip->m_total_files == MZ_UINT16_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + +- if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) +- { +- /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ +- if (!pZip->m_pState->m_zip64) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- } ++// if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); ++// } + +- /* No sense in trying to write to an archive that's already at the support max size */ +- if (pZip->m_pState->m_zip64) +- { +- if (pZip->m_total_files == MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +- } +- else +- { +- if (pZip->m_total_files == MZ_UINT16_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// pState = pZip->m_pState; + +- if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); +- } ++// if (pState->m_pFile) ++// { ++// #ifdef MINIZ_NO_STDIO ++// (void)pFilename; ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// #else ++// if (pZip->m_pIO_opaque != pZip) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- pState = pZip->m_pState; ++// if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) ++// { ++// if (!pFilename) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if (pState->m_pFile) +- { +-#ifdef MINIZ_NO_STDIO +- (void)pFilename; +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +-#else +- if (pZip->m_pIO_opaque != pZip) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ ++// if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) ++// { ++// /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ ++// mz_zip_reader_end_internal(pZip, MZ_FALSE); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); ++// } ++// } + +- if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) +- { +- if (!pFilename) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// pZip->m_pWrite = mz_zip_file_write_func; ++// pZip->m_pNeeds_keepalive = NULL; ++// #endif /* #ifdef MINIZ_NO_STDIO */ ++// } ++// else if (pState->m_pMem) ++// { ++// /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ ++// if (pZip->m_pIO_opaque != pZip) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ +- if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) +- { +- /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ +- mz_zip_reader_end_internal(pZip, MZ_FALSE); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); +- } +- } ++// pState->m_mem_capacity = pState->m_mem_size; ++// pZip->m_pWrite = mz_zip_heap_write_func; ++// pZip->m_pNeeds_keepalive = NULL; ++// } ++// /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ ++// else if (!pZip->m_pWrite) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- pZip->m_pWrite = mz_zip_file_write_func; +- pZip->m_pNeeds_keepalive = NULL; +-#endif /* #ifdef MINIZ_NO_STDIO */ +- } +- else if (pState->m_pMem) +- { +- /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ +- if (pZip->m_pIO_opaque != pZip) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// /* Start writing new files at the archive's current central directory location. */ ++// /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ ++// pZip->m_archive_size = pZip->m_central_directory_file_ofs; ++// pZip->m_central_directory_file_ofs = 0; + +- pState->m_mem_capacity = pState->m_mem_size; +- pZip->m_pWrite = mz_zip_heap_write_func; +- pZip->m_pNeeds_keepalive = NULL; +- } +- /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ +- else if (!pZip->m_pWrite) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ ++// /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ ++// /* TODO: We could easily maintain the sorted central directory offsets. */ ++// mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + +- /* Start writing new files at the archive's current central directory location. */ +- /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ +- pZip->m_archive_size = pZip->m_central_directory_file_ofs; +- pZip->m_central_directory_file_ofs = 0; ++// pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + +- /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ +- /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ +- /* TODO: We could easily maintain the sorted central directory offsets. */ +- mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); ++// return MZ_TRUE; ++// } + +- pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; ++// mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) ++// { ++// return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); ++// } + +- return MZ_TRUE; +-} ++/* TODO: pArchive_name is a terrible name here! */ ++// mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) ++// { ++// return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); ++// } + +-mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +-{ +- return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +-} ++// typedef struct ++// { ++// mz_zip_archive *m_pZip; ++// mz_uint64 m_cur_archive_file_ofs; ++// mz_uint64 m_comp_size; ++// } mz_zip_writer_add_state; + +-/* TODO: pArchive_name is a terrible name here! */ +-mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +-{ +- return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +-} ++// static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) ++// { ++// mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; ++// if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) ++// return MZ_FALSE; + +-typedef struct +-{ +- mz_zip_archive *m_pZip; +- mz_uint64 m_cur_archive_file_ofs; +- mz_uint64 m_comp_size; +-} mz_zip_writer_add_state; ++// pState->m_cur_archive_file_ofs += len; ++// pState->m_comp_size += len; ++// return MZ_TRUE; ++// } + +-static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +-{ +- mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; +- if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) +- return MZ_FALSE; ++// #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) ++// #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) ++// static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) ++// { ++// mz_uint8 *pDst = pBuf; ++// mz_uint32 field_size = 0; + +- pState->m_cur_archive_file_ofs += len; +- pState->m_comp_size += len; +- return MZ_TRUE; +-} ++// MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); ++// MZ_WRITE_LE16(pDst + 2, 0); ++// pDst += sizeof(mz_uint16) * 2; + +-#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +-#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +-static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +-{ +- mz_uint8 *pDst = pBuf; +- mz_uint32 field_size = 0; ++// if (pUncomp_size) ++// { ++// MZ_WRITE_LE64(pDst, *pUncomp_size); ++// pDst += sizeof(mz_uint64); ++// field_size += sizeof(mz_uint64); ++// } + +- MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); +- MZ_WRITE_LE16(pDst + 2, 0); +- pDst += sizeof(mz_uint16) * 2; ++// if (pComp_size) ++// { ++// MZ_WRITE_LE64(pDst, *pComp_size); ++// pDst += sizeof(mz_uint64); ++// field_size += sizeof(mz_uint64); ++// } + +- if (pUncomp_size) +- { +- MZ_WRITE_LE64(pDst, *pUncomp_size); +- pDst += sizeof(mz_uint64); +- field_size += sizeof(mz_uint64); +- } ++// if (pLocal_header_ofs) ++// { ++// MZ_WRITE_LE64(pDst, *pLocal_header_ofs); ++// pDst += sizeof(mz_uint64); ++// field_size += sizeof(mz_uint64); ++// } + +- if (pComp_size) +- { +- MZ_WRITE_LE64(pDst, *pComp_size); +- pDst += sizeof(mz_uint64); +- field_size += sizeof(mz_uint64); +- } ++// MZ_WRITE_LE16(pBuf + 2, field_size); + +- if (pLocal_header_ofs) +- { +- MZ_WRITE_LE64(pDst, *pLocal_header_ofs); +- pDst += sizeof(mz_uint64); +- field_size += sizeof(mz_uint64); +- } ++// return (mz_uint32)(pDst - pBuf); ++// } + +- MZ_WRITE_LE16(pBuf + 2, field_size); ++// static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) ++// { ++// (void)pZip; ++// memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); ++// return MZ_TRUE; ++// } + +- return (mz_uint32)(pDst - pBuf); +-} ++// static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, ++// mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, ++// mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, ++// mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, ++// mz_uint64 local_header_ofs, mz_uint32 ext_attributes) ++// { ++// (void)pZip; ++// memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); ++// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); ++// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); ++// return MZ_TRUE; ++// } + +-static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +-{ +- (void)pZip; +- memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); +- MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); +- MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); +- MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); +- MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); +- MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); +- MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); +- MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); +- MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); +- MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); +- MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); +- MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); +- return MZ_TRUE; +-} ++// static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, ++// const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, ++// mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, ++// mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, ++// mz_uint64 local_header_ofs, mz_uint32 ext_attributes, ++// const char *user_extra_data, mz_uint user_extra_data_len) ++// { ++// mz_zip_internal_state *pState = pZip->m_pState; ++// mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; ++// size_t orig_central_dir_size = pState->m_central_dir.m_size; ++// mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + +-static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, +- mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, +- mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, +- mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, +- mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +-{ +- (void)pZip; +- memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); +- MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); +- MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); +- MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); +- MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); +- MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); +- MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); +- MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); +- return MZ_TRUE; +-} ++// if (!pZip->m_pState->m_zip64) ++// { ++// if (local_header_ofs > 0xFFFFFFFF) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); ++// } + +-static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, +- const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, +- mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, +- mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, +- mz_uint64 local_header_ofs, mz_uint32 ext_attributes, +- const char *user_extra_data, mz_uint user_extra_data_len) +-{ +- mz_zip_internal_state *pState = pZip->m_pState; +- mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; +- size_t orig_central_dir_size = pState->m_central_dir.m_size; +- mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; ++// /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ ++// if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +- if (!pZip->m_pState->m_zip64) +- { +- if (local_header_ofs > 0xFFFFFFFF) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); +- } ++// if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +- /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ +- if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); ++// if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || ++// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || ++// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || ++// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || ++// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || ++// (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) ++// { ++// /* Try to resize the central directory array back into its original state. */ ++// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// } + +- if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// return MZ_TRUE; ++// } + +- if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || +- (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || +- (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || +- (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || +- (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || +- (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) +- { +- /* Try to resize the central directory array back into its original state. */ +- mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- } ++// static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) ++// { ++// /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ ++// if (*pArchive_name == '/') ++// return MZ_FALSE; + +- return MZ_TRUE; +-} ++// while (*pArchive_name) ++// { ++// if ((*pArchive_name == '\\') || (*pArchive_name == ':')) ++// return MZ_FALSE; + +-static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +-{ +- /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ +- if (*pArchive_name == '/') +- return MZ_FALSE; ++// pArchive_name++; ++// } + +- while (*pArchive_name) +- { +- if ((*pArchive_name == '\\') || (*pArchive_name == ':')) +- return MZ_FALSE; ++// return MZ_TRUE; ++// } + +- pArchive_name++; +- } ++// static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) ++// { ++// mz_uint32 n; ++// if (!pZip->m_file_offset_alignment) ++// return 0; ++// n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); ++// return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); ++// } + +- return MZ_TRUE; +-} ++// static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) ++// { ++// char buf[4096]; ++// memset(buf, 0, MZ_MIN(sizeof(buf), n)); ++// while (n) ++// { ++// mz_uint32 s = MZ_MIN(sizeof(buf), n); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +-static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +-{ +- mz_uint32 n; +- if (!pZip->m_file_offset_alignment) +- return 0; +- n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); +- return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +-} ++// cur_file_ofs += s; ++// n -= s; ++// } ++// return MZ_TRUE; ++// } + +-static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +-{ +- char buf[4096]; +- memset(buf, 0, MZ_MIN(sizeof(buf), n)); +- while (n) +- { +- mz_uint32 s = MZ_MIN(sizeof(buf), n); +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, ++// mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) ++// { ++// return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); ++// } + +- cur_file_ofs += s; +- n -= s; +- } +- return MZ_TRUE; +-} ++// mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, ++// mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, ++// const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) ++// { ++// if(!pZip) ++// { ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// } + +-mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +- mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +-{ +- return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +-} ++// mz_uint16 method = 0, dos_time = 0, dos_date = 0; ++// mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; ++// mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; ++// size_t archive_name_size; ++// mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; ++// tdefl_compressor *pComp = NULL; ++// mz_bool store_data_uncompressed; ++// mz_zip_internal_state *pState; ++// mz_uint8 *pExtra_data = NULL; ++// mz_uint32 extra_size = 0; ++// mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; ++// mz_uint16 bit_flags = 0; + +-mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, +- mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, +- const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +-{ +- if(!pZip) +- { +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- } ++// if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) ++// bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + +- mz_uint16 method = 0, dos_time = 0, dos_date = 0; +- mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; +- mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; +- size_t archive_name_size; +- mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; +- tdefl_compressor *pComp = NULL; +- mz_bool store_data_uncompressed; +- mz_zip_internal_state *pState; +- mz_uint8 *pExtra_data = NULL; +- mz_uint32 extra_size = 0; +- mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; +- mz_uint16 bit_flags = 0; ++// if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) ++// bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + +- if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) +- bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; ++// if ((int)level_and_flags < 0) ++// level_and_flags = MZ_DEFAULT_LEVEL; ++// level = level_and_flags & 0xF; ++// store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + +- if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) +- bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; ++// if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if ((int)level_and_flags < 0) +- level_and_flags = MZ_DEFAULT_LEVEL; +- level = level_and_flags & 0xF; +- store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); ++// pState = pZip->m_pState; + +- if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if (pState->m_zip64) ++// { ++// if (pZip->m_total_files == MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// } ++// else ++// { ++// if (pZip->m_total_files == MZ_UINT16_MAX) ++// { ++// pState->m_zip64 = MZ_TRUE; ++// /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ ++// } ++// if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) ++// { ++// pState->m_zip64 = MZ_TRUE; ++// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ ++// } ++// } + +- pState = pZip->m_pState; ++// if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if (pState->m_zip64) +- { +- if (pZip->m_total_files == MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +- } +- else +- { +- if (pZip->m_total_files == MZ_UINT16_MAX) +- { +- pState->m_zip64 = MZ_TRUE; +- /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ +- } +- if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) +- { +- pState->m_zip64 = MZ_TRUE; +- /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +- } +- } ++// if (!mz_zip_writer_validate_archive_name(pArchive_name)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +- if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// #ifndef MINIZ_NO_TIME ++// if (last_modified != NULL) ++// { ++// mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); ++// } ++// else ++// { ++// MZ_TIME_T cur_time; ++// time(&cur_time); ++// mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); ++// } ++// #endif /* #ifndef MINIZ_NO_TIME */ + +- if (!mz_zip_writer_validate_archive_name(pArchive_name)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); ++// archive_name_size = strlen(pArchive_name); ++// if (archive_name_size > MZ_UINT16_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +-#ifndef MINIZ_NO_TIME +- if (last_modified != NULL) +- { +- mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); +- } +- else +- { +- MZ_TIME_T cur_time; +- time(&cur_time); +- mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); +- } +-#endif /* #ifndef MINIZ_NO_TIME */ ++// num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + +- archive_name_size = strlen(pArchive_name); +- if (archive_name_size > MZ_UINT16_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); ++// /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ ++// if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +- num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); ++// if (!pState->m_zip64) ++// { ++// /* Bail early if the archive would obviously become too large */ ++// if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size ++// + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + ++// pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len ++// + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) ++// { ++// pState->m_zip64 = MZ_TRUE; ++// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ ++// } ++// } + +- /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ +- if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); ++// if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) ++// { ++// /* Set DOS Subdirectory attribute bit. */ ++// ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + +- if (!pState->m_zip64) +- { +- /* Bail early if the archive would obviously become too large */ +- if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size +- + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + +- pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len +- + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) +- { +- pState->m_zip64 = MZ_TRUE; +- /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +- } +- } ++// /* Subdirectories cannot contain data. */ ++// if ((buf_size) || (uncomp_size)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// } + +- if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) +- { +- /* Set DOS Subdirectory attribute bit. */ +- ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; ++// /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ ++// if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +- /* Subdirectories cannot contain data. */ +- if ((buf_size) || (uncomp_size)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- } ++// if ((!store_data_uncompressed) && (buf_size)) ++// { ++// if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// } + +- /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ +- if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// return MZ_FALSE; ++// } + +- if ((!store_data_uncompressed) && (buf_size)) +- { +- if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- } ++// local_dir_header_ofs += num_alignment_padding_bytes; ++// if (pZip->m_file_offset_alignment) ++// { ++// MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); ++// } ++// cur_archive_file_ofs += num_alignment_padding_bytes; + +- if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +- return MZ_FALSE; +- } ++// MZ_CLEAR_OBJ(local_dir_header); + +- local_dir_header_ofs += num_alignment_padding_bytes; +- if (pZip->m_file_offset_alignment) +- { +- MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); +- } +- cur_archive_file_ofs += num_alignment_padding_bytes; ++// if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) ++// { ++// method = MZ_DEFLATED; ++// } + +- MZ_CLEAR_OBJ(local_dir_header); ++// if (pState->m_zip64) ++// { ++// if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) ++// { ++// pExtra_data = extra_data; ++// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, ++// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); ++// } + +- if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +- { +- method = MZ_DEFLATED; +- } ++// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +- if (pState->m_zip64) +- { +- if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) +- { +- pExtra_data = extra_data; +- extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +- (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +- } ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// cur_archive_file_ofs += sizeof(local_dir_header); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// } ++// cur_archive_file_ofs += archive_name_size; + +- cur_archive_file_ofs += sizeof(local_dir_header); ++// if (pExtra_data != NULL) ++// { ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- } +- cur_archive_file_ofs += archive_name_size; ++// cur_archive_file_ofs += extra_size; ++// } ++// } ++// else ++// { ++// if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) ++// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); ++// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +- if (pExtra_data != NULL) +- { +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- cur_archive_file_ofs += extra_size; +- } +- } +- else +- { +- if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) +- return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); +- if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// cur_archive_file_ofs += sizeof(local_dir_header); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// } ++// cur_archive_file_ofs += archive_name_size; ++// } + +- cur_archive_file_ofs += sizeof(local_dir_header); ++// if (user_extra_data_len > 0) ++// { ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- } +- cur_archive_file_ofs += archive_name_size; +- } ++// cur_archive_file_ofs += user_extra_data_len; ++// } + +- if (user_extra_data_len > 0) +- { +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) ++// { ++// uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); ++// uncomp_size = buf_size; ++// if (uncomp_size <= 3) ++// { ++// level = 0; ++// store_data_uncompressed = MZ_TRUE; ++// } ++// } + +- cur_archive_file_ofs += user_extra_data_len; +- } ++// if (store_data_uncompressed) ++// { ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// } + +- if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +- { +- uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); +- uncomp_size = buf_size; +- if (uncomp_size <= 3) +- { +- level = 0; +- store_data_uncompressed = MZ_TRUE; +- } +- } ++// cur_archive_file_ofs += buf_size; ++// comp_size = buf_size; ++// } ++// else if (buf_size) ++// { ++// mz_zip_writer_add_state state; + +- if (store_data_uncompressed) +- { +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- } ++// state.m_pZip = pZip; ++// state.m_cur_archive_file_ofs = cur_archive_file_ofs; ++// state.m_comp_size = 0; + +- cur_archive_file_ofs += buf_size; +- comp_size = buf_size; +- } +- else if (buf_size) +- { +- mz_zip_writer_add_state state; ++// if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || ++// (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); ++// } + +- state.m_pZip = pZip; +- state.m_cur_archive_file_ofs = cur_archive_file_ofs; +- state.m_comp_size = 0; ++// comp_size = state.m_comp_size; ++// cur_archive_file_ofs = state.m_cur_archive_file_ofs; ++// } + +- if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || +- (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +- return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); +- } ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// pComp = NULL; + +- comp_size = state.m_comp_size; +- cur_archive_file_ofs = state.m_cur_archive_file_ofs; +- } ++// if (uncomp_size) ++// { ++// mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; ++// mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +- pComp = NULL; ++// MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + +- if (uncomp_size) +- { +- mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; +- mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; ++// MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); ++// MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); ++// if (pExtra_data == NULL) ++// { ++// if (comp_size > MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +- MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); ++// MZ_WRITE_LE32(local_dir_footer + 8, comp_size); ++// MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); ++// } ++// else ++// { ++// MZ_WRITE_LE64(local_dir_footer + 8, comp_size); ++// MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); ++// local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; ++// } + +- MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); +- MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); +- if (pExtra_data == NULL) +- { +- if (comp_size > MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) ++// return MZ_FALSE; + +- MZ_WRITE_LE32(local_dir_footer + 8, comp_size); +- MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); +- } +- else +- { +- MZ_WRITE_LE64(local_dir_footer + 8, comp_size); +- MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); +- local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; +- } ++// cur_archive_file_ofs += local_dir_footer_size; ++// } + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) +- return MZ_FALSE; ++// if (pExtra_data != NULL) ++// { ++// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, ++// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); ++// } + +- cur_archive_file_ofs += local_dir_footer_size; +- } ++// if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, ++// comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, ++// user_extra_data_central, user_extra_data_central_len)) ++// return MZ_FALSE; + +- if (pExtra_data != NULL) +- { +- extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +- (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +- } ++// pZip->m_total_files++; ++// pZip->m_archive_size = cur_archive_file_ofs; + +- if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, +- comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, +- user_extra_data_central, user_extra_data_central_len)) +- return MZ_FALSE; ++// return MZ_TRUE; ++// } + +- pZip->m_total_files++; +- pZip->m_archive_size = cur_archive_file_ofs; ++// #ifndef MINIZ_NO_STDIO ++// mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, ++// const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) ++// { ++// if(!pZip) ++// { ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// } + +- return MZ_TRUE; +-} ++// mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; ++// mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; ++// mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; ++// mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; ++// size_t archive_name_size; ++// mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; ++// mz_uint8 *pExtra_data = NULL; ++// mz_uint32 extra_size = 0; ++// mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; ++// mz_zip_internal_state *pState; + +-#ifndef MINIZ_NO_STDIO +-mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +- const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +-{ +- if(!pZip) +- { +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- } ++// if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) ++// gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + +- mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; +- mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; +- mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; +- mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; +- size_t archive_name_size; +- mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; +- mz_uint8 *pExtra_data = NULL; +- mz_uint32 extra_size = 0; +- mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; +- mz_zip_internal_state *pState; ++// if ((int)level_and_flags < 0) ++// level_and_flags = MZ_DEFAULT_LEVEL; ++// level = level_and_flags & 0xF; + +- if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) +- gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; ++// /* Sanity checks */ ++// if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if ((int)level_and_flags < 0) +- level_and_flags = MZ_DEFAULT_LEVEL; +- level = level_and_flags & 0xF; ++// pState = pZip->m_pState; + +- /* Sanity checks */ +- if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) ++// { ++// /* Source file is too large for non-zip64 */ ++// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ ++// pState->m_zip64 = MZ_TRUE; ++// } + +- pState = pZip->m_pState; ++// /* We could support this, but why? */ ++// if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) +- { +- /* Source file is too large for non-zip64 */ +- /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +- pState->m_zip64 = MZ_TRUE; +- } ++// if (!mz_zip_writer_validate_archive_name(pArchive_name)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +- /* We could support this, but why? */ +- if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if (pState->m_zip64) ++// { ++// if (pZip->m_total_files == MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// } ++// else ++// { ++// if (pZip->m_total_files == MZ_UINT16_MAX) ++// { ++// pState->m_zip64 = MZ_TRUE; ++// /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ ++// } ++// } + +- if (!mz_zip_writer_validate_archive_name(pArchive_name)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); ++// archive_name_size = strlen(pArchive_name); ++// if (archive_name_size > MZ_UINT16_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +- if (pState->m_zip64) +- { +- if (pZip->m_total_files == MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +- } +- else +- { +- if (pZip->m_total_files == MZ_UINT16_MAX) +- { +- pState->m_zip64 = MZ_TRUE; +- /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ +- } +- } ++// num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + +- archive_name_size = strlen(pArchive_name); +- if (archive_name_size > MZ_UINT16_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); ++// /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ ++// if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +- num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); ++// if (!pState->m_zip64) ++// { ++// /* Bail early if the archive would obviously become too large */ ++// if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE ++// + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 ++// + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) ++// { ++// pState->m_zip64 = MZ_TRUE; ++// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ ++// } ++// } + +- /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ +- if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); ++// #ifndef MINIZ_NO_TIME ++// if (pFile_time) ++// { ++// mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); ++// } ++// #endif + +- if (!pState->m_zip64) +- { +- /* Bail early if the archive would obviously become too large */ +- if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +- + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 +- + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) +- { +- pState->m_zip64 = MZ_TRUE; +- /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +- } +- } ++// if (uncomp_size <= 3) ++// level = 0; + +-#ifndef MINIZ_NO_TIME +- if (pFile_time) +- { +- mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); +- } +-#endif ++// if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) ++// { ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// } + +- if (uncomp_size <= 3) +- level = 0; ++// cur_archive_file_ofs += num_alignment_padding_bytes; ++// local_dir_header_ofs = cur_archive_file_ofs; + +- if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) +- { +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- } ++// if (pZip->m_file_offset_alignment) ++// { ++// MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); ++// } + +- cur_archive_file_ofs += num_alignment_padding_bytes; +- local_dir_header_ofs = cur_archive_file_ofs; ++// if (uncomp_size && level) ++// { ++// method = MZ_DEFLATED; ++// } + +- if (pZip->m_file_offset_alignment) +- { +- MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); +- } ++// MZ_CLEAR_OBJ(local_dir_header); ++// if (pState->m_zip64) ++// { ++// if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) ++// { ++// pExtra_data = extra_data; ++// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, ++// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); ++// } + +- if (uncomp_size && level) +- { +- method = MZ_DEFLATED; +- } ++// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +- MZ_CLEAR_OBJ(local_dir_header); +- if (pState->m_zip64) +- { +- if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) +- { +- pExtra_data = extra_data; +- extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +- (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +- } ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// cur_archive_file_ofs += sizeof(local_dir_header); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) ++// { ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// } + +- cur_archive_file_ofs += sizeof(local_dir_header); ++// cur_archive_file_ofs += archive_name_size; + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +- { +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- } ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- cur_archive_file_ofs += archive_name_size; ++// cur_archive_file_ofs += extra_size; ++// } ++// else ++// { ++// if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) ++// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); ++// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- cur_archive_file_ofs += extra_size; +- } +- else +- { +- if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) +- return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); +- if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// cur_archive_file_ofs += sizeof(local_dir_header); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) ++// { ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// } + +- cur_archive_file_ofs += sizeof(local_dir_header); ++// cur_archive_file_ofs += archive_name_size; ++// } + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +- { +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- } ++// if (user_extra_data_len > 0) ++// { ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- cur_archive_file_ofs += archive_name_size; +- } ++// cur_archive_file_ofs += user_extra_data_len; ++// } + +- if (user_extra_data_len > 0) +- { +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// if (uncomp_size) ++// { ++// mz_uint64 uncomp_remaining = uncomp_size; ++// void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); ++// if (!pRead_buf) ++// { ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// } + +- cur_archive_file_ofs += user_extra_data_len; +- } ++// if (!level) ++// { ++// while (uncomp_remaining) ++// { ++// mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); ++// if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// } ++// uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); ++// uncomp_remaining -= n; ++// cur_archive_file_ofs += n; ++// } ++// comp_size = uncomp_size; ++// } ++// else ++// { ++// mz_bool result = MZ_FALSE; ++// mz_zip_writer_add_state state; ++// tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); ++// if (!pComp) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); ++// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); ++// } + +- if (uncomp_size) +- { +- mz_uint64 uncomp_remaining = uncomp_size; +- void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); +- if (!pRead_buf) +- { +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- } ++// state.m_pZip = pZip; ++// state.m_cur_archive_file_ofs = cur_archive_file_ofs; ++// state.m_comp_size = 0; + +- if (!level) +- { +- while (uncomp_remaining) +- { +- mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); +- if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +- } +- uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); +- uncomp_remaining -= n; +- cur_archive_file_ofs += n; +- } +- comp_size = uncomp_size; +- } +- else +- { +- mz_bool result = MZ_FALSE; +- mz_zip_writer_add_state state; +- tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); +- if (!pComp) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +- return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +- } ++// if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); ++// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); ++// } + +- state.m_pZip = pZip; +- state.m_cur_archive_file_ofs = cur_archive_file_ofs; +- state.m_comp_size = 0; ++// for (;;) ++// { ++// size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); ++// tdefl_status status; ++// tdefl_flush flush = TDEFL_NO_FLUSH; + +- if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +- return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); +- } ++// if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); ++// break; ++// } + +- for (;;) +- { +- size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); +- tdefl_status status; +- tdefl_flush flush = TDEFL_NO_FLUSH; ++// uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); ++// uncomp_remaining -= in_buf_size; + +- if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) +- { +- mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +- break; +- } ++// if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) ++// flush = TDEFL_FULL_FLUSH; + +- uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); +- uncomp_remaining -= in_buf_size; ++// status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); ++// if (status == TDEFL_STATUS_DONE) ++// { ++// result = MZ_TRUE; ++// break; ++// } ++// else if (status != TDEFL_STATUS_OKAY) ++// { ++// mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); ++// break; ++// } ++// } + +- if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) +- flush = TDEFL_FULL_FLUSH; ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + +- status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); +- if (status == TDEFL_STATUS_DONE) +- { +- result = MZ_TRUE; +- break; +- } +- else if (status != TDEFL_STATUS_OKAY) +- { +- mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); +- break; +- } +- } ++// if (!result) ++// { ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); ++// return MZ_FALSE; ++// } + +- pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); ++// comp_size = state.m_comp_size; ++// cur_archive_file_ofs = state.m_cur_archive_file_ofs; ++// } + +- if (!result) +- { +- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +- return MZ_FALSE; +- } ++// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); ++// } + +- comp_size = state.m_comp_size; +- cur_archive_file_ofs = state.m_cur_archive_file_ofs; +- } ++// { ++// mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; ++// mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + +- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +- } ++// MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); ++// MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); ++// if (pExtra_data == NULL) ++// { ++// if (comp_size > MZ_UINT32_MAX) ++// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +- { +- mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; +- mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; ++// MZ_WRITE_LE32(local_dir_footer + 8, comp_size); ++// MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); ++// } ++// else ++// { ++// MZ_WRITE_LE64(local_dir_footer + 8, comp_size); ++// MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); ++// local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; ++// } + +- MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); +- MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); +- if (pExtra_data == NULL) +- { +- if (comp_size > MZ_UINT32_MAX) +- return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) ++// return MZ_FALSE; + +- MZ_WRITE_LE32(local_dir_footer + 8, comp_size); +- MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); +- } +- else +- { +- MZ_WRITE_LE64(local_dir_footer + 8, comp_size); +- MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); +- local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; +- } ++// cur_archive_file_ofs += local_dir_footer_size; ++// } + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) +- return MZ_FALSE; ++// if (pExtra_data != NULL) ++// { ++// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, ++// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); ++// } + +- cur_archive_file_ofs += local_dir_footer_size; +- } ++// if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, ++// uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, ++// user_extra_data_central, user_extra_data_central_len)) ++// return MZ_FALSE; + +- if (pExtra_data != NULL) +- { +- extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +- (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +- } ++// pZip->m_total_files++; ++// pZip->m_archive_size = cur_archive_file_ofs; + +- if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, +- uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, +- user_extra_data_central, user_extra_data_central_len)) +- return MZ_FALSE; ++// return MZ_TRUE; ++// } + +- pZip->m_total_files++; +- pZip->m_archive_size = cur_archive_file_ofs; ++// mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) ++// { ++// MZ_FILE *pSrc_file = NULL; ++// mz_uint64 uncomp_size = 0; ++// MZ_TIME_T file_modified_time; ++// MZ_TIME_T *pFile_time = NULL; ++// mz_bool status; + +- return MZ_TRUE; +-} ++// memset(&file_modified_time, 0, sizeof(file_modified_time)); + +-mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +-{ +- MZ_FILE *pSrc_file = NULL; +- mz_uint64 uncomp_size = 0; +- MZ_TIME_T file_modified_time; +- MZ_TIME_T *pFile_time = NULL; +- mz_bool status; +- +- memset(&file_modified_time, 0, sizeof(file_modified_time)); +- +-#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) +- pFile_time = &file_modified_time; +- if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +-#endif ++// #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) ++// pFile_time = &file_modified_time; ++// if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); ++// #endif + +- pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); +- if (!pSrc_file) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); ++// pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); ++// if (!pSrc_file) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +- MZ_FSEEK64(pSrc_file, 0, SEEK_END); +- uncomp_size = MZ_FTELL64(pSrc_file); +- MZ_FSEEK64(pSrc_file, 0, SEEK_SET); ++// MZ_FSEEK64(pSrc_file, 0, SEEK_END); ++// uncomp_size = MZ_FTELL64(pSrc_file); ++// MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + +- status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); ++// status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + +- MZ_FCLOSE(pSrc_file); ++// MZ_FCLOSE(pSrc_file); + +- return status; +-} +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// return status; ++// } ++// #endif /* #ifndef MINIZ_NO_STDIO */ + + // static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) + // { +@@ -7083,491 +7082,491 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, + // return MZ_TRUE; + // } + +-mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +-{ +- mz_zip_internal_state *pState; +- mz_uint64 central_dir_ofs, central_dir_size; +- mz_uint8 hdr[256]; ++// mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) ++// { ++// mz_zip_internal_state *pState; ++// mz_uint64 central_dir_ofs, central_dir_size; ++// mz_uint8 hdr[256]; + +- if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- pState = pZip->m_pState; ++// pState = pZip->m_pState; + +- if (pState->m_zip64) +- { +- if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +- } +- else +- { +- if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) +- return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +- } ++// if (pState->m_zip64) ++// { ++// if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// } ++// else ++// { ++// if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) ++// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); ++// } + +- central_dir_ofs = 0; +- central_dir_size = 0; +- if (pZip->m_total_files) +- { +- /* Write central directory */ +- central_dir_ofs = pZip->m_archive_size; +- central_dir_size = pState->m_central_dir.m_size; +- pZip->m_central_directory_file_ofs = central_dir_ofs; +- if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- +- pZip->m_archive_size += central_dir_size; +- } ++// central_dir_ofs = 0; ++// central_dir_size = 0; ++// if (pZip->m_total_files) ++// { ++// /* Write central directory */ ++// central_dir_ofs = pZip->m_archive_size; ++// central_dir_size = pState->m_central_dir.m_size; ++// pZip->m_central_directory_file_ofs = central_dir_ofs; ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- if (pState->m_zip64) +- { +- /* Write zip64 end of central directory header */ +- mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; +- +- MZ_CLEAR_OBJ(hdr); +- MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); +- MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); +- MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ +- MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); +- MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); +- MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); +- MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); +- MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); +- if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- +- pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; +- +- /* Write zip64 end of central directory locator */ +- MZ_CLEAR_OBJ(hdr); +- MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); +- MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); +- MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); +- if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +- +- pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; +- } ++// pZip->m_archive_size += central_dir_size; ++// } + +- /* Write end of central directory record */ +- MZ_CLEAR_OBJ(hdr); +- MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); +- MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); +- MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); +- MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); +- MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); ++// if (pState->m_zip64) ++// { ++// /* Write zip64 end of central directory header */ ++// mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; ++ ++// MZ_CLEAR_OBJ(hdr); ++// MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); ++// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); ++// MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ ++// MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); ++// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); ++// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); ++// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); ++// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); ++// pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + +-#ifndef MINIZ_NO_STDIO +- if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) +- return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// /* Write zip64 end of central directory locator */ ++// MZ_CLEAR_OBJ(hdr); ++// MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); ++// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); ++// MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; ++// pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; ++// } + +- pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; +- return MZ_TRUE; +-} ++// /* Write end of central directory record */ ++// MZ_CLEAR_OBJ(hdr); ++// MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); ++// MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); ++// MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); ++// MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); ++// MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + +-mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +-{ +- if ((!ppBuf) || (!pSize)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +- *ppBuf = NULL; +- *pSize = 0; ++// #ifndef MINIZ_NO_STDIO ++// if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) ++// return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); ++// #endif /* #ifndef MINIZ_NO_STDIO */ + +- if ((!pZip) || (!pZip->m_pState)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + +- if (pZip->m_pWrite != mz_zip_heap_write_func) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; ++// return MZ_TRUE; ++// } + +- if (!mz_zip_writer_finalize_archive(pZip)) +- return MZ_FALSE; ++// mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) ++// { ++// if ((!ppBuf) || (!pSize)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- *ppBuf = pZip->m_pState->m_pMem; +- *pSize = pZip->m_pState->m_mem_size; +- pZip->m_pState->m_pMem = NULL; +- pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; ++// *ppBuf = NULL; ++// *pSize = 0; + +- return MZ_TRUE; +-} ++// if ((!pZip) || (!pZip->m_pState)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +-mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +-{ +- return mz_zip_writer_end_internal(pZip, MZ_TRUE); +-} ++// if (pZip->m_pWrite != mz_zip_heap_write_func) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +-#ifndef MINIZ_NO_STDIO +-mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +-{ +- return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +-} ++// if (!mz_zip_writer_finalize_archive(pZip)) ++// return MZ_FALSE; + +-mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +-{ +- mz_bool status, created_new_archive = MZ_FALSE; +- mz_zip_archive zip_archive; +- struct MZ_FILE_STAT_STRUCT file_stat; +- mz_zip_error actual_err = MZ_ZIP_NO_ERROR; ++// *ppBuf = pZip->m_pState->m_pMem; ++// *pSize = pZip->m_pState->m_mem_size; ++// pZip->m_pState->m_pMem = NULL; ++// pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + +- mz_zip_zero_struct(&zip_archive); +- if ((int)level_and_flags < 0) +- level_and_flags = MZ_DEFAULT_LEVEL; ++// return MZ_TRUE; ++// } + +- if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) +- { +- if (pErr) +- *pErr = MZ_ZIP_INVALID_PARAMETER; +- return MZ_FALSE; +- } ++// mz_bool mz_zip_writer_end(mz_zip_archive *pZip) ++// { ++// return mz_zip_writer_end_internal(pZip, MZ_TRUE); ++// } + +- if (!mz_zip_writer_validate_archive_name(pArchive_name)) +- { +- if (pErr) +- *pErr = MZ_ZIP_INVALID_FILENAME; +- return MZ_FALSE; +- } ++// #ifndef MINIZ_NO_STDIO ++// mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) ++// { ++// return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); ++// } + +- /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ +- /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ +- if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) +- { +- /* Create a new archive. */ +- if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) +- { +- if (pErr) +- *pErr = zip_archive.m_last_error; +- return MZ_FALSE; +- } ++// mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) ++// { ++// mz_bool status, created_new_archive = MZ_FALSE; ++// mz_zip_archive zip_archive; ++// struct MZ_FILE_STAT_STRUCT file_stat; ++// mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + +- created_new_archive = MZ_TRUE; +- } +- else +- { +- /* Append to an existing archive. */ +- if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) +- { +- if (pErr) +- *pErr = zip_archive.m_last_error; +- return MZ_FALSE; +- } ++// mz_zip_zero_struct(&zip_archive); ++// if ((int)level_and_flags < 0) ++// level_and_flags = MZ_DEFAULT_LEVEL; + +- if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) +- { +- if (pErr) +- *pErr = zip_archive.m_last_error; ++// if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) ++// { ++// if (pErr) ++// *pErr = MZ_ZIP_INVALID_PARAMETER; ++// return MZ_FALSE; ++// } + +- mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); ++// if (!mz_zip_writer_validate_archive_name(pArchive_name)) ++// { ++// if (pErr) ++// *pErr = MZ_ZIP_INVALID_FILENAME; ++// return MZ_FALSE; ++// } + +- return MZ_FALSE; +- } +- } ++// /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ ++// /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ ++// if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) ++// { ++// /* Create a new archive. */ ++// if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) ++// { ++// if (pErr) ++// *pErr = zip_archive.m_last_error; ++// return MZ_FALSE; ++// } + +- status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); +- actual_err = zip_archive.m_last_error; ++// created_new_archive = MZ_TRUE; ++// } ++// else ++// { ++// /* Append to an existing archive. */ ++// if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) ++// { ++// if (pErr) ++// *pErr = zip_archive.m_last_error; ++// return MZ_FALSE; ++// } + +- /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ +- if (!mz_zip_writer_finalize_archive(&zip_archive)) +- { +- if (!actual_err) +- actual_err = zip_archive.m_last_error; ++// if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) ++// { ++// if (pErr) ++// *pErr = zip_archive.m_last_error; + +- status = MZ_FALSE; +- } ++// mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + +- if (!mz_zip_writer_end_internal(&zip_archive, status)) +- { +- if (!actual_err) +- actual_err = zip_archive.m_last_error; ++// return MZ_FALSE; ++// } ++// } + +- status = MZ_FALSE; +- } ++// status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); ++// actual_err = zip_archive.m_last_error; + +- if ((!status) && (created_new_archive)) +- { +- /* It's a new archive and something went wrong, so just delete it. */ +- int ignoredStatus = MZ_DELETE_FILE(pZip_filename); +- (void)ignoredStatus; +- } ++// /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ ++// if (!mz_zip_writer_finalize_archive(&zip_archive)) ++// { ++// if (!actual_err) ++// actual_err = zip_archive.m_last_error; + +- if (pErr) +- *pErr = actual_err; ++// status = MZ_FALSE; ++// } + +- return status; +-} ++// if (!mz_zip_writer_end_internal(&zip_archive, status)) ++// { ++// if (!actual_err) ++// actual_err = zip_archive.m_last_error; + +-void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +-{ +- mz_uint32 file_index; +- mz_zip_archive zip_archive; +- void *p = NULL; ++// status = MZ_FALSE; ++// } + +- if (pSize) +- *pSize = 0; ++// if ((!status) && (created_new_archive)) ++// { ++// /* It's a new archive and something went wrong, so just delete it. */ ++// int ignoredStatus = MZ_DELETE_FILE(pZip_filename); ++// (void)ignoredStatus; ++// } + +- if ((!pZip_filename) || (!pArchive_name)) +- { +- if (pErr) +- *pErr = MZ_ZIP_INVALID_PARAMETER; ++// if (pErr) ++// *pErr = actual_err; + +- return NULL; +- } ++// return status; ++// } + +- mz_zip_zero_struct(&zip_archive); +- if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) +- { +- if (pErr) +- *pErr = zip_archive.m_last_error; ++// void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) ++// { ++// mz_uint32 file_index; ++// mz_zip_archive zip_archive; ++// void *p = NULL; + +- return NULL; +- } ++// if (pSize) ++// *pSize = 0; + +- if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) +- { +- p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); +- } ++// if ((!pZip_filename) || (!pArchive_name)) ++// { ++// if (pErr) ++// *pErr = MZ_ZIP_INVALID_PARAMETER; + +- mz_zip_reader_end_internal(&zip_archive, p != NULL); ++// return NULL; ++// } + +- if (pErr) +- *pErr = zip_archive.m_last_error; ++// mz_zip_zero_struct(&zip_archive); ++// if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) ++// { ++// if (pErr) ++// *pErr = zip_archive.m_last_error; + +- return p; +-} ++// return NULL; ++// } + +-void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +-{ +- return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +-} ++// if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) ++// { ++// p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); ++// } ++ ++// mz_zip_reader_end_internal(&zip_archive, p != NULL); ++ ++// if (pErr) ++// *pErr = zip_archive.m_last_error; ++ ++// return p; ++// } ++ ++// void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) ++// { ++// return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); ++// } + +-#endif /* #ifndef MINIZ_NO_STDIO */ ++// #endif /* #ifndef MINIZ_NO_STDIO */ + +-#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ ++// #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + + /* ------------------- Misc utils */ + +-mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +-{ +- return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +-} ++// mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) ++// { ++// return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; ++// } + +-mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +-{ +- return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +-} ++// mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) ++// { ++// return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; ++// } + +-mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +-{ +- mz_zip_error prev_err; ++// mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) ++// { ++// mz_zip_error prev_err; + +- if (!pZip) +- return MZ_ZIP_INVALID_PARAMETER; ++// if (!pZip) ++// return MZ_ZIP_INVALID_PARAMETER; + +- prev_err = pZip->m_last_error; ++// prev_err = pZip->m_last_error; + +- pZip->m_last_error = err_num; +- return prev_err; +-} ++// pZip->m_last_error = err_num; ++// return prev_err; ++// } + +-mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +-{ +- if (!pZip) +- return MZ_ZIP_INVALID_PARAMETER; ++// mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) ++// { ++// if (!pZip) ++// return MZ_ZIP_INVALID_PARAMETER; + +- return pZip->m_last_error; +-} ++// return pZip->m_last_error; ++// } + +-mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +-{ +- return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +-} ++// mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) ++// { ++// return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); ++// } + +-mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +-{ +- mz_zip_error prev_err; ++// mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) ++// { ++// mz_zip_error prev_err; + +- if (!pZip) +- return MZ_ZIP_INVALID_PARAMETER; ++// if (!pZip) ++// return MZ_ZIP_INVALID_PARAMETER; + +- prev_err = pZip->m_last_error; ++// prev_err = pZip->m_last_error; + +- pZip->m_last_error = MZ_ZIP_NO_ERROR; +- return prev_err; +-} ++// pZip->m_last_error = MZ_ZIP_NO_ERROR; ++// return prev_err; ++// } + +-const char *mz_zip_get_error_string(mz_zip_error mz_err) +-{ +- switch (mz_err) +- { +- case MZ_ZIP_NO_ERROR: +- return "no error"; +- case MZ_ZIP_UNDEFINED_ERROR: +- return "undefined error"; +- case MZ_ZIP_TOO_MANY_FILES: +- return "too many files"; +- case MZ_ZIP_FILE_TOO_LARGE: +- return "file too large"; +- case MZ_ZIP_UNSUPPORTED_METHOD: +- return "unsupported method"; +- case MZ_ZIP_UNSUPPORTED_ENCRYPTION: +- return "unsupported encryption"; +- case MZ_ZIP_UNSUPPORTED_FEATURE: +- return "unsupported feature"; +- case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: +- return "failed finding central directory"; +- case MZ_ZIP_NOT_AN_ARCHIVE: +- return "not a ZIP archive"; +- case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: +- return "invalid header or archive is corrupted"; +- case MZ_ZIP_UNSUPPORTED_MULTIDISK: +- return "unsupported multidisk archive"; +- case MZ_ZIP_DECOMPRESSION_FAILED: +- return "decompression failed or archive is corrupted"; +- case MZ_ZIP_COMPRESSION_FAILED: +- return "compression failed"; +- case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: +- return "unexpected decompressed size"; +- case MZ_ZIP_CRC_CHECK_FAILED: +- return "CRC-32 check failed"; +- case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: +- return "unsupported central directory size"; +- case MZ_ZIP_ALLOC_FAILED: +- return "allocation failed"; +- case MZ_ZIP_FILE_OPEN_FAILED: +- return "file open failed"; +- case MZ_ZIP_FILE_CREATE_FAILED: +- return "file create failed"; +- case MZ_ZIP_FILE_WRITE_FAILED: +- return "file write failed"; +- case MZ_ZIP_FILE_READ_FAILED: +- return "file read failed"; +- case MZ_ZIP_FILE_CLOSE_FAILED: +- return "file close failed"; +- case MZ_ZIP_FILE_SEEK_FAILED: +- return "file seek failed"; +- case MZ_ZIP_FILE_STAT_FAILED: +- return "file stat failed"; +- case MZ_ZIP_INVALID_PARAMETER: +- return "invalid parameter"; +- case MZ_ZIP_INVALID_FILENAME: +- return "invalid filename"; +- case MZ_ZIP_BUF_TOO_SMALL: +- return "buffer too small"; +- case MZ_ZIP_INTERNAL_ERROR: +- return "internal error"; +- case MZ_ZIP_FILE_NOT_FOUND: +- return "file not found"; +- case MZ_ZIP_ARCHIVE_TOO_LARGE: +- return "archive is too large"; +- case MZ_ZIP_VALIDATION_FAILED: +- return "validation failed"; +- case MZ_ZIP_WRITE_CALLBACK_FAILED: +- return "write calledback failed"; +- default: +- break; +- } ++// const char *mz_zip_get_error_string(mz_zip_error mz_err) ++// { ++// switch (mz_err) ++// { ++// case MZ_ZIP_NO_ERROR: ++// return "no error"; ++// case MZ_ZIP_UNDEFINED_ERROR: ++// return "undefined error"; ++// case MZ_ZIP_TOO_MANY_FILES: ++// return "too many files"; ++// case MZ_ZIP_FILE_TOO_LARGE: ++// return "file too large"; ++// case MZ_ZIP_UNSUPPORTED_METHOD: ++// return "unsupported method"; ++// case MZ_ZIP_UNSUPPORTED_ENCRYPTION: ++// return "unsupported encryption"; ++// case MZ_ZIP_UNSUPPORTED_FEATURE: ++// return "unsupported feature"; ++// case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: ++// return "failed finding central directory"; ++// case MZ_ZIP_NOT_AN_ARCHIVE: ++// return "not a ZIP archive"; ++// case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: ++// return "invalid header or archive is corrupted"; ++// case MZ_ZIP_UNSUPPORTED_MULTIDISK: ++// return "unsupported multidisk archive"; ++// case MZ_ZIP_DECOMPRESSION_FAILED: ++// return "decompression failed or archive is corrupted"; ++// case MZ_ZIP_COMPRESSION_FAILED: ++// return "compression failed"; ++// case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: ++// return "unexpected decompressed size"; ++// case MZ_ZIP_CRC_CHECK_FAILED: ++// return "CRC-32 check failed"; ++// case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: ++// return "unsupported central directory size"; ++// case MZ_ZIP_ALLOC_FAILED: ++// return "allocation failed"; ++// case MZ_ZIP_FILE_OPEN_FAILED: ++// return "file open failed"; ++// case MZ_ZIP_FILE_CREATE_FAILED: ++// return "file create failed"; ++// case MZ_ZIP_FILE_WRITE_FAILED: ++// return "file write failed"; ++// case MZ_ZIP_FILE_READ_FAILED: ++// return "file read failed"; ++// case MZ_ZIP_FILE_CLOSE_FAILED: ++// return "file close failed"; ++// case MZ_ZIP_FILE_SEEK_FAILED: ++// return "file seek failed"; ++// case MZ_ZIP_FILE_STAT_FAILED: ++// return "file stat failed"; ++// case MZ_ZIP_INVALID_PARAMETER: ++// return "invalid parameter"; ++// case MZ_ZIP_INVALID_FILENAME: ++// return "invalid filename"; ++// case MZ_ZIP_BUF_TOO_SMALL: ++// return "buffer too small"; ++// case MZ_ZIP_INTERNAL_ERROR: ++// return "internal error"; ++// case MZ_ZIP_FILE_NOT_FOUND: ++// return "file not found"; ++// case MZ_ZIP_ARCHIVE_TOO_LARGE: ++// return "archive is too large"; ++// case MZ_ZIP_VALIDATION_FAILED: ++// return "validation failed"; ++// case MZ_ZIP_WRITE_CALLBACK_FAILED: ++// return "write calledback failed"; ++// default: ++// break; ++// } + +- return "unknown error"; +-} ++// return "unknown error"; ++// } + + /* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +-mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +-{ +- if ((!pZip) || (!pZip->m_pState)) +- return MZ_FALSE; ++// mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) ++// { ++// if ((!pZip) || (!pZip->m_pState)) ++// return MZ_FALSE; + +- return pZip->m_pState->m_zip64; +-} ++// return pZip->m_pState->m_zip64; ++// } + +-size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +-{ +- if ((!pZip) || (!pZip->m_pState)) +- return 0; ++// size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) ++// { ++// if ((!pZip) || (!pZip->m_pState)) ++// return 0; + +- return pZip->m_pState->m_central_dir.m_size; +-} ++// return pZip->m_pState->m_central_dir.m_size; ++// } + +-mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +-{ +- return pZip ? pZip->m_total_files : 0; +-} ++// mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) ++// { ++// return pZip ? pZip->m_total_files : 0; ++// } + +-mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +-{ +- if (!pZip) +- return 0; +- return pZip->m_archive_size; +-} ++// mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) ++// { ++// if (!pZip) ++// return 0; ++// return pZip->m_archive_size; ++// } + +-mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +-{ +- if ((!pZip) || (!pZip->m_pState)) +- return 0; +- return pZip->m_pState->m_file_archive_start_ofs; +-} ++// mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) ++// { ++// if ((!pZip) || (!pZip->m_pState)) ++// return 0; ++// return pZip->m_pState->m_file_archive_start_ofs; ++// } + +-MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +-{ +- if ((!pZip) || (!pZip->m_pState)) +- return 0; +- return pZip->m_pState->m_pFile; +-} ++// MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) ++// { ++// if ((!pZip) || (!pZip->m_pState)) ++// return 0; ++// return pZip->m_pState->m_pFile; ++// } + +-size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +-{ +- if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) +- return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) ++// { ++// if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) ++// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +- return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +-} ++// return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); ++// } + +-mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +-{ +- mz_uint n; +- const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +- if (!p) +- { +- if (filename_buf_size) +- pFilename[0] = '\0'; +- mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +- return 0; +- } +- n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +- if (filename_buf_size) +- { +- n = MZ_MIN(n, filename_buf_size - 1); +- memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); +- pFilename[n] = '\0'; +- } +- return n + 1; +-} ++// mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) ++// { ++// mz_uint n; ++// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); ++// if (!p) ++// { ++// if (filename_buf_size) ++// pFilename[0] = '\0'; ++// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); ++// return 0; ++// } ++// n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ++// if (filename_buf_size) ++// { ++// n = MZ_MIN(n, filename_buf_size - 1); ++// memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); ++// pFilename[n] = '\0'; ++// } ++// return n + 1; ++// } + +-mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +-{ +- return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +-} ++// mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) ++// { ++// return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); ++// } + +-mz_bool mz_zip_end(mz_zip_archive *pZip) +-{ +- if (!pZip) +- return MZ_FALSE; +- +- if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) +- return mz_zip_reader_end(pZip); +-#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +- else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) +- return mz_zip_writer_end(pZip); +-#endif ++// mz_bool mz_zip_end(mz_zip_archive *pZip) ++// { ++// if (!pZip) ++// return MZ_FALSE; + +- return MZ_FALSE; +-} ++// if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) ++// return mz_zip_reader_end(pZip); ++// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS ++// else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) ++// return mz_zip_writer_end(pZip); ++// #endif + +-#ifdef __cplusplus +-} +-#endif ++// return MZ_FALSE; ++// } ++ ++// #ifdef __cplusplus ++// } ++// #endif + +-#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ ++// #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/gomspace/libutil/src/zip/miniz/miniz.c b/gomspace/libutil/src/zip/miniz/miniz.c new file mode 100644 index 00000000..910d4b1f --- /dev/null +++ b/gomspace/libutil/src/zip/miniz/miniz.c @@ -0,0 +1,7572 @@ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + +#include "miniz.h" + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + 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; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +// #if 0 +// mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +// { +// static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, +// 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; +// mz_uint32 crcu32 = (mz_uint32)crc; +// if (!ptr) +// return MZ_CRC32_INIT; +// crcu32 = ~crcu32; +// while (buf_len--) +// { +// mz_uint8 b = *ptr++; +// crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; +// crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; +// } +// return ~crcu32; +// } +// #else +/* Faster, but larger CPU cache footprint. + */ +// mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +// { +// static const mz_uint32 s_crc_table[256] = +// { +// 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, +// 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, +// 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, +// 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, +// 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, +// 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, +// 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, +// 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, +// 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, +// 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, +// 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, +// 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, +// 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, +// 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, +// 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, +// 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, +// 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, +// 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, +// 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, +// 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, +// 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, +// 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, +// 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, +// 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, +// 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, +// 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, +// 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, +// 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, +// 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, +// 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, +// 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, +// 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, +// 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, +// 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, +// 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, +// 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, +// 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +// }; + +// mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; +// const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + +// while (buf_len >= 4) +// { +// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; +// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; +// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; +// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; +// pByte_buf += 4; +// buf_len -= 4; +// } + +// while (buf_len) +// { +// crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; +// ++pByte_buf; +// --buf_len; +// } + +// return ~crc32; +// } +// #endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +// void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +// { +// (void)opaque, (void)address, (void)items, (void)size; +// return MZ_REALLOC(address, items * size); +// } + +// const char *mz_version(void) +// { +// return MZ_VERSION; +// } + +#ifndef MINIZ_NO_ZLIB_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +// int mz_deflateReset(mz_streamp pStream) +// { +// if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) +// return MZ_STREAM_ERROR; +// pStream->total_in = pStream->total_out = 0; +// tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); +// return MZ_OK; +// } + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +// mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +// { +// (void)pStream; +// This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) +// return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +// } + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +// mz_ulong mz_compressBound(mz_ulong source_len) +// { +// return mz_deflateBound(NULL, source_len); +// } + +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; + +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 = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_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; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +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; +} + +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; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +// const char *mz_error(int err) +// { +// static struct +// { +// int m_err; +// const char *m_pDesc; +// } s_error_descs[] = +// { +// { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } +// }; +// mz_uint i; +// for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) +// if (s_error_descs[i].m_err == err) +// return s_error_descs[i].m_pDesc; +// return NULL; +// } + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + 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 +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 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, 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, + 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, 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, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 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, 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, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + 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; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint16 mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + mz_uint16 s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +// #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +// static mz_bool tdefl_compress_fast(tdefl_compressor *d) +// { +// /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ +// mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; +// mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; +// mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + +// while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) +// { +// const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; +// mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; +// mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); +// d->m_src_buf_left -= num_bytes_to_process; +// lookahead_size += num_bytes_to_process; + +// while (num_bytes_to_process) +// { +// mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); +// memcpy(d->m_dict + dst_pos, d->m_pSrc, n); +// if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) +// memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); +// d->m_pSrc += n; +// dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; +// num_bytes_to_process -= n; +// } + +// dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); +// if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) +// break; + +// while (lookahead_size >= 4) +// { +// mz_uint cur_match_dist, cur_match_len = 1; +// mz_uint8 *pCur_dict = d->m_dict + cur_pos; +// mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; +// mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; +// mz_uint probe_pos = d->m_hash[hash]; +// d->m_hash[hash] = (mz_uint16)lookahead_pos; + +// if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) +// { +// const mz_uint16 *p = (const mz_uint16 *)pCur_dict; +// const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); +// mz_uint32 probe_len = 32; +// do +// { +// } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && +// (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); +// cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); +// if (!probe_len) +// cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + +// if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) +// { +// cur_match_len = 1; +// *pLZ_code_buf++ = (mz_uint8)first_trigram; +// *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); +// d->m_huff_count[0][(mz_uint8)first_trigram]++; +// } +// else +// { +// mz_uint32 s0, s1; +// cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + +// MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + +// cur_match_dist--; + +// pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +// *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +// pLZ_code_buf += 3; +// *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + +// s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; +// s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; +// d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + +// d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; +// } +// } +// else +// { +// *pLZ_code_buf++ = (mz_uint8)first_trigram; +// *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); +// d->m_huff_count[0][(mz_uint8)first_trigram]++; +// } + +// if (--num_flags_left == 0) +// { +// num_flags_left = 8; +// pLZ_flags = pLZ_code_buf++; +// } + +// total_lz_bytes += cur_match_len; +// lookahead_pos += cur_match_len; +// dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); +// cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; +// MZ_ASSERT(lookahead_size >= cur_match_len); +// lookahead_size -= cur_match_len; + +// if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) +// { +// int n; +// d->m_lookahead_pos = lookahead_pos; +// d->m_lookahead_size = lookahead_size; +// d->m_dict_size = dict_size; +// d->m_total_lz_bytes = total_lz_bytes; +// d->m_pLZ_code_buf = pLZ_code_buf; +// d->m_pLZ_flags = pLZ_flags; +// d->m_num_flags_left = num_flags_left; +// if ((n = tdefl_flush_block(d, 0)) != 0) +// return (n < 0) ? MZ_FALSE : MZ_TRUE; +// total_lz_bytes = d->m_total_lz_bytes; +// pLZ_code_buf = d->m_pLZ_code_buf; +// pLZ_flags = d->m_pLZ_flags; +// num_flags_left = d->m_num_flags_left; +// } +// } + +// while (lookahead_size) +// { +// mz_uint8 lit = d->m_dict[cur_pos]; + +// total_lz_bytes++; +// *pLZ_code_buf++ = lit; +// *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); +// if (--num_flags_left == 0) +// { +// num_flags_left = 8; +// pLZ_flags = pLZ_code_buf++; +// } + +// d->m_huff_count[0][lit]++; + +// lookahead_pos++; +// dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); +// cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; +// lookahead_size--; + +// if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) +// { +// int n; +// d->m_lookahead_pos = lookahead_pos; +// d->m_lookahead_size = lookahead_size; +// d->m_dict_size = dict_size; +// d->m_total_lz_bytes = total_lz_bytes; +// d->m_pLZ_code_buf = pLZ_code_buf; +// d->m_pLZ_flags = pLZ_flags; +// d->m_num_flags_left = num_flags_left; +// if ((n = tdefl_flush_block(d, 0)) != 0) +// return (n < 0) ? MZ_FALSE : MZ_TRUE; +// total_lz_bytes = d->m_total_lz_bytes; +// pLZ_code_buf = d->m_pLZ_code_buf; +// pLZ_flags = d->m_pLZ_flags; +// num_flags_left = d->m_num_flags_left; +// } +// } +// } + +// d->m_lookahead_pos = lookahead_pos; +// d->m_lookahead_size = lookahead_size; +// d->m_dict_size = dict_size; +// d->m_total_lz_bytes = total_lz_bytes; +// d->m_pLZ_code_buf = pLZ_code_buf; +// d->m_pLZ_flags = pLZ_flags; +// d->m_num_flags_left = num_flags_left; +// return MZ_TRUE; +// } +// #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +// #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +// if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && +// ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && +// ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) +// { +// if (!tdefl_compress_fast(d)) +// return d->m_prev_return_status; +// } +// else +// #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +// tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +// { +// MZ_ASSERT(d->m_pPut_buf_func); +// return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +// } + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + memset(d->m_dict, 0, sizeof(d->m_dict)); // Initialize array to 0's + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +// tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +// { +// return d->m_prev_return_status; +// } + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +// mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +// { +// tdefl_compressor *pComp; +// mz_bool succeeded; +// if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) +// return MZ_FALSE; +// pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +// if (!pComp) +// return MZ_FALSE; +// succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); +// succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); +// MZ_FREE(pComp); +// return succeeded; +// } + +// typedef struct +// { +// size_t m_size, m_capacity; +// mz_uint8 *m_pBuf; +// mz_bool m_expandable; +// } tdefl_output_buffer; + +// static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +// { +// tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; +// size_t new_size = p->m_size + len; +// if (new_size > p->m_capacity) +// { +// size_t new_capacity = p->m_capacity; +// mz_uint8 *pNew_buf; +// if (!p->m_expandable) +// return MZ_FALSE; +// do +// { +// new_capacity = MZ_MAX(128U, new_capacity << 1U); +// } while (new_size > new_capacity); +// pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); +// if (!pNew_buf) +// return MZ_FALSE; +// p->m_pBuf = pNew_buf; +// p->m_capacity = new_capacity; +// } +// memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); +// p->m_size = new_size; +// return MZ_TRUE; +// } + +// void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +// { +// tdefl_output_buffer out_buf; +// MZ_CLEAR_OBJ(out_buf); +// if (!pOut_len) +// return MZ_FALSE; +// else +// *pOut_len = 0; +// out_buf.m_expandable = MZ_TRUE; +// if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) +// return NULL; +// *pOut_len = out_buf.m_size; +// return out_buf.m_pBuf; +// } + +// size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +// { +// tdefl_output_buffer out_buf; +// MZ_CLEAR_OBJ(out_buf); +// if (!pOut_buf) +// return 0; +// out_buf.m_pBuf = (mz_uint8 *)pOut_buf; +// out_buf.m_capacity = out_buf_len; +// if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) +// return 0; +// return out_buf.m_size; +// } + +static const mz_uint16 s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +// void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +// { +// /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ +// static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; +// tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +// tdefl_output_buffer out_buf; +// int i, bpl = w * num_chans, y, z; +// mz_uint32 c; +// *pLen_out = 0; +// if (!pComp) +// return NULL; +// MZ_CLEAR_OBJ(out_buf); +// out_buf.m_expandable = MZ_TRUE; +// out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); +// if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) +// { +// MZ_FREE(pComp); +// return NULL; +// } +// /* write dummy header */ +// for (z = 41; z; --z) +// tdefl_output_buffer_putter(&z, 1, &out_buf); +// /* compress image data */ +// tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); +// for (y = 0; y < h; ++y) +// { +// tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); +// tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); +// } +// if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) +// { +// MZ_FREE(pComp); +// MZ_FREE(out_buf.m_pBuf); +// return NULL; +// } +// /* write real header */ +// *pLen_out = out_buf.m_size - 41; +// { +// static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; +// mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, +// 0x0a, 0x1a, 0x0a, 0x00, 0x00, +// 0x00, 0x0d, 0x49, 0x48, 0x44, +// 0x52, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x08, +// 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x49, 0x44, 0x41, +// 0x54 }; +// pnghdr[18] = (mz_uint8)(w >> 8); +// pnghdr[19] = (mz_uint8)w; +// pnghdr[22] = (mz_uint8)(h >> 8); +// pnghdr[23] = (mz_uint8)h; +// pnghdr[25] = chans[num_chans]; +// pnghdr[33] = (mz_uint8)(*pLen_out >> 24); +// pnghdr[34] = (mz_uint8)(*pLen_out >> 16); +// pnghdr[35] = (mz_uint8)(*pLen_out >> 8); +// pnghdr[36] = (mz_uint8)*pLen_out; +// c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); +// for (i = 0; i < 4; ++i, c <<= 8) +// ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); +// memcpy(out_buf.m_pBuf, pnghdr, 41); +// } +// /* write footer (IDAT CRC-32, followed by IEND chunk) */ +// if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) +// { +// *pLen_out = 0; +// MZ_FREE(pComp); +// MZ_FREE(out_buf.m_pBuf); +// return NULL; +// } +// c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); +// for (i = 0; i < 4; ++i, c <<= 8) +// (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); +// /* compute final size of file, grab compressed data buffer and return */ +// *pLen_out += 57; +// MZ_FREE(pComp); +// return out_buf.m_pBuf; +// } +// void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +// { + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ +// return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +// } + +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +// tdefl_compressor *tdefl_compressor_alloc() +// { +// return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +// } + +// void tdefl_compressor_free(tdefl_compressor *pComp) +// { +// MZ_FREE(pComp); +// } + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#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 } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + 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. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#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 + +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 mz_uint16 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 mz_uint8 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 mz_uint16 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 mz_uint8 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 mz_uint16 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) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + 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)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + 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: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + 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; +} + +/* Higher level helper functions. */ +// void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +// { +// tinfl_decompressor decomp; +// void *pBuf = NULL, *pNew_buf; +// size_t src_buf_ofs = 0, out_buf_capacity = 0; +// *pOut_len = 0; +// tinfl_init(&decomp); +// for (;;) +// { +// size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; +// tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, +// (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); +// if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) +// { +// MZ_FREE(pBuf); +// *pOut_len = 0; +// return NULL; +// } +// src_buf_ofs += src_buf_size; +// *pOut_len += dst_buf_size; +// if (status == TINFL_STATUS_DONE) +// break; +// new_out_buf_capacity = out_buf_capacity * 2; +// if (new_out_buf_capacity < 128) +// new_out_buf_capacity = 128; +// pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); +// if (!pNew_buf) +// { +// MZ_FREE(pBuf); +// *pOut_len = 0; +// return NULL; +// } +// pBuf = pNew_buf; +// out_buf_capacity = new_out_buf_capacity; +// } +// return pBuf; +// } + +// size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +// { +// tinfl_decompressor decomp; +// tinfl_status status; +// tinfl_init(&decomp); +// status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); +// return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +// } + +// int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +// { +// int result = 0; +// tinfl_decompressor decomp; +// mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); +// size_t in_buf_ofs = 0, dict_ofs = 0; +// if (!pDict) +// return TINFL_STATUS_FAILED; +// tinfl_init(&decomp); +// for (;;) +// { +// size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; +// tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, +// (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); +// in_buf_ofs += in_buf_size; +// if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) +// break; +// if (status != TINFL_STATUS_HAS_MORE_OUTPUT) +// { +// result = (status == TINFL_STATUS_DONE); +// break; +// } +// dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); +// } +// MZ_FREE(pDict); +// *pIn_buf_size = in_buf_ofs; +// return result; +// } + +// tinfl_decompressor *tinfl_decompressor_alloc() +// { +// tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); +// if (pDecomp) +// tinfl_init(pDecomp); +// return pDecomp; +// } + +// void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +// { +// MZ_FREE(pDecomp); +// } + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + +// #ifndef MINIZ_NO_ARCHIVE_APIS + +// #ifdef __cplusplus +// extern "C" { +// #endif + +/* ------------------- .ZIP archive reading */ + +// #ifdef MINIZ_NO_STDIO +// #define MZ_FILE void * +// #else +// #include + +// #if defined(_MSC_VER) || defined(__MINGW64__) +// static FILE *mz_fopen(const char *pFilename, const char *pMode) +// { +// FILE *pFile = NULL; +// fopen_s(&pFile, pFilename, pMode); +// return pFile; +// } +// static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +// { +// FILE *pFile = NULL; +// if (freopen_s(&pFile, pPath, pMode, pStream)) +// return NULL; +// return pFile; +// } +// #ifndef MINIZ_NO_TIME +// #include +// #endif +// #define MZ_FOPEN mz_fopen +// #define MZ_FCLOSE fclose +// #define MZ_FREAD fread +// #define MZ_FWRITE fwrite +// #define MZ_FTELL64 _ftelli64 +// #define MZ_FSEEK64 _fseeki64 +// #define MZ_FILE_STAT_STRUCT _stat +// #define MZ_FILE_STAT _stat +// #define MZ_FFLUSH fflush +// #define MZ_FREOPEN mz_freopen +// #define MZ_DELETE_FILE remove +// #elif defined(__MINGW32__) +// #ifndef MINIZ_NO_TIME +// #include +// #endif +// #define MZ_FOPEN(f, m) fopen(f, m) +// #define MZ_FCLOSE fclose +// #define MZ_FREAD fread +// #define MZ_FWRITE fwrite +// #define MZ_FTELL64 ftello64 +// #define MZ_FSEEK64 fseeko64 +// #define MZ_FILE_STAT_STRUCT _stat +// #define MZ_FILE_STAT _stat +// #define MZ_FFLUSH fflush +// #define MZ_FREOPEN(f, m, s) freopen(f, m, s) +// #define MZ_DELETE_FILE remove +// #elif defined(__TINYC__) +// #ifndef MINIZ_NO_TIME +// #include +// #endif +// #define MZ_FOPEN(f, m) fopen(f, m) +// #define MZ_FCLOSE fclose +// #define MZ_FREAD fread +// #define MZ_FWRITE fwrite +// #define MZ_FTELL64 ftell +// #define MZ_FSEEK64 fseek +// #define MZ_FILE_STAT_STRUCT stat +// #define MZ_FILE_STAT stat +// #define MZ_FFLUSH fflush +// #define MZ_FREOPEN(f, m, s) freopen(f, m, s) +// #define MZ_DELETE_FILE remove +// #elif defined(__GNUC__) && _LARGEFILE64_SOURCE +// #ifndef MINIZ_NO_TIME +// #include +// #endif +// #define MZ_FOPEN(f, m) fopen64(f, m) +// #define MZ_FCLOSE fclose +// #define MZ_FREAD fread +// #define MZ_FWRITE fwrite +// #define MZ_FTELL64 ftello64 +// #define MZ_FSEEK64 fseeko64 +// #define MZ_FILE_STAT_STRUCT stat64 +// #define MZ_FILE_STAT stat64 +// #define MZ_FFLUSH fflush +// #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +// #define MZ_DELETE_FILE remove +// #elif defined(__APPLE__) && _LARGEFILE64_SOURCE +// #ifndef MINIZ_NO_TIME +// #include +// #endif +// #define MZ_FOPEN(f, m) fopen(f, m) +// #define MZ_FCLOSE fclose +// #define MZ_FREAD fread +// #define MZ_FWRITE fwrite +// #define MZ_FTELL64 ftello +// #define MZ_FSEEK64 fseeko +// #define MZ_FILE_STAT_STRUCT stat +// #define MZ_FILE_STAT stat +// #define MZ_FFLUSH fflush +// #define MZ_FREOPEN(p, m, s) freopen(p, m, s) +// #define MZ_DELETE_FILE remove + +// #else +// // #pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +// #ifndef MINIZ_NO_TIME +// #include +// #endif +// #define MZ_FOPEN(f, m) fopen(f, m) +// #define MZ_FCLOSE fclose +// #define MZ_FREAD fread +// #define MZ_FWRITE fwrite +// #ifdef __STRICT_ANSI__ +// #define MZ_FTELL64 ftell +// #define MZ_FSEEK64 fseek +// #else +// #define MZ_FTELL64 ftello +// #define MZ_FSEEK64 fseeko +// #endif +// #define MZ_FILE_STAT_STRUCT stat +// #define MZ_FILE_STAT stat +// #define MZ_FFLUSH fflush +// #define MZ_FREOPEN(f, m, s) freopen(f, m, s) +// #define MZ_DELETE_FILE remove +// #endif /* #ifdef _MSC_VER */ +// #endif /* #ifdef MINIZ_NO_STDIO */ + +// #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +// enum +// { +// /* ZIP archive identifiers and record sizes */ +// MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, +// MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, +// MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, +// MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, +// MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, +// MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + +// /* ZIP64 archive identifier and record sizes */ +// MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, +// MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, +// MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, +// MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, +// MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, +// MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, +// MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, +// MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + +// /* Central directory header record offsets */ +// MZ_ZIP_CDH_SIG_OFS = 0, +// MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, +// MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, +// MZ_ZIP_CDH_BIT_FLAG_OFS = 8, +// MZ_ZIP_CDH_METHOD_OFS = 10, +// MZ_ZIP_CDH_FILE_TIME_OFS = 12, +// MZ_ZIP_CDH_FILE_DATE_OFS = 14, +// MZ_ZIP_CDH_CRC32_OFS = 16, +// MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, +// MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, +// MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, +// MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, +// MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, +// MZ_ZIP_CDH_DISK_START_OFS = 34, +// MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, +// MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, +// MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + +// /* Local directory header offsets */ +// MZ_ZIP_LDH_SIG_OFS = 0, +// MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, +// MZ_ZIP_LDH_BIT_FLAG_OFS = 6, +// MZ_ZIP_LDH_METHOD_OFS = 8, +// MZ_ZIP_LDH_FILE_TIME_OFS = 10, +// MZ_ZIP_LDH_FILE_DATE_OFS = 12, +// MZ_ZIP_LDH_CRC32_OFS = 14, +// MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, +// MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, +// MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, +// MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, +// MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + +// /* End of central directory offsets */ +// MZ_ZIP_ECDH_SIG_OFS = 0, +// MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, +// MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, +// MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, +// MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, +// MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, +// MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, +// MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + +// /* ZIP64 End of central directory locator offsets */ +// MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ +// MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ +// MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ +// MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + +// /* ZIP64 End of central directory header offsets */ +// MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ +// MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ +// MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ +// MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ +// MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ +// MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ +// MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ +// MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ +// MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ +// MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ +// MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, +// MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, +// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, +// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, +// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, +// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, +// MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +// }; + +// typedef struct +// { +// void *m_p; +// size_t m_size, m_capacity; +// mz_uint m_element_size; +// } mz_zip_array; + +// struct mz_zip_internal_state_tag +// { +// mz_zip_array m_central_dir; +// mz_zip_array m_central_dir_offsets; +// mz_zip_array m_sorted_central_dir_offsets; + +// /* The flags passed in when the archive is initially opened. */ +// uint32_t m_init_flags; + +// /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ +// mz_bool m_zip64; + +// /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ +// mz_bool m_zip64_has_extended_info_fields; + +// /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ +// MZ_FILE *m_pFile; +// mz_uint64 m_file_archive_start_ofs; + +// void *m_pMem; +// size_t m_mem_size; +// size_t m_mem_capacity; +// }; + +// #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +// #if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) +// static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +// { +// MZ_ASSERT(index < pArray->m_size); +// return index; +// } +// #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +// #else +// #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +// #endif + +// static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +// { +// memset(pArray, 0, sizeof(mz_zip_array)); +// pArray->m_element_size = element_size; +// } + +// static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); +// memset(pArray, 0, sizeof(mz_zip_array)); +// } + +// static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +// { +// void *pNew_p; +// size_t new_capacity = min_new_capacity; +// MZ_ASSERT(pArray->m_element_size); +// if (pArray->m_capacity >= min_new_capacity) +// return MZ_TRUE; +// if (growing) +// { +// new_capacity = MZ_MAX(1, pArray->m_capacity); +// while (new_capacity < min_new_capacity) +// new_capacity *= 2; +// } +// if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) +// return MZ_FALSE; +// pArray->m_p = pNew_p; +// pArray->m_capacity = new_capacity; +// return MZ_TRUE; +// } + +// static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +// { +// if (new_capacity > pArray->m_capacity) +// { +// if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) +// return MZ_FALSE; +// } +// return MZ_TRUE; +// } + +// static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +// { +// if (new_size > pArray->m_capacity) +// { +// if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) +// return MZ_FALSE; +// } +// pArray->m_size = new_size; +// return MZ_TRUE; +// } + +// static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +// { +// return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +// } + +// static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +// { +// size_t orig_size = pArray->m_size; +// if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) +// return MZ_FALSE; +// memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); +// return MZ_TRUE; +// } + +// #ifndef MINIZ_NO_TIME +// static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +// { +// struct tm tm; +// memset(&tm, 0, sizeof(tm)); +// tm.tm_isdst = -1; +// tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; +// tm.tm_mon = ((dos_date >> 5) & 15) - 1; +// tm.tm_mday = dos_date & 31; +// tm.tm_hour = (dos_time >> 11) & 31; +// tm.tm_min = (dos_time >> 5) & 63; +// tm.tm_sec = (dos_time << 1) & 62; +// return mktime(&tm); +// } + +// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +// static void mz_zip_time_t_to_dos_time(MZ_TIME_T time_, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +// { +// #ifdef _MSC_VER +// struct tm tm_struct; +// struct tm *tm = &tm_struct; +// errno_t err = localtime_s(tm, &time_); +// if (err) +// { +// *pDOS_date = 0; +// *pDOS_time = 0; +// return; +// } +// #else +// struct tm *tm = localtime(&time_); +// #endif /* #ifdef _MSC_VER */ + +// *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); +// *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +// } +// #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +// #ifndef MINIZ_NO_STDIO +// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +// static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +// { +// struct MZ_FILE_STAT_STRUCT file_stat; + +// /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ +// if (MZ_FILE_STAT(pFilename, &file_stat) != 0) +// return MZ_FALSE; + +// *pTime = file_stat.st_mtime; + +// return MZ_TRUE; +// } +// #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +// static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +// { +// struct utimbuf t; + +// memset(&t, 0, sizeof(t)); +// t.actime = access_time; +// t.modtime = modified_time; + +// return !utime(pFilename, &t); +// } +// #endif /* #ifndef MINIZ_NO_STDIO */ +// #endif /* #ifndef MINIZ_NO_TIME */ + +// static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +// { +// if (pZip) +// pZip->m_last_error = err_num; +// return MZ_FALSE; +// } + +// static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +// { +// (void)flags; +// if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (!pZip->m_pAlloc) +// pZip->m_pAlloc = miniz_def_alloc_func; +// if (!pZip->m_pFree) +// pZip->m_pFree = miniz_def_free_func; +// if (!pZip->m_pRealloc) +// pZip->m_pRealloc = miniz_def_realloc_func; + +// pZip->m_archive_size = 0; +// pZip->m_central_directory_file_ofs = 0; +// pZip->m_total_files = 0; +// pZip->m_last_error = MZ_ZIP_NO_ERROR; + +// if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); +// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); +// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); +// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); +// pZip->m_pState->m_init_flags = flags; +// pZip->m_pState->m_zip64 = MZ_FALSE; +// pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + +// pZip->m_zip_mode = MZ_ZIP_MODE_READING; + +// return MZ_TRUE; +// } + +// static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +// { +// const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; +// const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); +// mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// mz_uint8 l = 0, r = 0; +// pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +// pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +// pE = pL + MZ_MIN(l_len, r_len); +// while (pL < pE) +// { +// if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) +// break; +// pL++; +// pR++; +// } +// return (pL == pE) ? (l_len < r_len) : (l < r); +// } +/* +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END +*/ +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +// static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +// { +// mz_zip_internal_state *pState = pZip->m_pState; +// const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; +// const mz_zip_array *pCentral_dir = &pState->m_central_dir; +// mz_uint32 *pIndices; +// mz_uint32 start, end; +// const mz_uint32 size = pZip->m_total_files; + +// if (size <= 1U) +// return; + +// pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + +// start = (size - 2U) >> 1U; +// for (;;) +// { +// mz_uint64 child, root = start; +// for (;;) +// { +// if ((child = (root << 1U) + 1U) >= size) +// break; +// child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); +// if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) +// break; +// MZ_SWAP_UINT32(pIndices[root], pIndices[child]); +// root = child; +// } +// if (!start) +// break; +// start--; +// } + +// end = size - 1; +// while (end > 0) +// { +// mz_uint64 child, root = 0; +// MZ_SWAP_UINT32(pIndices[end], pIndices[0]); +// for (;;) +// { +// if ((child = (root << 1U) + 1U) >= end) +// break; +// child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); +// if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) +// break; +// MZ_SWAP_UINT32(pIndices[root], pIndices[child]); +// root = child; +// } +// end--; +// } +// } + +// static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +// { +// mz_int64 cur_file_ofs; +// mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; +// mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + +// /* Basic sanity checks - reject files which are too small */ +// if (pZip->m_archive_size < record_size) +// return MZ_FALSE; + +// /* Find the record by scanning the file from the end towards the beginning. */ +// cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); +// for (;;) +// { +// int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) +// return MZ_FALSE; + +// for (i = n - 4; i >= 0; --i) +// { +// mz_uint s = MZ_READ_LE32(pBuf + i); +// if (s == record_sig) +// { +// if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) +// break; +// } +// } + +// if (i >= 0) +// { +// cur_file_ofs += i; +// break; +// } + +// /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ +// if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) +// return MZ_FALSE; + +// cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); +// } + +// *pOfs = cur_file_ofs; +// return MZ_TRUE; +// } + +// static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +// { +// mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; +// mz_uint64 cdir_ofs = 0; +// mz_int64 cur_file_ofs = 0; +// const mz_uint8 *p; + +// mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; +// mz_uint8 *pBuf = (mz_uint8 *)buf_u32; +// mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); +// mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +// mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + +// mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +// mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + +// mz_uint64 zip64_end_of_central_dir_ofs = 0; + +// /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ +// if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +// if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) +// return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + +// /* Read and verify the end of central directory record. */ +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +// if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) +// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +// if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) +// { +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) +// { +// if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) +// { +// zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); +// if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) +// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +// if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) +// { +// if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) +// { +// pZip->m_pState->m_zip64 = MZ_TRUE; +// } +// } +// } +// } +// } + +// pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); +// cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); +// num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); +// cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); +// cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); +// cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + +// if (pZip->m_pState->m_zip64) +// { +// mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); +// mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); +// mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); +// mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); +// mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + +// if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if (zip64_total_num_of_disks != 1U) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +// /* Check for miniz's practical limits */ +// if (zip64_cdir_total_entries > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + +// pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + +// if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + +// cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + +// /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ +// if (zip64_size_of_central_directory > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +// cdir_size = (mz_uint32)zip64_size_of_central_directory; + +// num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + +// cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + +// cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); +// } + +// if (pZip->m_total_files != cdir_entries_on_this_disk) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +// if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +// if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// pZip->m_central_directory_file_ofs = cdir_ofs; + +// if (pZip->m_total_files) +// { +// mz_uint i, n; +// /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ +// if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || +// (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// if (sort_central_dir) +// { +// if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +// /* Now create an index into the central directory file records, do some basic sanity checking on each record */ +// p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; +// for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) +// { +// mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; +// mz_uint64 comp_size, decomp_size, local_header_ofs; + +// if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + +// if (sort_central_dir) +// MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + +// comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); +// decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); +// local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); +// filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + +// if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && +// (ext_data_size) && +// (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) +// { +// /* Attempt to find zip64 extended information field in the entry's extra data */ +// mz_uint32 extra_size_remaining = ext_data_size; + +// if (extra_size_remaining) +// { +// const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + +// do +// { +// mz_uint32 field_id; +// mz_uint32 field_data_size; + +// if (extra_size_remaining < (sizeof(mz_uint16) * 2)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// field_id = MZ_READ_LE16(pExtra_data); +// field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + +// if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) +// { +// /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ +// pZip->m_pState->m_zip64 = MZ_TRUE; +// pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; +// break; +// } + +// pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; +// extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; +// } while (extra_size_remaining); +// } +// } + +// /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ +// if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) +// { +// if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// } + +// disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); +// if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + +// if (comp_size != MZ_UINT32_MAX) +// { +// if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// } + +// bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); +// if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + +// if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// n -= total_header_size; +// p += total_header_size; +// } +// } + +// if (sort_central_dir) +// mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + +// return MZ_TRUE; +// } + +// void mz_zip_zero_struct(mz_zip_archive *pZip) +// { +// if (pZip) +// MZ_CLEAR_OBJ(*pZip); +// } + +// static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +// { +// mz_bool status = MZ_TRUE; + +// if (!pZip) +// return MZ_FALSE; + +// if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) +// { +// if (set_last_error) +// pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + +// return MZ_FALSE; +// } + +// if (pZip->m_pState) +// { +// mz_zip_internal_state *pState = pZip->m_pState; +// pZip->m_pState = NULL; + +// mz_zip_array_clear(pZip, &pState->m_central_dir); +// mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); +// mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +// #ifndef MINIZ_NO_STDIO +// if (pState->m_pFile) +// { +// if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) +// { +// if (MZ_FCLOSE(pState->m_pFile) == EOF) +// { +// if (set_last_error) +// pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; +// status = MZ_FALSE; +// } +// } +// pState->m_pFile = NULL; +// } +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// } +// pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + +// return status; +// } + +// mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +// { +// return mz_zip_reader_end_internal(pZip, MZ_TRUE); +// } +// mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +// { +// if ((!pZip) || (!pZip->m_pRead)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (!mz_zip_reader_init_internal(pZip, flags)) +// return MZ_FALSE; + +// pZip->m_zip_type = MZ_ZIP_TYPE_USER; +// pZip->m_archive_size = size; + +// if (!mz_zip_reader_read_central_dir(pZip, flags)) +// { +// mz_zip_reader_end_internal(pZip, MZ_FALSE); +// return MZ_FALSE; +// } + +// return MZ_TRUE; +// } + +// static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +// { +// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +// size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); +// memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); +// return s; +// } + +// mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +// { +// if (!pMem) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + +// if (!mz_zip_reader_init_internal(pZip, flags)) +// return MZ_FALSE; + +// pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; +// pZip->m_archive_size = size; +// pZip->m_pRead = mz_zip_mem_read_func; +// pZip->m_pIO_opaque = pZip; +// pZip->m_pNeeds_keepalive = NULL; + +// #ifdef __cplusplus +// pZip->m_pState->m_pMem = const_cast(pMem); +// #else +// pZip->m_pState->m_pMem = (void *)pMem; +// #endif + +// pZip->m_pState->m_mem_size = size; + +// if (!mz_zip_reader_read_central_dir(pZip, flags)) +// { +// mz_zip_reader_end_internal(pZip, MZ_FALSE); +// return MZ_FALSE; +// } + +// return MZ_TRUE; +// } + +// #ifndef MINIZ_NO_STDIO +// static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +// { +// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +// mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + +// file_ofs += pZip->m_pState->m_file_archive_start_ofs; + +// if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) +// return 0; + +// return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +// } + +// mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +// { +// return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +// } + +// mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +// { +// mz_uint64 file_size; +// MZ_FILE *pFile; + +// if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pFile = MZ_FOPEN(pFilename, "rb"); +// if (!pFile) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +// file_size = archive_size; +// if (!file_size) +// { +// if (MZ_FSEEK64(pFile, 0, SEEK_END)) +// { +// MZ_FCLOSE(pFile); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); +// } + +// file_size = MZ_FTELL64(pFile); +// } + +// /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + +// if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +// { +// MZ_FCLOSE(pFile); +// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); +// } + +// if (!mz_zip_reader_init_internal(pZip, flags)) +// { +// MZ_FCLOSE(pFile); +// return MZ_FALSE; +// } + +// pZip->m_zip_type = MZ_ZIP_TYPE_FILE; +// pZip->m_pRead = mz_zip_file_read_func; +// pZip->m_pIO_opaque = pZip; +// pZip->m_pState->m_pFile = pFile; +// pZip->m_archive_size = file_size; +// pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + +// if (!mz_zip_reader_read_central_dir(pZip, flags)) +// { +// mz_zip_reader_end_internal(pZip, MZ_FALSE); +// return MZ_FALSE; +// } + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +// { +// mz_uint64 cur_file_ofs; + +// if ((!pZip) || (!pFile)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +// cur_file_ofs = MZ_FTELL64(pFile); + +// if (!archive_size) +// { +// if (MZ_FSEEK64(pFile, 0, SEEK_END)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + +// archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + +// if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); +// } + +// if (!mz_zip_reader_init_internal(pZip, flags)) +// return MZ_FALSE; + +// pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; +// pZip->m_pRead = mz_zip_file_read_func; + +// pZip->m_pIO_opaque = pZip; +// pZip->m_pState->m_pFile = pFile; +// pZip->m_archive_size = archive_size; +// pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + +// if (!mz_zip_reader_read_central_dir(pZip, flags)) +// { +// mz_zip_reader_end_internal(pZip, MZ_FALSE); +// return MZ_FALSE; +// } + +// return MZ_TRUE; +// } + +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +// { +// if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) +// return NULL; +// return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +// } + +// mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +// { +// mz_uint m_bit_flag; +// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +// if (!p) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// return MZ_FALSE; +// } + +// m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); +// return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +// } + +// mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +// { +// mz_uint bit_flag; +// mz_uint method; + +// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +// if (!p) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// return MZ_FALSE; +// } + +// method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +// bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + +// if ((method != 0) && (method != MZ_DEFLATED)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); +// return MZ_FALSE; +// } + +// if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); +// return MZ_FALSE; +// } + +// if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) +// { +// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); +// return MZ_FALSE; +// } + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +// { +// mz_uint filename_len, attribute_mapping_id, external_attr; +// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +// if (!p) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// return MZ_FALSE; +// } + +// filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// if (filename_len) +// { +// if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') +// return MZ_TRUE; +// } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ +// attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; +// (void)attribute_mapping_id; + +// external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); +// if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) +// { +// return MZ_TRUE; +// } + +// return MZ_FALSE; +// } + +// static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +// { +// mz_uint n; +// const mz_uint8 *p = pCentral_dir_header; + +// if (pFound_zip64_extra_data) +// *pFound_zip64_extra_data = MZ_FALSE; + +// if ((!p) || (!pStat)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// /* Extract fields from the central directory record. */ +// pStat->m_file_index = file_index; +// pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); +// pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); +// pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); +// pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); +// pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +// #ifndef MINIZ_NO_TIME +// pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +// #endif +// pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); +// pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); +// pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); +// pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); +// pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); +// pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + +// /* Copy as much of the filename and comment as possible. */ +// n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); +// memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); +// pStat->m_filename[n] = '\0'; + +// n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); +// n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); +// pStat->m_comment_size = n; +// memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); +// pStat->m_comment[n] = '\0'; + +// /* Set some flags for convienance */ +// pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); +// pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); +// pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + +// /* See if we need to read any zip64 extended information fields. */ +// /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ +// if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) +// { +// /* Attempt to find zip64 extended information field in the entry's extra data */ +// mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + +// if (extra_size_remaining) +// { +// const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + +// do +// { +// mz_uint32 field_id; +// mz_uint32 field_data_size; + +// if (extra_size_remaining < (sizeof(mz_uint16) * 2)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// field_id = MZ_READ_LE16(pExtra_data); +// field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + +// if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) +// { +// const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; +// mz_uint32 field_data_remaining = field_data_size; + +// if (pFound_zip64_extra_data) +// *pFound_zip64_extra_data = MZ_TRUE; + +// if (pStat->m_uncomp_size == MZ_UINT32_MAX) +// { +// if (field_data_remaining < sizeof(mz_uint64)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// pStat->m_uncomp_size = MZ_READ_LE64(pField_data); +// pField_data += sizeof(mz_uint64); +// field_data_remaining -= sizeof(mz_uint64); +// } + +// if (pStat->m_comp_size == MZ_UINT32_MAX) +// { +// if (field_data_remaining < sizeof(mz_uint64)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// pStat->m_comp_size = MZ_READ_LE64(pField_data); +// pField_data += sizeof(mz_uint64); +// field_data_remaining -= sizeof(mz_uint64); +// } + +// if (pStat->m_local_header_ofs == MZ_UINT32_MAX) +// { +// if (field_data_remaining < sizeof(mz_uint64)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); +// pField_data += sizeof(mz_uint64); +// // field_data_remaining -= sizeof(mz_uint64); +// } + +// break; +// } + +// pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; +// extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; +// } while (extra_size_remaining); +// } +// } + +// return MZ_TRUE; +// } + +// static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +// { +// mz_uint i; +// if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) +// return 0 == memcmp(pA, pB, len); +// for (i = 0; i < len; ++i) +// if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) +// return MZ_FALSE; +// return MZ_TRUE; +// } + +// static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +// { +// const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; +// mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// mz_uint8 l = 0, r = 0; +// pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +// pE = pL + MZ_MIN(l_len, r_len); +// while (pL < pE) +// { +// if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) +// break; +// pL++; +// pR++; +// } +// return (pL == pE) ? (int)(l_len - r_len) : (l - r); +// } + +// static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +// { +// mz_zip_internal_state *pState = pZip->m_pState; +// const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; +// const mz_zip_array *pCentral_dir = &pState->m_central_dir; +// mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); +// const uint32_t size = pZip->m_total_files; +// const mz_uint filename_len = (mz_uint)strlen(pFilename); + +// if (pIndex) +// *pIndex = 0; + +// if (size) +// { +// /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ +// /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ +// mz_int64 l = 0, h = (mz_int64)size - 1; + +// while (l <= h) +// { +// mz_int64 m = l + ((h - l) >> 1); +// uint32_t file_index = pIndices[(uint32_t)m]; + +// int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); +// if (!comp) +// { +// if (pIndex) +// *pIndex = file_index; +// return MZ_TRUE; +// } +// else if (comp < 0) +// l = m + 1; +// else +// h = m - 1; +// } +// } + +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +// } + +// int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +// { +// mz_uint32 index_; +// if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index_)) +// return -1; +// else +// return (int)index_; +// } + +// mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +// { +// mz_uint file_index; +// size_t name_len, comment_len; + +// if (pIndex) +// *pIndex = 0; + +// if ((!pZip) || (!pZip->m_pState) || (!pName)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// /* See if we can use a binary search */ +// if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && +// (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && +// ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) +// { +// return mz_zip_locate_file_binary_search(pZip, pName, pIndex); +// } + +// /* Locate the entry by scanning the entire central directory */ +// name_len = strlen(pName); +// if (name_len > MZ_UINT16_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// comment_len = pComment ? strlen(pComment) : 0; +// if (comment_len > MZ_UINT16_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// for (file_index = 0; file_index < pZip->m_total_files; file_index++) +// { +// const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +// mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; +// if (filename_len < name_len) +// continue; +// if (comment_len) +// { +// mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); +// const char *pFile_comment = pFilename + filename_len + file_extra_len; +// if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) +// continue; +// } +// if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) +// { +// int ofs = filename_len - 1; +// do +// { +// if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) +// break; +// } while (--ofs >= 0); +// ofs++; +// pFilename += ofs; +// filename_len -= ofs; +// } +// if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) +// { +// if (pIndex) +// *pIndex = file_index; +// return MZ_TRUE; +// } +// } + +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +// } + +// mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +// { +// int status = TINFL_STATUS_DONE; +// mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; +// mz_zip_archive_file_stat file_stat; +// void *pRead_buf; +// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; +// tinfl_decompressor inflator; + +// if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +// return MZ_FALSE; + +// /* A directory or zero length file */ +// if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) +// return MZ_TRUE; + +// /* Encryption and patch files are not supported. */ +// if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + +// /* This function only supports decompressing stored and deflate. */ +// if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + +// /* Ensure supplied output buffer is large enough. */ +// needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; +// if (buf_size < needed_size) +// return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + +// /* Read and parse the local directory entry. */ +// cur_file_ofs = file_stat.m_local_header_ofs; +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +// if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) +// { +// /* The file is stored or the caller has requested the compressed data. */ +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) +// { +// if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) +// return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); +// } +// #endif + +// return MZ_TRUE; +// } + +// /* Decompress the file either directly from memory or from a file input buffer. */ +// tinfl_init(&inflator); + +// if (pZip->m_pState->m_pMem) +// { +// /* Read directly from the archive in memory. */ +// pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; +// read_buf_size = read_buf_avail = file_stat.m_comp_size; +// comp_remaining = 0; +// } +// else if (pUser_read_buf) +// { +// /* Use a user provided read buffer. */ +// if (!user_read_buf_size) +// return MZ_FALSE; +// pRead_buf = (mz_uint8 *)pUser_read_buf; +// read_buf_size = user_read_buf_size; +// read_buf_avail = 0; +// comp_remaining = file_stat.m_comp_size; +// } +// else +// { +// /* Temporarily allocate a read buffer. */ +// read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); +// if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +// if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// read_buf_avail = 0; +// comp_remaining = file_stat.m_comp_size; +// } + +// do +// { +// /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ +// size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); +// if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) +// { +// read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +// { +// status = TINFL_STATUS_FAILED; +// mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); +// break; +// } +// cur_file_ofs += read_buf_avail; +// comp_remaining -= read_buf_avail; +// read_buf_ofs = 0; +// } +// in_buf_size = (size_t)read_buf_avail; +// status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); +// read_buf_avail -= in_buf_size; +// read_buf_ofs += in_buf_size; +// out_buf_ofs += out_buf_size; +// } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + +// if (status == TINFL_STATUS_DONE) +// { +// /* Make sure the entire file was decompressed, and check its CRC. */ +// if (out_buf_ofs != file_stat.m_uncomp_size) +// { +// mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); +// status = TINFL_STATUS_FAILED; +// } +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) +// { +// mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); +// status = TINFL_STATUS_FAILED; +// } +// #endif +// } + +// if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) +// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + +// return status == TINFL_STATUS_DONE; +// } + +// mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +// { +// mz_uint32 file_index; +// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +// return MZ_FALSE; +// return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +// } + +// mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +// { +// return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +// } + +// mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +// { +// return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +// } + +// void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +// { +// mz_uint64 comp_size, uncomp_size, alloc_size; +// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +// void *pBuf; + +// if (pSize) +// *pSize = 0; + +// if (!p) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// return NULL; +// } + +// comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); +// uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + +// alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +// if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); +// return NULL; +// } + +// if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) +// { +// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// return NULL; +// } + +// if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); +// return NULL; +// } + +// if (pSize) +// *pSize = (size_t)alloc_size; +// return pBuf; +// } + +// void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +// { +// mz_uint32 file_index; +// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +// { +// if (pSize) +// *pSize = 0; +// return MZ_FALSE; +// } +// return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +// } + +// mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +// { +// int status = TINFL_STATUS_DONE; +// mz_uint file_crc32 = MZ_CRC32_INIT; +// mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; +// mz_zip_archive_file_stat file_stat; +// void *pRead_buf = NULL; +// void *pWrite_buf = NULL; +// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + +// if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +// return MZ_FALSE; + +// /* A directory or zero length file */ +// if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) +// return MZ_TRUE; + +// /* Encryption and patch files are not supported. */ +// if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + +// /* This function only supports decompressing stored and deflate. */ +// if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + +// /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ +// cur_file_ofs = file_stat.m_local_header_ofs; +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +// if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// /* Decompress the file either directly from memory or from a file input buffer. */ +// if (pZip->m_pState->m_pMem) +// { +// pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; +// read_buf_size = read_buf_avail = file_stat.m_comp_size; +// comp_remaining = 0; +// } +// else +// { +// read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); +// if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// read_buf_avail = 0; +// comp_remaining = file_stat.m_comp_size; +// } + +// if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) +// { +// /* The file is stored or the caller has requested the compressed data. */ +// if (pZip->m_pState->m_pMem) +// { +// if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +// if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) +// { +// mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); +// status = TINFL_STATUS_FAILED; +// } +// else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +// { +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +// #endif +// } + +// cur_file_ofs += file_stat.m_comp_size; +// out_buf_ofs += file_stat.m_comp_size; +// comp_remaining = 0; +// } +// else +// { +// while (comp_remaining) +// { +// read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// status = TINFL_STATUS_FAILED; +// break; +// } + +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +// { +// file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); +// } +// #endif + +// if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +// { +// mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); +// status = TINFL_STATUS_FAILED; +// break; +// } + +// cur_file_ofs += read_buf_avail; +// out_buf_ofs += read_buf_avail; +// comp_remaining -= read_buf_avail; +// } +// } +// } +// else +// { +// tinfl_decompressor inflator; +// tinfl_init(&inflator); + +// if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) +// { +// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// status = TINFL_STATUS_FAILED; +// } +// else +// { +// do +// { +// mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); +// size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); +// if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) +// { +// read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); +// if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// status = TINFL_STATUS_FAILED; +// break; +// } +// cur_file_ofs += read_buf_avail; +// comp_remaining -= read_buf_avail; +// read_buf_ofs = 0; +// } + +// in_buf_size = (size_t)read_buf_avail; +// status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); +// read_buf_avail -= in_buf_size; +// read_buf_ofs += in_buf_size; + +// if (out_buf_size) +// { +// if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) +// { +// mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); +// status = TINFL_STATUS_FAILED; +// break; +// } + +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +// #endif +// if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) +// { +// mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); +// status = TINFL_STATUS_FAILED; +// break; +// } +// } +// } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); +// } +// } + +// if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) +// { +// /* Make sure the entire file was decompressed, and check its CRC. */ +// if (out_buf_ofs != file_stat.m_uncomp_size) +// { +// mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); +// status = TINFL_STATUS_FAILED; +// } +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// else if (file_crc32 != file_stat.m_crc32) +// { +// mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); +// status = TINFL_STATUS_FAILED; +// } +// #endif +// } + +// if (!pZip->m_pState->m_pMem) +// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + +// if (pWrite_buf) +// pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + +// return status == TINFL_STATUS_DONE; +// } + +// mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +// { +// mz_uint32 file_index; +// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +// return MZ_FALSE; + +// return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +// } + +// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +// { +// mz_zip_reader_extract_iter_state *pState; +// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + +// /* Argument sanity check */ +// if ((!pZip) || (!pZip->m_pState)) +// return NULL; + +// /* Allocate an iterator status structure */ +// pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); +// if (!pState) +// { +// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// return NULL; +// } + +// /* Fetch file details */ +// if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } + +// /* Encryption and patch files are not supported. */ +// if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } + +// /* This function only supports decompressing stored and deflate. */ +// if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } + +// /* Init state - save args */ +// pState->pZip = pZip; +// pState->flags = flags; + +// /* Init state - reset variables to defaults */ +// pState->status = TINFL_STATUS_DONE; +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// pState->file_crc32 = MZ_CRC32_INIT; +// #endif +// pState->read_buf_ofs = 0; +// pState->out_buf_ofs = 0; +// pState->pRead_buf = NULL; +// pState->pWrite_buf = NULL; +// pState->out_blk_remain = 0; + +// /* Read and parse the local directory entry. */ +// pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; +// if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } + +// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } + +// pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +// if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } + +// /* Decompress the file either directly from memory or from a file input buffer. */ +// if (pZip->m_pState->m_pMem) +// { +// pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; +// pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; +// pState->comp_remaining = pState->file_stat.m_comp_size; +// } +// else +// { +// if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) +// { +// /* Decompression required, therefore intermediate read buffer required */ +// pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +// if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) +// { +// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } +// } +// else +// { +// /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ +// pState->read_buf_size = 0; +// } +// pState->read_buf_avail = 0; +// pState->comp_remaining = pState->file_stat.m_comp_size; +// } + +// if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) +// { +// /* Decompression required, init decompressor */ +// tinfl_init( &pState->inflator ); + +// /* Allocate write buffer */ +// if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) +// { +// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// if (pState->pRead_buf) +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// return NULL; +// } +// } + +// return pState; +// } + +// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +// { +// mz_uint32 file_index; + +// /* Locate file index by name */ +// if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) +// return NULL; + +// /* Construct iterator */ +// return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +// } + +// size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +// { +// size_t copied_to_caller = 0; + +// /* Argument sanity check */ +// if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) +// return 0; + +// if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) +// { +// /* The file is stored or the caller has requested the compressed data, calc amount to return. */ +// copied_to_caller = MZ_MIN( buf_size, pState->comp_remaining ); + +// /* Zip is in memory....or requires reading from a file? */ +// if (pState->pZip->m_pState->m_pMem) +// { +// /* Copy data to caller's buffer */ +// memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); +// pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; +// } +// else +// { +// /* Read directly into caller's buffer */ +// if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) +// { +// /* Failed to read all that was asked for, flag failure and alert user */ +// mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); +// pState->status = TINFL_STATUS_FAILED; +// copied_to_caller = 0; +// } +// } + +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// /* Compute CRC if not returning compressed data only */ +// if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +// pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +// #endif + +// /* Advance offsets, dec counters */ +// pState->cur_file_ofs += copied_to_caller; +// pState->out_buf_ofs += copied_to_caller; +// pState->comp_remaining -= copied_to_caller; +// } +// else +// { +// do +// { +// /* Calc ptr to write buffer - given current output pos and block size */ +// mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + +// /* Calc max output size - given current output pos and block size */ +// size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + +// if (!pState->out_blk_remain) +// { +// /* Read more data from file if none available (and reading from file) */ +// if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) +// { +// /* Calc read size */ +// pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); +// if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) +// { +// mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); +// pState->status = TINFL_STATUS_FAILED; +// break; +// } + +// /* Advance offsets, dec counters */ +// pState->cur_file_ofs += pState->read_buf_avail; +// pState->comp_remaining -= pState->read_buf_avail; +// pState->read_buf_ofs = 0; +// } + +// /* Perform decompression */ +// in_buf_size = (size_t)pState->read_buf_avail; +// pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); +// pState->read_buf_avail -= in_buf_size; +// pState->read_buf_ofs += in_buf_size; + +// /* Update current output block size remaining */ +// pState->out_blk_remain = out_buf_size; +// } + +// if (pState->out_blk_remain) +// { +// /* Calc amount to return. */ +// size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + +// /* Copy data to caller's buffer */ +// memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// /* Perform CRC */ +// pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +// #endif + +// /* Decrement data consumed from block */ +// pState->out_blk_remain -= to_copy; + +// /* Inc output offset, while performing sanity check */ +// if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) +// { +// mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); +// pState->status = TINFL_STATUS_FAILED; +// break; +// } + +// /* Increment counter of data copied to caller */ +// copied_to_caller += to_copy; +// } +// } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); +// } + +// /* Return how many bytes were copied into user buffer */ +// return copied_to_caller; +// } + +// mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +// { +// int status; + +// /* Argument sanity check */ +// if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) +// return MZ_FALSE; + +// /* Was decompression completed and requested? */ +// if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) +// { +// /* Make sure the entire file was decompressed, and check its CRC. */ +// if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) +// { +// mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); +// pState->status = TINFL_STATUS_FAILED; +// } +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// else if (pState->file_crc32 != pState->file_stat.m_crc32) +// { +// mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); +// pState->status = TINFL_STATUS_FAILED; +// } +// #endif +// } + +// /* Free buffers */ +// if (!pState->pZip->m_pState->m_pMem) +// pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); +// if (pState->pWrite_buf) +// pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + +// /* Save status */ +// status = pState->status; + +// /* Free context */ +// pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + +// return status == TINFL_STATUS_DONE; +// } + +// #ifndef MINIZ_NO_STDIO +// static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +// { +// (void)ofs; + +// return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +// } + +// mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +// { +// mz_bool status; +// mz_zip_archive_file_stat file_stat; +// MZ_FILE *pFile; + +// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +// return MZ_FALSE; + +// if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + +// pFile = MZ_FOPEN(pDst_filename, "wb"); +// if (!pFile) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +// status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + +// if (MZ_FCLOSE(pFile) == EOF) +// { +// if (status) +// mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + +// status = MZ_FALSE; +// } + +// #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) +// if (status) +// mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +// #endif + +// return status; +// } + +// mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +// { +// mz_uint32 file_index; +// if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) +// return MZ_FALSE; + +// return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +// } + +// mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +// { +// mz_zip_archive_file_stat file_stat; + +// if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) +// return MZ_FALSE; + +// if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + +// return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +// } + +// mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +// { +// mz_uint32 file_index; +// if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) +// return MZ_FALSE; + +// return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +// } +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +// { +// mz_uint32 *p = (mz_uint32 *)pOpaque; +// (void)file_ofs; +// *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); +// return n; +// } + +// mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +// { +// mz_zip_archive_file_stat file_stat; +// mz_zip_internal_state *pState; +// const mz_uint8 *pCentral_dir_header; +// mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; +// mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; +// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; +// mz_uint64 local_header_ofs = 0; +// mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; +// mz_uint64 local_header_comp_size, local_header_uncomp_size; +// mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; +// mz_bool has_data_descriptor; +// mz_uint32 local_header_bit_flags; + +// mz_zip_array file_data_array; +// mz_zip_array_init(&file_data_array, 1); + +// if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (file_index > pZip->m_total_files) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pState = pZip->m_pState; + +// pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + +// if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) +// return MZ_FALSE; + +// /* A directory or zero length file */ +// if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) +// return MZ_TRUE; + +// /* Encryption and patch files are not supported. */ +// if (file_stat.m_is_encrypted) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + +// /* This function only supports stored and deflate. */ +// if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + +// if (!file_stat.m_is_supported) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + +// /* Read and parse the local directory entry. */ +// local_header_ofs = file_stat.m_local_header_ofs; +// if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); +// local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +// local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); +// local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); +// local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); +// local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); +// has_data_descriptor = (local_header_bit_flags & 8) != 0; + +// if (local_header_filename_len != strlen(file_stat.m_filename)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// if (local_header_filename_len) +// { +// if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// goto handle_failure; +// } + +// /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ +// if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) +// { +// mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); +// goto handle_failure; +// } +// } + +// if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) +// { +// mz_uint32 extra_size_remaining = local_header_extra_len; +// const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + +// if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// goto handle_failure; +// } + +// do +// { +// mz_uint32 field_id, field_data_size, field_total_size; + +// if (extra_size_remaining < (sizeof(mz_uint16) * 2)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// field_id = MZ_READ_LE16(pExtra_data); +// field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); +// field_total_size = field_data_size + sizeof(mz_uint16) * 2; + +// if (field_total_size > extra_size_remaining) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) +// { +// const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + +// if (field_data_size < sizeof(mz_uint64) * 2) +// { +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// goto handle_failure; +// } + +// local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); +// local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + +// found_zip64_ext_data_in_ldir = MZ_TRUE; +// break; +// } + +// pExtra_data += field_total_size; +// extra_size_remaining -= field_total_size; +// } while (extra_size_remaining); +// } + +// /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ +// /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ +// if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) +// { +// mz_uint8 descriptor_buf[32]; +// mz_bool has_id; +// const mz_uint8 *pSrc; +// mz_uint32 file_crc32; +// mz_uint64 comp_size = 0, uncomp_size = 0; + +// mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + +// if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// goto handle_failure; +// } + +// has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); +// pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + +// file_crc32 = MZ_READ_LE32(pSrc); + +// if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) +// { +// comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); +// uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); +// } +// else +// { +// comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); +// uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); +// } + +// if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); +// goto handle_failure; +// } +// } +// else +// { +// if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); +// goto handle_failure; +// } +// } + +// mz_zip_array_clear(pZip, &file_data_array); + +// if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) +// { +// if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) +// return MZ_FALSE; + +// /* 1 more check to be sure, although the extract checks too. */ +// if (uncomp_crc32 != file_stat.m_crc32) +// { +// mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); +// return MZ_FALSE; +// } +// } + +// return MZ_TRUE; + +// handle_failure: +// mz_zip_array_clear(pZip, &file_data_array); +// return MZ_FALSE; +// } + +// mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +// { +// mz_zip_internal_state *pState; +// uint32_t i; + +// if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pState = pZip->m_pState; + +// /* Basic sanity checks */ +// if (!pState->m_zip64) +// { +// if (pZip->m_total_files > MZ_UINT16_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +// if (pZip->m_archive_size > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); +// } +// else +// { +// if (pZip->m_total_files >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +// if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); +// } + +// for (i = 0; i < pZip->m_total_files; i++) +// { +// if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) +// { +// mz_uint32 found_index; +// mz_zip_archive_file_stat stat_; + +// if (!mz_zip_reader_file_stat(pZip, i, &stat_)) +// return MZ_FALSE; + +// if (!mz_zip_reader_locate_file_v2(pZip, stat_.m_filename, NULL, 0, &found_index)) +// return MZ_FALSE; + +// /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ +// if (found_index != i) +// return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); +// } + +// if (!mz_zip_validate_file(pZip, i, flags)) +// return MZ_FALSE; +// } + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +// { +// mz_bool success = MZ_TRUE; +// mz_zip_archive zip; +// mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + +// if ((!pMem) || (!size)) +// { +// if (pErr) +// *pErr = MZ_ZIP_INVALID_PARAMETER; +// return MZ_FALSE; +// } + +// mz_zip_zero_struct(&zip); + +// if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) +// { +// if (pErr) +// *pErr = zip.m_last_error; +// return MZ_FALSE; +// } + +// if (!mz_zip_validate_archive(&zip, flags)) +// { +// actual_err = zip.m_last_error; +// success = MZ_FALSE; +// } + +// if (!mz_zip_reader_end_internal(&zip, success)) +// { +// if (!actual_err) +// actual_err = zip.m_last_error; +// success = MZ_FALSE; +// } + +// if (pErr) +// *pErr = actual_err; + +// return success; +// } + +// #ifndef MINIZ_NO_STDIO +// mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +// { +// mz_bool success = MZ_TRUE; +// mz_zip_archive zip; +// mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + +// if (!pFilename) +// { +// if (pErr) +// *pErr = MZ_ZIP_INVALID_PARAMETER; +// return MZ_FALSE; +// } + +// mz_zip_zero_struct(&zip); + +// if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) +// { +// if (pErr) +// *pErr = zip.m_last_error; +// return MZ_FALSE; +// } + +// if (!mz_zip_validate_archive(&zip, flags)) +// { +// actual_err = zip.m_last_error; +// success = MZ_FALSE; +// } + +// if (!mz_zip_reader_end_internal(&zip, success)) +// { +// if (!actual_err) +// actual_err = zip.m_last_error; +// success = MZ_FALSE; +// } + +// if (pErr) +// *pErr = actual_err; + +// return success; +// } +// #endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +// { +// p[0] = (mz_uint8)v; +// p[1] = (mz_uint8)(v >> 8); +// } +// static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +// { +// p[0] = (mz_uint8)v; +// p[1] = (mz_uint8)(v >> 8); +// p[2] = (mz_uint8)(v >> 16); +// p[3] = (mz_uint8)(v >> 24); +// } +// static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +// { +// mz_write_le32(p, (mz_uint32)v); +// mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +// } + +// #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +// #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +// #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +// static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +// { +// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +// mz_zip_internal_state *pState = pZip->m_pState; +// mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + +// if (!n) +// return 0; + +// /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ +// if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); +// return 0; +// } + +// if (new_size > pState->m_mem_capacity) +// { +// void *pNew_block; +// size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + +// while (new_capacity < new_size) +// new_capacity *= 2; + +// if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) +// { +// mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// return 0; +// } + +// pState->m_pMem = pNew_block; +// pState->m_mem_capacity = new_capacity; +// } +// memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); +// pState->m_mem_size = (size_t)new_size; +// return n; +// } + +// static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +// { +// mz_zip_internal_state *pState; +// mz_bool status = MZ_TRUE; + +// if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) +// { +// if (set_last_error) +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// return MZ_FALSE; +// } + +// pState = pZip->m_pState; +// pZip->m_pState = NULL; +// mz_zip_array_clear(pZip, &pState->m_central_dir); +// mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); +// mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +// #ifndef MINIZ_NO_STDIO +// if (pState->m_pFile) +// { +// if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) +// { +// if (MZ_FCLOSE(pState->m_pFile) == EOF) +// { +// if (set_last_error) +// mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +// status = MZ_FALSE; +// } +// } + +// pState->m_pFile = NULL; +// } +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); +// pState->m_pMem = NULL; +// } + +// pZip->m_pFree(pZip->m_pAlloc_opaque, pState); +// pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; +// return status; +// } + +// mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +// { +// mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + +// if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +// { +// if (!pZip->m_pRead) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// } + +// if (pZip->m_file_offset_alignment) +// { +// /* Ensure user specified file offset alignment is a power of 2. */ +// if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// } + +// if (!pZip->m_pAlloc) +// pZip->m_pAlloc = miniz_def_alloc_func; +// if (!pZip->m_pFree) +// pZip->m_pFree = miniz_def_free_func; +// if (!pZip->m_pRealloc) +// pZip->m_pRealloc = miniz_def_realloc_func; + +// pZip->m_archive_size = existing_size; +// pZip->m_central_directory_file_ofs = 0; +// pZip->m_total_files = 0; + +// if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + +// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); +// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); +// MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + +// pZip->m_pState->m_zip64 = zip64; +// pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + +// pZip->m_zip_type = MZ_ZIP_TYPE_USER; +// pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +// { +// return mz_zip_writer_init_v2(pZip, existing_size, 0); +// } + +// mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +// { +// pZip->m_pWrite = mz_zip_heap_write_func; +// pZip->m_pNeeds_keepalive = NULL; + +// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +// pZip->m_pRead = mz_zip_mem_read_func; + +// pZip->m_pIO_opaque = pZip; + +// if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) +// return MZ_FALSE; + +// pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + +// if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) +// { +// if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) +// { +// mz_zip_writer_end_internal(pZip, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } +// pZip->m_pState->m_mem_capacity = initial_allocation_size; +// } + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +// { +// return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +// } + +// #ifndef MINIZ_NO_STDIO +// static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +// { +// mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; +// mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + +// file_ofs += pZip->m_pState->m_file_archive_start_ofs; + +// if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); +// return 0; +// } + +// return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +// } + +// mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +// { +// return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +// } + +// mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +// { +// MZ_FILE *pFile; + +// pZip->m_pWrite = mz_zip_file_write_func; +// pZip->m_pNeeds_keepalive = NULL; + +// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +// pZip->m_pRead = mz_zip_file_read_func; + +// pZip->m_pIO_opaque = pZip; + +// if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) +// return MZ_FALSE; + +// if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) +// { +// mz_zip_writer_end(pZip); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); +// } + +// pZip->m_pState->m_pFile = pFile; +// pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + +// if (size_to_reserve_at_beginning) +// { +// mz_uint64 cur_ofs = 0; +// char buf[4096]; + +// MZ_CLEAR_OBJ(buf); + +// do +// { +// size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) +// { +// mz_zip_writer_end(pZip); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } +// cur_ofs += n; +// size_to_reserve_at_beginning -= n; +// } while (size_to_reserve_at_beginning); +// } + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +// { +// pZip->m_pWrite = mz_zip_file_write_func; +// pZip->m_pNeeds_keepalive = NULL; + +// if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) +// pZip->m_pRead = mz_zip_file_read_func; + +// pZip->m_pIO_opaque = pZip; + +// if (!mz_zip_writer_init_v2(pZip, 0, flags)) +// return MZ_FALSE; + +// pZip->m_pState->m_pFile = pFile; +// pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); +// pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + +// return MZ_TRUE; +// } +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +// { +// mz_zip_internal_state *pState; + +// if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) +// { +// /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ +// if (!pZip->m_pState->m_zip64) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// } + +// /* No sense in trying to write to an archive that's already at the support max size */ +// if (pZip->m_pState->m_zip64) +// { +// if (pZip->m_total_files == MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +// } +// else +// { +// if (pZip->m_total_files == MZ_UINT16_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + +// if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); +// } + +// pState = pZip->m_pState; + +// if (pState->m_pFile) +// { +// #ifdef MINIZ_NO_STDIO +// (void)pFilename; +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// #else +// if (pZip->m_pIO_opaque != pZip) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) +// { +// if (!pFilename) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ +// if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) +// { +// /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ +// mz_zip_reader_end_internal(pZip, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); +// } +// } + +// pZip->m_pWrite = mz_zip_file_write_func; +// pZip->m_pNeeds_keepalive = NULL; +// #endif /* #ifdef MINIZ_NO_STDIO */ +// } +// else if (pState->m_pMem) +// { +// /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ +// if (pZip->m_pIO_opaque != pZip) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pState->m_mem_capacity = pState->m_mem_size; +// pZip->m_pWrite = mz_zip_heap_write_func; +// pZip->m_pNeeds_keepalive = NULL; +// } +// /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ +// else if (!pZip->m_pWrite) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// /* Start writing new files at the archive's current central directory location. */ +// /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ +// pZip->m_archive_size = pZip->m_central_directory_file_ofs; +// pZip->m_central_directory_file_ofs = 0; + +// /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ +// /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ +// /* TODO: We could easily maintain the sorted central directory offsets. */ +// mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + +// pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +// { +// return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +// } + +/* TODO: pArchive_name is a terrible name here! */ +// mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +// { +// return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +// } + +// typedef struct +// { +// mz_zip_archive *m_pZip; +// mz_uint64 m_cur_archive_file_ofs; +// mz_uint64 m_comp_size; +// } mz_zip_writer_add_state; + +// static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +// { +// mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; +// if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) +// return MZ_FALSE; + +// pState->m_cur_archive_file_ofs += len; +// pState->m_comp_size += len; +// return MZ_TRUE; +// } + +// #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +// #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +// static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +// { +// mz_uint8 *pDst = pBuf; +// mz_uint32 field_size = 0; + +// MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); +// MZ_WRITE_LE16(pDst + 2, 0); +// pDst += sizeof(mz_uint16) * 2; + +// if (pUncomp_size) +// { +// MZ_WRITE_LE64(pDst, *pUncomp_size); +// pDst += sizeof(mz_uint64); +// field_size += sizeof(mz_uint64); +// } + +// if (pComp_size) +// { +// MZ_WRITE_LE64(pDst, *pComp_size); +// pDst += sizeof(mz_uint64); +// field_size += sizeof(mz_uint64); +// } + +// if (pLocal_header_ofs) +// { +// MZ_WRITE_LE64(pDst, *pLocal_header_ofs); +// pDst += sizeof(mz_uint64); +// field_size += sizeof(mz_uint64); +// } + +// MZ_WRITE_LE16(pBuf + 2, field_size); + +// return (mz_uint32)(pDst - pBuf); +// } + +// static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +// { +// (void)pZip; +// memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); +// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); +// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); +// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); +// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); +// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); +// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); +// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); +// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); +// MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); +// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); +// MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); +// return MZ_TRUE; +// } + +// static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, +// mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, +// mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, +// mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, +// mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +// { +// (void)pZip; +// memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); +// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); +// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); +// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); +// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); +// MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); +// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); +// MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); +// return MZ_TRUE; +// } + +// static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, +// const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, +// mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, +// mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, +// mz_uint64 local_header_ofs, mz_uint32 ext_attributes, +// const char *user_extra_data, mz_uint user_extra_data_len) +// { +// mz_zip_internal_state *pState = pZip->m_pState; +// mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; +// size_t orig_central_dir_size = pState->m_central_dir.m_size; +// mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + +// if (!pZip->m_pState->m_zip64) +// { +// if (local_header_ofs > 0xFFFFFFFF) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); +// } + +// /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ +// if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +// if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +// if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || +// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || +// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || +// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || +// (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || +// (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) +// { +// /* Try to resize the central directory array back into its original state. */ +// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// return MZ_TRUE; +// } + +// static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +// { +// /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ +// if (*pArchive_name == '/') +// return MZ_FALSE; + +// while (*pArchive_name) +// { +// if ((*pArchive_name == '\\') || (*pArchive_name == ':')) +// return MZ_FALSE; + +// pArchive_name++; +// } + +// return MZ_TRUE; +// } + +// static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +// { +// mz_uint32 n; +// if (!pZip->m_file_offset_alignment) +// return 0; +// n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); +// return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +// } + +// static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +// { +// char buf[4096]; +// memset(buf, 0, MZ_MIN(sizeof(buf), n)); +// while (n) +// { +// mz_uint32 s = MZ_MIN(sizeof(buf), n); +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_file_ofs += s; +// n -= s; +// } +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +// mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +// { +// return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +// } + +// mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, +// mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, +// const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +// { +// if(!pZip) +// { +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// } + +// mz_uint16 method = 0, dos_time = 0, dos_date = 0; +// mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; +// mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; +// size_t archive_name_size; +// mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; +// tdefl_compressor *pComp = NULL; +// mz_bool store_data_uncompressed; +// mz_zip_internal_state *pState; +// mz_uint8 *pExtra_data = NULL; +// mz_uint32 extra_size = 0; +// mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; +// mz_uint16 bit_flags = 0; + +// if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) +// bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + +// if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) +// bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + +// if ((int)level_and_flags < 0) +// level_and_flags = MZ_DEFAULT_LEVEL; +// level = level_and_flags & 0xF; +// store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + +// if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pState = pZip->m_pState; + +// if (pState->m_zip64) +// { +// if (pZip->m_total_files == MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +// } +// else +// { +// if (pZip->m_total_files == MZ_UINT16_MAX) +// { +// pState->m_zip64 = MZ_TRUE; +// /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ +// } +// if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) +// { +// pState->m_zip64 = MZ_TRUE; +// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +// } +// } + +// if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (!mz_zip_writer_validate_archive_name(pArchive_name)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +// #ifndef MINIZ_NO_TIME +// if (last_modified != NULL) +// { +// mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); +// } +// else +// { +// MZ_TIME_T cur_time; +// time(&cur_time); +// mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); +// } +// #endif /* #ifndef MINIZ_NO_TIME */ + +// archive_name_size = strlen(pArchive_name); +// if (archive_name_size > MZ_UINT16_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +// num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + +// /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ +// if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +// if (!pState->m_zip64) +// { +// /* Bail early if the archive would obviously become too large */ +// if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size +// + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + +// pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len +// + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) +// { +// pState->m_zip64 = MZ_TRUE; +// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +// } +// } + +// if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) +// { +// /* Set DOS Subdirectory attribute bit. */ +// ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + +// /* Subdirectories cannot contain data. */ +// if ((buf_size) || (uncomp_size)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// } + +// /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ +// if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// if ((!store_data_uncompressed) && (buf_size)) +// { +// if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +// return MZ_FALSE; +// } + +// local_dir_header_ofs += num_alignment_padding_bytes; +// if (pZip->m_file_offset_alignment) +// { +// MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); +// } +// cur_archive_file_ofs += num_alignment_padding_bytes; + +// MZ_CLEAR_OBJ(local_dir_header); + +// if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +// { +// method = MZ_DEFLATED; +// } + +// if (pState->m_zip64) +// { +// if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) +// { +// pExtra_data = extra_data; +// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +// } + +// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += sizeof(local_dir_header); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } +// cur_archive_file_ofs += archive_name_size; + +// if (pExtra_data != NULL) +// { +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += extra_size; +// } +// } +// else +// { +// if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); +// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += sizeof(local_dir_header); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } +// cur_archive_file_ofs += archive_name_size; +// } + +// if (user_extra_data_len > 0) +// { +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += user_extra_data_len; +// } + +// if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) +// { +// uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); +// uncomp_size = buf_size; +// if (uncomp_size <= 3) +// { +// level = 0; +// store_data_uncompressed = MZ_TRUE; +// } +// } + +// if (store_data_uncompressed) +// { +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } + +// cur_archive_file_ofs += buf_size; +// comp_size = buf_size; +// } +// else if (buf_size) +// { +// mz_zip_writer_add_state state; + +// state.m_pZip = pZip; +// state.m_cur_archive_file_ofs = cur_archive_file_ofs; +// state.m_comp_size = 0; + +// if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || +// (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +// return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); +// } + +// comp_size = state.m_comp_size; +// cur_archive_file_ofs = state.m_cur_archive_file_ofs; +// } + +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +// pComp = NULL; + +// if (uncomp_size) +// { +// mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; +// mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + +// MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + +// MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); +// MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); +// if (pExtra_data == NULL) +// { +// if (comp_size > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +// MZ_WRITE_LE32(local_dir_footer + 8, comp_size); +// MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); +// } +// else +// { +// MZ_WRITE_LE64(local_dir_footer + 8, comp_size); +// MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); +// local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; +// } + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) +// return MZ_FALSE; + +// cur_archive_file_ofs += local_dir_footer_size; +// } + +// if (pExtra_data != NULL) +// { +// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +// } + +// if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, +// comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, +// user_extra_data_central, user_extra_data_central_len)) +// return MZ_FALSE; + +// pZip->m_total_files++; +// pZip->m_archive_size = cur_archive_file_ofs; + +// return MZ_TRUE; +// } + +// #ifndef MINIZ_NO_STDIO +// mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +// const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +// { +// if(!pZip) +// { +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// } + +// mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; +// mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; +// mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; +// mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; +// size_t archive_name_size; +// mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; +// mz_uint8 *pExtra_data = NULL; +// mz_uint32 extra_size = 0; +// mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; +// mz_zip_internal_state *pState; + +// if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) +// gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + +// if ((int)level_and_flags < 0) +// level_and_flags = MZ_DEFAULT_LEVEL; +// level = level_and_flags & 0xF; + +// /* Sanity checks */ +// if ((!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pState = pZip->m_pState; + +// if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) +// { +// /* Source file is too large for non-zip64 */ +// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +// pState->m_zip64 = MZ_TRUE; +// } + +// /* We could support this, but why? */ +// if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (!mz_zip_writer_validate_archive_name(pArchive_name)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +// if (pState->m_zip64) +// { +// if (pZip->m_total_files == MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +// } +// else +// { +// if (pZip->m_total_files == MZ_UINT16_MAX) +// { +// pState->m_zip64 = MZ_TRUE; +// /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ +// } +// } + +// archive_name_size = strlen(pArchive_name); +// if (archive_name_size > MZ_UINT16_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +// num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + +// /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ +// if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +// if (!pState->m_zip64) +// { +// /* Bail early if the archive would obviously become too large */ +// if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +// + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 +// + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) +// { +// pState->m_zip64 = MZ_TRUE; +// /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ +// } +// } + +// #ifndef MINIZ_NO_TIME +// if (pFile_time) +// { +// mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); +// } +// #endif + +// if (uncomp_size <= 3) +// level = 0; + +// if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) +// { +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } + +// cur_archive_file_ofs += num_alignment_padding_bytes; +// local_dir_header_ofs = cur_archive_file_ofs; + +// if (pZip->m_file_offset_alignment) +// { +// MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); +// } + +// if (uncomp_size && level) +// { +// method = MZ_DEFLATED; +// } + +// MZ_CLEAR_OBJ(local_dir_header); +// if (pState->m_zip64) +// { +// if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) +// { +// pExtra_data = extra_data; +// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +// } + +// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += sizeof(local_dir_header); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +// { +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } + +// cur_archive_file_ofs += archive_name_size; + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += extra_size; +// } +// else +// { +// if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); +// if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += sizeof(local_dir_header); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) +// { +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } + +// cur_archive_file_ofs += archive_name_size; +// } + +// if (user_extra_data_len > 0) +// { +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_archive_file_ofs += user_extra_data_len; +// } + +// if (uncomp_size) +// { +// mz_uint64 uncomp_remaining = uncomp_size; +// void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); +// if (!pRead_buf) +// { +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if (!level) +// { +// while (uncomp_remaining) +// { +// mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); +// if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// } +// uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); +// uncomp_remaining -= n; +// cur_archive_file_ofs += n; +// } +// comp_size = uncomp_size; +// } +// else +// { +// mz_bool result = MZ_FALSE; +// mz_zip_writer_add_state state; +// tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); +// if (!pComp) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// state.m_pZip = pZip; +// state.m_cur_archive_file_ofs = cur_archive_file_ofs; +// state.m_comp_size = 0; + +// if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); +// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +// return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); +// } + +// for (;;) +// { +// size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); +// tdefl_status status; +// tdefl_flush flush = TDEFL_NO_FLUSH; + +// if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) +// { +// mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// break; +// } + +// uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); +// uncomp_remaining -= in_buf_size; + +// if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) +// flush = TDEFL_FULL_FLUSH; + +// status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); +// if (status == TDEFL_STATUS_DONE) +// { +// result = MZ_TRUE; +// break; +// } +// else if (status != TDEFL_STATUS_OKAY) +// { +// mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); +// break; +// } +// } + +// pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + +// if (!result) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +// return MZ_FALSE; +// } + +// comp_size = state.m_comp_size; +// cur_archive_file_ofs = state.m_cur_archive_file_ofs; +// } + +// pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); +// } + +// { +// mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; +// mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + +// MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); +// MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); +// if (pExtra_data == NULL) +// { +// if (comp_size > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +// MZ_WRITE_LE32(local_dir_footer + 8, comp_size); +// MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); +// } +// else +// { +// MZ_WRITE_LE64(local_dir_footer + 8, comp_size); +// MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); +// local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; +// } + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) +// return MZ_FALSE; + +// cur_archive_file_ofs += local_dir_footer_size; +// } + +// if (pExtra_data != NULL) +// { +// extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, +// (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); +// } + +// if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, +// uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, +// user_extra_data_central, user_extra_data_central_len)) +// return MZ_FALSE; + +// pZip->m_total_files++; +// pZip->m_archive_size = cur_archive_file_ofs; + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +// { +// MZ_FILE *pSrc_file = NULL; +// mz_uint64 uncomp_size = 0; +// MZ_TIME_T file_modified_time; +// MZ_TIME_T *pFile_time = NULL; +// mz_bool status; + +// memset(&file_modified_time, 0, sizeof(file_modified_time)); + +// #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) +// pFile_time = &file_modified_time; +// if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +// #endif + +// pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); +// if (!pSrc_file) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + +// MZ_FSEEK64(pSrc_file, 0, SEEK_END); +// uncomp_size = MZ_FTELL64(pSrc_file); +// MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + +// status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + +// MZ_FCLOSE(pSrc_file); + +// return status; +// } +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +// { +// /* + 64 should be enough for any new zip64 data */ +// if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + +// if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) +// { +// mz_uint8 new_ext_block[64]; +// mz_uint8 *pDst = new_ext_block; +// mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); +// mz_write_le16(pDst + sizeof(mz_uint16), 0); +// pDst += sizeof(mz_uint16) * 2; + +// if (pUncomp_size) +// { +// mz_write_le64(pDst, *pUncomp_size); +// pDst += sizeof(mz_uint64); +// } + +// if (pComp_size) +// { +// mz_write_le64(pDst, *pComp_size); +// pDst += sizeof(mz_uint64); +// } + +// if (pLocal_header_ofs) +// { +// mz_write_le64(pDst, *pLocal_header_ofs); +// pDst += sizeof(mz_uint64); +// } + +// if (pDisk_start) +// { +// mz_write_le32(pDst, *pDisk_start); +// pDst += sizeof(mz_uint32); +// } + +// mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + +// if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if ((pExt) && (ext_len)) +// { +// mz_uint32 extra_size_remaining = ext_len; +// const mz_uint8 *pExtra_data = pExt; + +// do +// { +// mz_uint32 field_id, field_data_size, field_total_size; + +// if (extra_size_remaining < (sizeof(mz_uint16) * 2)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// field_id = MZ_READ_LE16(pExtra_data); +// field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); +// field_total_size = field_data_size + sizeof(mz_uint16) * 2; + +// if (field_total_size > extra_size_remaining) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) +// { +// if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// pExtra_data += field_total_size; +// extra_size_remaining -= field_total_size; +// } while (extra_size_remaining); +// } + +// return MZ_TRUE; +// } + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +// mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +// { +// mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; +// mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; +// mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; +// mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; +// mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; +// mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; +// size_t orig_central_dir_size; +// mz_zip_internal_state *pState; +// void *pBuf; +// const mz_uint8 *pSrc_central_header; +// mz_zip_archive_file_stat src_file_stat; +// mz_uint32 src_filename_len, src_comment_len, src_ext_len; +// mz_uint32 local_header_filename_size, local_header_extra_len; +// mz_uint64 local_header_comp_size, local_header_uncomp_size; +// mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + +// /* Sanity checks */ +// if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pState = pZip->m_pState; + +// /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ +// if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// /* Get pointer to the source central dir header and crack it */ +// if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); +// src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); +// src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + +// /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ +// if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + +// num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + +// if (!pState->m_zip64) +// { +// if (pZip->m_total_files == MZ_UINT16_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +// } +// else +// { +// /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ +// if (pZip->m_total_files == MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +// } + +// if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) +// return MZ_FALSE; + +// cur_src_file_ofs = src_file_stat.m_local_header_ofs; +// cur_dst_file_ofs = pZip->m_archive_size; + +// /* Read the source archive's local dir header */ +// if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +// if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + +// cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + +// /* Compute the total size we need to copy (filename+extra data+compressed data) */ +// local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); +// local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); +// local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); +// local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); +// src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + +// /* Try to find a zip64 extended information field */ +// if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) +// { +// mz_zip_array file_data_array; +// const mz_uint8 *pExtra_data; +// mz_uint32 extra_size_remaining = local_header_extra_len; + +// mz_zip_array_init(&file_data_array, 1); +// if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) +// { +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) +// { +// mz_zip_array_clear(pZip, &file_data_array); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// } + +// pExtra_data = (const mz_uint8 *)file_data_array.m_p; + +// do +// { +// mz_uint32 field_id, field_data_size, field_total_size; + +// if (extra_size_remaining < (sizeof(mz_uint16) * 2)) +// { +// mz_zip_array_clear(pZip, &file_data_array); +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// } + +// field_id = MZ_READ_LE16(pExtra_data); +// field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); +// field_total_size = field_data_size + sizeof(mz_uint16) * 2; + +// if (field_total_size > extra_size_remaining) +// { +// mz_zip_array_clear(pZip, &file_data_array); +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// } + +// if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) +// { +// // const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + +// if (field_data_size < sizeof(mz_uint64) * 2) +// { +// mz_zip_array_clear(pZip, &file_data_array); +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); +// } + +// // local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); +// // local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + +// found_zip64_ext_data_in_ldir = MZ_TRUE; +// break; +// } + +// pExtra_data += field_total_size; +// extra_size_remaining -= field_total_size; +// } while (extra_size_remaining); + +// mz_zip_array_clear(pZip, &file_data_array); +// } + +// if (!pState->m_zip64) +// { +// /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ +// /* We also check when the archive is finalized so this doesn't need to be perfect. */ +// mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + +// pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + +// if (approx_new_archive_size >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); +// } + +// /* Write dest archive padding */ +// if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) +// return MZ_FALSE; + +// cur_dst_file_ofs += num_alignment_padding_bytes; + +// local_dir_header_ofs = cur_dst_file_ofs; +// if (pZip->m_file_offset_alignment) +// { +// MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); +// } + +// /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + +// /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ +// if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// while (src_archive_bytes_remaining) +// { +// n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); +// if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// } +// cur_src_file_ofs += n; + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } +// cur_dst_file_ofs += n; + +// src_archive_bytes_remaining -= n; +// } + +// /* Now deal with the optional data descriptor */ +// bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); +// if (bit_flags & 8) +// { +// /* Copy data descriptor */ +// if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) +// { +// /* src is zip64, dest must be zip64 */ + +// /* name uint32_t's */ +// /* id 1 (optional in zip64?) */ +// /* crc 1 */ +// /* comp_size 2 */ +// /* uncomp_size 2 */ +// if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// } + +// n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); +// } +// else +// { +// /* src is NOT zip64 */ +// mz_bool has_id; + +// if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); +// } + +// has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + +// if (pZip->m_pState->m_zip64) +// { +// /* dest is zip64, so upgrade the data descriptor */ +// const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); +// const mz_uint32 src_crc32 = pSrc_descriptor[0]; +// const mz_uint64 src_comp_size = pSrc_descriptor[1]; +// const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; + +// mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); +// mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); +// mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); +// mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + +// n = sizeof(mz_uint32) * 6; +// } +// else +// { +// /* dest is NOT zip64, just copy it as-is */ +// n = sizeof(mz_uint32) * (has_id ? 4 : 3); +// } +// } + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) +// { +// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); +// } + +// // cur_src_file_ofs += n; +// cur_dst_file_ofs += n; +// } +// pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + +// /* Finally, add the new central dir header */ +// orig_central_dir_size = pState->m_central_dir.m_size; + +// memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + +// if (pState->m_zip64) +// { +// /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ +// const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; +// mz_zip_array new_ext_block; + +// mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + +// MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); +// MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); +// MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + +// if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) +// { +// mz_zip_array_clear(pZip, &new_ext_block); +// return MZ_FALSE; +// } + +// MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + +// if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) +// { +// mz_zip_array_clear(pZip, &new_ext_block); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) +// { +// mz_zip_array_clear(pZip, &new_ext_block); +// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) +// { +// mz_zip_array_clear(pZip, &new_ext_block); +// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) +// { +// mz_zip_array_clear(pZip, &new_ext_block); +// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// mz_zip_array_clear(pZip, &new_ext_block); +// } +// else +// { +// /* sanity checks */ +// if (cur_dst_file_ofs > MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +// if (local_dir_header_ofs >= MZ_UINT32_MAX) +// return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + +// MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + +// if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + +// if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) +// { +// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } +// } + +// /* This shouldn't trigger unless we screwed up during the initial sanity checks */ +// if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) +// { +// /* TODO: Support central dirs >= 32-bits in size */ +// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); +// } + +// n = (mz_uint32)orig_central_dir_size; +// if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) +// { +// mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); +// return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); +// } + +// pZip->m_total_files++; +// pZip->m_archive_size = cur_dst_file_ofs; + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +// { +// mz_zip_internal_state *pState; +// mz_uint64 central_dir_ofs, central_dir_size; +// mz_uint8 hdr[256]; + +// if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// pState = pZip->m_pState; + +// if (pState->m_zip64) +// { +// if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +// } +// else +// { +// if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) +// return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); +// } + +// central_dir_ofs = 0; +// central_dir_size = 0; +// if (pZip->m_total_files) +// { +// /* Write central directory */ +// central_dir_ofs = pZip->m_archive_size; +// central_dir_size = pState->m_central_dir.m_size; +// pZip->m_central_directory_file_ofs = central_dir_ofs; +// if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// pZip->m_archive_size += central_dir_size; +// } + +// if (pState->m_zip64) +// { +// /* Write zip64 end of central directory header */ +// mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + +// MZ_CLEAR_OBJ(hdr); +// MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); +// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); +// MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ +// MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); +// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); +// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); +// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); +// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); +// if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + +// /* Write zip64 end of central directory locator */ +// MZ_CLEAR_OBJ(hdr); +// MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); +// MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); +// MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); +// if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; +// } + +// /* Write end of central directory record */ +// MZ_CLEAR_OBJ(hdr); +// MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); +// MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); +// MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); +// MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); +// MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + +// if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +// #ifndef MINIZ_NO_STDIO +// if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) +// return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + +// pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +// { +// if ((!ppBuf) || (!pSize)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// *ppBuf = NULL; +// *pSize = 0; + +// if ((!pZip) || (!pZip->m_pState)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (pZip->m_pWrite != mz_zip_heap_write_func) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// if (!mz_zip_writer_finalize_archive(pZip)) +// return MZ_FALSE; + +// *ppBuf = pZip->m_pState->m_pMem; +// *pSize = pZip->m_pState->m_mem_size; +// pZip->m_pState->m_pMem = NULL; +// pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + +// return MZ_TRUE; +// } + +// mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +// { +// return mz_zip_writer_end_internal(pZip, MZ_TRUE); +// } + +// #ifndef MINIZ_NO_STDIO +// mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +// { +// return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +// } + +// mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +// { +// mz_bool status, created_new_archive = MZ_FALSE; +// mz_zip_archive zip_archive; +// struct MZ_FILE_STAT_STRUCT file_stat; +// mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + +// mz_zip_zero_struct(&zip_archive); +// if ((int)level_and_flags < 0) +// level_and_flags = MZ_DEFAULT_LEVEL; + +// if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) +// { +// if (pErr) +// *pErr = MZ_ZIP_INVALID_PARAMETER; +// return MZ_FALSE; +// } + +// if (!mz_zip_writer_validate_archive_name(pArchive_name)) +// { +// if (pErr) +// *pErr = MZ_ZIP_INVALID_FILENAME; +// return MZ_FALSE; +// } + +// /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ +// /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ +// if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) +// { +// /* Create a new archive. */ +// if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) +// { +// if (pErr) +// *pErr = zip_archive.m_last_error; +// return MZ_FALSE; +// } + +// created_new_archive = MZ_TRUE; +// } +// else +// { +// /* Append to an existing archive. */ +// if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) +// { +// if (pErr) +// *pErr = zip_archive.m_last_error; +// return MZ_FALSE; +// } + +// if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) +// { +// if (pErr) +// *pErr = zip_archive.m_last_error; + +// mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + +// return MZ_FALSE; +// } +// } + +// status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); +// actual_err = zip_archive.m_last_error; + +// /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ +// if (!mz_zip_writer_finalize_archive(&zip_archive)) +// { +// if (!actual_err) +// actual_err = zip_archive.m_last_error; + +// status = MZ_FALSE; +// } + +// if (!mz_zip_writer_end_internal(&zip_archive, status)) +// { +// if (!actual_err) +// actual_err = zip_archive.m_last_error; + +// status = MZ_FALSE; +// } + +// if ((!status) && (created_new_archive)) +// { +// /* It's a new archive and something went wrong, so just delete it. */ +// int ignoredStatus = MZ_DELETE_FILE(pZip_filename); +// (void)ignoredStatus; +// } + +// if (pErr) +// *pErr = actual_err; + +// return status; +// } + +// void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +// { +// mz_uint32 file_index; +// mz_zip_archive zip_archive; +// void *p = NULL; + +// if (pSize) +// *pSize = 0; + +// if ((!pZip_filename) || (!pArchive_name)) +// { +// if (pErr) +// *pErr = MZ_ZIP_INVALID_PARAMETER; + +// return NULL; +// } + +// mz_zip_zero_struct(&zip_archive); +// if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) +// { +// if (pErr) +// *pErr = zip_archive.m_last_error; + +// return NULL; +// } + +// if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) +// { +// p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); +// } + +// mz_zip_reader_end_internal(&zip_archive, p != NULL); + +// if (pErr) +// *pErr = zip_archive.m_last_error; + +// return p; +// } + +// void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +// { +// return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +// } + +// #endif /* #ifndef MINIZ_NO_STDIO */ + +// #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +// mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +// { +// return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +// } + +// mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +// { +// return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +// } + +// mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +// { +// mz_zip_error prev_err; + +// if (!pZip) +// return MZ_ZIP_INVALID_PARAMETER; + +// prev_err = pZip->m_last_error; + +// pZip->m_last_error = err_num; +// return prev_err; +// } + +// mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +// { +// if (!pZip) +// return MZ_ZIP_INVALID_PARAMETER; + +// return pZip->m_last_error; +// } + +// mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +// { +// return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +// } + +// mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +// { +// mz_zip_error prev_err; + +// if (!pZip) +// return MZ_ZIP_INVALID_PARAMETER; + +// prev_err = pZip->m_last_error; + +// pZip->m_last_error = MZ_ZIP_NO_ERROR; +// return prev_err; +// } + +// const char *mz_zip_get_error_string(mz_zip_error mz_err) +// { +// switch (mz_err) +// { +// case MZ_ZIP_NO_ERROR: +// return "no error"; +// case MZ_ZIP_UNDEFINED_ERROR: +// return "undefined error"; +// case MZ_ZIP_TOO_MANY_FILES: +// return "too many files"; +// case MZ_ZIP_FILE_TOO_LARGE: +// return "file too large"; +// case MZ_ZIP_UNSUPPORTED_METHOD: +// return "unsupported method"; +// case MZ_ZIP_UNSUPPORTED_ENCRYPTION: +// return "unsupported encryption"; +// case MZ_ZIP_UNSUPPORTED_FEATURE: +// return "unsupported feature"; +// case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: +// return "failed finding central directory"; +// case MZ_ZIP_NOT_AN_ARCHIVE: +// return "not a ZIP archive"; +// case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: +// return "invalid header or archive is corrupted"; +// case MZ_ZIP_UNSUPPORTED_MULTIDISK: +// return "unsupported multidisk archive"; +// case MZ_ZIP_DECOMPRESSION_FAILED: +// return "decompression failed or archive is corrupted"; +// case MZ_ZIP_COMPRESSION_FAILED: +// return "compression failed"; +// case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: +// return "unexpected decompressed size"; +// case MZ_ZIP_CRC_CHECK_FAILED: +// return "CRC-32 check failed"; +// case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: +// return "unsupported central directory size"; +// case MZ_ZIP_ALLOC_FAILED: +// return "allocation failed"; +// case MZ_ZIP_FILE_OPEN_FAILED: +// return "file open failed"; +// case MZ_ZIP_FILE_CREATE_FAILED: +// return "file create failed"; +// case MZ_ZIP_FILE_WRITE_FAILED: +// return "file write failed"; +// case MZ_ZIP_FILE_READ_FAILED: +// return "file read failed"; +// case MZ_ZIP_FILE_CLOSE_FAILED: +// return "file close failed"; +// case MZ_ZIP_FILE_SEEK_FAILED: +// return "file seek failed"; +// case MZ_ZIP_FILE_STAT_FAILED: +// return "file stat failed"; +// case MZ_ZIP_INVALID_PARAMETER: +// return "invalid parameter"; +// case MZ_ZIP_INVALID_FILENAME: +// return "invalid filename"; +// case MZ_ZIP_BUF_TOO_SMALL: +// return "buffer too small"; +// case MZ_ZIP_INTERNAL_ERROR: +// return "internal error"; +// case MZ_ZIP_FILE_NOT_FOUND: +// return "file not found"; +// case MZ_ZIP_ARCHIVE_TOO_LARGE: +// return "archive is too large"; +// case MZ_ZIP_VALIDATION_FAILED: +// return "validation failed"; +// case MZ_ZIP_WRITE_CALLBACK_FAILED: +// return "write calledback failed"; +// default: +// break; +// } + +// return "unknown error"; +// } + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +// mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +// { +// if ((!pZip) || (!pZip->m_pState)) +// return MZ_FALSE; + +// return pZip->m_pState->m_zip64; +// } + +// size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +// { +// if ((!pZip) || (!pZip->m_pState)) +// return 0; + +// return pZip->m_pState->m_central_dir.m_size; +// } + +// mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +// { +// return pZip ? pZip->m_total_files : 0; +// } + +// mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +// { +// if (!pZip) +// return 0; +// return pZip->m_archive_size; +// } + +// mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +// { +// if ((!pZip) || (!pZip->m_pState)) +// return 0; +// return pZip->m_pState->m_file_archive_start_ofs; +// } + +// MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +// { +// if ((!pZip) || (!pZip->m_pState)) +// return 0; +// return pZip->m_pState->m_pFile; +// } + +// size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +// { +// if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) +// return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + +// return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +// } + +// mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +// { +// mz_uint n; +// const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); +// if (!p) +// { +// if (filename_buf_size) +// pFilename[0] = '\0'; +// mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +// return 0; +// } +// n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); +// if (filename_buf_size) +// { +// n = MZ_MIN(n, filename_buf_size - 1); +// memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); +// pFilename[n] = '\0'; +// } +// return n + 1; +// } + +// mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +// { +// return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +// } + +// mz_bool mz_zip_end(mz_zip_archive *pZip) +// { +// if (!pZip) +// return MZ_FALSE; + +// if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) +// return mz_zip_reader_end(pZip); +// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +// else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) +// return mz_zip_writer_end(pZip); +// #endif + +// return MZ_FALSE; +// } + +// #ifdef __cplusplus +// } +// #endif + +// #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/gomspace/libutil/src/zip/miniz/miniz.h b/gomspace/libutil/src/zip/miniz/miniz.h new file mode 100644 index 00000000..e5172634 --- /dev/null +++ b/gomspace/libutil/src/zip/miniz/miniz.h @@ -0,0 +1,1329 @@ +/* miniz.c 2.0.6 beta - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +#define MINIZ_NO_TIME + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +// #if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +// #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +// #else +// #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +// #endif +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +// mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "10.0.1" +#define MZ_VERNUM 0xA010 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 0 +#define MZ_VER_REVISION 1 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +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 +}; + +/* 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; + +/* Returns the version string of miniz.c. */ +// const char *mz_version(void); + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +// int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +// mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +// mz_ulong mz_compressBound(mz_ulong source_len); + +/* Initializes a decompressor. */ +int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +// const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#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 Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +// #define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +// #define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +// #define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +// #define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +// #define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +// #define zlibVersion mz_version +// #define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif +#pragma once +#include +#include +#include +#include + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* 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 + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +// #ifdef MINIZ_NO_TIME +// typedef struct mz_dummy_time_t_tag +// { +// int m_dummy; +// } mz_dummy_time_t; +// #define MZ_TIME_T mz_dummy_time_t +// #else +// #define MZ_TIME_T time_t +// #endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#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 MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern void miniz_def_free_func(void *opaque, void *address); +// extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif +#pragma once + + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +// void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +// size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +// void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +// void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +// mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +// tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +// tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +// tdefl_compressor *tdefl_compressor_alloc(); +// void tdefl_compressor_free(tdefl_compressor *pComp); + +#ifdef __cplusplus +} +#endif +#pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +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 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +// void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +// size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +// int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ + +// tinfl_decompressor *tinfl_decompressor_alloc(); +// void tinfl_decompressor_free(tinfl_decompressor *pDecomp); + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + 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. */ +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 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#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]; +}; + +#ifdef __cplusplus +} +#endif + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +// #ifndef MINIZ_NO_ARCHIVE_APIS + +// #ifdef __cplusplus +// extern "C" { +// #endif + +// enum +// { + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ +// MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, +// MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, +// MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +// }; + +// typedef struct +// { +// /* Central directory file index. */ +// mz_uint32 m_file_index; + +// /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ +// mz_uint64 m_central_dir_ofs; + +// /* These fields are copied directly from the zip's central dir. */ +// mz_uint16 m_version_made_by; +// mz_uint16 m_version_needed; +// mz_uint16 m_bit_flag; +// mz_uint16 m_method; + +// #ifndef MINIZ_NO_TIME +// MZ_TIME_T m_time; +// #endif + +// /* CRC-32 of uncompressed data. */ +// mz_uint32 m_crc32; + +// /* File's compressed size. */ +// mz_uint64 m_comp_size; + +// /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ +// mz_uint64 m_uncomp_size; + +// /* Zip internal and external file attributes. */ +// mz_uint16 m_internal_attr; +// mz_uint32 m_external_attr; + +// /* Entry's local header file offset in bytes. */ +// mz_uint64 m_local_header_ofs; + +// /* Size of comment in bytes. */ +// mz_uint32 m_comment_size; + +// /* MZ_TRUE if the entry appears to be a directory. */ +// mz_bool m_is_directory; + +// /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ +// mz_bool m_is_encrypted; + +// /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ +// mz_bool m_is_supported; + +// /* Filename. If string ends in '/' it's a subdirectory entry. */ +// /* Guaranteed to be zero terminated, may be truncated to fit. */ +// char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + +// /* Comment field. */ +// /* Guaranteed to be zero terminated, may be truncated to fit. */ +// char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +// } mz_zip_archive_file_stat; + +// typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +// typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +// typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +// struct mz_zip_internal_state_tag; +// typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +// typedef enum { +// MZ_ZIP_MODE_INVALID = 0, +// MZ_ZIP_MODE_READING = 1, +// MZ_ZIP_MODE_WRITING = 2, +// MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +// } mz_zip_mode; + +// typedef enum { +// MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, +// MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, +// MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, +// MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + // MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + // MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + // MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ +// MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, +// MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 +// } mz_zip_flags; + +// typedef enum { +// MZ_ZIP_TYPE_INVALID = 0, +// MZ_ZIP_TYPE_USER, +// MZ_ZIP_TYPE_MEMORY, +// MZ_ZIP_TYPE_HEAP, +// MZ_ZIP_TYPE_FILE, +// MZ_ZIP_TYPE_CFILE, +// MZ_ZIP_TOTAL_TYPES +// } mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +// typedef enum { +// MZ_ZIP_NO_ERROR = 0, +// MZ_ZIP_UNDEFINED_ERROR, +// MZ_ZIP_TOO_MANY_FILES, +// MZ_ZIP_FILE_TOO_LARGE, +// MZ_ZIP_UNSUPPORTED_METHOD, +// MZ_ZIP_UNSUPPORTED_ENCRYPTION, +// MZ_ZIP_UNSUPPORTED_FEATURE, +// MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, +// MZ_ZIP_NOT_AN_ARCHIVE, +// MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, +// MZ_ZIP_UNSUPPORTED_MULTIDISK, +// MZ_ZIP_DECOMPRESSION_FAILED, +// MZ_ZIP_COMPRESSION_FAILED, +// MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, +// MZ_ZIP_CRC_CHECK_FAILED, +// MZ_ZIP_UNSUPPORTED_CDIR_SIZE, +// MZ_ZIP_ALLOC_FAILED, +// MZ_ZIP_FILE_OPEN_FAILED, +// MZ_ZIP_FILE_CREATE_FAILED, +// MZ_ZIP_FILE_WRITE_FAILED, +// MZ_ZIP_FILE_READ_FAILED, +// MZ_ZIP_FILE_CLOSE_FAILED, +// MZ_ZIP_FILE_SEEK_FAILED, +// MZ_ZIP_FILE_STAT_FAILED, +// MZ_ZIP_INVALID_PARAMETER, +// MZ_ZIP_INVALID_FILENAME, +// MZ_ZIP_BUF_TOO_SMALL, +// MZ_ZIP_INTERNAL_ERROR, +// MZ_ZIP_FILE_NOT_FOUND, +// MZ_ZIP_ARCHIVE_TOO_LARGE, +// MZ_ZIP_VALIDATION_FAILED, +// MZ_ZIP_WRITE_CALLBACK_FAILED, +// MZ_ZIP_TOTAL_ERRORS +// } mz_zip_error; + +// typedef struct +// { +// mz_uint64 m_archive_size; +// mz_uint64 m_central_directory_file_ofs; + +// /* We only support up to UINT32_MAX files in zip64 mode. */ +// mz_uint32 m_total_files; +// mz_zip_mode m_zip_mode; +// mz_zip_type m_zip_type; +// mz_zip_error m_last_error; + +// mz_uint64 m_file_offset_alignment; + +// mz_alloc_func m_pAlloc; +// mz_free_func m_pFree; +// mz_realloc_func m_pRealloc; +// void *m_pAlloc_opaque; + +// mz_file_read_func m_pRead; +// mz_file_write_func m_pWrite; +// mz_file_needs_keepalive m_pNeeds_keepalive; +// void *m_pIO_opaque; + +// mz_zip_internal_state *m_pState; + +// } mz_zip_archive; + +// typedef struct +// { +// mz_zip_archive *pZip; +// mz_uint flags; + +// int status; +// #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS +// mz_uint file_crc32; +// #endif +// mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; +// mz_zip_archive_file_stat file_stat; +// void *pRead_buf; +// void *pWrite_buf; + +// size_t out_blk_remain; + +// tinfl_decompressor inflator; + +// } mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +// mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +// mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +// #ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +// mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +// mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +// mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +// #endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +// mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +// void mz_zip_zero_struct(mz_zip_archive *pZip); + +// mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +// mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +// mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +// mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +// MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +// size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +// int mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +/* Returns MZ_FALSE if the file cannot be found. */ +// mz_bool mz_zip_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +// mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +// mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +// mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +// mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +// const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +// mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +// mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +// mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +// mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +// int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +// int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +// mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +// mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +// size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +// mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +// mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +// mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +// mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +// void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +// void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +// mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +// mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +// mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +// size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +// mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +// #ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +// mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +// mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +// mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +// mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +// #endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +// mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +// mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +// mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +// mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +// mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +// #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +// mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +// mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +// mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +// mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +// #ifndef MINIZ_NO_STDIO +// mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +// mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +// mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +// #endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +// mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +// mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +// mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +// mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +// mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +// mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, +// mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, +// const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +// #ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +// mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +// mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, +// const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, +// const char *user_extra_data_central, mz_uint user_extra_data_central_len); +// #endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +// mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +// mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +// mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +// mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +// mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +// mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +// void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +// void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); + +// #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +// #ifdef __cplusplus +// } +// #endif + +// #endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/gomspace/libutil/src/zip/zip.c b/gomspace/libutil/src/zip/zip.c new file mode 100644 index 00000000..b7fd00fc --- /dev/null +++ b/gomspace/libutil/src/zip/zip.c @@ -0,0 +1,357 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include "gs/util/zip/zip.h" +#include "miniz/miniz.h" + +#include +#include +#include + +#include + +static void cleanup(FILE *pInfile, FILE *pOutfile, uint8_t *stream_inbuf, uint8_t *stream_outbuf) +{ + if(pInfile) + fclose(pInfile); + + if(pOutfile) + fclose(pOutfile); + + if(stream_inbuf) + free(stream_inbuf); + + if(stream_outbuf) + free(stream_outbuf); +} + +int gs_zip_compress_file(const char *src, const char *dest) +{ + FILE *pInfile, *pOutfile; + uint32_t infile_size; + long file_loc; + + // Open input file. + pInfile = fopen(src, "rb"); + if (!pInfile) + { + log_error("Zip compress: Failed opening input file!"); + return GS_ERROR_IO; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if((file_loc < 0) || ((unsigned long)file_loc > UINT_MAX)) + { + log_error("Zip compress: File is too large to be processed."); + fclose(pInfile); + + return GS_ERROR_IO; + } + + infile_size = (uint32_t)file_loc; + uint32_t buffer_size = infile_size; + + // Allocate input buffer memory + uint8_t *stream_inbuf = malloc(buffer_size); + if (stream_inbuf == NULL) + { + log_error("Zip compress: Failed to allocate input buffer memory"); + fclose(pInfile); + + return GS_ERROR_IO; + } + + // Allocate output buffer memory + uint8_t *stream_outbuf = malloc(buffer_size); + if (stream_outbuf == NULL) + { + log_error("Zip compress: Failed to allocate output buffer memory"); + cleanup(pInfile, NULL, stream_inbuf, NULL); + + return GS_ERROR_IO; + } + + // Open output file. + pOutfile = fopen(dest, "wb"); + if (!pOutfile) + { + log_error("Zip compress: Failed opening output file!"); + cleanup(pInfile, NULL, stream_inbuf, stream_outbuf); + + return GS_ERROR_IO; + } + + // Init the z_stream + z_stream stream; + memset(&stream, 0, sizeof(stream)); + stream.next_in = stream_inbuf; + stream.avail_in = 0; + stream.next_out = stream_outbuf; + stream.avail_out = buffer_size; + + // Compression. + uint32_t infile_remaining = infile_size; + + if(deflateInit(&stream, Z_BEST_COMPRESSION) != Z_OK) + { + log_error("Zip compress: deflateInit() failed!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_DATA; + } + + for( ; ; ) + { + int status; + if(!stream.avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint32_t n = gs_min((uint32_t)buffer_size, infile_remaining); + + if (fread(stream_inbuf, 1, n, pInfile) != n) + { + log_error("Zip compress: Failed reading from input file!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_IO; + } + + stream.next_in = stream_inbuf; + stream.avail_in = n; + + infile_remaining -= n; + } + + status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH); + + if((status == Z_STREAM_END) || (!stream.avail_out)) + { + // Output buffer is full, or compression is done, so write buffer to output file. + uint32_t n = buffer_size - stream.avail_out; + if (fwrite(stream_outbuf, 1, n, pOutfile) != n) + { + log_error("Zip compress: Failed writing to output file!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_IO; + } + + stream.next_out = stream_outbuf; + stream.avail_out = buffer_size; + } + + if(status == Z_STREAM_END) + { + break; + } + else if(status != Z_OK) + { + log_error("Zip compress: deflate() failed with status %i!", status); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_DATA; + } + } + + if(deflateEnd(&stream) != Z_OK) + { + log_error("Zip compress: deflateEnd() failed!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_DATA; + } + + + cleanup(pInfile, NULL, stream_inbuf, stream_outbuf); + if(EOF == fclose(pOutfile)) + { + log_error("Zip compress: Failed writing to output file!"); + return GS_ERROR_IO; + } + + log_debug("Total input bytes: %u\n", (mz_uint32)stream.total_in); + log_debug("Total output bytes: %u\n", (mz_uint32)stream.total_out); + log_debug("Success.\n"); + + return GS_OK; +} + +int gs_zip_decompress_file(const char *src, const char *dest) +{ + FILE *pInfile, *pOutfile; + uint32_t infile_size; + long file_loc; + + // Open input file. + pInfile = fopen(src, "rb"); + if (!pInfile) + { + log_error("Zip decompress: Failed opening input file!"); + return GS_ERROR_IO; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if((file_loc < 0) || ((unsigned long)file_loc > UINT_MAX)) + { + log_error("Zip decompress: File is too large to be processed."); + fclose(pInfile); + + return GS_ERROR_IO; + } + + infile_size = (uint32_t)file_loc; + uint32_t buffer_size = infile_size; + + // Allocate input buffer memory + uint8_t *stream_inbuf = malloc(buffer_size); + if (stream_inbuf == NULL) + { + log_error("Zip decompress: Failed to allocate input buffer memory"); + fclose(pInfile); + + return GS_ERROR_IO; + } + + // Allocate output buffer memory + uint8_t *stream_outbuf = malloc(buffer_size); + if (stream_outbuf == NULL) + { + log_error("Zip decompress: Failed to allocate output buffer memory"); + cleanup(pInfile, NULL, stream_inbuf, NULL); + + return GS_ERROR_IO; + } + + // Open output file. + pOutfile = fopen(dest, "wb"); + if (!pOutfile) + { + log_error("Zip decompress: Failed opening output file!"); + cleanup(pInfile, NULL, stream_inbuf, stream_outbuf); + + return GS_ERROR_IO; + } + + // Init the z_stream + z_stream stream; + memset(&stream, 0, sizeof(stream)); + stream.next_in = stream_inbuf; + stream.avail_in = 0; + stream.next_out = stream_outbuf; + stream.avail_out = buffer_size; + + // Decompression. + uint32_t infile_remaining = infile_size; + + if(inflateInit(&stream)) + { + log_error("Zip decompress: inflateInit() failed!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_DATA; + } + + for( ; ; ) + { + int status; + if(!stream.avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint32_t n = gs_min((uint32_t)buffer_size, infile_remaining); + + if(fread(stream_inbuf, 1, n, pInfile) != n) + { + log_error("Zip decompress: Failed reading from input file!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_IO; + } + + stream.next_in = stream_inbuf; + stream.avail_in = n; + + infile_remaining -= n; + } + + status = inflate(&stream, Z_SYNC_FLUSH); + + if((status == Z_STREAM_END) || (!stream.avail_out)) + { + // Output buffer is full, or decompression is done, so write buffer to output file. + uint32_t n = buffer_size - stream.avail_out; + if(fwrite(stream_outbuf, 1, n, pOutfile) != n) + { + log_error("Zip decompress: Failed writing to output file!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_IO; + } + stream.next_out = stream_outbuf; + stream.avail_out = buffer_size; + } + + if(status == Z_STREAM_END) + { + break; + } + else if(status != Z_OK) + { + log_error("Zip decompress: inflate() failed with status %i!", status); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_DATA; + } + } + + if(inflateEnd(&stream) != Z_OK) + { + log_error("Zip decompress: inflateEnd() failed!"); + cleanup(pInfile, pOutfile, stream_inbuf, stream_outbuf); + + return GS_ERROR_DATA; + } + + cleanup(pInfile, NULL, stream_inbuf, stream_outbuf); + if(EOF == fclose(pOutfile)) + { + log_error("Zip decompress: Failed writing to output file!"); + return GS_ERROR_IO; + } + + log_debug("Total input bytes: %u", (mz_uint32)stream.total_in); + log_debug("Total output bytes: %u", (mz_uint32)stream.total_out); + log_debug("Success.\n"); + + return GS_OK; +} + +int gs_zip_compress_data(const unsigned char *src, uint32_t src_len, unsigned char *dest, uint32_t *dest_len) +{ + mz_ulong cmp_len = src_len; + if(compress(dest, &cmp_len, src, (mz_ulong)src_len) != MZ_OK) + { + return GS_ERROR_DATA; + } + + *dest_len = cmp_len; + + return GS_OK; +} + +int gs_zip_decompress_data(const unsigned char *src, uint32_t src_len, unsigned char *dest, uint32_t dest_len, uint32_t *decomp_len) +{ + mz_ulong tmp = dest_len; + if(uncompress(dest, &tmp, src, (mz_ulong)src_len) != MZ_OK) + return GS_ERROR_DATA; + + *decomp_len = tmp; + + return GS_OK; +} diff --git a/gomspace/libutil/wscript b/gomspace/libutil/wscript new file mode 100644 index 00000000..48421e39 --- /dev/null +++ b/gomspace/libutil/wscript @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. + +import gs_gcc +import gs_doc +import gs_dist +from waflib.Build import BuildContext + +APPNAME = 'util' + + +def options(ctx): + ctx.load('gs_gcc gs_doc') + gs_gcc.gs_recurse(ctx) + + gr = ctx.add_option_group('libutil options') + gr.add_option('--console-history-len', metavar='LEN', default=10, type=int, help='Command history length, 0=none') + gr.add_option('--console-input-len', metavar='LEN', default=100, type=int, help='Command input length') + gr.add_option('--util-enable-isr-logs', action='store_true', help='Enable ISR logs') + + +def configure(ctx): + ctx.load('gs_gcc gs_doc') + + ctx.env.append_unique('FILES_LIBUTIL', ['src/*.c', + 'src/gosh/**/*.c', + 'src/log/**/*.c', + 'src/vmem/**/*.c', + 'src/watchdog/**/*.c', + 'src/drivers/**/*.c']) + + if ctx.env.GS_ARCH not in ['avr8']: + ctx.env.append_unique('FILES_LIBUTIL', ['src/zip/**/*.c']) + + if ctx.gs_is_linux(): + ctx.env.append_unique('FILES_LIBUTIL', ['src/linux/**/*.c']) + + ctx.env.GS_UTIL_CMOCKA = ctx.check_cfg(package='cmocka', args='--cflags --libs', + atleast_version='1.0.1', mandatory=False) + + # Check compiler endianness - avr32 GCC doesn't support endian defines + endianness = ctx.check_endianness() + ctx.define_cond('UTIL_LITTLE_ENDIAN', endianness == 'little') + ctx.define_cond('UTIL_BIG_ENDIAN', endianness == 'big') + + ctx.define('GS_CONSOLE_HISTORY_LEN', ctx.options.console_history_len) + ctx.define('GS_CONSOLE_INPUT_LEN', ctx.options.console_input_len) + ctx.define_cond('GS_LOG_ENABLE_ISR_LOGS', ctx.options.util_enable_isr_logs) + + ctx.gs_write_config_header('include/conf_util.h', remove=True) + + ctx.gs_add_doxygen(example=['tst'], exclude=['*/include/gs/uthash/*', + '*/include/gs/util/zip/*', + '*/include/deprecated/util/*', + '*/include/deprecated/gs/gosh/*']) + + ctx.gs_register_handler(function='command_gen_4_0', filepath='./tools/waf_command.py') + + gs_gcc.gs_recurse(ctx) + + +def build(ctx): + gs_gcc.gs_recurse(ctx) + + public_include = ctx.gs_include(name=APPNAME, + includes=['include', 'include/gs', + 'include/deprecated', 'include/deprecated/gs/gosh/'], + config_header=['include/conf_util.h']) + + ctx.gs_objects(source=ctx.path.ant_glob(ctx.env.FILES_LIBUTIL), + target=APPNAME, + includes=['src'], + use=ctx.env.USE_LIBUTIL + [public_include]) + + ctx.gs_shlib(source=ctx.path.ant_glob(ctx.env.FILES_LIBUTIL), + target=APPNAME, + includes=['src'], + gs_use_shlib=ctx.env.USE_LIBUTIL, + use=[public_include], + lib=['pthread']) + + ctx.gs_python_bindings(source=ctx.path.ant_glob('src/bindings/python/*.c'), + target=APPNAME, + gs_use_shlib=ctx.env.USE_LIBUTIL + [APPNAME], + use=[public_include], + package='libutil') + + if ctx.env.GS_UTIL_CMOCKA: + ctx.gs_stlib(source=ctx.path.ant_glob('src/test/*.c'), + name=APPNAME + '_cmocka', # overwrite default naming + target=APPNAME + '_cmocka', + includes=['include']) + + +def doc(ctx): + gs_doc.gs_library_doc(ctx, keyvalues={ + 'gs_prod_name': 'lib'+APPNAME, + 'gs_prod_desc': 'Low level APIs and utilities', + 'gs_sphinx_exclude': ['CHANGELOG.rst'], + }) + + +class Doc(BuildContext): + cmd = fun = 'doc' + + +def gs_dist(ctx): + ctx.add_default_files(source_module=True) diff --git a/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock.h b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock.h new file mode 100644 index 00000000..0a4477ba --- /dev/null +++ b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock.h @@ -0,0 +1,47 @@ +#ifndef P60DOCK_H_ +#define P60DOCK_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +#include + +#include +#include +#include +#include +#include + +/** FRAM MEMORY MAP */ +#define P60DOCK_FRAM_PARAM 0x0400 +#define P60DOCK_FRAM_CAL 0x0800 +#define P60DOCK_FRAM_HK 0x0C00 + +#define P60DOCK_FRAM_GNDWDT 0x1F00 + +/** FRAM FILENAMES */ +#define P60DOCK_FNO_PARAM 1 +#define P60DOCK_FNO_PARAM_DFL 5 +#define P60DOCK_FNO_CAL 2 +#define P60DOCK_FNO_CAL_DFL 6 + +/** PARAM INDEX MAP */ +/* Index 0 is reserved for board param */ +#define P60DOCK_PARAM 1 +#define P60DOCK_CAL 2 +#define P60DOCK_SCRATCH 3 +#define P60DOCK_HK 4 + +#define P60DOCK_BATT_PACK_OTHER 0 +#define P60DOCK_BATT_PACK_BP2 1 +#define P60DOCK_BATT_PACK_BP4 2 +#define P60DOCK_BATT_PACK_BPX 3 + +int p60dock_get_hk(param_index_t * mem, uint8_t node, uint32_t timeout); +int p60dock_gndwdt_clear(uint8_t node, uint32_t timeout); +int p60dock_output_get(uint8_t node, char * ch_name, uint8_t * mode, uint8_t * ch_no); +int p60dock_output_set(uint8_t node, char * ch_name, uint8_t mode, uint8_t * ch_no); + +void cmd_p60dock_setup(void); + +#endif /* P60DOCK_H_ */ diff --git a/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_cal.h b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_cal.h new file mode 100644 index 00000000..905bc3db --- /dev/null +++ b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_cal.h @@ -0,0 +1,35 @@ +#ifndef P60DOCK_CAL_H_ +#define P60DOCK_CAL_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +/** + * Define memory space + */ + +#define P60DOCK_CAL_GAIN_V_OUT(i) (0x00 + (4 * i)) /* 13 * float */ +#define P60DOCK_CAL_GAIN_C_OUT(i) (0x34 + (4 * i)) /* 13 * float */ +#define P60DOCK_CAL_OFFSET_C_OUT(i) (0x68 + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_CAL_VREF 0x82 +#define P60DOCK_CAL_GAIN_VBAT_V 0x84 +#define P60DOCK_CAL_GAIN_VCC_C 0x88 +#define P60DOCK_CAL_OFFSET_VCC_C 0x8C +#define P60DOCK_CAL_GAIN_AUX1 0x90 +#define P60DOCK_CAL_GAIN_AUX2 0x94 +#define P60DOCK_CAL_OFFSET_AUX1 0x98 +#define P60DOCK_CAL_OFFSET_AUX2 0x9A +#define P60DOCK_CAL_GAIN_BATT_V 0x9C +#define P60DOCK_CAL_GAIN_BATT_CHRG 0xA0 +#define P60DOCK_CAL_OFFS_BATT_CHRG 0xA4 +#define P60DOCK_CAL_GAIN_BATT_DIS 0xA8 +#define P60DOCK_CAL_OFFS_BATT_DIS 0xAC + +/** Define the memory size */ +#define P60DOCK_CAL_SIZE 0xAE + +extern const param_table_t p60dock_calibration[]; +extern const int p60dock_cal_count; + +#endif /* P60DOCK_CAL_H_ */ diff --git a/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_hk.h b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_hk.h new file mode 100644 index 00000000..63797e37 --- /dev/null +++ b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_hk.h @@ -0,0 +1,51 @@ +#ifndef P60DOCK_HK_H_ +#define P60DOCK_HK_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +/** + * Define memory space + */ +#define P60DOCK_HK_C_OUT(i) (0x00 + (2 * i)) /* 13 * int16_t */ +#define P60DOCK_HK_V_OUT(i) (0x1A + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_HK_OUT_EN(i) (0x34 + (i)) /* 13 * uint8_t */ +#define P60DOCK_HK_TEMP(i) (0x44 + (2 * i)) /* 2 * int16_t */ +#define P60DOCK_HK_BOOT_CAUSE 0x48 +#define P60DOCK_HK_BOOT_COUNTER 0x4C +#define P60DOCK_HK_UPTIME 0x50 +#define P60DOCK_HK_RESET_CAUSE 0x54 +#define P60DOCK_HK_BATT_MODE 0x56 +#define P60DOCK_HK_HEATER_ON 0x57 +#define P60DOCK_HK_CONV_5V0_EN 0x58 + +#define P60DOCK_HK_LATCHUP(i) (0x5A + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_HK_VBAT_V 0x74 +#define P60DOCK_HK_VCC_C 0x76 +#define P60DOCK_HK_BATTERY_C 0x78 +#define P60DOCK_HK_BATTERY_V 0x7A +#define P60DOCK_HK_BP_TEMP(i) (0x7C + (2 * i)) /* 2 * int16_t */ +#define P60DOCK_HK_DEVICE_TYPE(i) (0x80 + (i)) /* 8 * uint8_t */ +#define P60DOCK_HK_DEVICE_STATUS(i) (0x88 + (i)) /* 8 * uint8_t */ +#define P60DOCK_HK_DEARM_STATUS 0x90 +#define P60DOCK_HK_CNT_WDTGND 0x94 +#define P60DOCK_HK_CNT_WDTI2C 0x98 +#define P60DOCK_HK_CNT_WDTCAN 0x9C +#define P60DOCK_HK_CNT_WDTCSP(i) (0xA0 + (4 * i)) /* 2 * uint32_t */ +#define P60DOCK_HK_WDTGND_LEFT 0xA8 +#define P60DOCK_HK_WDTI2C_LEFT 0xAC +#define P60DOCK_HK_WDTCAN_LEFT 0xB0 +#define P60DOCK_HK_WDTCSP_LEFT(i) (0xB4 + (i)) /* 2 * uint8_t */ +#define P60DOCK_HK_BATT_C_CHRG 0xB6 /* int16_t */ +#define P60DOCK_HK_BATT_C_DISCHRG 0xB8 /* int16_t */ +#define P60DOCK_HK_ANT6_DEPL 0xBA /* int8_t */ +#define P60DOCK_HK_AR6_DEPL 0xBB /* int8_t */ + +/** Define the memory size */ +#define P60DOCK_HK_SIZE 0xBC + +extern const param_table_t p60dock_hk[]; +extern const int p60dock_hk_count; + +#endif /* P60DOCK_HK_H_ */ diff --git a/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_param.h b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_param.h new file mode 100644 index 00000000..05dac011 --- /dev/null +++ b/gomspace/p60-dock_client/include/gs/p60-dock/param/p60dock_param.h @@ -0,0 +1,69 @@ +#ifndef P60DOCK_PARAM_H_ +#define P60DOCK_PARAM_H_ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include + +/** + * Define memory space + */ +#define P60DOCK_NAME_SIZE 8 + +#define P60DOCK_OUT_NAME(i) (0x00 + (P60DOCK_NAME_SIZE * i)) /* 13 * 8 */ +#define P60DOCK_OUT_EN(i) (0x68 + (i)) /* 13 * uint8_t */ +#define P60DOCK_OUT_ON_CNT(i) (0x76 + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_OUT_OFF_CNT(i) (0x90 + (2 * i)) /* 13 * uint16_t */ + +#define P60DOCK_INIT_OUT_NORM(i) (0xAA + (i)) /* 13 * uint8_t */ +#define P60DOCK_INIT_OUT_SAFE(i) (0xB7 + (i)) /* 13 * uint8_t */ +#define P60DOCK_INIT_ON_DELAY(i) (0xC4 + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_INIT_OFF_DELAY(i) (0xDE + (2 * i)) /* 13 * uint16_t */ + +#define P60DOCK_CUR_LU_LIM(i) (0xF8 + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_CUR_LIM(i) (0x112 + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_CUR_EMA(i) (0x12C + (2 * i)) /* 13 * uint16_t */ +#define P60DOCK_CUR_EMA_GAIN 0x148 + +#define P60DOCK_VCC_LINK(i) (0x14C + (i)) /* 4 * uint8_t */ +#define P60DOCK_VCC_VBAT_LINK(i) (0x150 + (i)) /* 4 * uint8_t */ + +#define P60DOCK_BATTERY_PACK 0x154 + +#define P60DOCK_BATT_HWMAX 0x156 +#define P60DOCK_BATT_MAX 0x158 +#define P60DOCK_BATT_NORM 0x15A +#define P60DOCK_BATT_SAFE 0x15C +#define P60DOCK_BATT_CRIT 0x15E + +#define P60DOCK_BP_HEATERMODE 0x160 +#define P60DOCK_BP_HEATER_LOW 0x162 +#define P60DOCK_BP_HEATER_HIGH 0x164 + +#define P60DOCK_WDTI2C_RST 0x166 +#define P60DOCK_WDTCAN_RST 0x167 +#define P60DOCK_WDTI2C 0x168 +#define P60DOCK_WDTCAN 0x16C +#define P60DOCK_WDTCSP(i) (0x170 + (4 * i)) /* 2 * uint32_t */ +#define P60DOCK_WDTCSP_PING_FAIL(i) (0x178 + (i)) /* 2 * uint8_t */ +#define P60DOCK_WDTCSP_CHAN(i) (0x17A + (i)) /* 2 * uint8_t */ +#define P60DOCK_WDTCSP_ADDR(i) (0x17C + (i)) /* 2 * uint8_t */ + +#define P60DOCK_P60ACU_CHAN(i) (0x17E + (i)) /* 2 * uint8_t */ +#define P60DOCK_P60ACU_ADDR(i) (0x180 + (i)) /* 2 * uint8_t */ +#define P60DOCK_P60PDU_CHAN(i) (0x182 + (i)) /* 4 * uint8_t */ +#define P60DOCK_P60PDU_ADDR(i) (0x186 + (i)) /* 4 * uint8_t */ + +#define P60DOCK_CONV_5V0_EN 0x18A + +#define P60DOCK_ANT6_ADDR(i) (0x190 + (i)) /* 2 * uint8_t */ +#define P60DOCK_AR6_ADDR(i) (0x194 + (i)) /* 4 * uint8_t */ +#define P60DOCK_DEPL_DELAY 0x198 /* uint32_t*/ + +/** Define the memory size */ +#define P60DOCK_PARAM_SIZE 0x19C + +extern const param_table_t p60dock_config[]; +extern const int p60dock_config_count; + +#endif /* P60DOCK_PARAM_H_ */ diff --git a/gomspace/p60-dock_client/src/p60dock_client.c b/gomspace/p60-dock_client/src/p60dock_client.c new file mode 100644 index 00000000..2fa3232b --- /dev/null +++ b/gomspace/p60-dock_client/src/p60dock_client.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include + +/** + * Setup info about config parameters + */ +const param_table_t p60dock_config[] = { + {.name = "out_name", .addr = P60DOCK_OUT_NAME(0), .type = PARAM_STRING, .size = P60DOCK_NAME_SIZE, .count = 13}, + {.name = "out_en", .addr = P60DOCK_OUT_EN(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 13}, + {.name = "out_on_cnt", .addr = P60DOCK_OUT_ON_CNT(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + {.name = "out_off_cnt", .addr = P60DOCK_OUT_OFF_CNT(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + + {.name = "init_out_norm", .addr = P60DOCK_INIT_OUT_NORM(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 13}, + {.name = "init_out_safe", .addr = P60DOCK_INIT_OUT_SAFE(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 13}, + {.name = "init_on_dly", .addr = P60DOCK_INIT_ON_DELAY(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + {.name = "init_off_dly", .addr = P60DOCK_INIT_OFF_DELAY(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + + {.name = "cur_lu_lim", .addr = P60DOCK_CUR_LU_LIM(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + {.name = "cur_lim", .addr = P60DOCK_CUR_LIM(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + {.name = "cur_ema", .addr = P60DOCK_CUR_EMA(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + {.name = "cur_ema_gain", .addr = P60DOCK_CUR_EMA_GAIN, .type = PARAM_FLOAT, .size = sizeof(float)}, + + {.name = "vcc_vbat_link", .addr = P60DOCK_VCC_VBAT_LINK(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 4}, + {.name = "vcc_link", .addr = P60DOCK_VCC_LINK(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 4}, + + {.name = "batt_pack", .addr = P60DOCK_BATTERY_PACK, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + + {.name = "batt_hwmax", .addr = P60DOCK_BATT_HWMAX, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "batt_max", .addr = P60DOCK_BATT_MAX, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "batt_norm", .addr = P60DOCK_BATT_NORM, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "batt_safe", .addr = P60DOCK_BATT_SAFE, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "batt_crit", .addr = P60DOCK_BATT_CRIT, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + + {.name = "bp_heat_mode", .addr = P60DOCK_BP_HEATERMODE, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "bp_heat_low", .addr = P60DOCK_BP_HEATER_LOW, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "bp_heat_high", .addr = P60DOCK_BP_HEATER_HIGH, .type = PARAM_INT16, .size = sizeof(int16_t)}, + + {.name = "wdt_i2c_rst", .addr = P60DOCK_WDTI2C_RST, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "wdt_can_rst", .addr = P60DOCK_WDTCAN_RST, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "wdt_i2c", .addr = P60DOCK_WDTI2C, .type = PARAM_UINT32, .size = sizeof(uint32_t)}, + {.name = "wdt_can", .addr = P60DOCK_WDTCAN, .type = PARAM_UINT32, .size = sizeof(uint32_t)}, + {.name = "wdt_csp", .addr = P60DOCK_WDTCSP(0), .type = PARAM_UINT32, .size = sizeof(uint32_t), .count = 2}, + {.name = "wdt_csp_ping", .addr = P60DOCK_WDTCSP_PING_FAIL(0),.type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 2}, + {.name = "wdt_csp_chan", .addr = P60DOCK_WDTCSP_CHAN(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 2}, + {.name = "wdt_csp_addr", .addr = P60DOCK_WDTCSP_ADDR(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 2}, + + {.name = "p60acu_chan", .addr = P60DOCK_P60ACU_CHAN(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 2}, + {.name = "p60acu_addr", .addr = P60DOCK_P60ACU_ADDR(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 2}, + {.name = "p60pdu_chan", .addr = P60DOCK_P60PDU_CHAN(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 4}, + {.name = "p60pdu_addr", .addr = P60DOCK_P60PDU_ADDR(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 4}, + + {.name = "conv_5v_en", .addr = P60DOCK_CONV_5V0_EN, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + + {.name = "ant6_addr", .addr = P60DOCK_ANT6_ADDR(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 2}, + {.name = "ar6_addr", .addr = P60DOCK_AR6_ADDR(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 4}, + + {.name = "depl_delay", .addr = P60DOCK_DEPL_DELAY, .type = PARAM_UINT32, .size = sizeof(uint32_t)}, +}; + +const int p60dock_config_count = sizeof(p60dock_config) / sizeof(p60dock_config[0]); + +/** + * Setup info about calibration parameters + */ +const param_table_t p60dock_calibration[] = { + {.name = "gain_v_out", .addr = P60DOCK_CAL_GAIN_V_OUT(0), .type = PARAM_FLOAT, .size = sizeof(float), .count = 13}, + {.name = "gain_c_out", .addr = P60DOCK_CAL_GAIN_C_OUT(0), .type = PARAM_FLOAT, .size = sizeof(float), .count = 13}, + {.name = "offs_c_out", .addr = P60DOCK_CAL_OFFSET_C_OUT(0),.type = PARAM_INT16, .size = sizeof(int16_t), .count = 13}, + {.name = "vref", .addr = P60DOCK_CAL_VREF, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "gain_vbat_v", .addr = P60DOCK_CAL_GAIN_VBAT_V, .type = PARAM_FLOAT, .size = sizeof(float)}, + {.name = "gain_vcc_c", .addr = P60DOCK_CAL_GAIN_VCC_C, .type = PARAM_FLOAT, .size = sizeof(float)}, + {.name = "offs_vcc_c", .addr = P60DOCK_CAL_OFFSET_VCC_C, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "gain_aux1", .addr = P60DOCK_CAL_GAIN_AUX1, .type = PARAM_FLOAT, .size = sizeof(float)}, + {.name = "gain_aux2", .addr = P60DOCK_CAL_GAIN_AUX2, .type = PARAM_FLOAT, .size = sizeof(float)}, + {.name = "offs_aux1", .addr = P60DOCK_CAL_OFFSET_AUX1, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "offs_aux2", .addr = P60DOCK_CAL_OFFSET_AUX2, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "gain_batt_v", .addr = P60DOCK_CAL_GAIN_BATT_V, .type = PARAM_FLOAT, .size = sizeof(float)}, + {.name = "gain_batt_chg", .addr = P60DOCK_CAL_GAIN_BATT_CHRG, .type = PARAM_FLOAT, .size = sizeof(float)}, + {.name = "offs_batt_chg", .addr = P60DOCK_CAL_OFFS_BATT_CHRG, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "gain_batt_dis", .addr = P60DOCK_CAL_GAIN_BATT_DIS, .type = PARAM_FLOAT, .size = sizeof(float)}, + {.name = "offs_batt_dis", .addr = P60DOCK_CAL_OFFS_BATT_DIS, .type = PARAM_INT16, .size = sizeof(int16_t)}, +}; + +const int p60dock_cal_count = sizeof(p60dock_calibration) / sizeof(p60dock_calibration[0]); + +/** + * Setup info about hk parameters + */ +const param_table_t p60dock_hk[] = { + {.name = "c_out", .addr = P60DOCK_HK_C_OUT(0), .type = PARAM_INT16, .size = sizeof(int16_t), .count = 13}, + {.name = "v_out", .addr = P60DOCK_HK_V_OUT(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13}, + {.name = "out_en", .addr = P60DOCK_HK_OUT_EN(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 13}, + {.name = "temp", .addr = P60DOCK_HK_TEMP(0), .type = PARAM_INT16, .size = sizeof(int16_t), .count = 2}, + {.name = "bootcause", .addr = P60DOCK_HK_BOOT_CAUSE, .type = PARAM_UINT32, .size = sizeof(uint32_t), .flags = PARAM_F_READONLY}, + {.name = "bootcnt", .addr = P60DOCK_HK_BOOT_COUNTER, .type = PARAM_UINT32, .size = sizeof(uint32_t), .flags = PARAM_F_PERSIST}, + {.name = "uptime", .addr = P60DOCK_HK_UPTIME, .type = PARAM_UINT32, .size = sizeof(uint32_t), .flags = PARAM_F_READONLY}, + {.name = "resetcause", .addr = P60DOCK_HK_RESET_CAUSE, .type = PARAM_UINT16, .size = sizeof(uint16_t), .flags = PARAM_F_PERSIST}, + {.name = "batt_mode", .addr = P60DOCK_HK_BATT_MODE, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "heater_on", .addr = P60DOCK_HK_HEATER_ON, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "conv_5v_en", .addr = P60DOCK_HK_CONV_5V0_EN, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "latchup", .addr = P60DOCK_HK_LATCHUP(0), .type = PARAM_UINT16, .size = sizeof(uint16_t), .count = 13, .flags = PARAM_F_PERSIST}, + {.name = "vbat_v", .addr = P60DOCK_HK_VBAT_V, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "vcc_c", .addr = P60DOCK_HK_VCC_C, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "batt_c", .addr = P60DOCK_HK_BATTERY_C, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "batt_v", .addr = P60DOCK_HK_BATTERY_V, .type = PARAM_UINT16, .size = sizeof(uint16_t)}, + {.name = "batt_temp", .addr = P60DOCK_HK_BP_TEMP(0), .type = PARAM_INT16, .size = sizeof(int16_t), .count = 2}, + {.name = "device_type", .addr = P60DOCK_HK_DEVICE_TYPE(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 8}, + {.name = "device_status", .addr = P60DOCK_HK_DEVICE_STATUS(0),.type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 8}, + {.name = "dearm_status", .addr = P60DOCK_HK_DEARM_STATUS, .type = PARAM_UINT8, .size = sizeof(uint8_t)}, + {.name = "wdt_cnt_gnd", .addr = P60DOCK_HK_CNT_WDTGND, .type = PARAM_UINT32, .size = sizeof(uint32_t), .flags = PARAM_F_PERSIST}, + {.name = "wdt_cnt_i2c", .addr = P60DOCK_HK_CNT_WDTI2C, .type = PARAM_UINT32, .size = sizeof(uint32_t), .flags = PARAM_F_PERSIST}, + {.name = "wdt_cnt_can", .addr = P60DOCK_HK_CNT_WDTCAN, .type = PARAM_UINT32, .size = sizeof(uint32_t), .flags = PARAM_F_PERSIST}, + {.name = "wdt_cnt_csp", .addr = P60DOCK_HK_CNT_WDTCSP(0), .type = PARAM_UINT32, .size = sizeof(uint32_t), .count = 2, .flags = PARAM_F_PERSIST}, + {.name = "wdt_gnd_left", .addr = P60DOCK_HK_WDTGND_LEFT, .type = PARAM_UINT32, .size = sizeof(uint32_t)}, + {.name = "wdt_i2c_left", .addr = P60DOCK_HK_WDTI2C_LEFT, .type = PARAM_UINT32, .size = sizeof(uint32_t)}, + {.name = "wdt_can_left", .addr = P60DOCK_HK_WDTCAN_LEFT, .type = PARAM_UINT32, .size = sizeof(uint32_t)}, + {.name = "wdt_csp_left", .addr = P60DOCK_HK_WDTCSP_LEFT(0), .type = PARAM_UINT8, .size = sizeof(uint8_t), .count = 2}, + {.name = "batt_chrg", .addr = P60DOCK_HK_BATT_C_CHRG, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "batt_dischrg", .addr = P60DOCK_HK_BATT_C_DISCHRG, .type = PARAM_INT16, .size = sizeof(int16_t)}, + {.name = "ant6_depl", .addr = P60DOCK_HK_ANT6_DEPL, .type = PARAM_INT8, .size = sizeof(int8_t)}, + {.name = "ar6_depl", .addr = P60DOCK_HK_AR6_DEPL, .type = PARAM_INT8, .size = sizeof(int8_t)}, +}; + +const int p60dock_hk_count = sizeof(p60dock_hk) / sizeof(p60dock_hk[0]); + +int p60dock_get_hk(param_index_t * mem, uint8_t node, uint32_t timeout) { + + mem->table = p60dock_hk; + mem->mem_id = P60DOCK_HK; + mem->count = p60dock_hk_count; + mem->size = P60DOCK_HK_SIZE; + int result = rparam_get_full_table(mem, node, P60_PORT_RPARAM, mem->mem_id, timeout); + + return (result == 0); + +} + +int p60dock_gndwdt_clear(uint8_t node, uint32_t timeout) { + uint8_t magic = 0x78; + return csp_transaction(CSP_PRIO_HIGH, node, P60_PORT_GNDWDT_RESET, timeout, &magic, 1, NULL, 0); +} diff --git a/gomspace/p60-dock_client/wscript b/gomspace/p60-dock_client/wscript new file mode 100644 index 00000000..8fdf39d6 --- /dev/null +++ b/gomspace/p60-dock_client/wscript @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. +# encoding: utf-8 + +import gs_gcc + +APPNAME = 'p60-dock_client' + + +def options(ctx): + gr = ctx.add_option_group('NanoPower-P60 Dock client options') + gr.add_option('--disable-p60-dock-cmd', action='store_true', help='Disable client cmd code for NanoPower-P60 Dock') + + +def configure(ctx): + ctx.env.append_unique('FILES_P60_DOCK_CLIENT', ['src/p60dock_client.c']) + if not ctx.options.disable_p60_dock_cmd: + ctx.env.append_unique('FILES_P60_DOCK_CLIENT', ['src/p60dock_cmd.c']) + + +def build(ctx): + gs_gcc.gs_call_handler(ctx, handler='param_gen_4_3', name=APPNAME, prefix='p60-dock', + generate_rst=True) + + public_include = APPNAME + '_h' + ctx(export_includes=['include', 'include/gs/p60-dock/param'], name=public_include) + + ctx.objects(source=ctx.path.ant_glob(ctx.env.FILES_P60_DOCK_CLIENT), + target=APPNAME, + use=['csp', 'gosh', 'param', 'param_client', 'p60_client', public_include]) + + +def gs_dist(ctx): + ctx.add_default_files(source_module=True) diff --git a/test/testtasks/P60DockTestTask.cpp b/test/testtasks/P60DockTestTask.cpp index 3db66933..8e929e75 100644 --- a/test/testtasks/P60DockTestTask.cpp +++ b/test/testtasks/P60DockTestTask.cpp @@ -73,6 +73,54 @@ ReturnValue_t P60DockTestTask::sendPacket(void){ } +ReturnValue_t P60DockTestTask::getParameters(void) { + gs_param_table_instance_t node_hk; +// int result = rparam_get_full_table(&node_hk, p60dock_node, P60_PORT_RPARAM, + uint32_t timeout; + int result = p60dock_get_hk(&node_hk, p60dock_node, timeout); + if (result != 0) { + sif::info << "Error retrieving P60 Dock housekeeping\n" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } else { + uint8_t tableOffsetTemperature = 0x44; + uint8_t temperature[2]; + size_t parameterSize = 2; + uint32_t flags = 0; + result = gs_param_get_data((gs_param_table_instance_t*) &node_hk, + tableOffsetTemperature, temperature, parameterSize, flags); + sif::info << "P60 Dock Temperature 1: " << temperature[0] << std::endl; + sif::info << "P60 Dock Temperature 2: " << temperature[1] << std::endl; + +// sif::info << "Retrieved P60 Dock housekeeping\n" << std::endl; +// /* List all out_en[] values, using parameter name */ +// const param_table_t * param = param_find_name(node_hk.table, +// node_hk.count, "out_en"); +// if (param != NULL) { +// for (uint8_t index = 0; index < 13; index++) { +// /* Read parameter using name */ +// uint8_t *out_en = param_read_addr( +// param->addr + param->size * index, &node_hk, +// param->size); +// sif::info << "out_en" << index << ": " << *out_en << std::endl; +// } +// } +// /* List all c_out[] values, using parameter address */ +// param = param_find_addr(node_hk.table, node_hk.count, 0x0000); +// if (param != NULL) { +// for (uint8_t index = 0; index < 13; index++) { +// /* Read parameter using address */ +// int16_t *c_out = param_read_addr( +// param->addr + param->size * index, &node_hk, +// param->size); +// sif::info << "c_out" << index << ": " << *c_out << "mA" +// << std::endl; +// } +// } + } + return HasReturnvaluesIF::RETURN_OK; +} + + ReturnValue_t P60DockTestTask::initializeCSPStack(void){ /* Init CSP and CSP buffer system */ if (csp_init(cspAddress) != CSP_ERR_NONE diff --git a/test/testtasks/P60DockTestTask.h b/test/testtasks/P60DockTestTask.h index bcccb487..90d810bd 100644 --- a/test/testtasks/P60DockTestTask.h +++ b/test/testtasks/P60DockTestTask.h @@ -2,7 +2,7 @@ * P60DockTestTask.h * * Created on: 18.11.2020 - * Author: jakob + * Author: Jakob Meier */ #ifndef TEST_TESTTASKS_P60DOCKTESTTASK_H_ @@ -11,6 +11,7 @@ #include #include #include +#include extern "C" { #include @@ -37,8 +38,12 @@ private: int bitrate = 1000; // bitrate of can int promisc = 0; // set to 0 to enable filter mode + uint8_t hk_mem[P60DOCK_HK_SIZE]; + uint8_t p60dock_node = 4; + ReturnValue_t sendPacket(void); ReturnValue_t initializeCSPStack(void); + ReturnValue_t getParameters(void); };