diff --git a/Makefile b/Makefile index df94de6f..80419a88 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ MISSION_PATH = mission CONFIG_PATH = config TEST_PATH = test UNITTEST_PATH = unittest +GOMSPACE_PATH = gomspace # Board specific paths BSP_PATH = $(BOARD_FILE_ROOT) @@ -109,7 +110,7 @@ INCLUDES := # Directories where $(directoryname).mk files should be included from SUBDIRS := $(FRAMEWORK_PATH) $(TEST_PATH) $(BSP_PATH) $(UNITTEST_PATH)\ - $(CONFIG_PATH) $(MISSION_PATH) + $(CONFIG_PATH) $(MISSION_PATH) $(GOMSPACE_PATH) # INCLUDES += framework/test/catch2 # ETL library include. diff --git a/config/objects/systemObjectList.h b/config/objects/systemObjectList.h index 3b5717fb..a77b8a7b 100644 --- a/config/objects/systemObjectList.h +++ b/config/objects/systemObjectList.h @@ -29,7 +29,8 @@ namespace objects { DUMMY_HANDLER = 0x4400AFFE, /* 0x49 ('I') for Communication Interfaces **/ - ARDUINO_COM_IF = 0x49000001 + ARDUINO_COM_IF = 0x49000001, + P60DOCK_TEST_TASK = 0x00005060 }; } diff --git a/gomspace/gomspace.mk b/gomspace/gomspace.mk new file mode 100644 index 00000000..d36afcd7 --- /dev/null +++ b/gomspace/gomspace.mk @@ -0,0 +1,10 @@ +CSRC += $(wildcard $(CURRENTPATH)/libgscsp/lib/libcsp/src/*.c) +CSRC += $(wildcard $(CURRENTPATH)/libgscsp/src/drivers/can/*.c) + +INCLUDES += $(CURRENTPATH)/libgscsp/include +INCLUDES += $(CURRENTPATH)/libgscsp/lib/libcsp/include/csp/interfaces +INCLUDES += $(CURRENTPATH)/libgscsp/lib/libcsp/include +INCLUDES += $(CURRENTPATH)/libgscsp/lib/libcsp/include/csp +INCLUDES += $(CURRENTPATH)/libutil/include +INCLUDES += $(CURRENTPATH)/libutil/include/gs/util +INCLUDES += $(CURRENTPATH)/lib/libcsp/include \ No newline at end of file diff --git a/gomspace/libgscsp/include/gs/csp/address.h b/gomspace/libgscsp/include/gs/csp/address.h new file mode 100644 index 00000000..9d6e701c --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/address.h @@ -0,0 +1,65 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_ADDRESS_H +#define LIBGSCSP_INCLUDE_GS_CSP_ADDRESS_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Default CSP addresses for nodes in the satellite - often changed for each project. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default address for OBC (On Board Computer). + Example: a3200-sdk. +*/ +#define GS_CSP_ADDR_OBC 1 +/** + Power supply. + Example: nano-power. +*/ +#define GS_CSP_ADDR_EPS 2 +/** + Payload address. +*/ +#define GS_CSP_ADDR_PAYLOAD_3 3 +/** + Default address for ADCS. + Example: a3200-adcs. +*/ +#define GS_CSP_ADDR_ADCS 4 +/** + Default address for radio. + Example: nanocom-ax. +*/ +#define GS_CSP_ADDR_NANOCOM 5 +/** + Payload address. +*/ +#define GS_CSP_ADDR_PAYLOAD_6 6 +/** + Battery pack. + Example: nano-power-bpx. +*/ +#define GS_CSP_ADDR_BPX 7 +/** + Payload address. +*/ +#define GS_CSP_ADDR_PAYLOAD_8 8 +/** + Reaction wheels. + Example: gsw-600. +*/ +#define GS_CSP_ADDR_GSW600 9 +/** + Antenna module. + Example: ant2150. +*/ +#define GS_CSP_ADDR_ANT2150 10 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/command.h b/gomspace/libgscsp/include/gs/csp/command.h new file mode 100644 index 00000000..79d518db --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/command.h @@ -0,0 +1,25 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_COMMAND_H +#define LIBGSCSP_INCLUDE_GS_CSP_COMMAND_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP commands. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Register CSP commands. + @return_gs_error_t +*/ +gs_error_t gs_csp_register_commands(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/conn.h b/gomspace/libgscsp/include/gs/csp/conn.h new file mode 100644 index 00000000..c8bd755e --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/conn.h @@ -0,0 +1,26 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_CONN_H +#define LIBGSCSP_INCLUDE_GS_CSP_CONN_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Extensions to standard libcsp connection interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Return number of open connections. + + @return number of open connections. +*/ +size_t gs_csp_conn_get_open(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/csp.h b/gomspace/libgscsp/include/gs/csp/csp.h new file mode 100644 index 00000000..6268bced --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/csp.h @@ -0,0 +1,202 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_CSP_H +#define LIBGSCSP_INCLUDE_GS_CSP_CSP_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Extensions to standard libcsp. +*/ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default CSP timeout (mS). + + Default timeout value for communicating with a satellite, based on round-trip time of approximately 500 mS. +*/ +#define GS_CSP_TIMEOUT 3000 + +/** + Check if address is a valid CSP adddress. + @param[in] address CSP address to verify + @return \a true if address is valid. +*/ +bool gs_csp_is_address_valid(uint8_t address); + +/** + gscsp and csp configuration. +*/ +typedef struct { + /** + Forward CSP logs to GomSpace log (libutil). + */ + bool use_gs_log; + + /** + Use command line options. + Configure interfaces specified on the command line. + @note Only supported on Linux - ignored on platforms without command line options. + */ + bool use_command_line_options; + + /** + Size of a CSP buffer (in bytes). + @see csp_buffer_init(). + */ + size_t csp_buffer_size; + /** + Number of CSP buffers to allocate. + @see csp_buffer_init(). + */ + size_t csp_buffers; + + /** + CSP address of the system + */ + uint8_t address; + /** + Host name, returned by the #CSP_CMP_IDENT request. + @note String must remain valid as long as the application is running. + */ + const char *hostname; + /** + Model, returned by the #CSP_CMP_IDENT request. + @note String must remain valid as long as the application is running. + */ + const char *model; + /** + Revision, returned by the #CSP_CMP_IDENT request + @note String must remain valid as long as the application is running. + */ + const char *revision; + +} gs_csp_conf_t; + +/** + Get default CSP configuration for server systems. + @param[in] conf user supplied configuration struct. +*/ +void gs_csp_conf_get_defaults_server(gs_csp_conf_t * conf); + +/** + Get default CSP configuration for embedded systems. + @param[in] conf user supplied configuration struct. +*/ +void gs_csp_conf_get_defaults_embedded(gs_csp_conf_t * conf); + +/** + Initialize CSP. + + Wraps initialization of libcsp, by calling following functions + - hooks CSP log system into GomSpace log system. + - initializes CSP buffers by calling csp_buffer_init() + - initializes CSP by calling csp_init() + - configure interfaces specificed on command line + - configure routing table specificed on command line, or default if only one interface is present. + + @param[in] conf configuration. + @return_gs_error_t +*/ +gs_error_t gs_csp_init(const gs_csp_conf_t * conf); + +/** + Perform an entire request/reply transaction, + + Copies both input buffer and reply to output buffer. + Also makes the connection and closes it again + Differ from 'csp_transaction()' by limiting input buffer size when input length is unknown + + @param[in] prio CSP Prio + @param[in] dest CSP Dest + @param[in] port CSP Port + @param[in] timeout timeout in ms + @param[in] tx_buf pointer to outgoing data buffer + @param[in] tx_len length of request to send + @param[out] rx_buf pointer to incoming data buffer + @param[in] rx_max_len length of expected reply (input buffer size) + @param[out] rx_len pointer to length of reply + @param[in] opts connection options. + @return GS_ERROR_OVERFLOW when input length larger than buffer - it still updates rx_buf + @return GS_ERROR_IO when 'csp_send()' or 'csp_read()' fails + @return GS_ERROR_ALLOC when 'csp_get_buffer()' fails + @return_gs_error_t + */ +gs_error_t gs_csp_transaction2(uint8_t prio, + uint8_t dest, + uint8_t port, + uint32_t timeout, + const void * tx_buf, + size_t tx_len, + void * rx_buf, + size_t rx_max_len, + size_t * rx_len, + uint32_t opts); + +/** + Perform an entire request/reply transaction, + + @param[in] prio CSP Prio + @param[in] dest CSP Dest + @param[in] port CSP Port + @param[in] timeout timeout in ms + @param[in] tx_buf pointer to outgoing data buffer + @param[in] tx_len length of request to send + @param[out] rx_buf pointer to incoming data buffer + @param[in] rx_max_len length of expected reply (input buffer size) + @param[out] rx_len pointer to length of reply + @return GS_ERROR_OVERFLOW when input length larger than buffer - it still updates rx_buf + @return GS_ERROR_IO when 'csp_send()' or 'csp_read()' fails + @return GS_ERROR_ALLOC when 'csp_get_buffer()' fails + @return_gs_error_t + @see gs_csp_transaction2() +*/ +static inline gs_error_t gs_csp_transaction(uint8_t prio, + uint8_t dest, + uint8_t port, + uint32_t timeout, + const void * tx_buf, + size_t tx_len, + void * rx_buf, + size_t rx_max_len, + size_t * rx_len) +{ + return gs_csp_transaction2(prio, dest, port, timeout, tx_buf, tx_len, rx_buf, rx_max_len, rx_len, 0); +} + +/** + Use an existing connection to perform a transaction. + + This is only possible if the next packet is on the same port and destination! + Differ from 'csp_transaction_persistent()' by limiting input buffer size when input length is unknown + + @param[in] conn pointer to connection structure + @param[in] timeout timeout in ms + @param[in] tx_buf pointer to outgoing data buffer + @param[in] tx_len length of request to send + @param[out] rx_buf pointer to incoming data buffer + @param[in] rx_max_len length of expected reply (input buffer size) + @param[out] rx_len pointer to length of reply + @return GS_ERROR_OVERFLOW when input length larger than buffer - it still updates rx_buf + @return GS_ERROR_IO when 'csp_send()' or 'csp_read()' fails + @return GS_ERROR_ALLOC when 'csp_get_buffer()' fails + @return_gs_error_t + */ +gs_error_t gs_csp_transaction_persistent(csp_conn_t * conn, + uint32_t timeout, + const void * tx_buf, + size_t tx_len, + void * rx_buf, + size_t rx_max_len, + size_t * rx_len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/drivers/can/can.h b/gomspace/libgscsp/include/gs/csp/drivers/can/can.h new file mode 100644 index 00000000..0d10c4fc --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/drivers/can/can.h @@ -0,0 +1,54 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_CAN_CAN_H +#define LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_CAN_CAN_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + GomSpace CAN API for CSP. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default CAN interface name. +*/ +#define GS_CSP_CAN_DEFAULT_IF_NAME "CAN" + +/** + Initialize CSP for CAN device. + + @param[in] device CAN device + @param[in] csp_addr CSP address. + @param[in] mtu MTU, normally CSP_CAN_MTU. + @param[in] name name of CSP interface, if NULL #GS_CSP_CAN_DEFAULT_IF_NAME will be used. + @param[in] set_default_route if \a true, the device will be set as default route. + @param[out] csp_if the added CSP interface. + @return #GS_ERROR_EXIST if device already exists. + @return_gs_error_t +*/ +gs_error_t gs_csp_can_init2(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, bool set_default_route, csp_iface_t ** csp_if); + +/** + Initialize CSP for CAN device. + + @param[in] device CAN device + @param[in] csp_addr CSP address. + @param[in] mtu MTU, normally CSP_CAN_MTU. + @param[in] name name of CSP interface, if NULL #GS_CSP_CAN_DEFAULT_IF_NAME will be used. + @param[out] csp_if the added CSP interface. + @return #GS_ERROR_EXIST if device already exists. + @return_gs_error_t + @see gs_csp_can_init2() +*/ +gs_error_t gs_csp_can_init(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, csp_iface_t ** csp_if); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/drivers/i2c/i2c.h b/gomspace/libgscsp/include/gs/csp/drivers/i2c/i2c.h new file mode 100644 index 00000000..4b6204f1 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/drivers/i2c/i2c.h @@ -0,0 +1,29 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_I2C_I2C_H +#define LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_I2C_I2C_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + GomSpace I2C API for CSP. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Initialize CSP for I2C device. + @note I2C device must be initialized allready with the same I2C address as the CSP address. + + @param[in] device I2C device. + @param[in] csp_addr CSP address. + @return_gs_error_t +*/ +gs_error_t gs_csp_i2c_init(uint8_t device, uint8_t csp_addr); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/drivers/kiss/kiss.h b/gomspace/libgscsp/include/gs/csp/drivers/kiss/kiss.h new file mode 100644 index 00000000..cb098fce --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/drivers/kiss/kiss.h @@ -0,0 +1,30 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_KISS_KISS_H +#define LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_KISS_KISS_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +/** + @file + + GomSpace KISS API for CSP. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Initialize CSP for KISS device. + @note KISS/UART device must be initialized all ready. + + @param[in] device KISS/UART device. + @return_gs_error_t +*/ +gs_error_t gs_csp_kiss_init(uint8_t device); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/error.h b/gomspace/libgscsp/include/gs/csp/error.h new file mode 100644 index 00000000..8de40787 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/error.h @@ -0,0 +1,28 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_ERROR_H +#define LIBGSCSP_INCLUDE_GS_CSP_ERROR_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Converting CSP error codes to #gs_error_t +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Convert CSP error. + + @param[in] csp_error CSP error + @return GS error code representing the CSP error. +*/ +gs_error_t gs_csp_error(int csp_error); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/linux/command_line.h b/gomspace/libgscsp/include/gs/csp/linux/command_line.h new file mode 100644 index 00000000..e416eda5 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/linux/command_line.h @@ -0,0 +1,24 @@ +#ifndef GS_CSP_LINUX_COMMAND_LINE_H +#define GS_CSP_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. +*/ +extern const struct argp_child gs_csp_command_line_options; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/log.h b/gomspace/libgscsp/include/gs/csp/log.h new file mode 100644 index 00000000..91360cc0 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/log.h @@ -0,0 +1,27 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_LOG_H +#define LIBGSCSP_INCLUDE_GS_CSP_LOG_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP log hook. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Hook into CSP log system. + All CSP logs will be logged through Log (libutil). + + @return_gs_error_t +*/ +gs_error_t gs_csp_log_init(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/port.h b/gomspace/libgscsp/include/gs/csp/port.h new file mode 100644 index 00000000..29fa32e4 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/port.h @@ -0,0 +1,111 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_PORT_H +#define LIBGSCSP_INCLUDE_GS_CSP_PORT_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Port definitions for standard CSP and GomSpace services. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Port definitions for standard CSP and GomSpace services. +*/ +typedef enum { + /** + CSP Management Protocol - standard CSP service. + */ + GS_CSP_CMP = CSP_CMP, // 0 + /** + Ping - standard CSP service. + */ + GS_CSP_PING = CSP_PING, // 1 + /** + Show process status - standard CSP service. + */ + GS_CSP_PS = CSP_PS, // 2 + /** + Show memory free - standard CSP service. + */ + GS_CSP_MEM_FREE = CSP_MEMFREE, // 3 + GS_CSP_MEMFREE = GS_CSP_MEM_FREE, + /** + Reboot/reset request - standard CSP service. + */ + GS_CSP_REBOOT = CSP_REBOOT, // 4 + /** + Show number of free CSP buffers - standard CSP service. + */ + GS_CSP_BUF_FREE = CSP_BUF_FREE, // 5 + /** + Show uptime (time since last reset) - standard CSP service. + */ + GS_CSP_UPTIME = CSP_UPTIME, // 6 + /** + Parameter service (libparam) + */ + GS_CSP_PORT_RPARAM = 7, + /** + File Transfer Service (libftp) + */ + GS_CSP_PORT_FTP = 9, + /** + Remote log service (liblog) + */ + GS_CSP_PORT_RLOG = 11, + /** + Remote GOSH service (librgosh) + */ + GS_CSP_PORT_RGOSH = 12, + /** + AIS command port (libais). + */ + GS_CSP_PORT_AIS = 13, + /** + ADS-B command port (libadsb). + */ + GS_CSP_PORT_ADSB = 14, + + /** + GomSpace Sensor Bus (libgssb). + */ + GS_CSP_PORT_GSSB = 16, + /** + Flight Planner (libfp). + */ + GS_CSP_PORT_FP = 18, + /** + ADCS (libadcs). + */ + GS_CSP_PORT_ADCS = 20, + /** + House Keeping (libhk). + */ + GS_CSP_PORT_HK = 21, + /** + G(omSpace) script service (libgosh) + */ + GS_CSP_PORT_GSCRIPT = 22, + /** + Remote shell (libgosh). + Executes shell commands (linux server only). + Requires CSP_O_RDP. + */ + GS_CSP_PORT_REMOTE_SHELL = 27, + /** + House keeping beacon port (libhk). + Default port for sending beacons from satellite to ground (configurable). + */ + GS_CSP_PORT_HK_BEACON = 30, + +} gs_csp_port_t; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/router.h b/gomspace/libgscsp/include/gs/csp/router.h new file mode 100644 index 00000000..dae8990a --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/router.h @@ -0,0 +1,36 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_ROUTER_H +#define LIBGSCSP_INCLUDE_GS_CSP_ROUTER_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP router task, with support for stopping/terminating router task. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Start CSP router (task/thread). + @param[in] stack_size stack size in bytes, minimum 300 bytes. + @param[in] priority task priority, normally GS_THREAD_PRIORITY_HIGH. + @return_gs_error_t +*/ +gs_error_t gs_csp_router_task_start(size_t stack_size, gs_thread_priority_t priority); + +/** + Stop CSP router task (for testing). + + Signal stop to the router and waits for it to terminate (join). + @note Join is performed, which may hang forever if the router doesn't respond to the stop request. + @return_gs_error_t +*/ +gs_error_t gs_csp_router_task_stop(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/rtable.h b/gomspace/libgscsp/include/gs/csp/rtable.h new file mode 100644 index 00000000..511b7bc2 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/rtable.h @@ -0,0 +1,31 @@ +#ifndef GS_CSP_RTABLE_H +#define GS_CSP_RTABLE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP routing table support. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Load routing table. + Extension to csp_rtable_load(). + + @param[in] rtable string containing the routing table to set/load. + @param[in] set_default_route if \a true, sets a default route if no routing table specified and only one interface configured. + @param[in] use_command_line_option if \a true (and command line supported), use command line options to configure routing. + @return_gs_error_t +*/ +gs_error_t gs_csp_rtable_load(const char * rtable, bool set_default_route, bool use_command_line_option); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/service_dispatcher.h b/gomspace/libgscsp/include/gs/csp/service_dispatcher.h new file mode 100644 index 00000000..e80ef66d --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/service_dispatcher.h @@ -0,0 +1,124 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_SERVICE_DISPATCHER_H +#define LIBGSCSP_INCLUDE_GS_CSP_SERVICE_DISPATCHER_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP Service Dispatcher. + + The dispatcher is a task/thread, listening on a configured number of ports and forwards the the incoming connection + to a configured CSP service handler. + The dispatcher touches a software watchdog for every handled connection. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + CSP service dispatcher configuration. +*/ +typedef struct { + /** + Name of dispatcher. + */ + const char * name; + /** + Array of service handlers - index'ed by CSP port number. + */ + const gs_csp_service_handler_t * handler_array; + /** + Array size of \a handler_array. + */ + unsigned int handler_array_size; + /** + Bind to any unbound ports (CSP_ANY). + Only one task/dispatcher can bind to any ports. This can be used to log unexpected incoming connections. + */ + bool bind_any; + /** + Disable watchdog. + The watchdog is created with a fixed timeout of 20 seconds. + */ + bool disable_watchdog; + /** + CSP connection backlog. + If 0 (zero), the backlog is set to 10. + */ + size_t listen_backlog; + /** + Callback after timeout or processsed connection. + The return value is the next timeout in milli seconds. + Return UINT32_MAX to use default timeout. + */ + unsigned int (*callback)(void); + /** + Socket options. + */ + uint32_t socket_options; +} gs_csp_service_dispatcher_conf_t; + +/** + Basic CSP service handlers. + + Can be used to configure handlers for all basic CSP services. +*/ +#define GS_CSP_BASIC_SERVICE_HANDLERS \ + [GS_CSP_CMP] = gs_csp_cmp_service_handler, \ + [GS_CSP_PING] = gs_csp_ping_service_handler, \ + [GS_CSP_PS] = gs_csp_ps_service_handler, \ + [GS_CSP_MEMFREE] = gs_csp_mem_free_service_handler, \ + [GS_CSP_REBOOT] = gs_csp_reboot_service_handler, \ + [GS_CSP_BUF_FREE] = gs_csp_buf_free_service_handler, \ + [GS_CSP_UPTIME] = gs_csp_uptime_service_handler + +/** + Service dispatcher handle. + @see gs_csp_service_dispatcher_create(), gs_csp_service_dispatcher_close() +*/ +typedef struct gs_csp_service_dispatcher * gs_csp_service_dispatcher_t; + +/** + Create service dispatcher (task/thread). + + @param[in] conf configuration - must remain valid as long as the dispatcher is running. + @param[in] stack_size thread stack size. + @param[in] priority thread priority. + @param[out] return_handle created dispatcher - use NULL if not used. + @return_gs_error_t +*/ +gs_error_t gs_csp_service_dispatcher_create(const gs_csp_service_dispatcher_conf_t * conf, + size_t stack_size, + gs_thread_priority_t priority, + gs_csp_service_dispatcher_t * return_handle); + +/** + Wake up service dispatcher. + + This will cause the disapther to wake up (from listing for new connections), and invoke the configured \a callback function, + before listing for new connections. + + @param[in] handle dispatcher. + @return_gs_error_t +*/ +gs_error_t gs_csp_service_dispatcher_wake_up(gs_csp_service_dispatcher_t handle); + +/** + Destroy/close service dispatcher (for testing). + + Signal stop to the dispatcher and waits for it to terminate (join). + + @note Join is performed, which may hang forever if the dispatcher doesn't respond to the stop request. + @param[in] handle dispatcher. + @return_gs_error_t +*/ +gs_error_t gs_csp_service_dispatcher_destroy(gs_csp_service_dispatcher_t handle); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/service_handler.h b/gomspace/libgscsp/include/gs/csp/service_handler.h new file mode 100644 index 00000000..52479129 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/service_handler.h @@ -0,0 +1,102 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_SERVICE_HANDLER_H +#define LIBGSCSP_INCLUDE_GS_CSP_SERVICE_HANDLER_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP Service handler. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + CSP Service handler. + + It is the handler's responsibility to process all pakcets on the connection and close the connection when done - even if a failure + is returned. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_csp_service_handler_t)(csp_conn_t * conn); + +/** + Service handler for CSP Management Protocol. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_cmp_service_handler(csp_conn_t * conn); + +/** + Service handler for ping. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_ping_service_handler(csp_conn_t * conn); + +/** + Service handler for getting process list. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_ps_service_handler(csp_conn_t * conn); + +/** + Service handler for getting memory free. + + Invokes GomSpace handler, which doesn't use malloc() to determine \a free memory. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_mem_free_service_handler(csp_conn_t * conn); + + +/** + Service handler for reboot (reset) of node. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_reboot_service_handler(csp_conn_t * conn); + +/** + Service handler for getting free CSP buffers. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_buf_free_service_handler(csp_conn_t * conn); + +/** + Service handler for getting uptime (in seconds). + + Invokes GomSpace handler (works on Linux). + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_uptime_service_handler(csp_conn_t * conn); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/lib/libcsp/CHANGELOG b/gomspace/libgscsp/lib/libcsp/CHANGELOG new file mode 100644 index 00000000..a4945716 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/CHANGELOG @@ -0,0 +1,113 @@ +libcsp 1.4, 07-05-2015 +---------------------- +- new: General rtable interface with support for STATIC or CIDR format +- new: CIDR (classless interdomain routing) route table format with netmasks +- new: Bridge capability +- new: Added routing table (de)serialization functions for load/save +- new: Automatic packet deduplication using CRC32 (compile time option) +- new: Autogenerated python bindings using ctypesgen +- new: Task-less operation with router invocation from external scheduler function +- api: Refactor route_if_add to csp_iflist_add +- api: Refactor route_set and friends to rtable_set +- api: Refactor csp_fifo_qos to csp_qfifo +- api: Added defined to be backwards compatible with 1.x +- interfaces: Drop packets on LOOP interface not for own address (blackhole) +- interfaces: New ZMQHUB interface (using zeroMQ over TCP) +- other: Increase stack size from 250 to 1100 for csp_can_rx_task +- other: Cleanup in csp_route.c +- other: Show incoming interface name in debug message +- other: Remove newlines from debug calls +- improvement: Reduce debug hook function complexity with valist passing +- fix: csp_sleep_ms did not work + +libcsp 1.3, 07-05-2015 +---------------------- +- new: Split long process lists into multiple packets +- new: Added posix csp_clock.h +- new: cmp clock functions (requires that you provide csp_clock.h implementation) +- new: Added SFP (Small fragmentation protocol) for larger data chunks +- fix: csp_if_fifo example +- fix: NULL char at the end of rps +- doc: Updated mtu documentation +- other: Tested with FreeRTOS 8.0 +- other: Added disable-stlib option to build only object files + +libcsp 1.2, 25-10-2013 +---------------------- +- Feature release +- New: CMP service for peek and poke of memory +- New: CMP interface statistics struct is now packed +- New: Faster O(1) buffer system with reference counting and automatic alignment +- New: Thread safe KISS driver with support for multiple interfaces +- New: CSP interface struct now holds an opaque pointer to driver handle +- New: removed TXBUF from KISS driver entirely to minimize stack usage, added TX lock instead +- New: Pre-calculated CRC table .romem or PROGMEM on __avr__ +- New: Added buffer overflow protection to KISS interface +- New: Allow posting null pointers on conn RX queues +- New: Lower memory usage on AVR8 +- New: csp_route_save and csp_route_load functions +- New: option --disable-verbose to disable filenames and linenumber on debug +- Protocol: KISS uses csp_crc32 instead of it own embedded crc32 +- Improvement: Use buffer clone function to copy promisc packets +- Bugfix: Fix pointer size (32/16bit) in cmp_peek/poke +- Bugfix: Issue with double free in KISS fixed +- Bugfix: Change rdp_send timeout from packet to connection timeout to make sending task block longer +- Bugfix: Fix conn pool leak when using security check and discarding new packets +- Bugfix: Add packet too short check for CRC32 +- Bugfix: Accept CRC32 responses from nodes without CRC support +- Bugfix: Ensure csp_ping works for packets > 256 bytes +- Bugfix: Cleanup printf inside ISR functions +- Bugfix: Do not add forwarded packets to promisc queue twice +- Bugfix: Fix return value bug of csp_buffer_get when out of buffers +- Bugfix: Always post null pointer with lowest priority, not highest +- Bugfix: Add check on debug level before calling do_csp_debug, fixes #35 +- Other: Export csp/arch include files +- Other: Remove the use of bool type from csp_debug +- Other: Moved csp debug functions to csp_debug.h instead of csp.h +- Other: Ensure assignment of id happens using the uint32_t .ext value of the union, quenches warning + +libcsp 1.1, 24-08-2012 +---------------------- +- Feature release +- Defacto stable since Feb 2012 +- New: I2C interface +- New: KISS interface +- New: USART drivers for Linux, Mac and Windows +- New: Windows/MinGW support +- New: MacOSX support +- New: Interface register function +- New: Interface search function +- New: CMP service for remote route updating +- New: CMP service for interface statistics +- Improvement: Better QoS support +- Improvement: Send RDP control messages with high priority +- Improvement: WAF distcheck now works +- Improvement: Automatic endian discovery +- Improvement: Accept packets with CRC32 checksum if compiled without CRC32 support +- Improvement: Do not wake the router task if RDP is not enabled +- Improvement: Save 102 bytes of RAM by packing route entries +- Cleanup: Simplify CAN configuration +- Cleanup: Move architecture specific code to src/arch +- Bugfix: CSP_MEMFREE gives wrong answer on freertos AVR due to truncation +- Bugfix: Fixed wrong 64-bit size_t in csp_service_handler +- Bugfix: Fixed problem in csp_if_kiss when out of buffers +- Bigfix: Handle bus-off CAN IRQ for AT90CAN128 + +libcsp 1.0.1, 30-10-2011 +------------------------ +- Hotfix release +- Bugfix: missing extern in csp_if_lo.h + +libcsp 1.0, 24-10-2011 +---------------------- +- First official release +- New: CSP 32-bit header 1.0 +- Features: Network Router with promiscous mode, broadcast and QoS +- Features: Connection-oriented transport protocol w. flow-control +- Features: Connection-less "UDP" like transport +- Features: Encryption, Authentication and message check +- Features: Loopback interface +- Features: Python Bindings +- Features: CAN interface w. drivers for several chips +- Features: CSP-services (ping, reboot, uptime, memfree, buffree, ident) + diff --git a/gomspace/libgscsp/lib/libcsp/CONTRIBUTORS b/gomspace/libgscsp/lib/libcsp/CONTRIBUTORS new file mode 100644 index 00000000..97240f60 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/CONTRIBUTORS @@ -0,0 +1,3 @@ +Jeppe Ledet-Pedersen +Johan De Claville Christiansen +Dan Erik Holmstrøm diff --git a/gomspace/libgscsp/lib/libcsp/COPYING b/gomspace/libgscsp/lib/libcsp/COPYING new file mode 100644 index 00000000..54c619ad --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/COPYING @@ -0,0 +1,503 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/gomspace/libgscsp/lib/libcsp/INSTALL.rst b/gomspace/libgscsp/lib/libcsp/INSTALL.rst new file mode 100644 index 00000000..e68a46ed --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/INSTALL.rst @@ -0,0 +1,30 @@ +How to install LibCSP +===================== + +CSP uses the `waf` build system (http://code.google.com/p/waf/). In order to +compile CSP, you first need to configure the toolchain, what operating system +to compile for, the location of required libraries and whether to enable +certain optional features. + +To configure CSP to build with the AVR32 toolchain for FreeRTOS and output +the compiled libcsp.a and header files to the install directory, issue: + +.. code-block:: bash + + ./waf configure --toolchain=avr32- --with-os=freertos --prefix=install + +When compiling for FreeRTOS, the path to the FreeRTOS header files must be +specified with `--with-freertos=PATH.` + +A number of optional features can be enabled by from the configure script. +Support for XTEA encryption can e.g. be enabled with `--enable-xtea`. Run +`./waf configure --help` to list the available configure options. + +The CAN drivers can be enabled by appending the configure option `--with-driver-can=CHIP`, +where CHIP is one of 'socketcan', 'at91sam7a1', 'at91sam7a3' or 'at90can128'. + +To build and copy the library to the location specified with --prefix, use: + +.. code-block:: bash + + ./waf build install diff --git a/gomspace/libgscsp/lib/libcsp/README.rst b/gomspace/libgscsp/lib/libcsp/README.rst new file mode 100644 index 00000000..c8aff3d8 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/README.rst @@ -0,0 +1,41 @@ +The Cubesat Space Protocol +========================== + +Cubesat Space Protocol (CSP) is a small protocol stack written in C. CSP is designed to ease communication between distributed embedded systems in smaller networks, such as Cubesats. The design follows the TCP/IP model and includes a transport protocol, a routing protocol and several MAC-layer interfaces. The core of libcsp includes a router, a socket buffer pool and a connection oriented socket API. + +The protocol is based on a 32-bit header containing both transport and network-layer information. Its implementation is designed for, but not limited to, embedded systems such as the 8-bit AVR microprocessor and the 32-bit ARM and AVR from Atmel. The implementation is written in GNU C and is currently ported to run on FreeRTOS or POSIX operating systems such as Linux. + +The idea is to give sub-system developers of cubesats the same features of a TCP/IP stack, but without adding the huge overhead of the IP header. The small footprint and simple implementation allows a small 8-bit system with less than 4 kB of RAM to be fully connected on the network. This allows all subsystems to provide their services on the same network level, without any master node required. Using a service oriented architecture has several advantages compared to the traditional mater/slave topology used on many cubesats. + + * Standardised network protocol: All subsystems can communicate with eachother + * Service loose coupling: Services maintain a relationship that minimizes dependencies between subsystems + * Service abstraction: Beyond descriptions in the service contract, services hide logic from the outside world + * Service reusability: Logic is divided into services with the intention of promoting reuse. + * Service autonomy: Services have control over the logic they encapsulate. + * Service Redundancy: Easily add redundant services to the bus + * Reduces single point of failure: The complexity is moved from a single master node to several well defines services on the network + +The implementation of LibCSP is written with simplicity in mind, but it's compile time configuration allows it to have some rather advanced features as well: + +Features +-------- + + * Thread safe Socket API + * Router task with Quality of Services + * Connection-oriented operation (RFC 908 and 1151). + * Connection-less operation (similar to UDP) + * ICMP-like requests such as ping and buffer status. + * Loopback interface + * Very Small Footprint 48 kB code and less that 1kB ram required on ARM + * Zero-copy buffer and queue system + * Modular network interface system + * Modular OS interface, ported to FreeRTOS, windows (cygwin) and Linux + * Broadcast traffic + * Promiscuous mode + * Encrypted packets with XTEA in CTR mode + * Truncated HMAC-SHA1 Authentication (RFC 2104) + +LGPL Software license +--------------------- +The source code is available under an LGPL 2.1 license. See COPYING for the license text. + diff --git a/gomspace/libgscsp/lib/libcsp/bindings/python/libcsp/__init__.py b/gomspace/libgscsp/lib/libcsp/bindings/python/libcsp/__init__.py new file mode 100644 index 00000000..39de36b5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/bindings/python/libcsp/__init__.py @@ -0,0 +1,6 @@ +import sys + +if sys.version_info >= (3, 0): + from libcsp_py3 import * +else: + from libcsp_py2 import * diff --git a/gomspace/libgscsp/lib/libcsp/doc/example.rst b/gomspace/libgscsp/lib/libcsp/doc/example.rst new file mode 100644 index 00000000..b82a055e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/example.rst @@ -0,0 +1,123 @@ +Client and server example +========================= + +The following examples show the initialization of the protocol stack and examples of client/server code. + +Initialization Sequence +----------------------- + +This code initializes the CSP buffer system, device drivers and router core. The example uses the CAN interface function csp_can_tx but the initialization is similar for other interfaces. The loopback interface does not require any explicit initialization. + +.. code-block:: c + + #include + #include + + /* CAN configuration struct for SocketCAN interface "can0" */ + struct csp_can_config can_conf = {.ifc = "can0"}; + + /* Init buffer system with 10 packets of maximum 320 bytes each */ + csp_buffer_init(10, 320); + + /* Init CSP with address 1 */ + csp_init(1); + + /* Init the CAN interface with hardware filtering */ + csp_can_init(CSP_CAN_MASKED, &can_conf) + + /* Setup default route to CAN interface */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_can_tx, CSP_HOST_MAC); + + /* Start router task with 500 word stack, OS task priority 1 */ + csp_route_start_task(500, 1); + +Server +------ + +This example shows how to create a server task that listens for incoming connections. CSP should be initialized before starting this task. Note the use of `csp_service_handler()` as the default branch in the port switch case. The service handler will automatically reply to ICMP-like requests, such as pings and buffer status requests. + +.. code-block:: c + + void csp_task(void *parameters) { + /* Create socket without any socket options */ + csp_socket_t *sock = csp_socket(CSP_SO_NONE); + + /* Bind all ports to socket */ + csp_bind(sock, CSP_ANY); + + /* Create 10 connections backlog queue */ + csp_listen(sock, 10); + + /* Pointer to current connection and packet */ + csp_conn_t *conn; + csp_packet_t *packet; + + /* Process incoming connections */ + while (1) { + /* Wait for connection, 10000 ms timeout */ + if ((conn = csp_accept(sock, 10000)) == NULL) + continue; + + /* Read packets. Timout is 1000 ms */ + while ((packet = csp_read(conn, 1000)) != NULL) { + switch (csp_conn_dport(conn)) { + case MY_PORT: + /* Process packet here */ + default: + /* Let the service handler reply pings, buffer use, etc. */ + csp_service_handler(conn, packet); + break; + } + } + + /* Close current connection, and handle next */ + csp_close(conn); + } + } + +Client +------ + +This example shows how to allocate a packet buffer, connect to another host and send the packet. CSP should be initialized before calling this function. RDP, XTEA, HMAC and CRC checksums can be enabled per connection, by setting the connection option to a bitwise OR of any combination of `CSP_O_RDP`, `CSP_O_XTEA`, `CSP_O_HMAC` and `CSP_O_CRC`. + +.. code-block:: c + + int send_packet(void) { + + /* Get packet buffer for data */ + csp_packet_t *packet = csp_buffer_get(data_size); + if (packet == NULL) { + /* Could not get buffer element */ + printf("Failed to get buffer element\\n"); + return -1; + } + + /* Connect to host HOST, port PORT with regular UDP-like protocol and 1000 ms timeout */ + csp_conn_t *conn = csp_connect(CSP_PRIO_NORM, HOST, PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + /* Connect failed */ + printf("Connection failed\\n"); + /* Remember to free packet buffer */ + csp_buffer_free(packet); + return -1; + } + + /* Copy message to packet */ + char *msg = "HELLO"; + strcpy(packet->data, msg); + + /* Set packet length */ + packet->length = strlen(msg); + + /* Send packet */ + if (!csp_send(conn, packet, 1000)) { + /* Send failed */ + printf("Send failed\\n"); + csp_buffer_free(packet); + } + + /* Close connection */ + csp_close(conn); + + return 0 + } diff --git a/gomspace/libgscsp/lib/libcsp/doc/history.rst b/gomspace/libgscsp/lib/libcsp/doc/history.rst new file mode 100644 index 00000000..ad064873 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/history.rst @@ -0,0 +1,17 @@ +History +======= + +The idea was developed by a group of students from Aalborg University in 2008. In 2009 the main developer started working for GomSpace, and CSP became integrated into the GomSpace products. The protocol is based on a 32-bit header containing both transport, network and MAC-layer information. It's implementation is designed for, but not limited to, embedded systems such as the 8-bit AVR microprocessor and the 32-bit ARM and AVR from Atmel. The implementation is written in C and is currently ported to run on FreeRTOS and POSIX and pthreads based operating systems like Linux and BSD. The three letter acronym CSP was originally an abbreviation for CAN Space Protocol because the first MAC-layer driver was written for CAN-bus. Now the physical layer has extended to include spacelink, I2C and RS232, the name was therefore extended to the more general CubeSat Space Protocol without changing the abbreviation. + +Satellites using CSP +-------------------- + +This is the known list of satellites or organisations that uses CSP. + + * GomSpace GATOSS GOMX-1 + * AAUSAT-3 + * EgyCubeSat + * EuroLuna + * NUTS + * Hawaiian Space Flight Laboratory + * GomSpace GOMX-3 diff --git a/gomspace/libgscsp/lib/libcsp/doc/interfaces.rst b/gomspace/libgscsp/lib/libcsp/doc/interfaces.rst new file mode 100644 index 00000000..5a80325c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/interfaces.rst @@ -0,0 +1,95 @@ +CSP Interfaces +============== + +This is an example of how to implement a new layer-2 interface in CSP. The example is going to show how to create a `csp_if_fifo`, using a set of [named pipes](http://en.wikipedia.org/wiki/Named_pipe). The complete interface example code can be found in `examples/fifo.c`. For an example of a fragmenting interface, see the CAN interface in `src/interfaces/csp_if_can.c`. + +CSP interfaces are declared in a `csp_iface_t` structure, which sets the interface nexthop function and name. A maximum transmission unit can also be set, which forces CSP to drop outgoing packets above a certain size. The fifo interface is defined as: + +.. code-block:: c + + #include + #include + + csp_iface_t csp_if_fifo = { + .name = "fifo", + .nexthop = csp_fifo_tx, + .mtu = BUF_SIZE, + }; + +Outgoing traffic +---------------- + +The nexthop function takes a pointer to a CSP packet and a timeout as parameters. All outgoing packets that are routed to the interface are passed to this function: + +.. code-block:: c + + int csp_fifo_tx(csp_packet_t *packet, uint32_t timeout) { + write(tx_channel, &packet->length, packet->length + sizeof(uint32_t) + sizeof(uint16_t)); + csp_buffer_free(packet); + return 1; + } + +In the fifo interface, we simply transmit the header, length field and data using a write to the fifo. CSP does not dictate the wire format, so other interfaces may decide to e.g. ignore the length field if the physical layer provides start/stop flags. + +_Important notice: If the transmission succeeds, the interface must free the packet and return 1. If transmission fails, the nexthop function should return 0 and not free the packet, to allow retransmissions by the caller._ + +Incoming traffic +---------------- + +The interface also needs to receive incoming packets and pass it to the CSP protocol stack. In the fifo interface, this is handled by a thread that blocks on the incoming fifo and waits for packets: + +.. code-block:: c + + void * fifo_rx(void * parameters) { + csp_packet_t *buf = csp_buffer_get(BUF_SIZE); + /* Wait for packet on fifo */ + while (read(rx_channel, &buf->length, BUF_SIZE) > 0) { + csp_qfifo_write(buf, &csp_if_fifo, NULL); + buf = csp_buffer_get(BUF_SIZE); + } + } + +A new CSP buffer is preallocated with csp_buffer_get(). When data is received, the packet is passed to CSP using `csp_qfifo_write()` and a new buffer is allocated for the next packet. In addition to the received packet, `csp_qfifo_write()` takes two additional arguments: + +.. code-block:: c + + void csp_qfifo_write(csp_packet_t *packet, csp_iface_t *interface, CSP_BASE_TYPE *pxTaskWoken); + +The calling interface must be passed in `interface` to avoid routing loops. Furthermore, `pxTaskWoken` must be set to a non-NULL value if the packet is received in an interrupt service routine. If the packet is received in task context, NULL must be passed. 'pxTaskWoken' only applies to FreeRTOS systems, and POSIX system should always set the value to NULL. + +`csp_qfifo_write` will either accept the packet or free the packet buffer, so the interface must never free the packet after passing it to CSP. + +Initialization +-------------- + +In order to initialize the interface, and make it available to the router, use the following function found in `csp/csp_interface.h`: + +.. code-block:: c + + csp_route_add_if(&csp_if_fifo); + +This actually happens automatically if you try to call `csp_route_add()` with an interface that is unknown to the router. This may however be removed in the future, in order to ensure that all interfaces are initialised before configuring the routing table. The reason is, that some products released in the future may ship with an empty routing table, which is then configured by a routing protocol rather than a static configuration. + +In order to setup a manual static route, use the following example where the default route is set to the fifo interface: + +.. code-block:: c + + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_fifo, CSP_NODE_MAC); + +All outgoing traffic except loopback, is now passed to the fifo interface's nexthop function. + +Building the example +-------------------- + +The fifo examples can be compiled with: + +.. code-block:: bash + + % gcc csp_if_fifo.c -o csp_if_fifo -I/include -L/build -lcsp -lpthread -lrt + +The two named pipes are created with: + +.. code-block:: bash + + % mkfifo server_to_client client_to_server + diff --git a/gomspace/libgscsp/lib/libcsp/doc/libcsp.rst b/gomspace/libgscsp/lib/libcsp/doc/libcsp.rst new file mode 100644 index 00000000..f866015f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/libcsp.rst @@ -0,0 +1,21 @@ +.. CSP Documentation master file. + +.. _libcsp: + +********************** +CubeSat Space Protocol +********************** + +.. toctree:: + :maxdepth: 3 + + ../README + history + structure + interfaces + memory + protocolstack + topology + mtu + example + diff --git a/gomspace/libgscsp/lib/libcsp/doc/memory.rst b/gomspace/libgscsp/lib/libcsp/doc/memory.rst new file mode 100644 index 00000000..4e38d711 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/memory.rst @@ -0,0 +1,28 @@ +How CSP uses memory +=================== + +CSP has been written for small microprocessor systems. The way memory is handled is therefore a tradeoff between the amount used and the code efficiency. This section tries to give some answers to what the memory is used for and how it it used. The primary memory blocks in use by CSP is: + + * Routing table + * Ports table + * Connection table + * Buffer pool + * Interface list + +Tables +------ +The reason for using tables for the routes, ports and connections is speed. When a new packet arrives the core of CSP needs to do a quick lookup in the connection so see if it can find an existing connection to which the packet matches. If this is not found, it will take a lookup in the ports table to see if there are any applications listening on the incoming port number. Another argument of using tables are pre-allocation. The linker will reserve an area of the memory for which the routes and connections can be stored. This avoid an expensive `malloc()` call during initialization of CSP, and practically costs zero CPU instructions. The downside of using tables are the wasted memory used by unallocated ports and connections. For the routing table the argumentation is the same, pre-allocation is better than calling `malloc()`. + +Buffer Pool +----------- + +The buffer handling system can be compiled for either static allocation or a one-time dynamic allocation of the main memory block. After this, the buffer system is entirely self-contained. All allocated elements are of the same size, so the buffer size must be chosen to be able to handle the maximum possible packet length. The buffer pool uses a queue to store pointers to free buffer elements. First of all, this gives a very quick method to get the next free element since the dequeue is an O(1) operation. Furthermore, since the queue is a protected operating system primitive, it can be accessed from both task-context and interrupt-context. The `csp_buffer_get` version is for task-context and `csp_buffer_get_isr` is for interrupt-context. Using fixed size buffer elements that are preallocated is again a question of speed and safety. + + +A basic concept of the buffer system is called Zero-Copy. This means that from userspace to the kernel-driver, the buffer is never copied from one buffer to another. This is a big deal for a small microprocessor, where a call to `memcpy()` can be very expensive. In practice when data is inserted into a packet, it is shifted a certain number of bytes in order to allow for a packet header to be prepended at the lower layers. This also means that there is a strict contract between the layers, which data can be modified and where. The buffer object is normally casted to a `csp_packet_t`, but when its given to an interface on the MAC layer it's casted to a `csp_i2c_frame_t` for example. + +Interface list +-------------- + +The interface list is a simple single-ended linked list of references to the interface specification structures. These structures are static const and allocated by the linker. The pointer to this data is inserted into the list one time during setup of the interface. Each entry in the routing table has a direct pointer to the interface element, thereby avoiding list lookup, but the list is needed in order for the dynamic route configuration to know which interfaces are available. + diff --git a/gomspace/libgscsp/lib/libcsp/doc/mtu.rst b/gomspace/libgscsp/lib/libcsp/doc/mtu.rst new file mode 100644 index 00000000..27753300 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/mtu.rst @@ -0,0 +1,19 @@ +Maximum Transfer Unit +===================== + +There are two things limiting the MTU of CSP. + + 1. The pre-allocated buffer pool’s allocation size + 2. The link layer protocol. + +So let’s assume that you have made a protocol called KISS with a MTU of 256. The 256 is the total amount of data that you can put into the CSP-packet. However, you need to take the overhead of the link layer into account. Typically this could consist of a length field and/or a start/stop flag. So the actual frame size on the link layer would for example be 256 bytes of data + 2 bytes sync flag + 2 bytes length field. + +This requires a buffer allocation of at lest 256 + 2 + 2. However, the CSP packet itself has some reserved bytes in the beginning of the packet (which you can see in csp.h) - so the recommended buffer allocation size is MAX MTU + 16 bytes. In this case the max MTU would be 256. + +If you try to pass data which is longer than the MTU, the chance is that you will also make a buffer overflow in the CSP buffer pool. However, lets assume that you have two interfaces one with an MTU of 200 bytes and another with an MTU of 100 bytes. In this case you might successfully transfer 150 bytes over the first interface, but the packet will be rejected once it comes to the second interface. + +If you want to increase your MTU of a specific link layer, it is up to the link layer protocol to implement its own fragmentation protocol. A good example is CAN-bus which only allows a frame size of 8 bytes. libcsp have a small protocol for this called the “CAN fragmentation protocol" or CFP for short. This allows data of much larger size to be transferred over the CAN bus. + +Okay, but what if you want to transfer 1000 bytes, and the network maximum MTU is 256? Well, since CSP does not include streaming sockets, only packet’s. Somebody will have to split that data up into chunks. It might be that you application have special knowledge about the datatype you are transmitting, and that it makes sense to split the 1000 byte content into 10 chunks of 100 byte status messages. This, application layer delimitation might be good if you have a situation with packet loss, because your receiver could still make good usage of the partially delivered chunks. + +But, what if you just want 1000 bytes transmitted, and you don’t care about the fragmentation unit, and also don’t want the hassle of writing the fragmentation code yourself? - In this case, libcsp now features a new (still experimental) feature called SFP (small fragmentation protocol) designed to work on the application layer. For this purpose you will not use csp_send and csp_recv, but csp_sfp_send and csp_sfp_recv. This will split your data into chunks of a certain size, enumerate them and transfer over a given connection. If a chunk is missing the SFP client will abort the reception, because SFP does not provide retransmission. If you wish to also have retransmission and orderly delivery you will have to open an RDP connection and send your SFP message to that connection. diff --git a/gomspace/libgscsp/lib/libcsp/doc/protocolstack.rst b/gomspace/libgscsp/lib/libcsp/doc/protocolstack.rst new file mode 100644 index 00000000..365aabbe --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/protocolstack.rst @@ -0,0 +1,54 @@ +The Protocol Stack +================== + +The CSP protocol stack includes functionality on all layers of the TCP/IP model: + +Layer 1: Drivers +---------------- + +Lib CSP is not designed for any specific processor or hardware peripheral, but yet these drivers are required in order to work. The intention of LibCSP is not to provide CAN, I2C or UART drivers for all platforms, however some drivers has been included for some platforms. If you do not find your platform supported, it is quite simple to add a driver that conforms to the CSP interfaces. For example the I2C driver just requires three functions: `init`, `send` and `recv`. For good stability and performance interrupt driven drivers are preferred in favor of polled drivers. Where applicable also DMA usage is recommended. + +Layer 2: MAC interfaces +----------------------- + +CSP has interfaces for I2C, CAN, RS232 (KISS) and Loopback. The layer 2 protocol software defines a frame-format that is suitable for the media. CSP can be easily extended with implementations for even more links. For example a radio-link and IP-networks. The file `csp_interface.h` declares the rx and tx functions needed in order to define a network interface in CSP. During initialisation of CSP each interface will be inserted into a linked list of interfaces that is available to the router. In cases where link-layer addresses are required, such as I2C, the routing table supports specifying next-hop link-layer address directly. This avoids the need to implement an address resolution protocol to translate CSP addresses to I2C addresses. + +Layer 3: Network Router +----------------------- + +The router core is the backbone of the CSP implementation. The router works by looking at a 32-bit CSP header which contains the delivery and source address together with port numbers for the connection. Each router supports both local delivery and forwarding of frames to another destination. Frames will never exit the router on the same interface that they arrives at, this concept is called split horizon, and helps prevent routing loops. + +The main purpose of the router is to accept incoming packets and deliver them to the right message queue. Therefore, in order to listen on a port-number on the network, a task must create a socket and call the accept() call. This will make the task block and wait for incoming traffic, just like a web-server or similar. When an incoming connection is opened, the task is woken. Depending on the task-priority, the task can even preempt another task and start execution immediately. + +There is no routing protocol for automatic route discovery, all routing tables are pre-programmed into the subsystems. The table itself contains a separate route to each of the possible 32 nodes in the network and the additional default route. This means that the overall topology must be decided before putting sub-systems together, as explained in the `topology.md` file. However CSP has an extension on port zero CMP (CSP management protocol), which allows for over-the-network routing table configuration. This has the advantage that default routes could be changed if for example the primary radio fails, and the secondary should be used instead. + +Layer 4: Transport Layer +------------------------ + +LibCSP implements two different Transport Layer protocols, they are called UDP (unreliable datagram protocol) and RDP (reliable datagram protocol). The name UDP has not been chosen to be an exact replica of the UDP (user datagram protocol) known from the TCP/IP model, but they have certain similarities. + +The most important thing to notice is that CSP is entirely a datagram service. There is no stream based service like TCP. A datagram is defined a block of data with a specified size and structure. This block enters the transport layer as a single datagram and exits the transport layer in the other end as a single datagram. CSP preserves this structure all the way to the physical layer for I2C, KISS and Loopback interfaces are used. The CAN-bus interface has to fragment the datagram into CAN-frames of 8 bytes, however only a fully completed datagram will arrive at the receiver. + +UDP +^^^ + +UDP uses a simple transmission model without implicit hand-shaking dialogues for guaranteeing reliability, ordering, or data integrity. Thus, UDP provides an unreliable service and datagrams may arrive out of order, appear duplicated, or go missing without notice. UDP assumes that error checking and correction is either not necessary or performed in the application, avoiding the overhead of such processing at the network interface level. Time-sensitive applications often use UDP because dropping packets is preferable to waiting for delayed packets, which may not be an option in a real-time system. + +UDP is very practical to implement request/reply based communication where a single packet forms the request and a single packet forms the reply. In this case a typical request and wait protocol is used between the client and server, which will simply return an error if a reply is not received within a specified time limit. An error would normally lead to a retransmission of the request from the user or operator which sent the request. + +While UDP is very simple, it also has some limitations. Normally a human in the loop is a good thing when operating the satellite over UDP. But when it comes to larger file transfers, the human becomes the bottleneck. When a high-speed file transfer is initiated data acknowledgment should be done automatically in order to speed up the transfer. This is where the RDP protocol can help. + +RDP +^^^ +CSP provides a transport layer extension called RDP (reliable datagram protocol) which is an implementation of RFC908 and RFC1151. RDP provides a few additional features: + + * Three-way handshake + * Flow Control + * Data-buffering + * Packet re-ordering + * Retransmission + * Windowing + * Extended Acknowledgment + +For more information on this, please refer to RFC908. + diff --git a/gomspace/libgscsp/lib/libcsp/doc/structure.rst b/gomspace/libgscsp/lib/libcsp/doc/structure.rst new file mode 100644 index 00000000..4c9b515c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/structure.rst @@ -0,0 +1,27 @@ +Structure +========= +The Cubesat Space Protocol library is structured as shown in the following table: + +============================= ========================================================================= +**Folder** **Description** +============================= ========================================================================= +libcsp/include/csp Main include files +libcsp/include/csp/arch Architecture include files +libcsp/include/csp/interfaces Interface include files +libcsp/include/csp/drivers Drivers include files +libcsp/src Main modules for CSP: io, router, connections, services +libcsp/src/interfaces Interface modules for CAN, I2C, KISS, LOOP and ZMQHUB +libcsp/src/drivers/can Driver for CAN +libcsp/src/drivers/usart Driver for USART +libcsp/src/arch/freertos FreeRTOS architecture module +libcsp/src/arch/macosx Mac OS X architecture module +libcsp/src/arch/posix Posix architecture module +libcsp/src/arch/windows Windows architecture module +libcsp/src/rtable Routing table module +libcsp/transport Transport module, UDP and RDP +libcsp/crypto Crypto module +libcsp/utils Utilities +libcsp/bindings/python Python wrapper for libcsp +libcsp/examples CSP examples (source code) +libasf/doc The doc folder contains the source code for this documentation +============================= ========================================================================= diff --git a/gomspace/libgscsp/lib/libcsp/doc/topology.rst b/gomspace/libgscsp/lib/libcsp/doc/topology.rst new file mode 100644 index 00000000..e629c29e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/topology.rst @@ -0,0 +1,26 @@ +Network Topology +================ + +CSP uses a network oriented terminology similar to what is known from the Internet and the TCP/IP model. A CSP network can be configured for several different topologies. The most common topology is to create two segments, one for the Satellite and one for the Ground-Station. + +.. code-block:: none + + I2C BUS + _______________________________ + / | | | \ + +---+ +---+ +---+ +---+ +---+ + |OBC| |COM| |EPS| |PL1| |PL2| Nodes 0 - 7 (Space segment) + +---+ +---+ +---+ +---+ +---+ + ^ + | Radio + v + +---+ +----+ + |TNC| ------- | PC | Nodes 8 - 15 (Ground segment) + +---+ USB +----+ + + Node 9 Node 10 + +The address range, from 0 to 15, has been segmented into two equal size segments. This allows for easy routing in the network. All addresses starting with binary 1 is on the ground-segment, and all addresses starting with 0 is on the space segment. From CSP v1.0 the address space has been increased to 32 addresses, 0 to 31. But for legacy purposes, the old 0 to 15 is still used in most products. + +The network is configured using static routes initialised at boot-up of each sub-system. This means that the basic routing table must be assigned compile-time of each subsystem. However each node supports assigning an individual route to every single node in the network and can be changed run-time. This means that the network topology can be easily reconfigured after startup. + diff --git a/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo.c b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo.c new file mode 100644 index 00000000..136fc3aa --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo.c @@ -0,0 +1,165 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TYPE_SERVER 1 +#define TYPE_CLIENT 2 +#define PORT 10 +#define BUF_SIZE 250 + +pthread_t rx_thread; +int rx_channel, tx_channel; + +int csp_fifo_tx(csp_iface_t *ifc, csp_packet_t *packet, uint32_t timeout); + +csp_iface_t csp_if_fifo = { + .name = "fifo", + .nexthop = csp_fifo_tx, + .mtu = BUF_SIZE, +}; + +int csp_fifo_tx(csp_iface_t *ifc, csp_packet_t *packet, uint32_t timeout) { + /* Write packet to fifo */ + if (write(tx_channel, &packet->length, packet->length + sizeof(uint32_t) + sizeof(uint16_t)) < 0) + printf("Failed to write frame\r\n"); + csp_buffer_free(packet); + return CSP_ERR_NONE; +} + +void * fifo_rx(void * parameters) { + csp_packet_t *buf = csp_buffer_get(BUF_SIZE); + /* Wait for packet on fifo */ + while (read(rx_channel, &buf->length, BUF_SIZE) > 0) { + csp_qfifo_write(buf, &csp_if_fifo, NULL); + buf = csp_buffer_get(BUF_SIZE); + } + + return NULL; +} + +int main(int argc, char **argv) { + + int me, other, type; + const char *message = "Testing CSP"; + const char *rx_channel_name; + const char *tx_channel_name; + csp_socket_t *sock; + csp_conn_t *conn; + csp_packet_t *packet; + + /* Run as either server or client */ + if (argc != 2) { + printf("usage: %s \r\n", argv[0]); + return -1; + } + + /* Set type */ + if (strcmp(argv[1], "server") == 0) { + me = 1; + other = 2; + tx_channel_name = "server_to_client"; + rx_channel_name = "client_to_server"; + type = TYPE_SERVER; + } else if (strcmp(argv[1], "client") == 0) { + me = 2; + other = 1; + tx_channel_name = "client_to_server"; + rx_channel_name = "server_to_client"; + type = TYPE_CLIENT; + } else { + printf("Invalid type. Must be either 'server' or 'client'\r\n"); + return -1; + } + + /* Init CSP and CSP buffer system */ + if (csp_init(me) != CSP_ERR_NONE || csp_buffer_init(10, 300) != CSP_ERR_NONE) { + printf("Failed to init CSP\r\n"); + return -1; + } + + tx_channel = open(tx_channel_name, O_RDWR); + if (tx_channel < 0) { + printf("Failed to open TX channel\r\n"); + return -1; + } + + rx_channel = open(rx_channel_name, O_RDWR); + if (rx_channel < 0) { + printf("Failed to open RX channel\r\n"); + return -1; + } + + /* Start fifo RX task */ + pthread_create(&rx_thread, NULL, fifo_rx, NULL); + + /* Set default route and start router */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_fifo, CSP_NODE_MAC); + csp_route_start_task(0, 0); + + /* Create socket and listen for incoming connections */ + if (type == TYPE_SERVER) { + sock = csp_socket(CSP_SO_NONE); + csp_bind(sock, PORT); + csp_listen(sock, 5); + } + + /* Super loop */ + while (1) { + if (type == TYPE_SERVER) { + /* Process incoming packet */ + conn = csp_accept(sock, 1000); + if (conn) { + packet = csp_read(conn, 0); + if (packet) + printf("Received: %s\r\n", packet->data); + csp_buffer_free(packet); + csp_close(conn); + } + } else { + /* Send a new packet */ + packet = csp_buffer_get(strlen(message)); + if (packet) { + strcpy((char *) packet->data, message); + packet->length = strlen(message); + + conn = csp_connect(CSP_PRIO_NORM, other, PORT, 1000, CSP_O_NONE); + printf("Sending: %s\r\n", message); + if (!conn || !csp_send(conn, packet, 1000)) + return -1; + csp_close(conn); + } + sleep(1); + } + } + + close(rx_channel); + close(tx_channel); + + return 0; +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo_windows.c b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo_windows.c new file mode 100644 index 00000000..5b360709 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo_windows.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#undef interface + +#include +#include + +#define PIPE_BUFSIZE 1024 + +#define TYPE_SERVER 1 +#define TYPE_CLIENT 2 +#define PORT 10 +#define BUF_SIZE 250 + + +static LPCTSTR pipeName = TEXT("\\\\.\\pipe\\CSP_Pipe"); + +static HANDLE pipe = INVALID_HANDLE_VALUE; + +unsigned WINAPI fifo_rx(void *); +unsigned WINAPI pipe_listener(void *); + +void printError(void); + +int csp_fifo_tx(csp_packet_t *packet, uint32_t timeout); + +csp_iface_t csp_if_fifo = { + .name = "fifo", + .nexthop = csp_fifo_tx, + .mtu = BUF_SIZE, +}; + +int csp_fifo_tx(csp_packet_t *packet, uint32_t timeout) { + printf("csp_fifo_tx tid: %lu\n", GetCurrentThreadId()); + DWORD expectedSent = packet->length + sizeof(uint32_t) + sizeof(uint16_t); + DWORD actualSent; + /* Write packet to fifo */ + if( !WriteFile(pipe, &packet->length, expectedSent, &actualSent, NULL) + || actualSent != expectedSent ) { + printError(); + } + + csp_buffer_free(packet); + return CSP_ERR_NONE; +} + + +int main(int argc, char *argv[]) { + int me, other, type; + char *message = "Testing CSP"; + csp_socket_t *sock = NULL; + csp_conn_t *conn = NULL; + csp_packet_t *packet = NULL; + + /* Run as either server or client */ + if (argc != 2) { + printf("usage: server \r\n"); + return -1; + } + + /* Set type */ + if (strcmp(argv[1], "server") == 0) { + me = 1; + other = 2; + type = TYPE_SERVER; + } else if (strcmp(argv[1], "client") == 0) { + me = 2; + other = 1; + type = TYPE_CLIENT; + } else { + printf("Invalid type. Must be either 'server' or 'client'\r\n"); + return -1; + } + + /* Init CSP and CSP buffer system */ + if (csp_init(me) != CSP_ERR_NONE || csp_buffer_init(10, 300) != CSP_ERR_NONE) { + printf("Failed to init CSP\r\n"); + return -1; + } + + if( type == TYPE_SERVER ) { + _beginthreadex(NULL, 0, pipe_listener, NULL, 0, 0); + } else { + pipe = CreateFile( + pipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + if( pipe == INVALID_HANDLE_VALUE ) { + printError(); + return -1; + } + } + + /* Set default route and start router */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_fifo, CSP_NODE_MAC); + csp_route_start_task(0, 0); + + /* Create socket and listen for incoming connections */ + if (type == TYPE_SERVER) { + sock = csp_socket(CSP_SO_NONE); + csp_bind(sock, PORT); + csp_listen(sock, 5); + } + + /* Super loop */ + while (1) { + if (type == TYPE_SERVER) { + /* Process incoming packet */ + conn = csp_accept(sock, 1000); + if (conn) { + packet = csp_read(conn, 0); + if (packet) + printf("Received: %s\r\n", packet->data); + csp_buffer_free(packet); + csp_close(conn); + } + } else { + /* Send a new packet */ + packet = csp_buffer_get(strlen(message)); + if (packet) { + strcpy((char *) packet->data, message); + packet->length = strlen(message); + + conn = csp_connect(CSP_PRIO_NORM, other, PORT, 1000, CSP_O_NONE); + printf("Sending: %s\r\n", message); + if (!conn || !csp_send(conn, packet, 1000)) + return -1; + csp_close(conn); + Sleep(1000); + } + } + } + + return 0; +} + +void printError(void) { + LPTSTR messageBuffer = NULL; + DWORD errorCode = GetLastError(); + DWORD formatMessageRet; + formatMessageRet = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&messageBuffer, + 0, + NULL); + + if( !formatMessageRet ) { + wprintf(L"FormatMessage error, code: %lu\n", GetLastError()); + return; + } + + printf("%s\n", messageBuffer); + LocalFree(messageBuffer); +} + +unsigned WINAPI pipe_listener(void *parameters) { + while(1) { + HANDLE pipe = CreateNamedPipe( + pipeName, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + PIPE_BUFSIZE, + PIPE_BUFSIZE, + 0, + NULL); + BOOL clientConnected; + if( pipe == INVALID_HANDLE_VALUE ) { + printf("Error creating named pipe. Code %lu\n", GetLastError()); + return -1; + } + + // True if client connects *after* server called ConnectNamedPipe + // or *between* CreateNamedPipe and ConnectNamedPipe + clientConnected = + ConnectNamedPipe(pipe, NULL) ? TRUE : GetLastError()==ERROR_PIPE_CONNECTED; + printf("Client connected!\n"); + + if( !clientConnected ) { + printf("Failure while listening for clients. Code %lu\n", GetLastError()); + CloseHandle(pipe); + return -1; + } + printf("Create client thread\n"); + _beginthreadex(NULL, 0, fifo_rx, (PVOID)pipe, 0, 0); + } + + return 0; +} + +unsigned WINAPI fifo_rx(void *handle) { + printf("fifo_rx tid: %lu\n", GetCurrentThreadId()); + HANDLE pipe = (HANDLE) handle; + csp_packet_t *buf = csp_buffer_get(BUF_SIZE); + DWORD bytesRead; + BOOL readSuccess; + + while(1) { + readSuccess = + ReadFile(pipe, &buf->length, BUF_SIZE, &bytesRead, NULL); + if( !readSuccess || bytesRead == 0 ) { + csp_buffer_free(buf); + printError(); + break; + } + csp_qfifo_write(buf, &csp_if_fifo, NULL); + buf = csp_buffer_get(BUF_SIZE); + } + printf("Closing pipe to client\n"); + CloseHandle(pipe); + + return 0; +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/kiss.c b/gomspace/libgscsp/lib/libcsp/examples/kiss.c new file mode 100644 index 00000000..c95eb2aa --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/kiss.c @@ -0,0 +1,151 @@ +/** + * Build this example on linux with: + * ./waf configure --enable-examples --enable-if-kiss --with-driver-usart=linux --enable-crc32 clean build + */ + +#include +#include +#include + +#include +#include + +#define PORT 10 +#define MY_ADDRESS 1 + +#define SERVER_TIDX 0 +#define CLIENT_TIDX 1 +#define USART_HANDLE 0 + +CSP_DEFINE_TASK(task_server) { + int running = 1; + csp_socket_t *socket = csp_socket(CSP_SO_NONE); + csp_conn_t *conn; + csp_packet_t *packet; + csp_packet_t *response; + + response = csp_buffer_get(sizeof(csp_packet_t) + 2); + if( response == NULL ) { + fprintf(stderr, "Could not allocate memory for response packet!\n"); + return CSP_TASK_RETURN; + } + response->data[0] = 'O'; + response->data[1] = 'K'; + response->length = 2; + + csp_bind(socket, CSP_ANY); + csp_listen(socket, 5); + + printf("Server task started\r\n"); + + while(running) { + if( (conn = csp_accept(socket, 10000)) == NULL ) { + continue; + } + + while( (packet = csp_read(conn, 100)) != NULL ) { + switch( csp_conn_dport(conn) ) { + case PORT: + if( packet->data[0] == 'q' ) + running = 0; + csp_buffer_free(packet); + csp_send(conn, response, 1000); + break; + default: + csp_service_handler(conn, packet); + break; + } + } + + csp_close(conn); + } + + csp_buffer_free(response); + + return CSP_TASK_RETURN; +} + +CSP_DEFINE_TASK(task_client) { + + char outbuf = 'q'; + char inbuf[3] = {0}; + int pingResult; + + for(int i = 50; i <= 200; i+= 50) { + pingResult = csp_ping(MY_ADDRESS, 1000, 100, CSP_O_NONE); + printf("Ping with payload of %d bytes, took %d ms\n", i, pingResult); + csp_sleep_ms(1000); + } + csp_ps(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + csp_memfree(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + csp_buf_free(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + csp_uptime(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + + csp_transaction(0, MY_ADDRESS, PORT, 1000, &outbuf, 1, inbuf, 2); + printf("Quit response from server: %s\n", inbuf); + + return CSP_TASK_RETURN; +} + +int main(int argc, char **argv) { + csp_debug_toggle_level(CSP_PACKET); + csp_debug_toggle_level(CSP_INFO); + + csp_buffer_init(10, 300); + csp_init(MY_ADDRESS); + + struct usart_conf conf; + +#if defined(CSP_WINDOWS) + conf.device = argc != 2 ? "COM4" : argv[1]; + conf.baudrate = CBR_9600; + conf.databits = 8; + conf.paritysetting = NOPARITY; + conf.stopbits = ONESTOPBIT; + conf.checkparity = FALSE; +#elif defined(CSP_POSIX) + conf.device = argc != 2 ? "/dev/ttyUSB0" : argv[1]; + conf.baudrate = 500000; +#elif defined(CSP_MACOSX) + conf.device = argc != 2 ? "/dev/tty.usbserial-FTSM9EGE" : argv[1]; + conf.baudrate = 115200; +#endif + + /* Run USART init */ + usart_init(&conf); + + /* Setup CSP interface */ + static csp_iface_t csp_if_kiss; + static csp_kiss_handle_t csp_kiss_driver; + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, usart_putc, usart_insert, "KISS"); + + /* Setup callback from USART RX to KISS RS */ + void my_usart_rx(uint8_t * buf, int len, void * pxTaskWoken) { + csp_kiss_rx(&csp_if_kiss, buf, len, pxTaskWoken); + } + usart_set_callback(my_usart_rx); + + csp_route_set(MY_ADDRESS, &csp_if_kiss, CSP_NODE_MAC); + csp_route_start_task(0, 0); + + csp_conn_print_table(); + csp_route_print_table(); + csp_route_print_interfaces(); + + csp_thread_handle_t handle_server; + csp_thread_create(task_server, "SERVER", 1000, NULL, 0, &handle_server); + csp_thread_handle_t handle_client; + csp_thread_create(task_client, "CLIENT", 1000, NULL, 0, &handle_client); + + /* Wait for program to terminate (ctrl + c) */ + while(1) { + csp_sleep_ms(1000000); + } + + return 0; + +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client.py b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client.py new file mode 100644 index 00000000..123ce36e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client.py @@ -0,0 +1,42 @@ +#!/usr/bin/python + +# libcsp must be build with at least these options to run this example client: +# ./waf distclean configure build --enable-bindings --enable-crc32 --enable-rdp --enable-if-zmq --with-driver-usart=linux --enable-if-kiss --enable-xtea --enable-if-can --enable-can-socketcan --enable-hmac --enable-examples + +# Can be run from root of libcsp like this: +# LD_LIBRARY_PATH=build PYTHONPATH=bindings/python:build python examples/python_bindings_example_client.py +# + +import os +import time +import libcsp as csp + + +if __name__ == "__main__": + + csp.buffer_init(10, 300) + csp.init(28) + csp.zmqhub_init(28, "localhost") + csp.rtable_set(27, 5, "ZMQHUB") + csp.route_start_task() + + ## allow router task startup + time.sleep(1) + + ## cmp_ident + (rc, host, model, rev, date, time) = csp.cmp_ident(27) + if rc == csp.CSP_ERR_NONE: + print (host, model, rev, date, time) + else: + print ("error in cmp_ident, rc=%i" % (rc)) + + ## transaction + outbuf = bytearray().fromhex('01') + inbuf = bytearray(1) + print ("using csp_transaction to send a single byte") + if csp.transaction(0, 27, 10, 1000, outbuf, inbuf) < 1: + print ("csp_transaction failed") + else: + print ("got reply, data=" + ''.join('{:02x}'.format(x) for x in inbuf)) + + diff --git a/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client_can.py b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client_can.py new file mode 100644 index 00000000..ec796572 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client_can.py @@ -0,0 +1,30 @@ +#!/usr/bin/python + +# libcsp must be build with at least these options to run this example client: +# ./waf distclean configure build --enable-bindings --enable-crc32 --enable-rdp --enable-if-zmq --with-driver-usart=linux --enable-if-kiss --enable-xtea --enable-if-can --enable-can-socketcan --enable-hmac --enable-examples + +# Can be run from root of libcsp like this: +# LD_LIBRARY_PATH=build PYTHONPATH=bindings/python:build python examples/python_bindings_example_client.py +# + +import os +import time +import libcsp as csp + + +if __name__ == "__main__": + + csp.buffer_init(10, 300) + csp.init(28) + csp.can_socketcan_init("can0") + csp.rtable_set(4, 5, "CAN") + csp.route_start_task() + + ## allow router task startup + time.sleep(1) + + + node = 4 + if csp.ping(node) < 0: + print ("Unable to ping node %d"%(node)) + diff --git a/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_server.py b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_server.py new file mode 100644 index 00000000..3cf3f5da --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_server.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +# libcsp must be build with at least these options to run this example server: +# ./waf distclean configure build --enable-bindings --enable-crc32 --enable-rdp --enable-if-zmq --with-driver-usart=linux --enable-if-kiss --enable-xtea --enable-if-can --enable-can-socketcan --enable-hmac --enable-examples + +# Can be run from root of libcsp like this: +# LD_LIBRARY_PATH=build PYTHONPATH=bindings/python:build python examples/python_bindings_example_server.py +# + +import os +import time +import sys +import libcsp as csp +import subprocess + +if __name__ == "__main__": + + # start a zmqproxy to transport messages to and from the client + zmqp = subprocess.Popen('build/zmqproxy') + + # init csp + csp.buffer_init(10, 300) + csp.init(27) + csp.zmqhub_init(27, "localhost") + csp.rtable_set(28, 5, "ZMQHUB") + csp.route_start_task() + + # set identity + csp.set_hostname("test_service") + csp.set_model("bindings") + csp.set_revision("1.2.3") + + # and read it back + print (csp.get_hostname()) + print (csp.get_model()) + print (csp.get_revision()) + + # start listening for packets... + sock = csp.socket() + csp.bind(sock, csp.CSP_ANY) + csp.listen(sock) + while True: + conn = csp.accept(sock) + if not conn: + continue + + print ("connection: source=%i:%i, dest=%i:%i" % (csp.conn_src(conn), + csp.conn_sport(conn), + csp.conn_dst(conn), + csp.conn_dport(conn))) + + while True: + packet = csp.read(conn) + if not packet: + break + + if csp.conn_dport(conn) == 10: + data = bytearray(csp.packet_get_data(packet)) + length = csp.packet_get_length(packet) + print ("got packet, len=" + str(length) + ", data=" + ''.join('{:02x}'.format(x) for x in data)) + + data[0] = data[0] + 1 + reply_packet = csp.buffer_get(1) + if reply_packet: + csp.packet_set_data(reply_packet, data) + csp.sendto_reply(packet, reply_packet, csp.CSP_O_NONE) + + csp.buffer_free(packet) + else: + csp.service_handler(conn, packet) + csp.close(conn) + diff --git a/gomspace/libgscsp/lib/libcsp/examples/simple.c b/gomspace/libgscsp/lib/libcsp/examples/simple.c new file mode 100644 index 00000000..b996f8c1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/simple.c @@ -0,0 +1,200 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include + +/* Using un-exported header file. + * This is allowed since we are still in libcsp */ +#include + +/** Example defines */ +#define MY_ADDRESS 1 // Address of local CSP node +#define MY_PORT 10 // Port to send test traffic to + +CSP_DEFINE_TASK(task_server) { + + /* Create socket without any socket options */ + csp_socket_t *sock = csp_socket(CSP_SO_NONE); + + /* Bind all ports to socket */ + csp_bind(sock, CSP_ANY); + + /* Create 10 connections backlog queue */ + csp_listen(sock, 10); + + /* Pointer to current connection and packet */ + csp_conn_t *conn; + csp_packet_t *packet; + + /* Process incoming connections */ + while (1) { + + /* Wait for connection, 10000 ms timeout */ + if ((conn = csp_accept(sock, 10000)) == NULL) + continue; + + /* Read packets. Timout is 100 ms */ + while ((packet = csp_read(conn, 100)) != NULL) { + switch (csp_conn_dport(conn)) { + case MY_PORT: + /* Process packet here */ + printf("Packet received on MY_PORT: %s\r\n", (char *) packet->data); + csp_buffer_free(packet); + break; + + default: + /* Let the service handler reply pings, buffer use, etc. */ + csp_service_handler(conn, packet); + break; + } + } + + /* Close current connection, and handle next */ + csp_close(conn); + + } + + return CSP_TASK_RETURN; + +} + +CSP_DEFINE_TASK(task_client) { + + csp_packet_t * packet; + csp_conn_t * conn; + + while (1) { + + /** + * Try ping + */ + + csp_sleep_ms(1000); + + int result = csp_ping(MY_ADDRESS, 100, 100, CSP_O_NONE); + printf("Ping result %d [ms]\r\n", result); + + csp_sleep_ms(1000); + + /** + * Try data packet to server + */ + + /* Get packet buffer for data */ + packet = csp_buffer_get(100); + if (packet == NULL) { + /* Could not get buffer element */ + printf("Failed to get buffer element\n"); + return CSP_TASK_RETURN; + } + + /* Connect to host HOST, port PORT with regular UDP-like protocol and 1000 ms timeout */ + conn = csp_connect(CSP_PRIO_NORM, MY_ADDRESS, MY_PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + /* Connect failed */ + printf("Connection failed\n"); + /* Remember to free packet buffer */ + csp_buffer_free(packet); + return CSP_TASK_RETURN; + } + + /* Copy dummy data to packet */ + const char *msg = "Hello World"; + strcpy((char *) packet->data, msg); + + /* Set packet length */ + packet->length = strlen(msg); + + /* Send packet */ + if (!csp_send(conn, packet, 1000)) { + /* Send failed */ + printf("Send failed\n"); + csp_buffer_free(packet); + } + + /* Close connection */ + csp_close(conn); + + } + + return CSP_TASK_RETURN; +} + +int main(int argc, char * argv[]) { + + /** + * Initialise CSP, + * No physical interfaces are initialised in this example, + * so only the loopback interface is registered. + */ + + /* Init buffer system with 10 packets of maximum 300 bytes each */ + printf("Initialising CSP\r\n"); + csp_buffer_init(5, 300); + + /* Init CSP with address MY_ADDRESS */ + csp_init(MY_ADDRESS); + + /* Start router task with 500 word stack, OS task priority 1 */ + csp_route_start_task(500, 1); + + /* Enable debug output from CSP */ + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) { + printf("Debug enabed\r\n"); + csp_debug_toggle_level(3); + csp_debug_toggle_level(4); + + printf("Conn table\r\n"); + csp_conn_print_table(); + + printf("Route table\r\n"); + csp_route_print_table(); + + printf("Interfaces\r\n"); + csp_route_print_interfaces(); + + } + + /** + * Initialise example threads, using pthreads. + */ + + /* Server */ + printf("Starting Server task\r\n"); + csp_thread_handle_t handle_server; + csp_thread_create(task_server, "SERVER", 1000, NULL, 0, &handle_server); + + /* Client */ + printf("Starting Client task\r\n"); + csp_thread_handle_t handle_client; + csp_thread_create(task_client, "SERVER", 1000, NULL, 0, &handle_client); + + /* Wait for execution to end (ctrl+c) */ + while(1) { + csp_sleep_ms(100000); + } + + return 0; + +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/zmqproxy.c b/gomspace/libgscsp/lib/libcsp/examples/zmqproxy.c new file mode 100644 index 00000000..5e259579 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/zmqproxy.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include +#include + +static void * task_capture(void *ctx) { + + /* Subscriber (RX) */ + void *subscriber = zmq_socket(ctx, ZMQ_SUB); + assert(zmq_connect(subscriber, "tcp://localhost:7000") == 0); + assert(zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0) == 0); + + while (1) { + zmq_msg_t msg; + zmq_msg_init_size(&msg, 1024); + + /* Receive data */ + if (zmq_msg_recv(&msg, subscriber, 0) < 0) { + zmq_msg_close(&msg); + csp_log_error("ZMQ: %s\r\n", zmq_strerror(zmq_errno())); + continue; + } + + int datalen = zmq_msg_size(&msg); + if (datalen < 5) { + csp_log_warn("ZMQ: Too short datalen: %u\r\n", datalen); + while(zmq_msg_recv(&msg, subscriber, ZMQ_NOBLOCK) > 0) + zmq_msg_close(&msg); + continue; + } + + /* Create new csp packet */ + csp_packet_t * packet = malloc(1024); + if (packet == NULL) { + zmq_msg_close(&msg); + continue; + } + + /* Copy the data from zmq to csp */ + char * satidptr = ((char *) &packet->id) - 1; + memcpy(satidptr, zmq_msg_data(&msg), datalen); + packet->length = datalen - 4 - 1; + + printf("Input: Src %u, Dst %u, Dport %u, Sport %u, Pri %u, Flags 0x%02X, Size %"PRIu16"\r\n", + packet->id.src, packet->id.dst, packet->id.dport, + packet->id.sport, packet->id.pri, packet->id.flags, packet->length); + + free(packet); + zmq_msg_close(&msg); + } +} + +int main(int argc, char ** argv) { + + /** + * ZMQ PROXY + */ + void * ctx = zmq_ctx_new(); + assert(ctx); + + void *frontend = zmq_socket(ctx, ZMQ_XSUB); + assert(frontend); + assert(zmq_bind (frontend, "tcp://*:6000") == 0); + + void *backend = zmq_socket(ctx, ZMQ_XPUB); + assert(backend); + assert(zmq_bind(backend, "tcp://*:7000") == 0); + + pthread_t capworker; + pthread_create(&capworker, NULL, task_capture, ctx); + + printf("Starting ZMQproxy\r\n"); + zmq_proxy(frontend, backend, NULL); + + printf("Closing ZMQproxy\r\n"); + zmq_ctx_destroy(ctx); + return 0; + +} diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_clock.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_clock.h new file mode 100644 index 00000000..3c19c887 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_clock.h @@ -0,0 +1,60 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CLOCK_H_ +#define _CSP_CLOCK_H_ + +/** + @file + + Clock API. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + Cross-platform timestamp. +*/ +typedef struct { + //! Seconds + uint32_t tv_sec; + //! Nano-seconds. + uint32_t tv_nsec; +} csp_timestamp_t; + +/** + Get time - must be implemented by the user. +*/ +__attribute__((weak)) extern void clock_get_time(csp_timestamp_t * time); + +/** + Set time - must be implemented by the user. +*/ +__attribute__((weak)) extern void clock_set_time(csp_timestamp_t * time); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_CLOCK_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_malloc.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_malloc.h new file mode 100644 index 00000000..12602d1b --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_malloc.h @@ -0,0 +1,39 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_MALLOC_H_ +#define _CSP_MALLOC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +void * csp_malloc(size_t size); +void csp_free(void * ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_MALLOC_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_queue.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_queue.h new file mode 100644 index 00000000..3156c05e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_queue.h @@ -0,0 +1,49 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_QUEUE_H_ +#define _CSP_QUEUE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_QUEUE_FULL 0 +#define CSP_QUEUE_ERROR 0 +#define CSP_QUEUE_OK 1 +typedef void * csp_queue_handle_t; + +#include +#include + +csp_queue_handle_t csp_queue_create(int length, size_t item_size); +void csp_queue_remove(csp_queue_handle_t queue); +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout); +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken); +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout); +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void * buf, CSP_BASE_TYPE * task_woken); +int csp_queue_size(csp_queue_handle_t handle); +int csp_queue_size_isr(csp_queue_handle_t handle); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_QUEUE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_semaphore.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_semaphore.h new file mode 100644 index 00000000..c8068da2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_semaphore.h @@ -0,0 +1,109 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_SEMAPHORE_H_ +#define _CSP_SEMAPHORE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* POSIX interface */ +#if defined(CSP_POSIX) + +#include +#include + +#define CSP_SEMAPHORE_OK 1 +#define CSP_SEMAPHORE_ERROR 2 +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef sem_t csp_bin_sem_handle_t; +typedef pthread_mutex_t csp_mutex_t; + +#endif // CSP_POSIX + +/* MAC OS X interface */ +#if defined(CSP_MACOSX) + +#include +#include "posix/pthread_queue.h" + +#define CSP_SEMAPHORE_OK PTHREAD_QUEUE_OK +#define CSP_SEMAPHORE_ERROR PTHREAD_QUEUE_EMPTY +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef pthread_queue_t * csp_bin_sem_handle_t; +typedef pthread_queue_t * csp_mutex_t; + +#endif // CSP_MACOSX + +#if defined(CSP_WINDOWS) + +#include +#undef interface + +#define CSP_SEMAPHORE_OK 1 +#define CSP_SEMAPHORE_ERROR 2 +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef HANDLE csp_bin_sem_handle_t; +typedef HANDLE csp_mutex_t; + +#endif + +/* FreeRTOS interface */ +#if defined(CSP_FREERTOS) + +#include +#include + +#define CSP_SEMAPHORE_OK pdPASS +#define CSP_SEMAPHORE_ERROR pdFAIL +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef xSemaphoreHandle csp_bin_sem_handle_t; +typedef xSemaphoreHandle csp_mutex_t; + +#endif // CSP_FREERTOS + +int csp_mutex_create(csp_mutex_t * mutex); +int csp_mutex_remove(csp_mutex_t * mutex); +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout); +int csp_mutex_unlock(csp_mutex_t * mutex); +int csp_bin_sem_create(csp_bin_sem_handle_t * sem); +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem); +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout); +int csp_bin_sem_post(csp_bin_sem_handle_t * sem); +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_SEMAPHORE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_system.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_system.h new file mode 100644 index 00000000..c6c0e5af --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_system.h @@ -0,0 +1,74 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_SYSTEM_H_ +#define _CSP_SYSTEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define COLOR_MASK_COLOR 0x0F +#define COLOR_MASK_MODIFIER 0xF0 + +typedef enum { + /* Colors */ + COLOR_RESET = 0xF0, + COLOR_BLACK = 0x01, + COLOR_RED = 0x02, + COLOR_GREEN = 0x03, + COLOR_YELLOW = 0x04, + COLOR_BLUE = 0x05, + COLOR_MAGENTA = 0x06, + COLOR_CYAN = 0x07, + COLOR_WHITE = 0x08, + /* Modifiers */ + COLOR_NORMAL = 0x0F, + COLOR_BOLD = 0x10, + COLOR_UNDERLINE = 0x20, + COLOR_BLINK = 0x30, + COLOR_HIDE = 0x40, +} csp_color_t; + +/** + * Writes out a task list into a pre-allocate buffer, + * use csp_sys_tasklist_size to get sizeof buffer to allocate + * @param out pointer to output buffer + * @return + */ +int csp_sys_tasklist(char * out); + +/** + * @return Size of tasklist buffer to allocate for the csp_sys_tasklist call + */ +int csp_sys_tasklist_size(void); + +uint32_t csp_sys_memfree(void); +int csp_sys_reboot(void); +int csp_sys_shutdown(void); +void csp_sys_set_color(csp_color_t color); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_SYSTEM_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_thread.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_thread.h new file mode 100644 index 00000000..3c6ea171 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_thread.h @@ -0,0 +1,100 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_THREAD_H_ +#define _CSP_THREAD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* POSIX interface */ +#if defined(CSP_POSIX) || defined(CSP_MACOSX) + +#include +#include + +#define csp_thread_exit() pthread_exit(NULL) + +typedef pthread_t csp_thread_handle_t; +typedef void * csp_thread_return_t; + +#define CSP_DEFINE_TASK(task_name) csp_thread_return_t task_name(void * param) +#define CSP_TASK_RETURN NULL + +#define csp_sleep_ms(time_ms) usleep(time_ms * 1000); + +#endif // CSP_POSIX + +/* Windows interface */ +#if defined(CSP_WINDOWS) + +#include +#undef interface +#include + +#define csp_thread_exit() _endthreadex(0) + +typedef HANDLE csp_thread_handle_t; +typedef unsigned int csp_thread_return_t; + +#define CSP_DEFINE_TASK(task_name) csp_thread_return_t __attribute__((stdcall)) task_name(void * param) +#define CSP_TASK_RETURN 0 + +#define csp_sleep_ms(time_ms) Sleep(time_ms); + +#endif // CSP_WINDOWS + +/* FreeRTOS interface */ +#if defined(CSP_FREERTOS) + +#include +#include + +#if INCLUDE_vTaskDelete +#define csp_thread_exit() vTaskDelete(NULL) +#else +#define csp_thread_exit() +#endif + +typedef xTaskHandle csp_thread_handle_t; +typedef void csp_thread_return_t; + +#define CSP_DEFINE_TASK(task_name) csp_thread_return_t task_name(void * param) +#define CSP_TASK_RETURN + +#define csp_sleep_ms(time_ms) vTaskDelay(time_ms / portTICK_RATE_MS); + +#endif // CSP_FREERTOS + +#ifndef CSP_WINDOWS +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle); +#else +int csp_thread_create(csp_thread_return_t (* routine)(void *)__attribute__((stdcall)), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_THREAD_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_time.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_time.h new file mode 100644 index 00000000..aa72ab8f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_time.h @@ -0,0 +1,57 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_TIME_H_ +#define _CSP_TIME_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Blackfin/x86 on Linux */ +#if defined(CSP_POSIX) + +#include +#include +#include + +#endif // CSP_POSIX + +/* AVR/ARM on FreeRTOS */ +#if defined(CSP_FREERTOS) + +#include +#include + +#endif // CSP_FREERTOS + +uint32_t csp_get_ms(void); +uint32_t csp_get_ms_isr(void); +uint32_t csp_get_s(void); +uint32_t csp_get_s_isr(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_TIME_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/posix/pthread_queue.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/posix/pthread_queue.h new file mode 100644 index 00000000..44ef596e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/posix/pthread_queue.h @@ -0,0 +1,118 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _PTHREAD_QUEUE_H_ +#define _PTHREAD_QUEUE_H_ + +/** + @file + + Queue implemented using pthread locks and conds. + + Inspired by c-pthread-queue by Matthew Dickinson: http://code.google.com/p/c-pthread-queue/ +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +/** + Queue error codes. + @{ +*/ +/** + General error code - something went wrong. +*/ +#define PTHREAD_QUEUE_ERROR CSP_QUEUE_ERROR +/** + Queue is empty - cannot extract element. +*/ +#define PTHREAD_QUEUE_EMPTY CSP_QUEUE_ERROR +/** + Queue is full - cannot insert element. +*/ +#define PTHREAD_QUEUE_FULL CSP_QUEUE_ERROR +/** + Ok - no error. +*/ +#define PTHREAD_QUEUE_OK CSP_QUEUE_OK +/** @{ */ + +/** + Queue handle. +*/ +typedef struct pthread_queue_s { + //! Memory area. + void * buffer; + //! Memory size. + int size; + //! Item/element size. + int item_size; + //! Items/elements in queue. + int items; + //! Insert point. + int in; + //! Extract point. + int out; + //! Lock. + pthread_mutex_t mutex; + //! Wait because queue is full (insert). + pthread_cond_t cond_full; + //! Wait because queue is empty (extract). + pthread_cond_t cond_empty; +} pthread_queue_t; + +/** + Create queue. +*/ +pthread_queue_t * pthread_queue_create(int length, size_t item_size); + +/** + Delete queue. +*/ +void pthread_queue_delete(pthread_queue_t * q); + +/** + Enqueue/insert element. +*/ +int pthread_queue_enqueue(pthread_queue_t * queue, void * value, uint32_t timeout); + +/** + Dequeue/extract element. +*/ +int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout); + +/** + Return number of elements in the queue. +*/ +int pthread_queue_items(pthread_queue_t * queue); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _PTHREAD_QUEUE_H_ + diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_hmac.h b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_hmac.h new file mode 100644 index 00000000..8c3f5d6a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_hmac.h @@ -0,0 +1,73 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_HMAC_H_ +#define _CSP_HMAC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_HMAC_LENGTH 4 + +/** + * Append HMAC to packet + * @param packet Pointer to packet + * @param include_header use header in hmac calculation (this will not modify the flags field) + * @return 0 on success, negative on failure + */ +int csp_hmac_append(csp_packet_t * packet, bool include_header); + +/** + * Verify HMAC of packet + * @param packet Pointer to packet + * @param include_header use header in hmac calculation (this will not modify the flags field) + * @return 0 on success, negative on failure + */ +int csp_hmac_verify(csp_packet_t * packet, bool include_header); + +/** + * Calculate HMAC on buffer + * + * This function is used by append/verify but cal also be called separately. + * @param key HMAC key + * @param keylen HMAC key length + * @param data pointer to data + * @param datalen lehgth of data + * @param hmac output HMAC calculation (CSP_HMAC_LENGTH) + * @return 0 on success, negative on failure + */ +int csp_hmac_memory(const uint8_t * key, uint32_t keylen, const uint8_t * data, uint32_t datalen, uint8_t * hmac); + +/** + * Save a copy of the key string for use by the append/verify functions + * @param key HMAC key + * @param keylen HMAC key length + * @return Always returns 0 + */ +int csp_hmac_set_key(char * key, uint32_t keylen); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_HMAC_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_sha1.h b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_sha1.h new file mode 100644 index 00000000..aa7e7a0d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_sha1.h @@ -0,0 +1,81 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_SHA1_H_ +#define _CSP_SHA1_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* The SHA1 block and message digest size in bytes */ +#define SHA1_BLOCKSIZE 64 +#define SHA1_DIGESTSIZE 20 + +/** + SHA1 state structure +*/ +typedef struct { + //! Internal SHA1 state. + uint64_t length; + //! Internal SHA1 state. + uint32_t state[5]; + //! Internal SHA1 state. + uint32_t curlen; + //! Internal SHA1 state. + uint8_t buf[SHA1_BLOCKSIZE]; +} csp_sha1_state; + +/** + * Initialize the hash state + * @param sha1 The hash state you wish to initialize + */ +void csp_sha1_init(csp_sha1_state * sha1); + +/** + * Process a block of memory though the hash + * @param sha1 The hash state + * @param in The data to hash + * @param inlen The length of the data (octets) + */ +void csp_sha1_process(csp_sha1_state * sha1, const uint8_t * in, uint32_t inlen); + +/** + * Terminate the hash to get the digest + * @param sha1 The hash state + * @param out [out] The destination of the hash (20 bytes) + */ +void csp_sha1_done(csp_sha1_state * sha1, uint8_t * out); + +/** + * Calculate SHA1 hash of block of memory. + * @param msg Pointer to message buffer + * @param len Length of message + * @param sha1 Pointer to SHA1 output buffer. Must be 20 bytes or more! + */ +void csp_sha1_memory(const uint8_t * msg, uint32_t len, uint8_t * hash); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_SHA1_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_xtea.h b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_xtea.h new file mode 100644 index 00000000..f740b8d5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_xtea.h @@ -0,0 +1,52 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_XTEA_H_ +#define _CSP_XTEA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_XTEA_IV_LENGTH 8 + +/** + * XTEA encrypt byte array + * @param plain Pointer to plain text + * @param len Length of plain text + * @param iv Initialization vector + */ +int csp_xtea_encrypt(uint8_t * plain, const uint32_t len, uint32_t iv[2]); + +/** + * Decrypt XTEA encrypted byte array + * @param cipher Pointer to cipher text + * @param len Length of plain text + * @param iv Initialization vector + */ +int csp_xtea_decrypt(uint8_t * cipher, const uint32_t len, uint32_t iv[2]); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_XTEA_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp.h new file mode 100644 index 00000000..6962195b --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp.h @@ -0,0 +1,545 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_H_ +#define _CSP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes */ +#include + +#include + +/* CSP includes */ +#include "csp_types.h" +#include "csp_platform.h" +#include "csp_error.h" +#include "csp_debug.h" +#include "csp_buffer.h" +#include "csp_rtable.h" +#include "csp_iflist.h" + +/** csp_init + * Start up the can-space protocol + * @param my_node_address The CSP node address + */ +int csp_init(uint8_t my_node_address); + +/** csp_set_address + * Set the systems own address + * @param addr The new address of the system + */ +void csp_set_address(uint8_t addr); + +/** csp_get_address + * Get the systems own address + * @return The current address of the system + */ +uint8_t csp_get_address(void); + +/** csp_set_hostname + * Set subsystem hostname. + * This function takes a pointer to a string, which should remain static + * @param hostname Hostname to set + */ +void csp_set_hostname(const char *hostname); + +/** csp_get_hostname + * Get current subsystem hostname. + * @return Pointer to char array with current hostname. + */ +const char *csp_get_hostname(void); + +/** csp_set_model + * Set subsystem model name. + * This function takes a pointer to a string, which should remain static + * @param model Model name to set + */ +void csp_set_model(const char *model); + +/** csp_get_model + * Get current model name. + * @return Pointer to char array with current model name. + */ +const char *csp_get_model(void); + +/** csp_set_revision + * Set subsystem revision. This can be used to override the CMP revision field. + * This function takes a pointer to a string, which should remain static + * @param revision Revision name to set + */ +void csp_set_revision(const char *revision); + +/** csp_get_revision + * Get subsystem revision. + * @return Pointer to char array with software revision. + */ +const char *csp_get_revision(void); + +/** csp_socket + * Create CSP socket endpoint + * @param opts Socket options + * @return Pointer to socket on success, NULL on failure + */ +csp_socket_t *csp_socket(uint32_t opts); + +/** + * Wait for a new connection on a socket created by csp_socket + * @param socket Socket to accept connections on + * @param timeout use CSP_MAX_DELAY for infinite timeout + * @return Return pointer to csp_conn_t or NULL if timeout was reached + */ +csp_conn_t *csp_accept(csp_socket_t *socket, uint32_t timeout); + +/** + * Read data from a connection + * This fuction uses the RX queue of a connection to receive a packet + * If no packet is available and a timeout has been specified + * The call will block. + * Do NOT call this from ISR + * @param conn pointer to connection + * @param timeout timeout in ms, use CSP_MAX_DELAY for infinite blocking time + * @return Returns pointer to csp_packet_t, which you MUST free yourself, either by calling csp_buffer_free() or reusing the buffer for a new csp_send. + */ +csp_packet_t *csp_read(csp_conn_t *conn, uint32_t timeout); + +/** + * Send a packet on an already established connection + * @param conn pointer to connection + * @param packet pointer to packet, + * @param timeout a timeout to wait for TX to complete. NOTE: not all underlying drivers supports flow-control. + * @return returns 1 if successful and 0 otherwise. you MUST free the frame yourself if the transmission was not successful. + */ +int csp_send(csp_conn_t *conn, csp_packet_t *packet, uint32_t timeout); + +/** + * Send a packet on an already established connection, and change the default priority of the connection + * + * @note When using this function, the priority of the connection will change. If you need to change it back + * use another call to csp_send_prio, or ensure that all packets sent on a given connection is using send_prio call. + * + * @param prio csp priority + * @param conn pointer to connection + * @param packet pointer to packet, + * @param timeout a timeout to wait for TX to complete. NOTE: not all underlying drivers supports flow-control. + * @return returns 1 if successful and 0 otherwise. you MUST free the frame yourself if the transmission was not successful. + */ +int csp_send_prio(uint8_t prio, csp_conn_t *conn, csp_packet_t *packet, uint32_t timeout); + +/** + * Perform an entire request/reply transaction + * Copies both input buffer and reply to output buffeer. + * Also makes the connection and closes it again + * @param prio CSP Prio + * @param dest CSP Dest + * @param port CSP Port + * @param timeout timeout in ms + * @param outbuf pointer to outgoing data buffer + * @param outlen length of request to send + * @param inbuf pointer to incoming data buffer + * @param inlen length of expected reply, -1 for unknown size (note inbuf MUST be large enough) + * @return Return 1 or reply size if successful, 0 if error or incoming length does not match or -1 if timeout was reached + */ +int csp_transaction(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen); + +/** + * Perform an entire request/reply transaction + * Copies both input buffer and reply to output buffeer. + * Also makes the connection and closes it again + * @param prio CSP Prio + * @param dest CSP Dest + * @param port CSP Port + * @param timeout timeout in ms + * @param outbuf pointer to outgoing data buffer + * @param outlen length of request to send + * @param inbuf pointer to incoming data buffer + * @param inlen length of expected reply, -1 for unknown size (note inbuf MUST be large enough) + * @param opts Connection options. + * @return Return 1 or reply size if successful, 0 if error or incoming length does not match or -1 if timeout was reached + */ +int csp_transaction2(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen, uint32_t opts); + +/** + * Use an existing connection to perform a transaction, + * This is only possible if the next packet is on the same port and destination! + * @param conn pointer to connection structure + * @param timeout timeout in ms + * @param outbuf pointer to outgoing data buffer + * @param outlen length of request to send + * @param inbuf pointer to incoming data buffer + * @param inlen length of expected reply, -1 for unknown size (note inbuf MUST be large enough) + * @return + */ +int csp_transaction_persistent(csp_conn_t *conn, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen); + +/** + * Read data from a connection-less server socket + * This fuction uses the socket directly to receive a frame + * If no packet is available and a timeout has been specified the call will block. + * Do NOT call this from ISR + * @return Returns pointer to csp_packet_t, which you MUST free yourself, either by calling csp_buffer_free() or reusing the buffer for a new csp_send. + */ +csp_packet_t *csp_recvfrom(csp_socket_t *socket, uint32_t timeout); + +/** + * Send a packet without previously opening a connection + * @param prio CSP_PRIO_x + * @param dest destination node + * @param dport destination port + * @param src_port source port + * @param opts CSP_O_x + * @param packet pointer to packet + * @param timeout timeout used by interfaces with blocking send + * @return -1 if error (you must free packet), 0 if OK (you must discard pointer) + */ +int csp_sendto(uint8_t prio, uint8_t dest, uint8_t dport, uint8_t src_port, uint32_t opts, csp_packet_t *packet, uint32_t timeout); + +/** + * Send a packet as a direct reply to the source of an incoming packet, + * but still without holding an entire connection + * @param request_packet pointer to packet to reply to + * @param reply_packet actual reply data + * @param opts CSP_O_x + * @param timeout timeout used by interfaces with blocking send + * @return -1 if error (you must free packet), 0 if OK (you must discard pointer) + */ +int csp_sendto_reply(csp_packet_t * request_packet, csp_packet_t * reply_packet, uint32_t opts, uint32_t timeout); + +/** csp_connect + * Used to establish outgoing connections + * This function searches the port table for free slots and finds an unused + * connection from the connection pool + * There is no handshake in the CSP protocol + * @param prio Connection priority. + * @param dest Destination address. + * @param dport Destination port. + * @param timeout Timeout in ms. + * @param opts Connection options. + * @return a pointer to a new connection or NULL + */ +csp_conn_t *csp_connect(uint8_t prio, uint8_t dest, uint8_t dport, uint32_t timeout, uint32_t opts); + +/** csp_close + * Closes a given connection and frees buffers used. + * @param conn pointer to connection structure + * @return CSP_ERR_NONE if connection was closed. Otherwise, an err code is returned. + */ +int csp_close(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return destination port of an incoming connection + */ +int csp_conn_dport(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return source port of an incoming connection + */ +int csp_conn_sport(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return destination address of an incoming connection + */ +int csp_conn_dst(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return source address of an incoming connection + */ +int csp_conn_src(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return flags field of an incoming connection + */ +int csp_conn_flags(csp_conn_t *conn); + +/** + * Set socket to listen for incoming connections + * @param socket Socket to enable listening on + * @param conn_queue_length Lenght of backlog connection queue + * @return 0 on success, -1 on error. + */ +int csp_listen(csp_socket_t *socket, size_t conn_queue_length); + +/** + * Bind port to socket + * @param socket Socket to bind port to + * @param port Port number to bind + * @return 0 on success, -1 on error. + */ +int csp_bind(csp_socket_t *socket, uint8_t port); + +/** + * Start the router task. + * @param task_stack_size The number of portStackType to allocate. This only affects FreeRTOS systems. + * @param priority The OS task priority of the router + */ +int csp_route_start_task(unsigned int task_stack_size, unsigned int priority); + +/** + * Call the router worker function manually (without the router task) + * This must be run inside a loop or called periodically for the csp router to work. + * Use this function instead of calling and starting the router task. + * @param timeout max blocking time + * @return -1 if no packet was processed, 0 otherwise + */ +int csp_route_work(uint32_t timeout); + +/** + * Start the bridge task. + * @param task_stack_size The number of portStackType to allocate. This only affects FreeRTOS systems. + * @param priority The OS task priority of the router + * @param _if_a pointer to first side + * @param _if_b pointer to second side + * @return CSP_ERR type + */ +int csp_bridge_start(unsigned int task_stack_size, unsigned int task_priority, csp_iface_t * _if_a, csp_iface_t * _if_b); + +/** + * Enable promiscuous mode packet queue + * This function is used to enable promiscuous mode for the router. + * If enabled, a copy of all incoming packets are placed in a queue + * that can be read with csp_promisc_get(). Not all interface drivers + * support promiscuous mode. + * + * @param buf_size Size of buffer for incoming packets + */ +int csp_promisc_enable(unsigned int buf_size); + +/** + * Disable promiscuous mode. + * If the queue was initialised prior to this, it can be re-enabled + * by calling promisc_enable(0) + */ +void csp_promisc_disable(void); + +/** + * Get packet from promiscuous mode packet queue + * Returns the first packet from the promiscuous mode packet queue. + * The queue is FIFO, so the returned packet is the oldest one + * in the queue. + * + * @param timeout Timeout in ms to wait for a new packet + */ +csp_packet_t *csp_promisc_read(uint32_t timeout); + +/** + * Send multiple packets using the simple fragmentation protocol + * CSP will add total size and offset to all packets + * This can be read by the client using the csp_sfp_recv, if the CSP_FFRAG flag is set + * @param conn pointer to connection + * @param data pointer to data to send + * @param totalsize size of data to send + * @param mtu maximum transfer unit + * @param timeout timeout in ms to wait for csp_send() + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_send(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout); + +/** + * Same as csp_sfp_send but with option to supply your own memcpy function. + * This is usefull if you wish to send data stored in flash memory or another location + * @param conn pointer to connection + * @param data pointer to data to send + * @param totalsize size of data to send + * @param mtu maximum transfer unit + * @param timeout timeout in ms to wait for csp_send() + * @param memcpyfcn, pointer to memcpy function + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout, void * (*memcpyfcn)(void *, const void *, size_t)); + +/** + * This is the counterpart to the csp_sfp_send function + * @param conn pointer to active conn, on which you expect to receive sfp packed data + * @param dataout pointer to NULL pointer, whill be overwritten with malloc pointer + * @param datasize actual size of received data + * @param timeout timeout in ms to wait for csp_recv() + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_recv(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout); + +/** + * This is the counterpart to the csp_sfp_send function + * @param conn pointer to active conn, on which you expect to receive sfp packed data + * @param dataout pointer to NULL pointer, whill be overwritten with malloc pointer + * @param datasize actual size of received data + * @param timeout timeout in ms to wait for csp_recv() + * @param first_packet This is a pointer to the first SFP packet (previously received with csp_read) + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_recv_fp(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout, csp_packet_t * first_packet); + +/** + * If the given packet is a service-request (that is uses one of the csp service ports) + * it will be handled according to the CSP service handler. + * This function will either use the packet buffer or delete it, + * so this function is typically called in the last "default" clause of + * a switch/case statement in a csp_listener task. + * In order to listen to csp service ports, bind your listener to the CSP_ANY port. + * This function may only be called from task context. + * @param conn Pointer to the new connection + * @param packet Pointer to the first packet, obtained by using csp_read() + */ +void csp_service_handler(csp_conn_t *conn, csp_packet_t *packet); + +/** + * Send a single ping/echo packet + * @param node node id + * @param timeout timeout in ms + * @param size size of packet to transmit + * @param conn_options csp connection options + * @return >0 = Echo time in ms, -1 = ERR + */ +int csp_ping(uint8_t node, uint32_t timeout, unsigned int size, uint8_t conn_options); + +/** + * Send a single ping/echo packet without waiting for reply + * @param node node id + */ +void csp_ping_noreply(uint8_t node); + +/** + * Request process list. + * @note This is only available for FreeRTOS systems + * @param node node id + * @param timeout timeout in ms + */ +void csp_ps(uint8_t node, uint32_t timeout); + +/** + * Request amount of free memory + * @param node node id + * @param timeout timeout in ms + */ +void csp_memfree(uint8_t node, uint32_t timeout); + +/** + * Request number of free buffer elements + * @param node node id + * @param timeout timeout in ms + */ +void csp_buf_free(uint8_t node, uint32_t timeout); + +/** + * Reboot subsystem + * @param node node id + */ +void csp_reboot(uint8_t node); + +/** + * Shutdown subsystem + * @param node node id + */ +void csp_shutdown(uint8_t node); + +/** + * Request subsystem uptime + * @param node node id + * @param timeout timeout in ms + */ +void csp_uptime(uint8_t node, uint32_t timeout); + +/** + * Set RDP options + * @param window_size Window size + * @param conn_timeout_ms Connection timeout in ms + * @param packet_timeout_ms Packet timeout in ms + * @param delayed_acks Enable/disable delayed acknowledgements + * @param ack_timeout Acknowledgement timeout when delayed ACKs is enabled + * @param ack_delay_count Send acknowledgement for every ack_delay_count packets + */ +void csp_rdp_set_opt(unsigned int window_size, unsigned int conn_timeout_ms, + unsigned int packet_timeout_ms, unsigned int delayed_acks, + unsigned int ack_timeout, unsigned int ack_delay_count); + +/** + * Get RDP options + * @param window_size Window size + * @param conn_timeout_ms Connection timeout in ms + * @param packet_timeout_ms Packet timeout in ms + * @param delayed_acks Enable/disable delayed acknowledgements + * @param ack_timeout Acknowledgement timeout when delayed ACKs is enabled + * @param ack_delay_count Send acknowledgement for every ack_delay_count packets + */ +void csp_rdp_get_opt(unsigned int *window_size, unsigned int *conn_timeout_ms, + unsigned int *packet_timeout_ms, unsigned int *delayed_acks, + unsigned int *ack_timeout, unsigned int *ack_delay_count); + +/** + * Set XTEA key + * @param key Pointer to key array + * @param keylen Length of key + * @return 0 if key was successfully set, -1 otherwise + */ +int csp_xtea_set_key(char *key, uint32_t keylen); + +/** + * Set HMAC key + * @param key Pointer to key array + * @param keylen Length of key + * @return 0 if key was successfully set, -1 otherwise + */ +int csp_hmac_set_key(char *key, uint32_t keylen); + +/** + * Print connection table + */ +void csp_conn_print_table(void); +int csp_conn_print_table_str(char * str_buf, int str_size); + +/** + * Print buffer usage table + */ +void csp_buffer_print_table(void); + +/** + * Hex dump to stdout + */ +void csp_hex_dump(const char *desc, void *addr, int len); + +#ifdef __AVR__ +typedef uint32_t csp_memptr_t; +#else +typedef void * csp_memptr_t; +#endif + +typedef csp_memptr_t (*csp_memcpy_fnc_t)(csp_memptr_t, const csp_memptr_t, size_t); +void csp_cmp_set_memcpy(csp_memcpy_fnc_t fnc); + +/** + * Set csp_debug hook function + * @param f Hook function + */ +#include +typedef void (*csp_debug_hook_func_t)(csp_debug_level_t level, const char *format, va_list args); +void csp_debug_hook_set(csp_debug_hook_func_t f); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_autoconfig.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_autoconfig.h new file mode 100644 index 00000000..77746afd --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_autoconfig.h @@ -0,0 +1,41 @@ +#ifndef W_INCLUDE_CSP_CSP_AUTOCONFIG_H_WAF +#define W_INCLUDE_CSP_CSP_AUTOCONFIG_H_WAF + +#define ENABLE_NANOPOWER2_CLIENT 1 +#define GIT_REV "unknown" +/* #undef CSP_FREERTOS */ +#define CSP_POSIX 1 +/* #undef CSP_WINDOWS */ +/* #undef CSP_MACOSX */ +#define HAVE_LIBZMQ 1 +#define CSP_DEBUG 1 +#define CSP_USE_RDP 1 +#define CSP_USE_CRC32 1 +#define CSP_USE_HMAC 1 +#define CSP_USE_XTEA 1 +#define CSP_USE_PROMISC 1 +#define CSP_USE_QOS 1 +/* #undef CSP_USE_DEDUP */ +/* #undef CSP_USE_INIT_SHUTDOWN */ +#define CSP_USE_CAN 1 +#define CSP_USE_I2C 1 +#define CSP_USE_KISS 1 +#define CSP_USE_ZMQHUB 1 +#define CSP_CONN_MAX 10 +#define CSP_CONN_QUEUE_LENGTH 100 +#define CSP_FIFO_INPUT 100 +#define CSP_MAX_BIND_PORT 31 +#define CSP_RDP_MAX_WINDOW 20 +#define CSP_PADDING_BYTES 8 +#define CSP_CONNECTION_SO 64 +#define CSP_LOG_LEVEL_DEBUG 1 +#define CSP_LOG_LEVEL_INFO 1 +#define CSP_LOG_LEVEL_WARN 1 +#define CSP_LOG_LEVEL_ERROR 1 +#define CSP_LITTLE_ENDIAN 1 +/* #undef CSP_BIG_ENDIAN */ +#define CSP_HAVE_STDBOOL_H 1 +#define CSP_HAVE_LIBSOCKETCAN 1 +#define LIBCSP_VERSION "1.5" + +#endif /* W_INCLUDE_CSP_CSP_AUTOCONFIG_H_WAF */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_buffer.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_buffer.h new file mode 100644 index 00000000..9ed6df77 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_buffer.h @@ -0,0 +1,92 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_BUFFER_H_ +#define _CSP_BUFFER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Start the buffer handling system + * You must specify the number for buffers and the size. All buffers are fixed + * size so you must specify the size of your largest buffer. + * + * @param count Number of buffers to allocate + * @param size Buffer size in bytes. + * + * @return CSP_ERR_NONE if malloc() succeeded, CSP_ERR message otherwise. + */ +int csp_buffer_init(int count, int size); + +/** + * Get a reference to a free buffer. This function can only be called + * from task context. + * + * @param size Specify what data-size you will put in the buffer + * @return pointer to a free csp_packet_t or NULL if out of memory + */ +void * csp_buffer_get(size_t size); + +/** + * Get a reference to a free buffer. This function can only be called + * from interrupt context. + * + * @param buf_size Specify what data-size you will put in the buffer + * @return pointer to a free csp_packet_t or NULL if out of memory + */ +void * csp_buffer_get_isr(size_t buf_size); + +/** + * Free a buffer after use. + * @param packet pointer to memory area, must be acquired by csp_buffer_get(). + */ +void csp_buffer_free(void *packet); + +/** + * Free a buffer after use in ISR context. + * @param packet pointer to memory area, must be acquired by csp_buffer_get(). + */ +void csp_buffer_free_isr(void *packet); + +/** + * Clone an existing packet and increase/decrease cloned packet size. + * @param buffer Existing buffer to clone. + */ +void * csp_buffer_clone(void *buffer); + +/** + * Return how many buffers that are currently free. + * @return number of free buffers + */ +int csp_buffer_remaining(void); + +/** + * Return the size of the CSP buffers + * @return size of CSP buffers + */ +int csp_buffer_size(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_BUFFER_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_cmp.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_cmp.h new file mode 100644 index 00000000..114a8eab --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_cmp.h @@ -0,0 +1,189 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CMP_H_ +#define _CSP_CMP_H_ + +/** + @file + CSP management protocol (CMP). +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + CMP type. + @{ +*/ +/** + CMP request. +*/ +#define CSP_CMP_REQUEST 0x00 +/** + CMP reply. +*/ +#define CSP_CMP_REPLY 0xff +/**@}*/ + +/** + CMP requests. + @{ +*/ +/** + CMP request codes. +*/ +/** + Request identification, compile time, revision, name. +*/ +#define CSP_CMP_IDENT 1 +/** + Set/configure routing. +*/ +#define CSP_CMP_ROUTE_SET 2 +/** + Request interface statistics. +*/ +#define CSP_CMP_IF_STATS 3 +/** + Peek/read data from memory. +*/ +#define CSP_CMP_PEEK 4 +/** + Poke/write data from memory. +*/ +#define CSP_CMP_POKE 5 +/** + Get/set clock. +*/ +#define CSP_CMP_CLOCK 6 +/**@}*/ + +/** + CMP identification - max revision length. +*/ +#define CSP_CMP_IDENT_REV_LEN 20 +/** + CMP identification - max date length. +*/ +#define CSP_CMP_IDENT_DATE_LEN 12 +/** + CMP identification - max time length. +*/ +#define CSP_CMP_IDENT_TIME_LEN 9 + +/** + CMP interface statistics - max interface name length. +*/ +#define CSP_CMP_ROUTE_IFACE_LEN 11 + +/** + CMP peek/read memeory - max read length. +*/ +#define CSP_CMP_PEEK_MAX_LEN 200 + +/** + CMP poke/write memeory - max write length. +*/ +#define CSP_CMP_POKE_MAX_LEN 200 + +/** + CSP management protocol description. +*/ +struct csp_cmp_message { + //! CMP request type. + uint8_t type; + //! CMP request code. + uint8_t code; + union { + struct { + char hostname[CSP_HOSTNAME_LEN]; + char model[CSP_MODEL_LEN]; + char revision[CSP_CMP_IDENT_REV_LEN]; + char date[CSP_CMP_IDENT_DATE_LEN]; + char time[CSP_CMP_IDENT_TIME_LEN]; + } ident; + struct { + uint8_t dest_node; + uint8_t next_hop_mac; + char interface[CSP_CMP_ROUTE_IFACE_LEN]; + } route_set; + struct __attribute__((__packed__)) { + char interface[CSP_CMP_ROUTE_IFACE_LEN]; + uint32_t tx; + uint32_t rx; + uint32_t tx_error; + uint32_t rx_error; + uint32_t drop; + uint32_t autherr; + uint32_t frame; + uint32_t txbytes; + uint32_t rxbytes; + uint32_t irq; + } if_stats; + struct { + uint32_t addr; + uint8_t len; + char data[CSP_CMP_PEEK_MAX_LEN]; + } peek; + struct { + uint32_t addr; + uint8_t len; + char data[CSP_CMP_POKE_MAX_LEN]; + } poke; + csp_timestamp_t clock; + }; +} __attribute__ ((packed)); + +/** + Macro for calculating size of management message. +*/ +#define CMP_SIZE(_memb) (sizeof(((struct csp_cmp_message *)0)->_memb) + sizeof(uint8_t) + sizeof(uint8_t)) + +/** + Generic send management message request. +*/ +int csp_cmp(uint8_t node, uint32_t timeout, uint8_t code, int membsize, struct csp_cmp_message *msg); + +/** + Macro for defining management handling function. +*/ +#define CMP_MESSAGE(_code, _memb) \ +static inline int csp_cmp_##_memb(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg) { \ + return csp_cmp(node, timeout, _code, CMP_SIZE(_memb), msg); \ +} + +CMP_MESSAGE(CSP_CMP_IDENT, ident) +CMP_MESSAGE(CSP_CMP_ROUTE_SET, route_set) +CMP_MESSAGE(CSP_CMP_IF_STATS, if_stats) +CMP_MESSAGE(CSP_CMP_PEEK, peek) +CMP_MESSAGE(CSP_CMP_POKE, poke) +CMP_MESSAGE(CSP_CMP_CLOCK, clock) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_CMP_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_crc32.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_crc32.h new file mode 100644 index 00000000..a474eaf8 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_crc32.h @@ -0,0 +1,63 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CRC32_H_ +#define _CSP_CRC32_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generate precomputed CRC32 table + */ +void csp_crc32_gentab(void); + +/** + * Append CRC32 checksum to packet + * @param packet Packet to append checksum + * @param include_header use header in calculation (this will not modify the flags field) + * @return 0 on success, -1 on error + */ +int csp_crc32_append(csp_packet_t * packet, bool include_header); + +/** + * Verify CRC32 checksum on packet + * @param packet Packet to verify + * @param include_header use header in calculation (this will not modify the flags field) + * @return 0 if checksum is valid, -1 otherwise + */ +int csp_crc32_verify(csp_packet_t * packet, bool include_header); + +/** + * Calculate checksum for a given memory area + * @param data pointer to memory + * @param length length of memory to do checksum on + * @return return uint32_t checksum + */ +uint32_t csp_crc32_memory(const uint8_t * data, uint32_t length); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_CRC32_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_debug.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_debug.h new file mode 100644 index 00000000..64429d49 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_debug.h @@ -0,0 +1,150 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_DEBUG_H_ +#define _CSP_DEBUG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Debug levels */ +typedef enum { + CSP_ERROR = 0, + CSP_WARN = 1, + CSP_INFO = 2, + CSP_BUFFER = 3, + CSP_PACKET = 4, + CSP_PROTOCOL = 5, + CSP_LOCK = 6, +} csp_debug_level_t; + +/* Extract filename component from path */ +#define BASENAME(_file) ((strrchr(_file, '/') ? : (strrchr(_file, '\\') ? : _file)) + 1) + +/* Implement csp_assert_fail_action to override default failure action */ +extern void __attribute__((weak)) csp_assert_fail_action(const char *assertion, const char *file, int line); + +#ifndef NDEBUG + #define csp_assert(exp) \ + do { \ + if (!(exp)) { \ + const char *assertion = #exp; \ + const char *file = BASENAME(__FILE__); \ + int line = __LINE__; \ + printf("\E[1;31m[%02" PRIu8 "] Assertion \'%s\' failed in %s:%d\E[0m\r\n", \ + csp_get_address(), assertion, file, line); \ + if (csp_assert_fail_action) \ + csp_assert_fail_action(assertion, file, line); \ + } \ + } while (0) +#else + #define csp_assert(...) do {} while (0) +#endif + +#ifdef __AVR__ + #include + #include + #define CONSTSTR(data) PSTR(data) + #undef printf + #undef sscanf + #undef scanf + #undef sprintf + #undef snprintf + #define printf(s, ...) printf_P(PSTR(s), ## __VA_ARGS__) + #define sscanf(buf, s, ...) sscanf_P(buf, PSTR(s), ## __VA_ARGS__) + #define scanf(s, ...) scanf_P(PSTR(s), ## __VA_ARGS__) + #define sprintf(buf, s, ...) sprintf_P(buf, PSTR(s), ## __VA_ARGS__) + #define snprintf(buf, size, s, ...) snprintf_P(buf, size, PSTR(s), ## __VA_ARGS__) +#else + #define CONSTSTR(data) data +#endif + +#ifdef CSP_DEBUG + #define csp_debug(level, format, ...) do { do_csp_debug(level, CONSTSTR(format), ##__VA_ARGS__); } while(0) +#else + #define csp_debug(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_ERROR + #define csp_log_error(format, ...) csp_debug(CSP_ERROR, format, ##__VA_ARGS__) +#else + #define csp_log_error(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_WARN + #define csp_log_warn(format, ...) csp_debug(CSP_WARN, format, ##__VA_ARGS__) +#else + #define csp_log_warn(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_INFO + #define csp_log_info(format, ...) csp_debug(CSP_INFO, format, ##__VA_ARGS__) +#else + #define csp_log_info(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_DEBUG + #define csp_log_buffer(format, ...) csp_debug(CSP_BUFFER, format, ##__VA_ARGS__) + #define csp_log_packet(format, ...) csp_debug(CSP_PACKET, format, ##__VA_ARGS__) + #define csp_log_protocol(format, ...) csp_debug(CSP_PROTOCOL, format, ##__VA_ARGS__) + #define csp_log_lock(format, ...) csp_debug(CSP_LOCK, format, ##__VA_ARGS__) +#else + #define csp_log_buffer(...) do {} while (0) + #define csp_log_packet(...) do {} while (0) + #define csp_log_protocol(...) do {} while (0) + #define csp_log_lock(...) do {} while (0) +#endif + +/** + * This function should not be used directly, use csp_log_() macro instead + * @param level + * @param format + */ +void do_csp_debug(csp_debug_level_t level, const char *format, ...); + +/** + * Toggle debug level on/off + * @param level Level to toggle + */ +void csp_debug_toggle_level(csp_debug_level_t level); + +/** + * Set debug level + * @param level Level to set + * @param value New level value + */ +void csp_debug_set_level(csp_debug_level_t level, bool value); + +/** + * Get current debug level value + * @param level Level value to get + * @return Level value + */ +int csp_debug_get_level(csp_debug_level_t level); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_DEBUG_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_endian.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_endian.h new file mode 100644 index 00000000..e63a73c2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_endian.h @@ -0,0 +1,170 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_ENDIAN_H_ +#define _CSP_ENDIAN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Convert 16-bit integer from host byte order to network byte order + * @param h16 Host byte order 16-bit integer + */ +uint16_t csp_hton16(uint16_t h16); + +/** + * Convert 16-bit integer from host byte order to host byte order + * @param n16 Network byte order 16-bit integer + */ +uint16_t csp_ntoh16(uint16_t n16); + +/** + * Convert 32-bit integer from host byte order to network byte order + * @param h32 Host byte order 32-bit integer + */ +uint32_t csp_hton32(uint32_t h32); + +/** + * Convert 32-bit integer from host byte order to host byte order + * @param n32 Network byte order 32-bit integer + */ +uint32_t csp_ntoh32(uint32_t n32); + +/** + * Convert 64-bit integer from host byte order to network byte order + * @param h64 Host byte order 64-bit integer + */ +uint64_t csp_hton64(uint64_t h64); + +/** + * Convert 64-bit integer from host byte order to host byte order + * @param n64 Network byte order 64-bit integer + */ +uint64_t csp_ntoh64(uint64_t n64); + +/** + * Convert 16-bit integer from host byte order to big endian byte order + * @param h16 Host byte order 16-bit integer + */ +uint16_t csp_htobe16(uint16_t h16); + +/** + * Convert 16-bit integer from host byte order to little endian byte order + * @param h16 Host byte order 16-bit integer + */ +uint16_t csp_htole16(uint16_t h16); + +/** + * Convert 16-bit integer from big endian byte order to little endian byte order + * @param be16 Big endian byte order 16-bit integer + */ +uint16_t csp_betoh16(uint16_t be16); + +/** + * Convert 16-bit integer from little endian byte order to host byte order + * @param le16 Little endian byte order 16-bit integer + */ +uint16_t csp_letoh16(uint16_t le16); + +/** + * Convert 32-bit integer from host byte order to big endian byte order + * @param h32 Host byte order 32-bit integer + */ +uint32_t csp_htobe32(uint32_t h32); + +/** + * Convert 32-bit integer from little endian byte order to host byte order + * @param h32 Host byte order 32-bit integer + */ +uint32_t csp_htole32(uint32_t h32); + +/** + * Convert 32-bit integer from big endian byte order to host byte order + * @param be32 Big endian byte order 32-bit integer + */ +uint32_t csp_betoh32(uint32_t be32); + +/** + * Convert 32-bit integer from little endian byte order to host byte order + * @param le32 Little endian byte order 32-bit integer + */ +uint32_t csp_letoh32(uint32_t le32); + +/** + * Convert 64-bit integer from host byte order to big endian byte order + * @param h64 Host byte order 64-bit integer + */ +uint64_t csp_htobe64(uint64_t h64); + +/** + * Convert 64-bit integer from host byte order to little endian byte order + * @param h64 Host byte order 64-bit integer + */ +uint64_t csp_htole64(uint64_t h64); + +/** + * Convert 64-bit integer from big endian byte order to host byte order + * @param be64 Big endian byte order 64-bit integer + */ +uint64_t csp_betoh64(uint64_t be64); + +/** + * Convert 64-bit integer from little endian byte order to host byte order + * @param le64 Little endian byte order 64-bit integer + */ +uint64_t csp_letoh64(uint64_t le64); + +/** + * Convert float from host to network byte order + * @param f Float in host order + * @return Float in network order + */ +float csp_htonflt(float f); + +/** + * Convert float from network to host byte order + * @param f Float in network order + * @return Float in host order + */ +float csp_ntohflt(float f); + +/** + * Convert double from host to network byte order + * @param d Double in host order + * @return Double in network order + */ +double csp_htondbl(double d); + +/** + * Convert double from network to host order + * @param d Double in network order + * @return Double in host order + */ +double csp_ntohdbl(double d); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_ENDIAN_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_error.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_error.h new file mode 100644 index 00000000..31866607 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_error.h @@ -0,0 +1,50 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_ERROR_H_ +#define _CSP_ERROR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_ERR_NONE 0 /* No error */ +#define CSP_ERR_NOMEM -1 /* Not enough memory */ +#define CSP_ERR_INVAL -2 /* Invalid argument */ +#define CSP_ERR_TIMEDOUT -3 /* Operation timed out */ +#define CSP_ERR_USED -4 /* Resource already in use */ +#define CSP_ERR_NOTSUP -5 /* Operation not supported */ +#define CSP_ERR_BUSY -6 /* Device or resource busy */ +#define CSP_ERR_ALREADY -7 /* Connection already in progress */ +#define CSP_ERR_RESET -8 /* Connection reset */ +#define CSP_ERR_NOBUFS -9 /* No more buffer space available */ +#define CSP_ERR_TX -10 /* Transmission failed */ +#define CSP_ERR_DRIVER -11 /* Error in driver layer */ +#define CSP_ERR_AGAIN -12 /* Resource temporarily unavailable */ + +#define CSP_ERR_HMAC -100 /* HMAC failed */ +#define CSP_ERR_XTEA -101 /* XTEA failed */ +#define CSP_ERR_CRC32 -102 /* CRC32 failed */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_ERROR_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_iflist.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_iflist.h new file mode 100644 index 00000000..55875657 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_iflist.h @@ -0,0 +1,56 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_IFLIST_H_ +#define CSP_IFLIST_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Add interface to list + * @param ifc Pointer to interface to add + */ +void csp_iflist_add(csp_iface_t *ifc); + +/** + * Lookup interface by name + * @param name String with interface name + * @return Pointer to interface or NULL if not found + */ +csp_iface_t * csp_iflist_get_by_name(const char *name); + +/** + * Print list of interfaces to stdout + */ +void csp_iflist_print(void); + +/** + * Return list of registered interfaces. + */ +csp_iface_t * csp_iflist_get(void); + +#ifdef __cplusplus +} +#endif +#endif /* CSP_IFLIST_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_interface.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_interface.h new file mode 100644 index 00000000..8f04bee3 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_interface.h @@ -0,0 +1,54 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_INTERFACE_H_ +#define _CSP_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Inputs a new packet into the system + * This function is called from interface drivers ISR to route and accept packets. + * But it can also be called from a task, provided that the pxTaskWoken parameter is NULL! + * + * EXTREMELY IMPORTANT: + * pxTaskWoken arg must ALWAYS be NULL if called from task, + * and ALWAYS be NON NULL if called from ISR! + * If this condition is met, this call is completely thread-safe + * + * This function is fire and forget, it returns void, meaning + * that a packet will always be either accepted or dropped + * so the memory will always be freed. + * + * @param packet A pointer to the incoming packet + * @param interface A pointer to the incoming interface TX function. + * @param pxTaskWoken This must be a pointer a valid variable if called from ISR or NULL otherwise! + */ +void csp_qfifo_write(csp_packet_t *packet, csp_iface_t *interface, CSP_BASE_TYPE *pxTaskWoken); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_INTERFACE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_platform.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_platform.h new file mode 100644 index 00000000..b33292e9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_platform.h @@ -0,0 +1,56 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_PLATFORM_H_ +#define _CSP_PLATFORM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Set OS */ +#if defined(CSP_POSIX) || defined(CSP_WINDOWS) || defined(CSP_MACOSX) + #define CSP_BASE_TYPE int + #define CSP_MAX_DELAY (UINT32_MAX) + #define CSP_INFINITY (UINT32_MAX) + #define CSP_DEFINE_CRITICAL(lock) static csp_bin_sem_handle_t lock + #define CSP_INIT_CRITICAL(lock) ({(csp_bin_sem_create(&lock) == CSP_SEMAPHORE_OK) ? CSP_ERR_NONE : CSP_ERR_NOMEM;}) + #define CSP_ENTER_CRITICAL(lock) do { csp_bin_sem_wait(&lock, CSP_MAX_DELAY); } while(0) + #define CSP_EXIT_CRITICAL(lock) do { csp_bin_sem_post(&lock); } while(0) +#elif defined(CSP_FREERTOS) + #include "FreeRTOS.h" + #define CSP_BASE_TYPE portBASE_TYPE + #define CSP_MAX_DELAY portMAX_DELAY + #define CSP_INFINITY portMAX_DELAY + #define CSP_DEFINE_CRITICAL(lock) + #define CSP_INIT_CRITICAL(lock) ({CSP_ERR_NONE;}) + #define CSP_ENTER_CRITICAL(lock) do { portENTER_CRITICAL(); } while (0) + #define CSP_EXIT_CRITICAL(lock) do { portEXIT_CRITICAL(); } while (0) +#else + #error "OS must be either CSP_POSIX, CSP_MACOSX, CSP_FREERTOS OR CSP_WINDOWS" +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_PLATFORM_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_rtable.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_rtable.h new file mode 100644 index 00000000..34cd18e2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_rtable.h @@ -0,0 +1,149 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_RTABLE_H_ +#define CSP_RTABLE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_NODE_MAC 0xFF +#define CSP_ROUTE_COUNT (CSP_ID_HOST_MAX + 2) +#define CSP_ROUTE_TABLE_SIZE 5 * CSP_ROUTE_COUNT + +/** + * Find outgoing interface in routing table + * @param id Destination node + * @return pointer to outgoing interface or NULL + */ +csp_iface_t * csp_rtable_find_iface(uint8_t id); + +/** + * Find MAC address associated with node + * @param id Destination node + * @return MAC address + */ +uint8_t csp_rtable_find_mac(uint8_t id); + +/** + * Setup routing entry + * @param node Host + * @param mask Number of bits in netmask + * @param ifc Interface + * @param mac MAC address + * @return CSP error type + */ +int csp_rtable_set(uint8_t node, uint8_t mask, csp_iface_t *ifc, uint8_t mac); + +/** + * Print routing table to stdout + */ +void csp_rtable_print(void); + + +/** + * Load the routing table from a buffer + * (deprecated, please use new csp_rtable_load) + * + * Warning: + * The table will be RAW from memory and contains direct pointers, not interface names. + * Therefore it's very important that a saved routing table is deleted after a firmware update + * + * @param route_table_in pointer to routing table buffer + */ +void csp_route_table_load(uint8_t route_table_in[CSP_ROUTE_TABLE_SIZE]); + +/** + * Save the routing table to a buffer + * (deprecated, please use new csp_rtable_save) + * + * Warning: + * The table will be RAW from memory and contains direct pointers, not interface names. + * Therefore it's very important that a saved routing table is deleted after a firmware update + * + * @param route_table_out pointer to routing table buffer + */ +void csp_route_table_save(uint8_t route_table_out[CSP_ROUTE_TABLE_SIZE]); + +/** + * Save routing table as a string to a buffer, which can be parsed + * again by csp_rtable_load. + * @param buffer pointer to buffer + * @param maxlen length of buffer + * @return length of saved string + */ +int csp_rtable_save(char * buffer, int maxlen); + +/** + * Load routing table from a string in the format + * %u/%u %s %u + * - Address + * - Netmask + * - Ifname + * - Mac Address (this field is optional) + * An example routing string is "0/0 I2C, 8/2 KISS" + * The string must be \0 null terminated + * @param buffer Pointer to string + */ +void csp_rtable_load(const char * buffer); + +/** + * Check string for valid routing table + * @param buffer Pointer to string + * @return number of valid entries found + */ +int csp_rtable_check(const char * buffer); + +/** + * Clear routing table: + * This could be done before load to ensure an entire clean table is loaded. + */ +void csp_rtable_clear(void); + +/** + * Setup routing entry to single node + * (deprecated, please use csp_rtable_set) + * + * @param node Host + * @param ifc Interface + * @param mac MAC address + * @return CSP error type + */ +#define csp_route_set(node, ifc, mac) csp_rtable_set(node, CSP_ID_HOST_SIZE, ifc, mac) + +/** + * Print routing table + * (deprecated, please use csp_rtable_print) + */ +#define csp_route_print_table() csp_rtable_print(); + +/** + * Print list of interfaces + * (deprecated, please use csp_iflist_print) + */ +#define csp_route_print_interfaces() csp_iflist_print(); + +#ifdef __cplusplus +} +#endif +#endif /* CSP_RTABLE_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_types.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_types.h new file mode 100644 index 00000000..a9cc28cd --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_types.h @@ -0,0 +1,235 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_TYPES_H_ +#define CSP_TYPES_H_ + +#include +#include // -> CSP_HAVE_X defines +#ifdef CSP_HAVE_STDBOOL_H +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Make bool for compilers without stdbool.h */ +#ifndef CSP_HAVE_STDBOOL_H +#define bool int +#define false 0 +#define true !false +#endif + +/** + * RESERVED PORTS (SERVICES) + */ + +enum csp_reserved_ports_e { + CSP_CMP = 0, + CSP_PING = 1, + CSP_PS = 2, + CSP_MEMFREE = 3, + CSP_REBOOT = 4, + CSP_BUF_FREE = 5, + CSP_UPTIME = 6, + CSP_ANY = (CSP_MAX_BIND_PORT + 1), + CSP_PROMISC = (CSP_MAX_BIND_PORT + 2) +}; + +typedef enum { + CSP_PRIO_CRITICAL = 0, + CSP_PRIO_HIGH = 1, + CSP_PRIO_NORM = 2, + CSP_PRIO_LOW = 3, +} csp_prio_t; + +#define CSP_PRIORITIES (1 << CSP_ID_PRIO_SIZE) + +#ifdef CSP_USE_QOS +#define CSP_RX_QUEUE_LENGTH (CSP_CONN_QUEUE_LENGTH / CSP_PRIORITIES) +#define CSP_ROUTE_FIFOS CSP_PRIORITIES +#define CSP_RX_QUEUES CSP_PRIORITIES +#else +#define CSP_RX_QUEUE_LENGTH CSP_CONN_QUEUE_LENGTH +#define CSP_ROUTE_FIFOS 1 +#define CSP_RX_QUEUES 1 +#endif + +/** Size of bit-fields in CSP header */ +#define CSP_ID_PRIO_SIZE 2 +#define CSP_ID_HOST_SIZE 5 +#define CSP_ID_PORT_SIZE 6 +#define CSP_ID_FLAGS_SIZE 8 + +#define CSP_HEADER_BITS (CSP_ID_PRIO_SIZE + 2 * CSP_ID_HOST_SIZE + 2 * CSP_ID_PORT_SIZE + CSP_ID_FLAGS_SIZE) +#define CSP_HEADER_LENGTH (CSP_HEADER_BITS / 8) + +#if CSP_HEADER_BITS != 32 && __GNUC__ +#error "Header length must be 32 bits" +#endif + +/** Highest number to be entered in field */ +#define CSP_ID_PRIO_MAX ((1 << (CSP_ID_PRIO_SIZE)) - 1) +#define CSP_ID_HOST_MAX ((1 << (CSP_ID_HOST_SIZE)) - 1) +#define CSP_ID_PORT_MAX ((1 << (CSP_ID_PORT_SIZE)) - 1) +#define CSP_ID_FLAGS_MAX ((1 << (CSP_ID_FLAGS_SIZE)) - 1) + +/** Identifier field masks */ +#define CSP_ID_PRIO_MASK ((uint32_t) CSP_ID_PRIO_MAX << (CSP_ID_FLAGS_SIZE + 2 * CSP_ID_PORT_SIZE + 2 * CSP_ID_HOST_SIZE)) +#define CSP_ID_SRC_MASK ((uint32_t) CSP_ID_HOST_MAX << (CSP_ID_FLAGS_SIZE + 2 * CSP_ID_PORT_SIZE + 1 * CSP_ID_HOST_SIZE)) +#define CSP_ID_DST_MASK ((uint32_t) CSP_ID_HOST_MAX << (CSP_ID_FLAGS_SIZE + 2 * CSP_ID_PORT_SIZE)) +#define CSP_ID_DPORT_MASK ((uint32_t) CSP_ID_PORT_MAX << (CSP_ID_FLAGS_SIZE + 1 * CSP_ID_PORT_SIZE)) +#define CSP_ID_SPORT_MASK ((uint32_t) CSP_ID_PORT_MAX << (CSP_ID_FLAGS_SIZE)) +#define CSP_ID_FLAGS_MASK ((uint32_t) CSP_ID_FLAGS_MAX << (0)) + +#define CSP_ID_CONN_MASK (CSP_ID_SRC_MASK | CSP_ID_DST_MASK | CSP_ID_DPORT_MASK | CSP_ID_SPORT_MASK) + +/** + CSP identifier. +*/ +typedef union { + //! Entire identifier. + uint32_t ext; + //! Individual fields. + struct __attribute__((__packed__)) { +#if defined(CSP_BIG_ENDIAN) && !defined(CSP_LITTLE_ENDIAN) + unsigned int pri : CSP_ID_PRIO_SIZE; + unsigned int src : CSP_ID_HOST_SIZE; + unsigned int dst : CSP_ID_HOST_SIZE; + unsigned int dport : CSP_ID_PORT_SIZE; + unsigned int sport : CSP_ID_PORT_SIZE; + unsigned int flags : CSP_ID_FLAGS_SIZE; +#elif defined(CSP_LITTLE_ENDIAN) && !defined(CSP_BIG_ENDIAN) + unsigned int flags : CSP_ID_FLAGS_SIZE; + unsigned int sport : CSP_ID_PORT_SIZE; + unsigned int dport : CSP_ID_PORT_SIZE; + unsigned int dst : CSP_ID_HOST_SIZE; + unsigned int src : CSP_ID_HOST_SIZE; + unsigned int pri : CSP_ID_PRIO_SIZE; +#else +#error "Must define one of CSP_BIG_ENDIAN or CSP_LITTLE_ENDIAN in csp_platform.h" +#endif + }; +} csp_id_t; + +/** Broadcast address */ +#define CSP_BROADCAST_ADDR CSP_ID_HOST_MAX + +/** Default routing address */ +#define CSP_DEFAULT_ROUTE (CSP_ID_HOST_MAX + 1) + +/** CSP Flags */ +#define CSP_FRES1 0x80 // Reserved for future use +#define CSP_FRES2 0x40 // Reserved for future use +#define CSP_FRES3 0x20 // Reserved for future use +#define CSP_FFRAG 0x10 // Use fragmentation +#define CSP_FHMAC 0x08 // Use HMAC verification +#define CSP_FXTEA 0x04 // Use XTEA encryption +#define CSP_FRDP 0x02 // Use RDP protocol +#define CSP_FCRC32 0x01 // Use CRC32 checksum + +/** CSP Socket options */ +#define CSP_SO_NONE 0x0000 // No socket options +#define CSP_SO_RDPREQ 0x0001 // Require RDP +#define CSP_SO_RDPPROHIB 0x0002 // Prohibit RDP +#define CSP_SO_HMACREQ 0x0004 // Require HMAC +#define CSP_SO_HMACPROHIB 0x0008 // Prohibit HMAC +#define CSP_SO_XTEAREQ 0x0010 // Require XTEA +#define CSP_SO_XTEAPROHIB 0x0020 // Prohibit HMAC +#define CSP_SO_CRC32REQ 0x0040 // Require CRC32 +#define CSP_SO_CRC32PROHIB 0x0080 // Prohibit CRC32 +#define CSP_SO_CONN_LESS 0x0100 // Enable Connection Less mode +#define CSP_SO_INTERNAL_LISTEN 0x1000 // Internal flag: listen called on socket + +/** CSP Connect options */ +#define CSP_O_NONE CSP_SO_NONE // No connection options +#define CSP_O_RDP CSP_SO_RDPREQ // Enable RDP +#define CSP_O_NORDP CSP_SO_RDPPROHIB // Disable RDP +#define CSP_O_HMAC CSP_SO_HMACREQ // Enable HMAC +#define CSP_O_NOHMAC CSP_SO_HMACPROHIB // Disable HMAC +#define CSP_O_XTEA CSP_SO_XTEAREQ // Enable XTEA +#define CSP_O_NOXTEA CSP_SO_XTEAPROHIB // Disable XTEA +#define CSP_O_CRC32 CSP_SO_CRC32REQ // Enable CRC32 +#define CSP_O_NOCRC32 CSP_SO_CRC32PROHIB // Disable CRC32 + +/** + * CSP PACKET STRUCTURE + * Note: This structure is constructed to fit + * with all interface frame types in order to + * have buffer reuse + */ +typedef struct __attribute__((__packed__)) { + uint8_t padding[CSP_PADDING_BYTES]; /**< Interface dependent padding */ + uint16_t length; /**< Length field must be just before CSP ID */ + csp_id_t id; /**< CSP id must be just before data */ + union { + uint8_t data[0]; /**< This just points to the rest of the buffer, without a size indication. */ + uint16_t data16[0]; /**< The data 16 and 32 types makes it easy to reference an integer (properly aligned) */ + uint32_t data32[0]; /**< without the compiler warning about strict aliasing rules. */ + }; +} csp_packet_t; + +/** Interface TX function */ +struct csp_iface_s; +typedef int (*nexthop_t)(struct csp_iface_s * interface, csp_packet_t *packet, uint32_t timeout); + +/** Interface struct */ +typedef struct csp_iface_s { + const char *name; /**< Interface name (keep below 10 bytes) */ + void * driver; /**< Pointer to interface handler structure */ + nexthop_t nexthop; /**< Next hop function */ + uint16_t mtu; /**< Maximum Transmission Unit of interface */ + uint8_t split_horizon_off; /**< Disable the route-loop prevention on if */ + uint32_t tx; /**< Successfully transmitted packets */ + uint32_t rx; /**< Successfully received packets */ + uint32_t tx_error; /**< Transmit errors */ + uint32_t rx_error; /**< Receive errors */ + uint32_t drop; /**< Dropped packets */ + uint32_t autherr; /**< Authentication errors */ + uint32_t frame; /**< Frame format errors */ + uint32_t txbytes; /**< Transmitted bytes */ + uint32_t rxbytes; /**< Received bytes */ + uint32_t irq; /**< Interrupts */ + struct csp_iface_s *next; /**< Next interface */ +} csp_iface_t; + +/** + * This define must be equal to the size of the packet overhead in csp_packet_t. + * It is used in csp_buffer_get() to check the allocated buffer size against + * the required buffer size. + */ +#define CSP_BUFFER_PACKET_OVERHEAD (sizeof(csp_packet_t) - sizeof(((csp_packet_t *)0)->data)) + +/** Forward declaration of socket and connection structures */ +typedef struct csp_conn_s csp_socket_t; +typedef struct csp_conn_s csp_conn_t; + +#define CSP_HOSTNAME_LEN 20 +#define CSP_MODEL_LEN 30 + +/* CSP_REBOOT magic values */ +#define CSP_REBOOT_MAGIC 0x80078007 +#define CSP_REBOOT_SHUTDOWN_MAGIC 0xD1E5529A + +#ifdef __cplusplus +} +#endif +#endif /* CSP_TYPES_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/drivers/can_socketcan.h b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/can_socketcan.h new file mode 100644 index 00000000..0963b414 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/can_socketcan.h @@ -0,0 +1,22 @@ +/* + * can_socketcan.h + * + * Created on: Feb 6, 2017 + * Author: johan + */ + +#ifndef LIB_CSP_INCLUDE_CSP_DRIVERS_CAN_SOCKETCAN_H_ +#define LIB_CSP_INCLUDE_CSP_DRIVERS_CAN_SOCKETCAN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +csp_iface_t * csp_can_socketcan_init(const char * ifc, int bitrate, int promisc); + +#ifdef __cplusplus +} +#endif +#endif /* LIB_CSP_INCLUDE_CSP_DRIVERS_CAN_SOCKETCAN_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/drivers/i2c.h b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/i2c.h new file mode 100644 index 00000000..3cf605ee --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/i2c.h @@ -0,0 +1,120 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file + * Common I2C interface, + * This file is derived from the Gomspace I2C driver, + * + */ + +#ifndef I2C_H_ +#define I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The return value of the driver is a bit strange, + * It should return E_NO_ERR if successfull and the value is -1 + */ +#define E_NO_ERR -1 + +/** + * Maximum transfer length on I2C + */ +#define I2C_MTU 256 + +/** + I2C device modes + @{ +*/ +/** + I2C Master mode. +*/ +#define I2C_MASTER 0 +/** + I2C Slave mode. +*/ +#define I2C_SLAVE 1 +/**@}*/ + +/** + Data structure for I2C frames. + This structs fits on top of #csp_packet_t, removing the need for copying data. +*/ +typedef struct __attribute__((packed)) i2c_frame_s { + //! Not used by CSP + uint8_t padding; + //! Not used by CSP - cleared before Tx + uint8_t retries; + //! Not used by CSP + uint32_t reserved; + //! Destination address + uint8_t dest; + //! Not used by CSP - cleared before Tx + uint8_t len_rx; + //! Length of \a data part + uint16_t len; + //! CSP data + uint8_t data[I2C_MTU]; +} i2c_frame_t; + +/** + Callback for receiving data. + + @param[in] frame received I2C frame + @param[out] pxTaskWoken can be set, if context switch is required due to received data. +*/ +typedef void (*i2c_callback_t) (i2c_frame_t * frame, void * pxTaskWoken); + +/** + Initialise the I2C driver + + Functions is called by csp_i2c_init(). + + @param handle Which I2C bus (if more than one exists) + @param mode I2C device mode. Must be either I2C_MASTER or I2C_SLAVE + @param addr Own slave address + @param speed Bus speed in kbps + @param queue_len_tx Length of transmit queue + @param queue_len_rx Length of receive queue + @param callback If this value is set, the driver will call this function instead of using an RX queue + @return Error code +*/ +int i2c_init(int handle, int mode, uint8_t addr, uint16_t speed, int queue_len_tx, int queue_len_rx, i2c_callback_t callback); + +/** + User I2C transmit function. + + Called by CSP, when sending message over I2C. + + @param handle Handle to the device + @param frame Pointer to I2C frame + @param timeout Ticks to wait + @return Error code +*/ +int i2c_send(int handle, i2c_frame_t * frame, uint16_t timeout); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/drivers/usart.h b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/usart.h new file mode 100644 index 00000000..d2da8448 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/usart.h @@ -0,0 +1,107 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file + * Common USART interface, + * This file is derived from the Gomspace USART driver, + * the main difference is the assumption that only one USART will be present on a PC + */ + +#ifndef USART_H_ +#define USART_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Usart configuration, to be used with the usart_init call. +*/ +struct usart_conf { + //! USART device. + const char *device; + //! bits per second. + uint32_t baudrate; + //! Number of data bits. + uint8_t databits; + //! Number of stop bits. + uint8_t stopbits; + //! Parity setting. + uint8_t paritysetting; + //! Enable parity checking (Windows only). + uint8_t checkparity; +}; + +/** + Initialise UART with the usart_conf data structure + @param[in] conf full configuration structure +*/ +void usart_init(struct usart_conf *conf); + +/** + In order to catch incoming chars use the callback. + Only one callback per interface. + @param[in] handle usart[0,1,2,3] + @param[in] callback function pointer +*/ +typedef void (*usart_callback_t) (uint8_t *buf, int len, void *pxTaskWoken); + +/** + Set callback for receiving data. +*/ +void usart_set_callback(usart_callback_t callback); + +/** + Insert a character to the RX buffer of a usart + + @param[in] c character to insert + @param[out] pxTaskWoken can be set, if context switch is required due to received data. +*/ +void usart_insert(char c, void *pxTaskWoken); + +/** + Polling putchar (stdin). + + @param[in] c Character to transmit +*/ +void usart_putc(char c); + +/** + Send char buffer on UART (stdout). + + @param[in] buf Pointer to data + @param[in] len Length of data +*/ +void usart_putstr(char *buf, int len); + +/** + Buffered getchar (stdin). + + @return Character received +*/ +char usart_getc(void); + +#ifdef __cplusplus +} +#endif +#endif /* USART_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_can.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_can.h new file mode 100644 index 00000000..229671f2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_can.h @@ -0,0 +1,76 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_CAN_H_ +#define _CSP_IF_CAN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/* CAN header macros */ +#define CFP_HOST_SIZE 5 +#define CFP_TYPE_SIZE 1 +#define CFP_REMAIN_SIZE 8 +#define CFP_ID_SIZE 10 + +/* Macros for extracting header fields */ +#define CFP_FIELD(id,rsiz,fsiz) ((uint32_t)((uint32_t)((id) >> (rsiz)) & (uint32_t)((1 << (fsiz)) - 1))) +#define CFP_SRC(id) CFP_FIELD(id, CFP_HOST_SIZE + CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE, CFP_HOST_SIZE) +#define CFP_DST(id) CFP_FIELD(id, CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE, CFP_HOST_SIZE) +#define CFP_TYPE(id) CFP_FIELD(id, CFP_REMAIN_SIZE + CFP_ID_SIZE, CFP_TYPE_SIZE) +#define CFP_REMAIN(id) CFP_FIELD(id, CFP_ID_SIZE, CFP_REMAIN_SIZE) +#define CFP_ID(id) CFP_FIELD(id, 0, CFP_ID_SIZE) + +/* Macros for building CFP headers */ +#define CFP_MAKE_FIELD(id,fsiz,rsiz) ((uint32_t)(((id) & (uint32_t)((uint32_t)(1 << (fsiz)) - 1)) << (rsiz))) +#define CFP_MAKE_SRC(id) CFP_MAKE_FIELD(id, CFP_HOST_SIZE, CFP_HOST_SIZE + CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE) +#define CFP_MAKE_DST(id) CFP_MAKE_FIELD(id, CFP_HOST_SIZE, CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE) +#define CFP_MAKE_TYPE(id) CFP_MAKE_FIELD(id, CFP_TYPE_SIZE, CFP_REMAIN_SIZE + CFP_ID_SIZE) +#define CFP_MAKE_REMAIN(id) CFP_MAKE_FIELD(id, CFP_REMAIN_SIZE, CFP_ID_SIZE) +#define CFP_MAKE_ID(id) CFP_MAKE_FIELD(id, CFP_ID_SIZE, 0) + +/* Mask to uniquely separate connections */ +#define CFP_ID_CONN_MASK (CFP_MAKE_SRC((uint32_t)(1 << CFP_HOST_SIZE) - 1) | \ + CFP_MAKE_DST((uint32_t)(1 << CFP_HOST_SIZE) - 1) | \ + CFP_MAKE_ID((uint32_t)(1 << CFP_ID_SIZE) - 1)) + +/** + Default Maximum Transmission Unit (MTU) for CSP over CAN. + Maximum value is 2042 bytes. +*/ +#define CSP_CAN_MTU 256 + +int csp_can_rx(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc, CSP_BASE_TYPE *task_woken); +int csp_can_tx(csp_iface_t *interface, csp_packet_t *packet, uint32_t timeout); + +/* Must be implemented by the driver */ +int csp_can_tx_frame(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_IF_CAN_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_i2c.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_i2c.h new file mode 100644 index 00000000..c2169843 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_i2c.h @@ -0,0 +1,51 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_I2C_H_ +#define _CSP_IF_I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include + +extern csp_iface_t csp_if_i2c; + +/** + * Capture I2C RX events for CSP + * @param opt_addr local i2c address + * @param handle which i2c device to use + * @param speed interface speed in kHz (normally 100 or 400) + * @return csp_error.h code + */ +int csp_i2c_init(uint8_t opt_addr, int handle, int speed); + +void csp_i2c_rx(i2c_frame_t * frame, void * pxTaskWoken); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_IF_I2C_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_kiss.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_kiss.h new file mode 100644 index 00000000..f164cad1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_kiss.h @@ -0,0 +1,110 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_KISS_H_ +#define _CSP_IF_KISS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/** + * The KISS interface relies on the USART callback in order to parse incoming + * messaged from the serial interface. The USART callback however does not + * support passing the handle number of the responding USART, so you need to implement + * a USART callback for each handle and then call kiss_rx subsequently. + * + * In order to initialize the KISS interface. Fist call kiss_init() and then + * setup your usart to call csp_kiss_rx when new data is available. + * + * When a byte is not a part of a kiss packet, it will be returned to your + * usart driver using the usart_insert funtion that you provide. + * + * @param csp_iface pointer to interface + * @param buf pointer to incoming data + * @param len length of incoming data + * @param pxTaskWoken NULL if task context, pointer to variable if ISR + */ +void csp_kiss_rx(csp_iface_t * interface, uint8_t *buf, int len, void *pxTaskWoken); + +/** + * The putc function is used by the kiss interface to send + * a string of data to the serial port. This function must + * be implemented by the user, and passed to the kiss + * interface through the kiss_init function. + * @param buf byte to push + */ +typedef void (*csp_kiss_putc_f)(char buf); + +/** + * The characters not accepted by the kiss interface, are discarded + * using this function, which must be implemented by the user + * and passed through the kiss_init function. + * + * This reject function is typically used to display ASCII strings + * sent over the serial port, which are not in KISS format. Such as + * debugging information. + * + * @param c rejected character + * @param pxTaskWoken NULL if task context, pointer to variable if ISR + */ +typedef void (*csp_kiss_discard_f)(char c, void *pxTaskWoken); + +typedef enum { + KISS_MODE_NOT_STARTED, + KISS_MODE_STARTED, + KISS_MODE_ESCAPED, + KISS_MODE_SKIP_FRAME, +} kiss_mode_e; + +/** + * This structure should be statically allocated by the user + * and passed to the kiss interface during the init function + * no member information should be changed + */ +typedef struct csp_kiss_handle_s { + //! Put character on usart (tx). + csp_kiss_putc_f kiss_putc; + //! Discard - not KISS data (rx). + csp_kiss_discard_f kiss_discard; + //! Internal KISS state. + unsigned int rx_length; + //! Internal KISS state. + kiss_mode_e rx_mode; + //! Internal KISS state. + unsigned int rx_first; + //! Not used. + volatile unsigned char *rx_cbuf; + //! Internal KISS state. + csp_packet_t * rx_packet; +} csp_kiss_handle_t; + +void csp_kiss_init(csp_iface_t * csp_iface, csp_kiss_handle_t * csp_kiss_handle, csp_kiss_putc_f kiss_putc_f, csp_kiss_discard_f kiss_discard_f, const char * name); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_IF_KISS_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_lo.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_lo.h new file mode 100644 index 00000000..793c45ac --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_lo.h @@ -0,0 +1,38 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_LO_H_ +#define _CSP_IF_LO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* CSP includes */ +#include +#include + +extern csp_iface_t csp_if_lo; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_IF_LO_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_zmqhub.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_zmqhub.h new file mode 100644 index 00000000..f9ab43bf --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_zmqhub.h @@ -0,0 +1,26 @@ +#ifndef CSP_IF_ZMQHUB_H_ +#define CSP_IF_ZMQHUB_H_ + +#include + +extern csp_iface_t csp_if_zmqhub; + +/** + * Setup ZMQ interface + * @param addr only receive messages matching this address (255 means all) + * @param host Pointer to string containing zmqproxy host + * @return CSP_ERR + */ +int csp_zmqhub_init(uint8_t addr, const char * host); + +/** + * Setup ZMQ interface + * @param addr only receive messages matching this address (255 means all) + * @param publisher_endpoint Pointer to string containing zmqproxy publisher endpoint + * @param subscriber_endpoint Pointer to string containing zmqproxy subscriber endpoint + * @return CSP_ERR + */ +int csp_zmqhub_init_w_endpoints(uint8_t addr, const char * publisher_url, + const char * subscriber_url); + +#endif /* CSP_IF_ZMQHUB_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_malloc.c new file mode 100644 index 00000000..97e5c8f4 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_malloc.c @@ -0,0 +1,33 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* FreeRTOS includes */ +#include + +void * csp_malloc(size_t size) { + return pvPortMalloc(size); +} + +void csp_free(void *ptr) { + vPortFree(ptr); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_queue.c new file mode 100644 index 00000000..44efd0eb --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_queue.c @@ -0,0 +1,66 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* FreeRTOS includes */ +#include +#include + +/* CSP includes */ +#include + +#include + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return xQueueCreate(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + vQueueDelete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void * value, uint32_t timeout) { + if (timeout != CSP_MAX_DELAY) + timeout = timeout / portTICK_RATE_MS; + return xQueueSendToBack(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + return xQueueSendToBackFromISR(handle, value, task_woken); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void * buf, uint32_t timeout) { + if (timeout != CSP_MAX_DELAY) + timeout = timeout / portTICK_RATE_MS; + return xQueueReceive(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void * buf, CSP_BASE_TYPE * task_woken) { + return xQueueReceiveFromISR(handle, buf, task_woken); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return uxQueueMessagesWaiting(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return uxQueueMessagesWaitingFromISR(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_semaphore.c new file mode 100644 index 00000000..b91757e5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_semaphore.c @@ -0,0 +1,96 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* FreeRTOS includes */ +#include +#include + +/* CSP includes */ +#include + +#include +#include +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + *mutex = xSemaphoreCreateMutex(); + if (*mutex) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + return csp_bin_sem_remove(mutex); +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + return csp_bin_sem_wait(mutex, timeout); +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + return csp_bin_sem_post(mutex); +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + vSemaphoreCreateBinary(*sem); + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + if ((sem != NULL) && (*sem != NULL)) { + csp_queue_remove(*sem); + } + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + csp_log_lock("Wait: %p", sem); + if (timeout != CSP_MAX_DELAY) + timeout = timeout / portTICK_RATE_MS; + if (xSemaphoreTake(*sem, timeout) == pdPASS) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + csp_log_lock("Post: %p", sem); + if (xSemaphoreGive(*sem) == pdPASS) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + csp_log_lock("Post: %p", sem); + if (xSemaphoreGiveFromISR(*sem, task_woken) == pdPASS) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_system.c new file mode 100644 index 00000000..a81c84b4 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_system.c @@ -0,0 +1,139 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { +#if (tskKERNEL_VERSION_MAJOR < 8) + vTaskList((signed portCHAR *) out); +#else + vTaskList(out); +#endif + return CSP_ERR_NONE; +} + +int csp_sys_tasklist_size(void) { + return 40 * uxTaskGetNumberOfTasks(); +} + +uint32_t csp_sys_memfree(void) { + + uint32_t total = 0, max = UINT32_MAX, size; + void * pmem; + + /* If size_t is less than 32 bits, start with 10 KiB */ + size = sizeof(uint32_t) > sizeof(size_t) ? 10000 : 1000000; + + while (1) { + pmem = pvPortMalloc(size + total); + if (pmem == NULL) { + max = size + total; + size = size / 2; + } else { + total += size; + if (total + size >= max) + size = size / 2; + vPortFree(pmem); + } + if (size < 32) break; + } + + return total; +} + +int csp_sys_reboot(void) { + + extern void __attribute__((weak)) cpu_set_reset_cause(unsigned int); + if (cpu_set_reset_cause) + cpu_set_reset_cause(1); + + extern void __attribute__((weak)) cpu_reset(void); + if (cpu_reset) { + cpu_reset(); + while (1); + } + + csp_log_error("Failed to reboot"); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { + + extern void __attribute__((weak)) cpu_shutdown(void); + if (cpu_shutdown) { + cpu_shutdown(); + while (1); + } + + csp_log_error("Failed to shutdown"); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + + unsigned int color_code, modifier_code; + switch (color & COLOR_MASK_COLOR) { + case COLOR_BLACK: + color_code = 30; break; + case COLOR_RED: + color_code = 31; break; + case COLOR_GREEN: + color_code = 32; break; + case COLOR_YELLOW: + color_code = 33; break; + case COLOR_BLUE: + color_code = 34; break; + case COLOR_MAGENTA: + color_code = 35; break; + case COLOR_CYAN: + color_code = 36; break; + case COLOR_WHITE: + color_code = 37; break; + case COLOR_RESET: + default: + color_code = 0; break; + } + + switch (color & COLOR_MASK_MODIFIER) { + case COLOR_BOLD: + modifier_code = 1; break; + case COLOR_UNDERLINE: + modifier_code = 2; break; + case COLOR_BLINK: + modifier_code = 3; break; + case COLOR_HIDE: + modifier_code = 4; break; + case COLOR_NORMAL: + default: + modifier_code = 0; break; + } + + printf("\033[%u;%um", modifier_code, color_code); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_thread.c new file mode 100644 index 00000000..af8296cd --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_thread.c @@ -0,0 +1,38 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { +#if (tskKERNEL_VERSION_MAJOR >= 8) + portBASE_TYPE ret = xTaskCreate(routine, thread_name, stack_depth, parameters, priority, handle); +#else + portBASE_TYPE ret = xTaskCreate(routine, (signed char *) thread_name, stack_depth, parameters, priority, handle); +#endif + if (ret != pdTRUE) + return CSP_ERR_NOMEM; + return CSP_ERR_NONE; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_time.c new file mode 100644 index 00000000..fd54a8cb --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_time.c @@ -0,0 +1,46 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* FreeRTOS includes */ +#include +#include + +/* CSP includes */ +#include + +#include + +uint32_t csp_get_ms(void) { + return (uint32_t)(xTaskGetTickCount() * (1000/configTICK_RATE_HZ)); +} + +uint32_t csp_get_ms_isr(void) { + return (uint32_t)(xTaskGetTickCountFromISR() * (1000/configTICK_RATE_HZ)); +} + +uint32_t csp_get_s(void) { + return (uint32_t)(xTaskGetTickCount()/configTICK_RATE_HZ); +} + +uint32_t csp_get_s_isr(void) { + return (uint32_t)(xTaskGetTickCountFromISR()/configTICK_RATE_HZ); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_malloc.c new file mode 100644 index 00000000..95bb8cc7 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_malloc.c @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +void * csp_malloc(size_t size) { + return malloc(size); +} + +void csp_free(void *ptr) { + free(ptr); +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_queue.c new file mode 100644 index 00000000..a2fb1b4f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_queue.c @@ -0,0 +1,64 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include +#include + + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return pthread_queue_create(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + return pthread_queue_delete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout) { + return pthread_queue_enqueue(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + if (task_woken != NULL) + *task_woken = 0; + return csp_queue_enqueue(handle, value, 0); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout) { + return pthread_queue_dequeue(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void *buf, CSP_BASE_TYPE * task_woken) { + *task_woken = 0; + return csp_queue_dequeue(handle, buf, 0); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_semaphore.c new file mode 100644 index 00000000..915447f3 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_semaphore.c @@ -0,0 +1,105 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + csp_log_lock("Mutex init: %p", mutex); + *mutex = pthread_queue_create(1, sizeof(int)); + if (mutex) { + int dummy = 0; + pthread_queue_enqueue(*mutex, &dummy, 0); + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + pthread_queue_delete(*mutex); + return CSP_SEMAPHORE_OK; +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + + int ret; + csp_log_lock("Wait: %p timeout %"PRIu32, mutex, timeout); + + if (timeout == CSP_INFINITY) { + /* TODO: fix this to be infinite */ + int dummy = 0; + if (pthread_queue_dequeue(*mutex, &dummy, timeout) == PTHREAD_QUEUE_OK) + ret = CSP_MUTEX_OK; + else + ret = CSP_MUTEX_ERROR; + } else { + int dummy = 0; + if (pthread_queue_dequeue(*mutex, &dummy, timeout) == PTHREAD_QUEUE_OK) + ret = CSP_MUTEX_OK; + else + ret = CSP_MUTEX_ERROR; + } + + return ret == CSP_MUTEX_ERROR ? CSP_SEMAPHORE_ERROR : CSP_SEMAPHORE_OK; +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + int dummy = 0; + if (pthread_queue_enqueue(*mutex, &dummy, 0) == PTHREAD_QUEUE_OK) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + return csp_mutex_create(sem); +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + return csp_mutex_remove(sem); +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + return csp_mutex_lock(sem, timeout); +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + return csp_mutex_unlock(sem); +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + return csp_mutex_unlock(sem); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_system.c new file mode 100644 index 00000000..834cb210 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_system.c @@ -0,0 +1,99 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { + strcpy(out, "Tasklist not available on OSX"); + return CSP_ERR_NONE; +} + +int csp_sys_tasklist_size(void) { + return 100; +} + +uint32_t csp_sys_memfree(void) { + /* TODO: Fix memory free on OSX */ + uint32_t total = 0; + return total; +} + +int csp_sys_reboot(void) { + /* TODO: Fix reboot on OSX */ + csp_log_error("Failed to reboot"); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { + /* TODO: Fix shutdown on OSX */ + csp_log_error("Failed to shutdown"); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + + unsigned int color_code, modifier_code; + switch (color & COLOR_MASK_COLOR) { + case COLOR_BLACK: + color_code = 30; break; + case COLOR_RED: + color_code = 31; break; + case COLOR_GREEN: + color_code = 32; break; + case COLOR_YELLOW: + color_code = 33; break; + case COLOR_BLUE: + color_code = 34; break; + case COLOR_MAGENTA: + color_code = 35; break; + case COLOR_CYAN: + color_code = 36; break; + case COLOR_WHITE: + color_code = 37; break; + case COLOR_RESET: + default: + color_code = 0; break; + } + + switch (color & COLOR_MASK_MODIFIER) { + case COLOR_BOLD: + modifier_code = 1; break; + case COLOR_UNDERLINE: + modifier_code = 2; break; + case COLOR_BLINK: + modifier_code = 3; break; + case COLOR_HIDE: + modifier_code = 4; break; + case COLOR_NORMAL: + default: + modifier_code = 0; break; + } + + printf("\033[%u;%um", modifier_code, color_code); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_thread.c new file mode 100644 index 00000000..ed64856a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_thread.c @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { + return pthread_create(handle, NULL, routine, parameters); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_time.c new file mode 100644 index 00000000..a53f27e6 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_time.c @@ -0,0 +1,65 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include + +#include + +uint32_t csp_get_ms(void) { + struct timespec ts; + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + return (uint32_t)(ts.tv_sec*1000+ts.tv_nsec/1000000); +} + +uint32_t csp_get_ms_isr(void) { + return csp_get_ms(); +} + +uint32_t csp_get_s(void) { + struct timespec ts; + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + return (uint32_t)ts.tv_sec; +} + +uint32_t csp_get_s_isr(void) { + return csp_get_s(); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/pthread_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/pthread_queue.c new file mode 100644 index 00000000..c4ac8c1d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/pthread_queue.c @@ -0,0 +1,179 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* +Inspired by c-pthread-queue by Matthew Dickinson +http://code.google.com/p/c-pthread-queue/ +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +pthread_queue_t * pthread_queue_create(int 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; + +} + +void pthread_queue_delete(pthread_queue_t * q) { + + if (q == NULL) + return; + + free(q->buffer); + free(q); + + return; + +} + +int pthread_queue_enqueue(pthread_queue_t * queue, void * value, uint32_t timeout) { + + int ret; + + /* Calculate timeout */ + struct timespec ts; + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + 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) { + ret = pthread_cond_timedwait(&(queue->cond_full), &(queue->mutex), &ts); + if (ret != 0) { + pthread_mutex_unlock(&(queue->mutex)); + return PTHREAD_QUEUE_FULL; + } + } + + /* 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; + +} + +int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) { + + int ret; + + /* Calculate timeout */ + struct timespec ts; + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + 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) { + ret = pthread_cond_timedwait(&(queue->cond_empty), &(queue->mutex), &ts); + if (ret != 0) { + pthread_mutex_unlock(&(queue->mutex)); + return PTHREAD_QUEUE_EMPTY; + } + } + + /* 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; + +} + +int pthread_queue_items(pthread_queue_t * queue) { + + pthread_mutex_lock(&(queue->mutex)); + int items = queue->items; + pthread_mutex_unlock(&(queue->mutex)); + + return items; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_malloc.c new file mode 100644 index 00000000..95bb8cc7 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_malloc.c @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +void * csp_malloc(size_t size) { + return malloc(size); +} + +void csp_free(void *ptr) { + free(ptr); +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_queue.c new file mode 100644 index 00000000..a2fb1b4f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_queue.c @@ -0,0 +1,64 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include +#include + + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return pthread_queue_create(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + return pthread_queue_delete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout) { + return pthread_queue_enqueue(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + if (task_woken != NULL) + *task_woken = 0; + return csp_queue_enqueue(handle, value, 0); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout) { + return pthread_queue_dequeue(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void *buf, CSP_BASE_TYPE * task_woken) { + *task_woken = 0; + return csp_queue_dequeue(handle, buf, 0); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_semaphore.c new file mode 100644 index 00000000..6829dec2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_semaphore.c @@ -0,0 +1,164 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + csp_log_lock("Mutex init: %p", mutex); + if (pthread_mutex_init(mutex, NULL) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + if (pthread_mutex_destroy(mutex) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + + int ret; + struct timespec ts; + uint32_t sec, nsec; + + csp_log_lock("Wait: %p timeout %"PRIu32, mutex, timeout); + + if (timeout == CSP_INFINITY) { + ret = pthread_mutex_lock(mutex); + } else { + if (clock_gettime(CLOCK_REALTIME, &ts)) + return CSP_SEMAPHORE_ERROR; + + sec = timeout / 1000; + 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; + + ret = pthread_mutex_timedlock(mutex, &ts); + } + + if (ret != 0) + return CSP_SEMAPHORE_ERROR; + + return CSP_SEMAPHORE_OK; +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + if (pthread_mutex_unlock(mutex) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + csp_log_lock("Semaphore init: %p", sem); + if (sem_init(sem, 0, 1) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + if (sem_destroy(sem) == 0) + return CSP_SEMAPHORE_OK; + else + return CSP_SEMAPHORE_ERROR; +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + + int ret; + struct timespec ts; + uint32_t sec, nsec; + + csp_log_lock("Wait: %p timeout %"PRIu32, sem, timeout); + + if (timeout == CSP_INFINITY) { + ret = sem_wait(sem); + } else { + if (clock_gettime(CLOCK_REALTIME, &ts)) + return CSP_SEMAPHORE_ERROR; + + sec = timeout / 1000; + 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; + + ret = sem_timedwait(sem, &ts); + } + + if (ret != 0) + return CSP_SEMAPHORE_ERROR; + + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + CSP_BASE_TYPE dummy = 0; + return csp_bin_sem_post_isr(sem, &dummy); +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + csp_log_lock("Post: %p", sem); + *task_woken = 0; + + int value; + sem_getvalue(sem, &value); + if (value > 0) + return CSP_SEMAPHORE_OK; + + if (sem_post(sem) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_system.c new file mode 100644 index 00000000..6c882c7c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_system.c @@ -0,0 +1,131 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { + strcpy(out, "Tasklist not available on POSIX"); + return CSP_ERR_NONE; +} + +int csp_sys_tasklist_size(void) { + return 100; +} + +uint32_t csp_sys_memfree(void) { + uint32_t total = 0; + struct sysinfo info; + sysinfo(&info); + total = info.freeram * info.mem_unit; + return total; +} + +int csp_sys_reboot(void) { +#ifdef CSP_USE_INIT_SHUTDOWN + /* Let init(1) handle the reboot */ + int ret = system("reboot"); + (void) ret; /* Silence warning */ +#else + int magic = LINUX_REBOOT_CMD_RESTART; + + /* Sync filesystem before reboot */ + sync(); + reboot(magic); +#endif + + /* If reboot(2) returns, it is an error */ + csp_log_error("Failed to reboot: %s", strerror(errno)); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { +#ifdef CSP_USE_INIT_SHUTDOWN + /* Let init(1) handle the shutdown */ + int ret = system("halt"); + (void) ret; /* Silence warning */ +#else + int magic = LINUX_REBOOT_CMD_HALT; + + /* Sync filesystem before reboot */ + sync(); + reboot(magic); +#endif + + /* If reboot(2) returns, it is an error */ + csp_log_error("Failed to shutdown: %s", strerror(errno)); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + + unsigned int color_code, modifier_code; + switch (color & COLOR_MASK_COLOR) { + case COLOR_BLACK: + color_code = 30; break; + case COLOR_RED: + color_code = 31; break; + case COLOR_GREEN: + color_code = 32; break; + case COLOR_YELLOW: + color_code = 33; break; + case COLOR_BLUE: + color_code = 34; break; + case COLOR_MAGENTA: + color_code = 35; break; + case COLOR_CYAN: + color_code = 36; break; + case COLOR_WHITE: + color_code = 37; break; + case COLOR_RESET: + default: + color_code = 0; break; + } + + switch (color & COLOR_MASK_MODIFIER) { + case COLOR_BOLD: + modifier_code = 1; break; + case COLOR_UNDERLINE: + modifier_code = 2; break; + case COLOR_BLINK: + modifier_code = 3; break; + case COLOR_HIDE: + modifier_code = 4; break; + case COLOR_NORMAL: + default: + modifier_code = 0; break; + } + + printf("\033[%u;%um", modifier_code, color_code); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_thread.c new file mode 100644 index 00000000..3277d35d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_thread.c @@ -0,0 +1,55 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { + pthread_attr_t attributes, *attr_ref; + int return_code; + + if( pthread_attr_init(&attributes) == 0 ) + { + unsigned int stack_size = PTHREAD_STACK_MIN;// use at least one memory page + + while(stack_size < stack_depth)// must reach at least the provided size + { + stack_size += PTHREAD_STACK_MIN;// keep memory page boundary (some systems may fail otherwise)) + } + attr_ref = &attributes; + + pthread_attr_setdetachstate(attr_ref, PTHREAD_CREATE_DETACHED);// do not waste memory on each call + pthread_attr_setstacksize(attr_ref, stack_size); + } + else + { + attr_ref = NULL; + } + return_code = pthread_create(handle, attr_ref, routine, parameters); + if( attr_ref != NULL ) pthread_attr_destroy(attr_ref); + + return return_code; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_time.c new file mode 100644 index 00000000..c9677443 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_time.c @@ -0,0 +1,54 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include + +#include + +uint32_t csp_get_ms(void) { + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (uint32_t)(ts.tv_sec*1000+ts.tv_nsec/1000000); + else + return 0; +} + +uint32_t csp_get_ms_isr(void) { + return csp_get_ms(); +} + +uint32_t csp_get_s(void) { + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (uint32_t)ts.tv_sec; + else + return 0; +} + +uint32_t csp_get_s_isr(void) { + return csp_get_s(); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/pthread_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/pthread_queue.c new file mode 100644 index 00000000..e8b6d4ab --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/pthread_queue.c @@ -0,0 +1,243 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* +Inspired by c-pthread-queue by Matthew Dickinson +http://code.google.com/p/c-pthread-queue/ +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +static inline int get_deadline(struct timespec *ts, uint32_t timeout_ms) +{ + int ret = clock_gettime(CLOCK_MONOTONIC, ts); + + if (ret < 0) { + return ret; + } + + uint32_t sec = timeout_ms / 1000; + uint32_t nsec = (timeout_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; + + return ret; +} + +static inline int init_cond_clock_monotonic(pthread_cond_t * cond) +{ + + int ret; + pthread_condattr_t attr; + + pthread_condattr_init(&attr); + ret = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + + if (ret == 0) { + ret = pthread_cond_init(cond, &attr); + } + + pthread_condattr_destroy(&attr); + return ret; + +} + +pthread_queue_t * pthread_queue_create(int 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) || init_cond_clock_monotonic(&(q->cond_full)) || init_cond_clock_monotonic(&(q->cond_empty))) { + free(q->buffer); + free(q); + q = NULL; + } + } else { + free(q); + q = NULL; + } + } + + return q; + +} + +void pthread_queue_delete(pthread_queue_t * q) { + + if (q == NULL) + return; + + free(q->buffer); + free(q); + + return; + +} + + +static inline int wait_slot_available(pthread_queue_t * queue, struct timespec *ts) { + + int ret; + + while (queue->items == queue->size) { + + if (ts != NULL) { + ret = pthread_cond_timedwait(&(queue->cond_full), &(queue->mutex), ts); + } else { + ret = pthread_cond_wait(&(queue->cond_full), &(queue->mutex)); + } + + if (ret != 0 && errno != EINTR) { + return PTHREAD_QUEUE_FULL; //Timeout + } + } + + return PTHREAD_QUEUE_OK; + +} + +int pthread_queue_enqueue(pthread_queue_t * queue, void * value, uint32_t timeout) { + + int ret; + struct timespec ts; + struct timespec *pts = NULL; + + /* Calculate timeout */ + if (timeout != CSP_MAX_DELAY) { + if (get_deadline(&ts, timeout) != 0) { + return PTHREAD_QUEUE_ERROR; + } + pts = &ts; + } else { + pts = NULL; + } + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + + ret = wait_slot_available(queue, pts); + if (ret == PTHREAD_QUEUE_OK) { + /* Copy 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)); + + if (ret == PTHREAD_QUEUE_OK) { + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_empty)); + } + + return ret; + +} + +static inline int wait_item_available(pthread_queue_t * queue, struct timespec *ts) { + + int ret; + + while (queue->items == 0) { + + if (ts != NULL) { + ret = pthread_cond_timedwait(&(queue->cond_empty), &(queue->mutex), ts); + } else { + ret = pthread_cond_wait(&(queue->cond_empty), &(queue->mutex)); + } + + if (ret != 0 && errno != EINTR) { + return PTHREAD_QUEUE_EMPTY; //Timeout + } + } + + return PTHREAD_QUEUE_OK; + +} + +int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) { + + int ret; + struct timespec ts; + struct timespec *pts; + + /* Calculate timeout */ + if (timeout != CSP_MAX_DELAY) { + if (get_deadline(&ts, timeout) != 0) { + return PTHREAD_QUEUE_ERROR; + } + pts = &ts; + } else { + pts = NULL; + } + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + + ret = wait_item_available(queue, pts); + if (ret == PTHREAD_QUEUE_OK) { + /* 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)); + + if (ret == PTHREAD_QUEUE_OK) { + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_full)); + } + + return ret; + +} + +int pthread_queue_items(pthread_queue_t * queue) { + + pthread_mutex_lock(&(queue->mutex)); + int items = queue->items; + pthread_mutex_unlock(&(queue->mutex)); + + return items; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/README b/gomspace/libgscsp/lib/libcsp/src/arch/windows/README new file mode 100644 index 00000000..b97ce7f5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/README @@ -0,0 +1,18 @@ +This directory contains files specific to the windows port of libcsp. + +To compile and create a static library, execute: + + python waf configure --with-os=windows build + +from the root of this project. Note python must be in PATH. + +The build requirements are: + * Windows Vista SP1 + * A recent version of MinGW _or_ MinGW-w64 + * Windows API headers + * cPython 2.5 or newer + +What provides the Windows API headers depends on the development environment: +Using MinGW: Headers provided by w32api package. windows_glue.h header is needed because these headers do not declare condition variables. +Using MinGW-w64: Headers should be available in the default configuration. You may have to compile the distribution from source. windows_glue.h should not be needed. + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_malloc.c new file mode 100644 index 00000000..4b301e49 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_malloc.c @@ -0,0 +1,9 @@ +#include + +void * csp_malloc(size_t size) { + return malloc(size); +} + +void csp_free(void * ptr) { + free(ptr); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_queue.c new file mode 100644 index 00000000..177f8fa9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_queue.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include "windows_queue.h" + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return windows_queue_create(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + windows_queue_delete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout) { + return windows_queue_enqueue(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + if( task_woken != NULL ) + *task_woken = 0; + return windows_queue_enqueue(handle, value, 0); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout) { + return windows_queue_dequeue(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void * buf, CSP_BASE_TYPE * task_woken) { + if( task_woken != NULL ) + *task_woken = 0; + return windows_queue_dequeue(handle, buf, 0); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return windows_queue_items(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return windows_queue_items(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_semaphore.c new file mode 100644 index 00000000..aa69251e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_semaphore.c @@ -0,0 +1,74 @@ +#include +#include +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + HANDLE mutexHandle = CreateMutex(NULL, FALSE, FALSE); + if( mutexHandle == NULL ) { + return CSP_MUTEX_ERROR; + } + *mutex = mutexHandle; + return CSP_MUTEX_OK; +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + if( !CloseHandle(*mutex) ) { + return CSP_MUTEX_ERROR; + } + return CSP_MUTEX_OK; +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + if(WaitForSingleObject(*mutex, timeout) == WAIT_OBJECT_0) { + return CSP_MUTEX_OK; + } + return CSP_MUTEX_ERROR; + +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + if( !ReleaseMutex(*mutex) ) { + return CSP_MUTEX_ERROR; + } + return CSP_MUTEX_OK; +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + HANDLE semHandle = CreateSemaphore(NULL, 1, 1, NULL); + if( semHandle == NULL ) { + return CSP_SEMAPHORE_ERROR; + } + *sem = semHandle; + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + if( !CloseHandle(*sem) ) { + return CSP_SEMAPHORE_ERROR; + } + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + if( WaitForSingleObject(*sem, timeout) == WAIT_OBJECT_0 ) { + return CSP_SEMAPHORE_OK; + } + return CSP_SEMAPHORE_ERROR; + +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + if( !ReleaseSemaphore(*sem, 1, NULL) ) { + return CSP_SEMAPHORE_ERROR; + } + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + if( task_woken != NULL ) { + *task_woken = 0; + } + return csp_bin_sem_post(sem); +} + + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_system.c new file mode 100644 index 00000000..262c2052 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_system.c @@ -0,0 +1,60 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { + strcpy(out, "Tasklist not available on Windows"); + return CSP_ERR_NONE; +} + +uint32_t csp_sys_memfree(void) { + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + DWORDLONG freePhysicalMem = statex.ullAvailPhys; + size_t total = (size_t) freePhysicalMem; + return (uint32_t)total; +} + +int csp_sys_reboot(void) { + /* TODO: Fix reboot on Windows */ + csp_log_error("Failed to reboot"); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { + /* TODO: Fix shutdown on Windows */ + csp_log_error("Failed to shutdown"); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + /* TODO: Add Windows color output here */ +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_thread.c new file mode 100644 index 00000000..ef46a948 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_thread.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *)__attribute__((stdcall)), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { + HANDLE taskHandle = (HANDLE) _beginthreadex(NULL, stack_depth, routine, parameters, 0, 0); + if( taskHandle == 0 ) + return CSP_ERR_NOMEM; // Failure + *handle = taskHandle; + return CSP_ERR_NONE; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_time.c new file mode 100644 index 00000000..618292ab --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_time.c @@ -0,0 +1,20 @@ +#include +#include +#include + +uint32_t csp_get_ms(void) { + return (uint32_t)GetTickCount(); +} + +uint32_t csp_get_ms_isr(void) { + return csp_get_ms(); +} + +uint32_t csp_get_s(void) { + uint32_t time_ms = csp_get_ms(); + return time_ms/1000; +} + +uint32_t csp_get_s_isr(void) { + return csp_get_s(); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_glue.h b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_glue.h new file mode 100644 index 00000000..6e0cf6db --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_glue.h @@ -0,0 +1,23 @@ +#ifndef WINDOWS_GLUE_H +#define WINDOWS_GLUE_H + +#include +#undef interface + +#if (_WIN32_WINNT >= 0x0600) + +#define RTL_CONDITION_VARIABLE_INIT 0 +#define RTL_CONDITION_VARIABLE_LOCKMODE_SHARED 1 +#define CONDITION_VARIABLE_INIT RTL_CONDITION_VARIABLE_INIT +#define CONDITION_VARIABLE_LOCKMODE_SHARED RTL_CONDITION_VARIABLE_LOCKMODE_SHARED + +typedef PVOID RTL_CONDITION_VARIABLE; +typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE; + +WINBASEAPI VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable); +WINBASEAPI WINBOOL WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds); +WINBASEAPI VOID WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable); +WINBASEAPI VOID WINAPI WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable); + +#endif // _WIN#"_WINNT +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.c new file mode 100644 index 00000000..aa337dc8 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.c @@ -0,0 +1,91 @@ +#include "windows_queue.h" +#include "windows_glue.h" +#include + +static int queueFull(windows_queue_t * queue) { + return queue->items == queue->size; +} + +static int queueEmpty(windows_queue_t * queue) { + return queue->items == 0; +} + +windows_queue_t * windows_queue_create(int length, size_t item_size) { + windows_queue_t *queue = (windows_queue_t*)malloc(sizeof(windows_queue_t)); + if(queue == NULL) + goto queue_malloc_failed; + + queue->buffer = malloc(length*item_size); + if(queue->buffer == NULL) + goto buffer_malloc_failed; + + queue->size = length; + queue->item_size = item_size; + queue->items = 0; + queue->head_idx = 0; + + InitializeCriticalSection(&(queue->mutex)); + InitializeConditionVariable(&(queue->cond_full)); + InitializeConditionVariable(&(queue->cond_empty)); + goto queue_init_success; + +buffer_malloc_failed: + free(queue); + queue = NULL; +queue_malloc_failed: +queue_init_success: + return queue; +} + +void windows_queue_delete(windows_queue_t * q) { + if(q==NULL) return; + DeleteCriticalSection(&(q->mutex)); + free(q->buffer); + free(q); +} + +int windows_queue_enqueue(windows_queue_t * queue, void * value, int timeout) { + int offset; + EnterCriticalSection(&(queue->mutex)); + while(queueFull(queue)) { + int ret = SleepConditionVariableCS(&(queue->cond_full), &(queue->mutex), timeout); + if( !ret ) { + LeaveCriticalSection(&(queue->mutex)); + return ret == WAIT_TIMEOUT ? WINDOWS_QUEUE_FULL : WINDOWS_QUEUE_ERROR; + } + } + offset = ((queue->head_idx+queue->items) % queue->size) * queue->item_size; + memcpy((unsigned char*)queue->buffer + offset, value, queue->item_size); + queue->items++; + + LeaveCriticalSection(&(queue->mutex)); + WakeAllConditionVariable(&(queue->cond_empty)); + return WINDOWS_QUEUE_OK; +} + +int windows_queue_dequeue(windows_queue_t * queue, void * buf, int timeout) { + EnterCriticalSection(&(queue->mutex)); + while(queueEmpty(queue)) { + int ret = SleepConditionVariableCS(&(queue->cond_empty), &(queue->mutex), timeout); + if( !ret ) { + LeaveCriticalSection(&(queue->mutex)); + return ret == WAIT_TIMEOUT ? WINDOWS_QUEUE_EMPTY : WINDOWS_QUEUE_ERROR; + } + } + memcpy(buf, (unsigned char*)queue->buffer+(queue->head_idx%queue->size*queue->item_size), queue->item_size); + queue->items--; + queue->head_idx = (queue->head_idx + 1) % queue->size; + + LeaveCriticalSection(&(queue->mutex)); + WakeAllConditionVariable(&(queue->cond_full)); + return WINDOWS_QUEUE_OK; +} + +int windows_queue_items(windows_queue_t * queue) { + int items; + EnterCriticalSection(&(queue->mutex)); + items = queue->items; + LeaveCriticalSection(&(queue->mutex)); + + return items; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.h b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.h new file mode 100644 index 00000000..e6bc5423 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.h @@ -0,0 +1,41 @@ +#ifndef _WINDOWS_QUEUE_H_ +#define _WINDOWS_QUEUE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "windows_glue.h" +#undef interface + +#include + +#define WINDOWS_QUEUE_ERROR CSP_QUEUE_ERROR +#define WINDOWS_QUEUE_EMPTY CSP_QUEUE_ERROR +#define WINDOWS_QUEUE_FULL CSP_QUEUE_ERROR +#define WINDOWS_QUEUE_OK CSP_QUEUE_OK + +typedef struct windows_queue_s { + void * buffer; + int size; + int item_size; + int items; + int head_idx; + CRITICAL_SECTION mutex; + CONDITION_VARIABLE cond_full; + CONDITION_VARIABLE cond_empty; +} windows_queue_t; + +windows_queue_t * windows_queue_create(int length, size_t item_size); +void windows_queue_delete(windows_queue_t * q); +int windows_queue_enqueue(windows_queue_t * queue, void * value, int timeout); +int windows_queue_dequeue(windows_queue_t * queue, void * buf, int timeout); +int windows_queue_items(windows_queue_t * queue); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _WINDOWS_QUEUE_H_ + diff --git a/gomspace/libgscsp/lib/libcsp/src/bindings/python/pycsp.c b/gomspace/libgscsp/lib/libcsp/src/bindings/python/pycsp.c new file mode 100644 index 00000000..f1009d1a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/bindings/python/pycsp.c @@ -0,0 +1,1052 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION == 3 +#define IS_PY3 +#endif + +static int is_capsule_of_type(PyObject* capsule, const char* expected_type) { + const char* capsule_name = PyCapsule_GetName(capsule); + if (strcmp(capsule_name, expected_type) != 0) { + PyErr_Format( + PyExc_TypeError, + "capsule contains unexpected type, expected=%s, got=%s", + expected_type, capsule_name); // TypeError is thrown + return 0; + } + return 1; +} + +/** + * csp/csp.h + */ + +/* + * void csp_service_handler(csp_conn_t *conn, csp_packet_t *packet); + */ +static PyObject* pycsp_service_handler(PyObject *self, PyObject *args) { + PyObject* conn_capsule; + PyObject* packet_capsule; + if (!PyArg_ParseTuple(args, "OO", &conn_capsule, &packet_capsule)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(conn_capsule, "csp_conn_t") || + !is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_service_handler( + (csp_conn_t*)PyCapsule_GetPointer(conn_capsule, "csp_conn_t"), + (csp_packet_t*)PyCapsule_GetPointer(packet_capsule, "csp_packet_t")); + + Py_RETURN_NONE; +} + +/* + * int csp_init(uint8_t my_node_address); + */ +static PyObject* pycsp_init(PyObject *self, PyObject *args) { + uint8_t my_node_address; + if (!PyArg_ParseTuple(args, "b", &my_node_address)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_init(my_node_address)); +} + +/* + * void csp_set_hostname(const char *hostname); + */ +static PyObject* pycsp_set_hostname(PyObject *self, PyObject *args) { + char* hostname; + if (!PyArg_ParseTuple(args, "s", &hostname)) { + return NULL; // TypeError is thrown + } + + csp_set_hostname(hostname); + Py_RETURN_NONE; +} + +/* + * const char *csp_get_hostname(void); + */ +static PyObject* pycsp_get_hostname(PyObject *self, PyObject *args) { + return Py_BuildValue("s", csp_get_hostname()); +} + +/* + * void csp_set_model(const char *model); + */ +static PyObject* pycsp_set_model(PyObject *self, PyObject *args) { + char* model; + if (!PyArg_ParseTuple(args, "s", &model)) { + return NULL; // TypeError is thrown + } + + csp_set_model(model); + Py_RETURN_NONE; +} + +/* + * const char *csp_get_model(void); + */ +static PyObject* pycsp_get_model(PyObject *self, PyObject *args) { + return Py_BuildValue("s", csp_get_model()); +} + +/* + * void csp_set_revision(const char *revision); + */ +static PyObject* pycsp_set_revision(PyObject *self, PyObject *args) { + char* revision; + if (!PyArg_ParseTuple(args, "s", &revision)) { + return NULL; // TypeError is thrown + } + + csp_set_revision(revision); + Py_RETURN_NONE; +} + +/* + * const char *csp_get_revision(void); + */ +static PyObject* pycsp_get_revision(PyObject *self, PyObject *args) { + return Py_BuildValue("s", csp_get_revision()); +} + +/* + * csp_socket_t *csp_socket(uint32_t opts); + */ +static PyObject* pycsp_socket(PyObject *self, PyObject *args) { + uint32_t opts = CSP_SO_NONE; + if (!PyArg_ParseTuple(args, "|I", &opts)) { + return NULL; // TypeError is thrown + } + + return PyCapsule_New(csp_socket(opts), "csp_socket_t", NULL); +} + +/* + * csp_conn_t *csp_accept(csp_socket_t *socket, uint32_t timeout); + */ +static PyObject* pycsp_accept(PyObject *self, PyObject *args) { + PyObject* socket_capsule; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "O|I", &socket_capsule, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(socket_capsule, "csp_socket_t")) { + return NULL; // TypeError is thrown + } + + void* socket = PyCapsule_GetPointer(socket_capsule, "csp_socket_t"); + csp_conn_t* conn = csp_accept((csp_socket_t*)socket, timeout); + if (conn == NULL) { + Py_RETURN_NONE; // because a capsule cannot contain a NULL-pointer + } + + return PyCapsule_New(conn, "csp_conn_t", NULL); +} + +/* + * csp_packet_t *csp_read(csp_conn_t *conn, uint32_t timeout); + */ +static PyObject* pycsp_read(PyObject *self, PyObject *args) { + PyObject* conn_capsule; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "O|I", &conn_capsule, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + csp_packet_t* packet = csp_read((csp_conn_t*)conn, timeout); + if (packet == NULL) { + Py_RETURN_NONE; // because capsule cannot contain a NULL-pointer + } + + return PyCapsule_New(packet, "csp_packet_t", NULL); +} + +/* +* int csp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) +*/ +static PyObject* pycsp_send(PyObject *self, PyObject *args) { + PyObject* conn_capsule; + PyObject* packet_capsule; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "OO|I", &conn_capsule, &packet_capsule, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + if (packet == NULL) { + Py_RETURN_NONE; + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + + int result = csp_send(conn, packet, timeout); + + return Py_BuildValue("i", result); +} + +/* + * int csp_transaction(uint8_t prio, uint8_t dest, uint8_t port, + * uint32_t timeout, void *outbuf, int outlen, + * void *inbuf, int inlen); + */ +static PyObject* pycsp_transaction(PyObject *self, PyObject *args) { + uint8_t prio; + uint8_t dest; + uint8_t port; + uint32_t timeout; + Py_buffer inbuf; + Py_buffer outbuf; + if (!PyArg_ParseTuple(args, "bbbIw*w*", &prio, &dest, &port, &timeout, &outbuf, &inbuf)) { + return NULL; // TypeError is thrown + } + + int result = csp_transaction(prio, dest, port, timeout, + outbuf.buf, outbuf.len, + inbuf.buf, inbuf.len); + + return Py_BuildValue("i", result); +} + +/* int csp_sendto(uint8_t prio, uint8_t dest, uint8_t dport, uint8_t src_port, uint32_t opts, csp_packet_t *packet, uint32_t timeout); */ +static PyObject* pycsp_sendto(PyObject *self, PyObject *args) { + uint8_t prio; + uint8_t dest; + uint8_t dport; + uint8_t src_port; + uint32_t opts; + PyObject* packet_capsule; + uint32_t timeout; + if (!PyArg_ParseTuple(args, "bbbbIOI", &prio, &dest, &dport, &src_port, &opts, &packet_capsule, &timeout)) { + Py_RETURN_NONE; + } + + void* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + if (packet == NULL) { + Py_RETURN_NONE; + } + + return Py_BuildValue("i", csp_sendto(prio, + dest, + dport, + src_port, + opts, + (csp_packet_t*)packet, + timeout)); +} + + +/* + * int csp_sendto_reply(csp_packet_t * request_packet, + * csp_packet_t * reply_packet, + * uint32_t opts, uint32_t timeout); + */ +static PyObject* pycsp_sendto_reply(PyObject *self, PyObject *args) { + PyObject* request_packet_capsule; + PyObject* reply_packet_capsule; + uint32_t opts = CSP_O_NONE; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "OO|II", &request_packet_capsule, &reply_packet_capsule, &opts, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(request_packet_capsule, "csp_packet_t") || + !is_capsule_of_type(reply_packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + void* request_packet = PyCapsule_GetPointer(request_packet_capsule, "csp_packet_t"); + void* reply_packet = PyCapsule_GetPointer(reply_packet_capsule, "csp_packet_t"); + + return Py_BuildValue("i", csp_sendto_reply((csp_packet_t*)request_packet, + (csp_packet_t*)reply_packet, + opts, + timeout)); +} + +/* + * csp_conn_t *csp_connect(uint8_t prio, uint8_t dest, uint8_t dport, uint32_t timeout, uint32_t opts); + */ +static PyObject* pycsp_connect(PyObject *self, PyObject *args) { + uint8_t prio; + uint8_t dest; + uint8_t dport; + uint32_t timeout; + uint32_t opts; + if (!PyArg_ParseTuple(args, "bbbII", &prio, &dest, &dport, &timeout, &opts)) { + return NULL; // TypeError is thrown + } + + csp_conn_t *conn = csp_connect(prio, dest, dport, timeout,opts); + + return PyCapsule_New(conn, "csp_conn_t", NULL); +} + +/* + * int csp_close(csp_conn_t *conn); + */ +static PyObject* pycsp_close(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void *conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_close((csp_conn_t*)conn)); +} + +/* + * int csp_conn_dport(csp_conn_t *conn); + */ +static PyObject* pycsp_conn_dport(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_dport((csp_conn_t*)conn)); +} + +/* + * int csp_conn_sport(csp_conn_t *conn); + */ +static PyObject* pycsp_conn_sport(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_sport((csp_conn_t*)conn)); +} + +/* int csp_conn_dst(csp_conn_t *conn); */ +static PyObject* pycsp_conn_dst(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_dst((csp_conn_t*)conn)); +} + +/* + * int csp_conn_src(csp_conn_t *conn); + */ +static PyObject* pycsp_conn_src(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_src((csp_conn_t*)conn)); +} + +/* int csp_listen(csp_socket_t *socket, size_t conn_queue_length); */ +static PyObject* pycsp_listen(PyObject *self, PyObject *args) { + PyObject* socket_capsule; + size_t conn_queue_len = 10; + if (!PyArg_ParseTuple(args, "O|n", &socket_capsule, &conn_queue_len)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(socket_capsule, "csp_socket_t")) { + return NULL; // TypeError is thrown + } + + void* sock = PyCapsule_GetPointer(socket_capsule, "csp_socket_t"); + return Py_BuildValue("i", csp_listen((csp_socket_t*)sock, conn_queue_len)); +} + +/* int csp_bind(csp_socket_t *socket, uint8_t port); */ +static PyObject* pycsp_bind(PyObject *self, PyObject *args) { + PyObject* socket_capsule; + uint8_t port; + if (!PyArg_ParseTuple(args, "Ob", &socket_capsule, &port)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(socket_capsule, "csp_socket_t")) { + return NULL; // TypeError is thrown + } + + void* sock = PyCapsule_GetPointer(socket_capsule, "csp_socket_t"); + return Py_BuildValue("i", csp_bind((csp_socket_t*)sock, port)); +} + +/* int csp_route_start_task(unsigned int task_stack_size, unsigned int priority); */ +static PyObject* pycsp_route_start_task(PyObject *self, PyObject *args) { + unsigned int priority = CSP_PRIO_NORM; + if (!PyArg_ParseTuple(args, "|I", &priority)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_route_start_task(0, priority)); +} + +/* + * int csp_ping(uint8_t node, uint32_t timeout, + * unsigned int size, uint8_t conn_options); + */ +static PyObject* pycsp_ping(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout = 500; + unsigned int size = 100; + uint8_t conn_options = CSP_O_NONE; + if (!PyArg_ParseTuple(args, "b|IIb", &node, &timeout, &size, &conn_options)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_ping(node, timeout, size, conn_options)); +} + +/* + * void csp_reboot(uint8_t node); + */ +static PyObject* pycsp_reboot(PyObject *self, PyObject *args) { + uint8_t node; + if (!PyArg_ParseTuple(args, "b", &node)) { + return NULL; // TypeError is thrown + } + + csp_reboot(node); + Py_RETURN_NONE; +} + +/* + * void csp_shutdown(uint8_t node); + */ +static PyObject* pycsp_shutdown(PyObject *self, PyObject *args) { + uint8_t node; + if (!PyArg_ParseTuple(args, "b", &node)) { + return NULL; // TypeError is thrown + } + + csp_shutdown(node); + Py_RETURN_NONE; +} + +/* + * void csp_rdp_set_opt(unsigned int window_size, + * unsigned int conn_timeout_ms, + * unsigned int packet_timeout_ms, + * unsigned int delayed_acks, + * unsigned int ack_timeout, + * unsigned int ack_delay_count); + */ +static PyObject* pycsp_rdp_set_opt(PyObject *self, PyObject *args) { + unsigned int window_size; + unsigned int conn_timeout_ms; + unsigned int packet_timeout_ms; + unsigned int delayed_acks; + unsigned int ack_timeout; + unsigned int ack_delay_count; + if (!PyArg_ParseTuple(args, "IIIIII", &window_size, &conn_timeout_ms, + &packet_timeout_ms, &delayed_acks, + &ack_timeout, &ack_delay_count)) { + return NULL; // TypeError is thrown + } +#ifdef CSP_USE_RDP + csp_rdp_set_opt(window_size, conn_timeout_ms, packet_timeout_ms, + delayed_acks, ack_timeout, ack_delay_count); +#endif + Py_RETURN_NONE; +} + +/* + * void csp_rdp_get_opt(unsigned int *window_size, + * unsigned int *conn_timeout_ms, + * unsigned int *packet_timeout_ms, + * unsigned int *delayed_acks, + * unsigned int *ack_timeout, + * unsigned int *ack_delay_count); + */ +static PyObject* pycsp_rdp_get_opt(PyObject *self, PyObject *args) { + + unsigned int window_size = 0; + unsigned int conn_timeout_ms = 0; + unsigned int packet_timeout_ms = 0; + unsigned int delayed_acks = 0; + unsigned int ack_timeout = 0; + unsigned int ack_delay_count = 0; +#ifdef CSP_USE_RDP + csp_rdp_get_opt(&window_size, + &conn_timeout_ms, + &packet_timeout_ms, + &delayed_acks, + &ack_timeout, + &ack_delay_count); +#endif + return Py_BuildValue("IIIIII", + window_size, + conn_timeout_ms, + packet_timeout_ms, + delayed_acks, + ack_timeout, + ack_delay_count); +} + + +/* + * + * int csp_xtea_set_key(char *key, uint32_t keylen); + */ +static PyObject* pycsp_xtea_set_key(PyObject *self, PyObject *args) { + char* key; + uint32_t keylen; + if (!PyArg_ParseTuple(args, "si", &key, &keylen)) { + return NULL; // TypeError is thrown + } + return Py_BuildValue("i", csp_xtea_set_key(key, keylen)); +} +/** + * csp/csp_rtable.h + */ + +/* + * int csp_rtable_set(uint8_t node, uint8_t mask, + * csp_iface_t *ifc, uint8_t mac); + */ +static PyObject* pycsp_rtable_set(PyObject *self, PyObject *args) { + uint8_t node; + uint8_t mask; + char* interface_name; + uint8_t mac = CSP_NODE_MAC; + if (!PyArg_ParseTuple(args, "bbs|b", &node, &mask, &interface_name, &mac)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_rtable_set(node, + mask, + csp_iflist_get_by_name(interface_name), + mac)); +} + +/* + * void csp_rtable_clear(void); + */ +static PyObject* pycsp_rtable_clear(PyObject *self, PyObject *args) { + csp_rtable_clear(); + Py_RETURN_NONE; +} + +/* +* int csp_rtable_check(const char * buffer) +*/ +static PyObject* pycsp_rtable_check(PyObject *self, PyObject *args) { + char* buffer; + if (!PyArg_ParseTuple(args, "s", &buffer)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_rtable_check(buffer)); +} + +/* +* void csp_rtable_load(const char * buffer) +*/ +static PyObject* pycsp_rtable_load(PyObject *self, PyObject *args) { + char* buffer; + if (!PyArg_ParseTuple(args, "s", &buffer)) { + return NULL; // TypeError is thrown + } + + csp_rtable_load(buffer); + Py_RETURN_NONE; +} + +/** + * csp/csp_buffer.h + */ + +/* + * int csp_buffer_init(int count, int size); + */ +static PyObject* pycsp_buffer_init(PyObject *self, PyObject *args) { + int count; + int size; + if (!PyArg_ParseTuple(args, "ii", &count, &size)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_buffer_init(count, size)); +} + +/* + * void * csp_buffer_get(size_t size); + */ +static PyObject* pycsp_buffer_get(PyObject *self, PyObject *args) { + size_t size; + if (!PyArg_ParseTuple(args, "n", &size)) { + return NULL; // TypeError is thrown + } + + void* packet = csp_buffer_get(size); + if (packet == NULL) { + Py_RETURN_NONE; + } + + return PyCapsule_New(packet, "csp_packet_t", NULL); +} +/* + * void csp_buffer_free(void *packet); + */ +static PyObject* pycsp_buffer_free(PyObject *self, PyObject *args) { + PyObject* packet_capsule; + if (!PyArg_ParseTuple(args, "O", &packet_capsule)) { + return NULL; // TypeError is thrown + } + + + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_buffer_free(PyCapsule_GetPointer(packet_capsule, "csp_packet_t")); + Py_RETURN_NONE; +} + +/* + * int csp_buffer_remaining(void); + */ +static PyObject* pycsp_buffer_remaining(PyObject *self, PyObject *args) { + return Py_BuildValue("i", csp_buffer_remaining()); +} + +/** + * csp/csp_cmp.h + */ + +/* + * static inline int csp_cmp_ident(uint8_t node, uint32_t timeout, + * struct csp_cmp_message *msg) + */ +static PyObject* pycsp_cmp_ident(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "b|i", &node, &timeout)) { + return NULL; // TypeError is thrown + } + + struct csp_cmp_message msg; + int rc = csp_cmp_ident(node, timeout, &msg); + return Py_BuildValue("isssss", + rc, + msg.ident.hostname, + msg.ident.model, + msg.ident.revision, + msg.ident.date, + msg.ident.time); +} + +/* + * static inline int csp_cmp_route_set(uint8_t node, uint32_t timeout, + * struct csp_cmp_message *msg) + */ +static PyObject* pycsp_cmp_route_set(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout = 500; + uint8_t addr; + uint8_t mac; + char* ifstr; + if (!PyArg_ParseTuple(args, "bibbs", &node, &timeout, &addr, &mac, &ifstr)) { + return NULL; // TypeError is thrown + } + + struct csp_cmp_message msg; + msg.route_set.dest_node = addr; + msg.route_set.next_hop_mac = mac; + strncpy(msg.route_set.interface, ifstr, CSP_CMP_ROUTE_IFACE_LEN); + int rc = csp_cmp_route_set(node, timeout, &msg); + return Py_BuildValue("i", + rc); +} + +/* static inline int pycsp_cmp_peek(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg); */ +static PyObject* pycsp_cmp_peek(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout; + uint8_t len; + uint32_t addr; + Py_buffer outbuf; + + if (!PyArg_ParseTuple(args, "biibw*", &node, &timeout, &addr, &len, &outbuf)) { + Py_RETURN_NONE; + } + + if (len > CSP_CMP_PEEK_MAX_LEN) { + len = CSP_CMP_PEEK_MAX_LEN; + } + struct csp_cmp_message msg; + msg.peek.addr = csp_hton32(addr); + msg.peek.len = len; + int rc = csp_cmp_peek(node, timeout, &msg); + if (rc != CSP_ERR_NONE) { + Py_RETURN_NONE; + } + memcpy(outbuf.buf, msg.peek.data, len); + outbuf.len = len; + + return Py_BuildValue("i", rc); +} + +/* static inline int pycsp_cmp_poke(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg); */ +static PyObject* pycsp_cmp_poke(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout; + uint8_t len; + uint32_t addr; + Py_buffer inbuf; + + if (!PyArg_ParseTuple(args, "biibw*", &node, &timeout, &addr, &len, &inbuf)) { + Py_RETURN_NONE; + } + + if (len > CSP_CMP_POKE_MAX_LEN) { + len = CSP_CMP_POKE_MAX_LEN; + } + struct csp_cmp_message msg; + msg.poke.addr = csp_hton32(addr); + msg.poke.len = len; + memcpy(msg.poke.data, inbuf.buf, len); + int rc = csp_cmp_poke(node, timeout, &msg); + if (rc != CSP_ERR_NONE) { + Py_RETURN_NONE; + } + + return Py_BuildValue("i", rc); +} + +/* static inline int csp_cmp_clock(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg); */ +static PyObject* pycsp_cmp_clock(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout; + uint32_t sec; + uint32_t nsec; + if (!PyArg_ParseTuple(args, "bIII", &node, &timeout, &sec, &nsec)) { + Py_RETURN_NONE; + } + + struct csp_cmp_message msg; + msg.clock.tv_sec = csp_hton32(sec); + msg.clock.tv_nsec = csp_hton32(nsec); + return Py_BuildValue("i", csp_cmp_clock(node, timeout, &msg)); +} + +/** + * csp/interfaces/csp_if_zmqhub.h + */ + +/* + * int csp_zmqhub_init(char addr, char * host); + */ +static PyObject* pycsp_zmqhub_init(PyObject *self, PyObject *args) { + char addr; + char* host; + if (!PyArg_ParseTuple(args, "bs", &addr, &host)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_zmqhub_init(addr, host)); +} + +/** + * csp/drivers/can_socketcan.h + */ + +/* + * csp_iface_t * csp_can_socketcan_init(const char * ifc, int bitrate, int promisc); + */ +static PyObject* pycsp_can_socketcan_init(PyObject *self, PyObject *args) +{ + char* ifc; + int bitrate = 1000000; + int promisc = 0; + + if (!PyArg_ParseTuple(args, "s|ii", &ifc, &bitrate, &promisc)) + { + return NULL; + } + + csp_can_socketcan_init(ifc, bitrate, promisc); + Py_RETURN_NONE; +} + + +/** + * csp/interfaces/csp_if_kiss.h + */ + +/* + * int csp_kiss_init(char addr, char * host); + */ +static PyObject* pycsp_kiss_init(PyObject *self, PyObject *args) { + char* device; + uint32_t baudrate = 500000; + uint32_t mtu = 512; + const char* if_name = "KISS"; + if (!PyArg_ParseTuple(args, "s|IIs", &device, &baudrate, &mtu, &if_name)) { + return NULL; // TypeError is thrown + } + + static csp_iface_t csp_if_kiss; + static csp_kiss_handle_t csp_kiss_driver; + csp_if_kiss.mtu = (uint16_t) mtu; + struct usart_conf conf = {.device = device, .baudrate = baudrate}; + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, usart_putc, usart_insert, if_name); + usart_init(&conf); + + void my_usart_rx(uint8_t * buf, int len, void * pxTaskWoken) { + csp_kiss_rx(&csp_if_kiss, buf, len, pxTaskWoken); + } + usart_set_callback(my_usart_rx); + + Py_RETURN_NONE; +} + +/** + * Helpers - accessing csp_packet_t members + */ +static PyObject* pycsp_packet_set_data(PyObject *self, PyObject *args) { + PyObject* packet_capsule; + Py_buffer data; + if (!PyArg_ParseTuple(args, "Ow*", &packet_capsule, &data)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_packet_t* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + + memcpy((char *)packet->data, data.buf, data.len); + packet->length = data.len; + + Py_RETURN_NONE; +} +static PyObject* pycsp_packet_get_data(PyObject *self, PyObject *packet_capsule) { + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_packet_t* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); +#ifdef IS_PY3 + return Py_BuildValue("y#", packet->data, packet->length); +#else + return Py_BuildValue("s#", packet->data, packet->length); +#endif +} + +static PyObject* pycsp_packet_get_length(PyObject *self, PyObject *packet_capsule) { + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_packet_t* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + return Py_BuildValue("H", packet->length); +} + +static PyMethodDef methods[] = { + + /* csp/csp.h */ + {"service_handler", pycsp_service_handler, METH_VARARGS, ""}, + {"init", pycsp_init, METH_VARARGS, ""}, + {"set_hostname", pycsp_set_hostname, METH_VARARGS, ""}, + {"get_hostname", pycsp_get_hostname, METH_NOARGS, ""}, + {"set_model", pycsp_set_model, METH_VARARGS, ""}, + {"get_model", pycsp_get_model, METH_NOARGS, ""}, + {"set_revision", pycsp_set_revision, METH_VARARGS, ""}, + {"get_revision", pycsp_get_revision, METH_NOARGS, ""}, + {"socket", pycsp_socket, METH_VARARGS, ""}, + {"accept", pycsp_accept, METH_VARARGS, ""}, + {"read", pycsp_read, METH_VARARGS, ""}, + {"send", pycsp_send, METH_VARARGS, ""}, + {"transaction", pycsp_transaction, METH_VARARGS, ""}, + {"sendto_reply", pycsp_sendto_reply, METH_VARARGS, ""}, + {"sendto", pycsp_sendto, METH_VARARGS, ""}, + {"connect", pycsp_connect, METH_VARARGS, ""}, + {"close", pycsp_close, METH_O, ""}, + {"conn_dport", pycsp_conn_dport, METH_O, ""}, + {"conn_sport", pycsp_conn_sport, METH_O, ""}, + {"conn_dst", pycsp_conn_dst, METH_O, ""}, + {"conn_src", pycsp_conn_src, METH_O, ""}, + {"listen", pycsp_listen, METH_VARARGS, ""}, + {"bind", pycsp_bind, METH_VARARGS, ""}, + {"route_start_task", pycsp_route_start_task, METH_VARARGS, ""}, + {"ping", pycsp_ping, METH_VARARGS, ""}, + {"reboot", pycsp_reboot, METH_VARARGS, ""}, + {"shutdown", pycsp_shutdown, METH_VARARGS, ""}, + {"rdp_set_opt", pycsp_rdp_set_opt, METH_VARARGS, ""}, + {"rdp_get_opt", pycsp_rdp_get_opt, METH_NOARGS, ""}, + {"xtea_set_key", pycsp_xtea_set_key, METH_VARARGS, ""}, + + /* csp/csp_rtable.h */ + {"rtable_set", pycsp_rtable_set, METH_VARARGS, ""}, + {"rtable_clear", pycsp_rtable_clear, METH_NOARGS, ""}, + {"rtable_check", pycsp_rtable_check, METH_VARARGS, ""}, + {"rtable_load", pycsp_rtable_load, METH_VARARGS, ""}, + + /* csp/csp_buffer.h */ + {"buffer_init", pycsp_buffer_init, METH_VARARGS, ""}, + {"buffer_free", pycsp_buffer_free, METH_VARARGS, ""}, + {"buffer_get", pycsp_buffer_get, METH_VARARGS, ""}, + {"buffer_remaining", pycsp_buffer_remaining, METH_NOARGS, ""}, + + /* csp/csp_cmp.h */ + {"cmp_ident", pycsp_cmp_ident, METH_VARARGS, ""}, + {"cmp_route_set", pycsp_cmp_route_set, METH_VARARGS, ""}, + {"cmp_peek", pycsp_cmp_peek, METH_VARARGS, ""}, + {"cmp_poke", pycsp_cmp_poke, METH_VARARGS, ""}, + {"cmp_clock", pycsp_cmp_clock, METH_VARARGS, ""}, + + + /* csp/interfaces/csp_if_zmqhub.h */ + {"zmqhub_init", pycsp_zmqhub_init, METH_VARARGS, ""}, + {"kiss_init", pycsp_kiss_init, METH_VARARGS, ""}, + + /* csp/drivers/can_socketcan.h */ + {"can_socketcan_init", pycsp_can_socketcan_init, METH_VARARGS, ""}, + + /* helpers */ + {"packet_get_length", pycsp_packet_get_length, METH_O, ""}, + {"packet_get_data", pycsp_packet_get_data, METH_O, ""}, + {"packet_set_data", pycsp_packet_set_data, METH_VARARGS, ""}, + + /* sentinel */ + {NULL, NULL, 0, NULL} +}; + +#ifdef IS_PY3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "libcsp_py3", + NULL, + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#ifdef IS_PY3 +PyMODINIT_FUNC PyInit_libcsp_py3(void) { +#else + PyMODINIT_FUNC initlibcsp_py2(void) { +#endif + + PyObject* m; + +#ifdef IS_PY3 + m = PyModule_Create(&moduledef); +#else + m = Py_InitModule("libcsp_py2", methods); +#endif + /** + * csp/csp_types.h + */ + + /* RESERVED PORTS */ + PyModule_AddIntConstant(m, "CSP_CMP", CSP_CMP); + PyModule_AddIntConstant(m, "CSP_PING", CSP_PING); + PyModule_AddIntConstant(m, "CSP_PS", CSP_PS); + PyModule_AddIntConstant(m, "CSP_MEMFREE", CSP_MEMFREE); + PyModule_AddIntConstant(m, "CSP_REBOOT", CSP_REBOOT); + PyModule_AddIntConstant(m, "CSP_BUF_FREE", CSP_BUF_FREE); + PyModule_AddIntConstant(m, "CSP_UPTIME", CSP_UPTIME); + PyModule_AddIntConstant(m, "CSP_ANY", CSP_MAX_BIND_PORT + 1); + PyModule_AddIntConstant(m, "CSP_PROMISC", CSP_MAX_BIND_PORT + 2); + + /* PRIORITIES */ + PyModule_AddIntConstant(m, "CSP_PRIO_CRITICAL", CSP_PRIO_CRITICAL); + PyModule_AddIntConstant(m, "CSP_PRIO_HIGH", CSP_PRIO_HIGH); + PyModule_AddIntConstant(m, "CSP_PRIO_NORM", CSP_PRIO_NORM); + PyModule_AddIntConstant(m, "CSP_PRIO_LOW", CSP_PRIO_LOW); + + /* FLAGS */ + PyModule_AddIntConstant(m, "CSP_FFRAG", CSP_FFRAG); + PyModule_AddIntConstant(m, "CSP_FHMAC", CSP_FHMAC); + PyModule_AddIntConstant(m, "CSP_FXTEA", CSP_FXTEA); + PyModule_AddIntConstant(m, "CSP_FRDP", CSP_FRDP); + PyModule_AddIntConstant(m, "CSP_FCRC32", CSP_FCRC32); + + /* SOCKET OPTIONS */ + PyModule_AddIntConstant(m, "CSP_SO_NONE", CSP_SO_NONE); + PyModule_AddIntConstant(m, "CSP_SO_RDPREQ", CSP_SO_RDPREQ); + PyModule_AddIntConstant(m, "CSP_SO_RDPPROHIB", CSP_SO_RDPPROHIB); + PyModule_AddIntConstant(m, "CSP_SO_HMACREQ", CSP_SO_HMACREQ); + PyModule_AddIntConstant(m, "CSP_SO_HMACPROHIB", CSP_SO_HMACPROHIB); + PyModule_AddIntConstant(m, "CSP_SO_XTEAREQ", CSP_SO_XTEAREQ); + PyModule_AddIntConstant(m, "CSP_SO_XTEAPROHIB", CSP_SO_XTEAPROHIB); + PyModule_AddIntConstant(m, "CSP_SO_CRC32REQ", CSP_SO_CRC32REQ); + PyModule_AddIntConstant(m, "CSP_SO_CRC32PROHIB", CSP_SO_CRC32PROHIB); + PyModule_AddIntConstant(m, "CSP_SO_CONN_LESS", CSP_SO_CONN_LESS); + + /* CONNECT OPTIONS */ + PyModule_AddIntConstant(m, "CSP_O_NONE", CSP_O_NONE); + PyModule_AddIntConstant(m, "CSP_O_RDP", CSP_O_RDP); + PyModule_AddIntConstant(m, "CSP_O_NORDP", CSP_O_NORDP); + PyModule_AddIntConstant(m, "CSP_O_HMAC", CSP_O_HMAC); + PyModule_AddIntConstant(m, "CSP_O_NOHMAC", CSP_O_NOHMAC); + PyModule_AddIntConstant(m, "CSP_O_XTEA", CSP_O_XTEA); + PyModule_AddIntConstant(m, "CSP_O_NOXTEA", CSP_O_NOXTEA); + PyModule_AddIntConstant(m, "CSP_O_CRC32", CSP_O_CRC32); + PyModule_AddIntConstant(m, "CSP_O_NOCRC32", CSP_O_NOCRC32); + + + /** + * csp/csp_error.h + */ + + PyModule_AddIntConstant(m, "CSP_ERR_NONE", CSP_ERR_NONE); + PyModule_AddIntConstant(m, "CSP_ERR_NOMEM", CSP_ERR_NOMEM); + PyModule_AddIntConstant(m, "CSP_ERR_INVAL", CSP_ERR_INVAL); + PyModule_AddIntConstant(m, "CSP_ERR_TIMEDOUT", CSP_ERR_TIMEDOUT); + PyModule_AddIntConstant(m, "CSP_ERR_USED", CSP_ERR_USED); + PyModule_AddIntConstant(m, "CSP_ERR_NOTSUP", CSP_ERR_NOTSUP); + PyModule_AddIntConstant(m, "CSP_ERR_BUSY", CSP_ERR_BUSY); + PyModule_AddIntConstant(m, "CSP_ERR_ALREADY", CSP_ERR_ALREADY); + PyModule_AddIntConstant(m, "CSP_ERR_RESET", CSP_ERR_RESET); + PyModule_AddIntConstant(m, "CSP_ERR_NOBUFS", CSP_ERR_NOBUFS); + PyModule_AddIntConstant(m, "CSP_ERR_TX", CSP_ERR_TX); + PyModule_AddIntConstant(m, "CSP_ERR_DRIVER", CSP_ERR_DRIVER); + PyModule_AddIntConstant(m, "CSP_ERR_AGAIN", CSP_ERR_AGAIN); + PyModule_AddIntConstant(m, "CSP_ERR_HMAC", CSP_ERR_HMAC); + PyModule_AddIntConstant(m, "CSP_ERR_XTEA", CSP_ERR_XTEA); + PyModule_AddIntConstant(m, "CSP_ERR_CRC32", CSP_ERR_CRC32); + + /** + * csp/rtable.h + */ + PyModule_AddIntConstant(m, "CSP_NODE_MAC", CSP_NODE_MAC); + +#ifdef IS_PY3 + return m; +#endif + } + diff --git a/gomspace/libgscsp/lib/libcsp/src/crypto/csp_hmac.c b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_hmac.c new file mode 100644 index 00000000..ae7fbb00 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_hmac.c @@ -0,0 +1,202 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Hash-based Message Authentication Code - based on code from libtom.org */ + +#include +#include +#include + +/* CSP includes */ +#include + +#include +#include + +#ifdef CSP_USE_HMAC + +#define HMAC_KEY_LENGTH 16 + +/* HMAC key */ +static uint8_t csp_hmac_key[HMAC_KEY_LENGTH]; + +/* HMAC state structure */ +typedef struct { + csp_sha1_state md; + uint8_t key[SHA1_BLOCKSIZE]; +} hmac_state; + +static int csp_hmac_init(hmac_state * hmac, const uint8_t * key, uint32_t keylen) { + uint32_t i; + uint8_t buf[SHA1_BLOCKSIZE]; + + /* NULL pointer and key check */ + if (!hmac || !key || keylen < 1) + return CSP_ERR_INVAL; + + /* Make sure we have a large enough key */ + if(keylen > SHA1_BLOCKSIZE) { + csp_sha1_memory(key, keylen, hmac->key); + if(SHA1_DIGESTSIZE < SHA1_BLOCKSIZE) + memset((hmac->key) + SHA1_DIGESTSIZE, 0, (size_t)(SHA1_BLOCKSIZE - SHA1_DIGESTSIZE)); + } else { + memcpy(hmac->key, key, (size_t)keylen); + if(keylen < SHA1_BLOCKSIZE) + memset((hmac->key) + keylen, 0, (size_t)(SHA1_BLOCKSIZE - keylen)); + } + + /* Create the initial vector */ + for(i = 0; i < SHA1_BLOCKSIZE; i++) + buf[i] = hmac->key[i] ^ 0x36; + + /* Prepend to the hash data */ + csp_sha1_init(&hmac->md); + csp_sha1_process(&hmac->md, buf, SHA1_BLOCKSIZE); + + return CSP_ERR_NONE; +} + +static int csp_hmac_process(hmac_state * hmac, const uint8_t * in, uint32_t inlen) { + + /* NULL pointer check */ + if (!hmac || !in) + return CSP_ERR_INVAL; + + /* Process data */ + csp_sha1_process(&hmac->md, in, inlen); + + return CSP_ERR_NONE; +} + +static int csp_hmac_done(hmac_state * hmac, uint8_t * out) { + + uint32_t i; + uint8_t buf[SHA1_BLOCKSIZE]; + uint8_t isha[SHA1_DIGESTSIZE]; + + if (!hmac || !out) + return CSP_ERR_INVAL; + + /* Get the hash of the first HMAC vector plus the data */ + csp_sha1_done(&hmac->md, isha); + + /* Create the second HMAC vector vector */ + for(i = 0; i < SHA1_BLOCKSIZE; i++) + buf[i] = hmac->key[i] ^ 0x5C; + + /* Now calculate the outer hash */ + csp_sha1_init(&hmac->md); + csp_sha1_process(&hmac->md, buf, SHA1_BLOCKSIZE); + csp_sha1_process(&hmac->md, isha, SHA1_DIGESTSIZE); + csp_sha1_done(&hmac->md, buf); + + /* Copy to output */ + for (i = 0; i < SHA1_DIGESTSIZE; i++) + out[i] = buf[i]; + + return CSP_ERR_NONE; +} + +int csp_hmac_memory(const uint8_t * key, uint32_t keylen, const uint8_t * data, uint32_t datalen, uint8_t * hmac) { + hmac_state state; + + /* NULL pointer check */ + if (!key || !data || !hmac) + return CSP_ERR_INVAL; + + /* Init HMAC state */ + if (csp_hmac_init(&state, key, keylen) != 0) + return CSP_ERR_INVAL; + + /* Process data */ + if (csp_hmac_process(&state, data, datalen) != 0) + return CSP_ERR_INVAL; + + /* Output HMAC */ + if (csp_hmac_done(&state, hmac) != 0) + return CSP_ERR_INVAL; + + return CSP_ERR_NONE; +} + +int csp_hmac_set_key(char * key, uint32_t keylen) { + + /* Use SHA1 as KDF */ + uint8_t hash[SHA1_DIGESTSIZE]; + csp_sha1_memory((uint8_t *)key, keylen, hash); + + /* Copy key */ + memcpy(csp_hmac_key, hash, HMAC_KEY_LENGTH); + + return CSP_ERR_NONE; + +} + +int csp_hmac_append(csp_packet_t * packet, bool include_header) { + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + uint8_t hmac[SHA1_DIGESTSIZE]; + + /* Calculate HMAC */ + if (include_header) { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, (uint8_t *) &packet->id, packet->length + sizeof(packet->id), hmac); + } else { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, packet->data, packet->length, hmac); + } + + /* Truncate hash and copy to packet */ + memcpy(&packet->data[packet->length], hmac, CSP_HMAC_LENGTH); + packet->length += CSP_HMAC_LENGTH; + + return CSP_ERR_NONE; + +} + +int csp_hmac_verify(csp_packet_t * packet, bool include_header) { + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + uint8_t hmac[SHA1_DIGESTSIZE]; + + /* Calculate HMAC */ + if (include_header) { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, (uint8_t *) &packet->id, packet->length + sizeof(packet->id) - CSP_HMAC_LENGTH, hmac); + } else { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, packet->data, packet->length - CSP_HMAC_LENGTH, hmac); + } + + /* Compare calculated HMAC with packet header */ + if (memcmp(&packet->data[packet->length] - CSP_HMAC_LENGTH, hmac, CSP_HMAC_LENGTH) != 0) { + /* HMAC failed */ + return CSP_ERR_HMAC; + } else { + /* Strip HMAC */ + packet->length -= CSP_HMAC_LENGTH; + return CSP_ERR_NONE; + } + +} + +#endif // CSP_USE_HMAC diff --git a/gomspace/libgscsp/lib/libcsp/src/crypto/csp_sha1.c b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_sha1.c new file mode 100644 index 00000000..6c3920e9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_sha1.c @@ -0,0 +1,217 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Code originally from Python's SHA1 Module, who based it on libtom.org */ + +#include +#include + +/* CSP includes */ +#include + +#include + +#if defined(CSP_USE_HMAC) || defined(CSP_USE_XTEA) + +/* Rotate left macro */ +#define ROL(x,y) (((x) << (y)) | ((x) >> (32-y))) + +/* Endian Neutral macros that work on all platforms */ +#define STORE32H(x, y) do { (y)[0] = (uint8_t)(((x) >> 24) & 0xff); \ + (y)[1] = (uint8_t)(((x) >> 16) & 0xff); \ + (y)[2] = (uint8_t)(((x) >> 8) & 0xff); \ + (y)[3] = (uint8_t)(((x) >> 0) & 0xff); } while (0) + +#define LOAD32H(x, y) do { (x) = ((uint32_t)((y)[0] & 0xff) << 24) | \ + ((uint32_t)((y)[1] & 0xff) << 16) | \ + ((uint32_t)((y)[2] & 0xff) << 8) | \ + ((uint32_t)((y)[3] & 0xff) << 0); } while (0) + +#define STORE64H(x, y) do { (y)[0] = (uint8_t)(((x) >> 56) & 0xff); \ + (y)[1] = (uint8_t)(((x) >> 48) & 0xff); \ + (y)[2] = (uint8_t)(((x) >> 40) & 0xff); \ + (y)[3] = (uint8_t)(((x) >> 32) & 0xff); \ + (y)[4] = (uint8_t)(((x) >> 24) & 0xff); \ + (y)[5] = (uint8_t)(((x) >> 16) & 0xff); \ + (y)[6] = (uint8_t)(((x) >> 8) & 0xff); \ + (y)[7] = (uint8_t)(((x) >> 0) & 0xff); } while (0) + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +/* SHA1 macros */ +#define F0(x,y,z) (z ^ (x & (y ^ z))) +#define F1(x,y,z) (x ^ y ^ z) +#define F2(x,y,z) ((x & y) | (z & (x | y))) +#define F3(x,y,z) (x ^ y ^ z) + +#define FF_0(a, b, c, d, e, i) do {e = (ROL(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROL(b, 30);} while (0) +#define FF_1(a, b, c, d, e, i) do {e = (ROL(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROL(b, 30);} while (0) +#define FF_2(a, b, c, d, e, i) do {e = (ROL(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROL(b, 30);} while (0) +#define FF_3(a, b, c, d, e, i) do {e = (ROL(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROL(b, 30);} while (0) + +static void csp_sha1_compress(csp_sha1_state * sha1, const uint8_t * buf) { + + uint32_t a, b, c, d, e, W[80], i; + + /* Copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + LOAD32H(W[i], buf + (4*i)); + + /* Copy state */ + a = sha1->state[0]; + b = sha1->state[1]; + c = sha1->state[2]; + d = sha1->state[3]; + e = sha1->state[4]; + + /* Expand it */ + for (i = 16; i < 80; i++) + W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); + + /* Compress */ + i = 0; + + /* Round one */ + for (; i < 20;) { + FF_0(a, b, c, d, e, i++); + FF_0(e, a, b, c, d, i++); + FF_0(d, e, a, b, c, i++); + FF_0(c, d, e, a, b, i++); + FF_0(b, c, d, e, a, i++); + } + + /* Round two */ + for (; i < 40;) { + FF_1(a, b, c, d, e, i++); + FF_1(e, a, b, c, d, i++); + FF_1(d, e, a, b, c, i++); + FF_1(c, d, e, a, b, i++); + FF_1(b, c, d, e, a, i++); + } + + /* Round three */ + for (; i < 60;) { + FF_2(a, b, c, d, e, i++); + FF_2(e, a, b, c, d, i++); + FF_2(d, e, a, b, c, i++); + FF_2(c, d, e, a, b, i++); + FF_2(b, c, d, e, a, i++); + } + + /* Round four */ + for (; i < 80;) { + FF_3(a, b, c, d, e, i++); + FF_3(e, a, b, c, d, i++); + FF_3(d, e, a, b, c, i++); + FF_3(c, d, e, a, b, i++); + FF_3(b, c, d, e, a, i++); + } + + /* Store */ + sha1->state[0] += a; + sha1->state[1] += b; + sha1->state[2] += c; + sha1->state[3] += d; + sha1->state[4] += e; + +} + +void csp_sha1_init(csp_sha1_state * sha1) { + + sha1->state[0] = 0x67452301UL; + sha1->state[1] = 0xefcdab89UL; + sha1->state[2] = 0x98badcfeUL; + sha1->state[3] = 0x10325476UL; + sha1->state[4] = 0xc3d2e1f0UL; + sha1->curlen = 0; + sha1->length = 0; + +} + +void csp_sha1_process(csp_sha1_state * sha1, const uint8_t * in, uint32_t inlen) { + + uint32_t n; + while (inlen > 0) { + if (sha1->curlen == 0 && inlen >= SHA1_BLOCKSIZE) { + csp_sha1_compress(sha1, in); + sha1->length += SHA1_BLOCKSIZE * 8; + in += SHA1_BLOCKSIZE; + inlen -= SHA1_BLOCKSIZE; + } else { + n = MIN(inlen, (SHA1_BLOCKSIZE - sha1->curlen)); + memcpy(sha1->buf + sha1->curlen, in, (size_t)n); + sha1->curlen += n; + in += n; + inlen -= n; + if (sha1->curlen == SHA1_BLOCKSIZE) { + csp_sha1_compress(sha1, sha1->buf); + sha1->length += 8*SHA1_BLOCKSIZE; + sha1->curlen = 0; + } + } + } + +} + +void csp_sha1_done(csp_sha1_state * sha1, uint8_t * out) { + + uint32_t i; + + /* Increase the length of the message */ + sha1->length += sha1->curlen * 8; + + /* Append the '1' bit */ + sha1->buf[sha1->curlen++] = 0x80; + + /* If the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (sha1->curlen > 56) { + while (sha1->curlen < 64) + sha1->buf[sha1->curlen++] = 0; + csp_sha1_compress(sha1, sha1->buf); + sha1->curlen = 0; + } + + /* Pad up to 56 bytes of zeroes */ + while (sha1->curlen < 56) + sha1->buf[sha1->curlen++] = 0; + + /* Store length */ + STORE64H(sha1->length, sha1->buf + 56); + csp_sha1_compress(sha1, sha1->buf); + + /* Copy output */ + for (i = 0; i < 5; i++) + STORE32H(sha1->state[i], out + (4 * i)); + +} + +void csp_sha1_memory(const uint8_t * msg, uint32_t len, uint8_t * hash) { + + csp_sha1_state md; + csp_sha1_init(&md); + csp_sha1_process(&md, msg, len); + csp_sha1_done(&md, hash); + +} + +#endif // CSP_USE_HMAC diff --git a/gomspace/libgscsp/lib/libcsp/src/crypto/csp_xtea.c b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_xtea.c new file mode 100644 index 00000000..718824d1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_xtea.c @@ -0,0 +1,134 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Simple implementation of XTEA in CTR mode */ + +#include +#include + +/* CSP includes */ +#include +#include +#include +#include + +#ifdef CSP_USE_XTEA + +#define XTEA_BLOCKSIZE 8 +#define XTEA_ROUNDS 32 +#define XTEA_KEY_LENGTH 16 + +/* XTEA key */ +static uint32_t csp_xtea_key[XTEA_KEY_LENGTH/sizeof(uint32_t)] __attribute__ ((aligned(sizeof(uint32_t)))); + +#define STORE32L(x, y) do { (y)[3] = (uint8_t)(((x) >> 24) & 0xff); \ + (y)[2] = (uint8_t)(((x) >> 16) & 0xff); \ + (y)[1] = (uint8_t)(((x) >> 8) & 0xff); \ + (y)[0] = (uint8_t)(((x) >> 0) & 0xff); } while (0) + +#define LOAD32L(x, y) do { (x) = ((uint32_t)((y)[3] & 0xff) << 24) | \ + ((uint32_t)((y)[2] & 0xff) << 16) | \ + ((uint32_t)((y)[1] & 0xff) << 8) | \ + ((uint32_t)((y)[0] & 0xff) << 0); } while (0) + +/* This function takes 64 bits of data in block and the 128 bits key in key */ +static inline void csp_xtea_encrypt_block(uint8_t *block, uint8_t const *key) { + + uint32_t i, v0, v1, delta = 0x9E3779B9, sum = 0, k[4]; + + LOAD32L(k[0], &key[0]); + LOAD32L(k[1], &key[4]); + LOAD32L(k[2], &key[8]); + LOAD32L(k[3], &key[12]); + + LOAD32L(v0, &block[0]); + LOAD32L(v1, &block[4]); + + for (i = 0; i < XTEA_ROUNDS; i++) { + v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); + sum += delta; + v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]); + } + + STORE32L(v0, &block[0]); + STORE32L(v1, &block[4]); + +} + +static inline void csp_xtea_xor_byte(uint8_t * dst, uint8_t * src, uint32_t len) { + + unsigned int i; + for (i = 0; i < len; i++) + dst[i] ^= src[i]; + +} + +int csp_xtea_set_key(char * key, uint32_t keylen) { + + /* Use SHA1 as KDF */ + uint8_t hash[SHA1_DIGESTSIZE]; + csp_sha1_memory((uint8_t *)key, keylen, hash); + + /* Copy key */ + memcpy(csp_xtea_key, hash, XTEA_KEY_LENGTH); + + return CSP_ERR_NONE; + +} + +int csp_xtea_encrypt(uint8_t * plain, const uint32_t len, uint32_t iv[2]) { + + unsigned int i; + uint32_t stream[2]; + + uint32_t blocks = (len + XTEA_BLOCKSIZE - 1)/ XTEA_BLOCKSIZE; + uint32_t remain; + + /* Initialize stream */ + stream[0] = csp_htobe32(iv[0]); + stream[1] = csp_htobe32(iv[1]); + + for (i = 0; i < blocks; i++) { + /* Create stream */ + csp_xtea_encrypt_block((uint8_t *)stream, (uint8_t *)csp_xtea_key); + + /* Calculate remaining bytes */ + remain = len - i * XTEA_BLOCKSIZE; + + /* XOR plain text with stream to generate cipher text */ + csp_xtea_xor_byte(&plain[len - remain], (uint8_t *)stream, remain < XTEA_BLOCKSIZE ? remain : XTEA_BLOCKSIZE); + + /* Increment counter */ + stream[0] = csp_htobe32(iv[0]); + stream[1] = csp_htobe32(iv[1]++); + } + + return CSP_ERR_NONE; + +} + +int csp_xtea_decrypt(uint8_t * cipher, const uint32_t len, uint32_t iv[2]) { + + /* Since we use counter mode, we can reuse the encryption function */ + return csp_xtea_encrypt(cipher, len, iv); + +} + +#endif // CSP_USE_XTEA diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_bridge.c b/gomspace/libgscsp/lib/libcsp/src/csp_bridge.c new file mode 100644 index 00000000..1c579a9f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_bridge.c @@ -0,0 +1,94 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "csp_route.h" +#include "csp_qfifo.h" +#include "csp_io.h" +#include "csp_promisc.h" + +static csp_iface_t* if_a = NULL; +static csp_iface_t* if_b = NULL; + +static CSP_DEFINE_TASK(csp_bridge) { + + csp_qfifo_t input; + csp_packet_t * packet; + + /* Here there be bridging */ + while (1) { + + /* Get next packet to route */ + if (csp_qfifo_read(&input) != CSP_ERR_NONE) + continue; + + packet = input.packet; + + csp_log_packet("Input: Src %u, Dst %u, Dport %u, Sport %u, Pri %u, Flags 0x%02X, Size %"PRIu16, + packet->id.src, packet->id.dst, packet->id.dport, + packet->id.sport, packet->id.pri, packet->id.flags, packet->length); + + /* Here there be promiscuous mode */ +#ifdef CSP_USE_PROMISC + csp_promisc_add(packet); +#endif + + /* Find the opposing interface */ + csp_iface_t * ifout; + if (input.interface == if_a) { + ifout = if_b; + } else { + ifout = if_a; + } + + /* Send to the interface directly, no hassle */ + if (csp_send_direct(packet->id, packet, ifout, 0) != CSP_ERR_NONE) { + csp_log_warn("Router failed to send"); + csp_buffer_free(packet); + } + + /* Next message, please */ + continue; + + } + + return CSP_TASK_RETURN; + +} + +int csp_bridge_start(unsigned int task_stack_size, unsigned int task_priority, csp_iface_t * _if_a, csp_iface_t * _if_b) { + + /* Set static references to A/B side of bridge */ + if_a = _if_a; + if_b = _if_b; + + static csp_thread_handle_t handle; + int ret = csp_thread_create(csp_bridge, "BRIDGE", task_stack_size, NULL, task_priority, &handle); + + if (ret != 0) { + csp_log_error("Failed to start task"); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_buffer.c b/gomspace/libgscsp/lib/libcsp/src/csp_buffer.c new file mode 100644 index 00000000..8947f337 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_buffer.c @@ -0,0 +1,224 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include +#include +#include +#include + +#ifndef CSP_BUFFER_ALIGN +#define CSP_BUFFER_ALIGN (sizeof(int *)) +#endif + +typedef struct csp_skbf_s { + unsigned int refcount; + void * skbf_addr; + char skbf_data[]; +} csp_skbf_t; + +static csp_queue_handle_t csp_buffers; +static char * csp_buffer_pool; +static unsigned int count, size; + +CSP_DEFINE_CRITICAL(csp_critical_lock); + +int csp_buffer_init(int buf_count, int buf_size) { + + unsigned int i; + csp_skbf_t * buf; + + count = buf_count; + size = buf_size + CSP_BUFFER_PACKET_OVERHEAD; + unsigned int skbfsize = (sizeof(csp_skbf_t) + size); + skbfsize = CSP_BUFFER_ALIGN * ((skbfsize + CSP_BUFFER_ALIGN - 1) / CSP_BUFFER_ALIGN); + unsigned int poolsize = count * skbfsize; + + csp_buffer_pool = csp_malloc(poolsize); + if (csp_buffer_pool == NULL) + goto fail_malloc; + + csp_buffers = csp_queue_create(count, sizeof(void *)); + if (!csp_buffers) + goto fail_queue; + + if (CSP_INIT_CRITICAL(csp_critical_lock) != CSP_ERR_NONE) + goto fail_critical; + + memset(csp_buffer_pool, 0, poolsize); + + for (i = 0; i < count; i++) { + + /* We have already taken care of pointer alignment since + * skbfsize is an integer multiple of sizeof(int *) + * but the explicit cast to a void * is still necessary + * to tell the compiler so. + */ + buf = (void *) &csp_buffer_pool[i * skbfsize]; + buf->refcount = 0; + buf->skbf_addr = buf; + + csp_queue_enqueue(csp_buffers, &buf, 0); + + } + + return CSP_ERR_NONE; + +fail_critical: + csp_queue_remove(csp_buffers); +fail_queue: + csp_free(csp_buffer_pool); +fail_malloc: + return CSP_ERR_NOMEM; + +} + +void *csp_buffer_get_isr(size_t buf_size) { + + csp_skbf_t * buffer = NULL; + CSP_BASE_TYPE task_woken = 0; + + if (buf_size + CSP_BUFFER_PACKET_OVERHEAD > size) + return NULL; + + csp_queue_dequeue_isr(csp_buffers, &buffer, &task_woken); + if (buffer == NULL) + return NULL; + + if (buffer != buffer->skbf_addr) + return NULL; + + buffer->refcount++; + return buffer->skbf_data; + +} + +void *csp_buffer_get(size_t buf_size) { + + csp_skbf_t * buffer = NULL; + + if (buf_size + CSP_BUFFER_PACKET_OVERHEAD > size) { + csp_log_error("Attempt to allocate too large block %u", buf_size); + return NULL; + } + + csp_queue_dequeue(csp_buffers, &buffer, 0); + if (buffer == NULL) { + csp_log_error("Out of buffers"); + return NULL; + } + + csp_log_buffer("GET: %p %p", buffer, buffer->skbf_addr); + + if (buffer != buffer->skbf_addr) { + csp_log_error("Corrupt CSP buffer"); + return NULL; + } + + buffer->refcount++; + return buffer->skbf_data; +} + +void csp_buffer_free_isr(void *packet) { + CSP_BASE_TYPE task_woken = 0; + if (!packet) + return; + + csp_skbf_t * buf = packet - sizeof(csp_skbf_t); + + if (((uintptr_t) buf % CSP_BUFFER_ALIGN) > 0) + return; + + if (buf->skbf_addr != buf) + return; + + if (buf->refcount == 0) { + return; + } else if (buf->refcount > 1) { + buf->refcount--; + return; + } else { + buf->refcount = 0; + csp_queue_enqueue_isr(csp_buffers, &buf, &task_woken); + } + +} + +void csp_buffer_free(void *packet) { + if (!packet) { + csp_log_error("Attempt to free null pointer"); + return; + } + + csp_skbf_t * buf = packet - sizeof(csp_skbf_t); + + if (((uintptr_t) buf % CSP_BUFFER_ALIGN) > 0) { + csp_log_error("FREE: Unaligned CSP buffer pointer %p", packet); + return; + } + + if (buf->skbf_addr != buf) { + csp_log_error("FREE: Invalid CSP buffer pointer %p", packet); + return; + } + + if (buf->refcount == 0) { + csp_log_error("FREE: Buffer already free %p", buf); + return; + } else if (buf->refcount > 1) { + buf->refcount--; + csp_log_error("FREE: Buffer %p in use by %u users", buf, buf->refcount); + return; + } else { + buf->refcount = 0; + csp_log_buffer("FREE: %p", buf); + csp_queue_enqueue(csp_buffers, &buf, 0); + } + +} + +void *csp_buffer_clone(void *buffer) { + + csp_packet_t *packet = (csp_packet_t *) buffer; + + if (!packet) + return NULL; + + csp_packet_t *clone = csp_buffer_get(packet->length); + + if (clone) + memcpy(clone, packet, size); + + return clone; + +} + +int csp_buffer_remaining(void) { + return csp_queue_size(csp_buffers); +} + +int csp_buffer_size(void) { + return size; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_conn.c b/gomspace/libgscsp/lib/libcsp/src/csp_conn.c new file mode 100644 index 00000000..7daa569d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_conn.c @@ -0,0 +1,498 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include +#include + +#include +#include +#include +#include +#include + +#include "csp_conn.h" +#include "transport/csp_transport.h" + +/* Static connection pool */ +static csp_conn_t arr_conn[CSP_CONN_MAX]; + +/* Connection pool lock */ +static csp_bin_sem_handle_t conn_lock; + +/* Source port */ +static uint8_t sport; + +/* Source port lock */ +static csp_bin_sem_handle_t sport_lock; + +void csp_conn_check_timeouts(void) { +#ifdef CSP_USE_RDP + int i; + for (i = 0; i < CSP_CONN_MAX; i++) + if (arr_conn[i].state == CONN_OPEN) + if (arr_conn[i].idin.flags & CSP_FRDP) + csp_rdp_check_timeouts(&arr_conn[i]); +#endif +} + +int csp_conn_get_rxq(int prio) { + +#ifdef CSP_USE_QOS + return prio; +#else + return 0; +#endif + +} + +int csp_conn_lock(csp_conn_t * conn, uint32_t timeout) { + + if (csp_mutex_lock(&conn->lock, timeout) != CSP_MUTEX_OK) + return CSP_ERR_TIMEDOUT; + + return CSP_ERR_NONE; + +} + +int csp_conn_unlock(csp_conn_t * conn) { + + csp_mutex_unlock(&conn->lock); + + return CSP_ERR_NONE; + +} + +int csp_conn_enqueue_packet(csp_conn_t * conn, csp_packet_t * packet) { + + if (!conn) + return CSP_ERR_INVAL; + + int rxq; + if (packet != NULL) { + rxq = csp_conn_get_rxq(packet->id.pri); + } else { + rxq = CSP_RX_QUEUES - 1; + } + + if (csp_queue_enqueue(conn->rx_queue[rxq], &packet, 0) != CSP_QUEUE_OK) { + csp_log_error("RX queue %p full with %u items", conn->rx_queue[rxq], csp_queue_size(conn->rx_queue[rxq])); + return CSP_ERR_NOMEM; + } + +#ifdef CSP_USE_QOS + int event = 0; + if (csp_queue_enqueue(conn->rx_event, &event, 0) != CSP_QUEUE_OK) { + csp_log_error("QOS event queue full"); + return CSP_ERR_NOMEM; + } +#endif + + return CSP_ERR_NONE; +} + +int csp_conn_init(void) { + + /* Initialize source port */ + srand(csp_get_ms()); + sport = (rand() % (CSP_ID_PORT_MAX - CSP_MAX_BIND_PORT)) + (CSP_MAX_BIND_PORT + 1); + + if (csp_bin_sem_create(&sport_lock) != CSP_SEMAPHORE_OK) { + csp_log_error("No more memory for sport semaphore"); + return CSP_ERR_NOMEM; + } + + int i, prio; + for (i = 0; i < CSP_CONN_MAX; i++) { + for (prio = 0; prio < CSP_RX_QUEUES; prio++) + arr_conn[i].rx_queue[prio] = csp_queue_create(CSP_RX_QUEUE_LENGTH, sizeof(csp_packet_t *)); + +#ifdef CSP_USE_QOS + arr_conn[i].rx_event = csp_queue_create(CSP_CONN_QUEUE_LENGTH, sizeof(int)); +#endif + arr_conn[i].state = CONN_CLOSED; + + if (csp_mutex_create(&arr_conn[i].lock) != CSP_MUTEX_OK) { + csp_log_error("Failed to create connection lock"); + return CSP_ERR_NOMEM; + } + +#ifdef CSP_USE_RDP + if (csp_rdp_allocate(&arr_conn[i]) != CSP_ERR_NONE) { + csp_log_error("Failed to create queues for RDP in csp_conn_init"); + return CSP_ERR_NOMEM; + } +#endif + } + + if (csp_bin_sem_create(&conn_lock) != CSP_SEMAPHORE_OK) { + csp_log_error("No more memory for conn semaphore"); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} + +csp_conn_t * csp_conn_find(uint32_t id, uint32_t mask) { + + /* Search for matching connection */ + int i; + csp_conn_t * conn; + id = (id & mask); + for (i = 0; i < CSP_CONN_MAX; i++) { + conn = &arr_conn[i]; + if ((conn->state != CONN_CLOSED) && (conn->type == CONN_CLIENT) && ((conn->idin.ext & mask) == id)) + return conn; + } + + return NULL; + +} + +static int csp_conn_flush_rx_queue(csp_conn_t * conn) { + + csp_packet_t * packet; + + int prio; + + /* Flush packet queues */ + for (prio = 0; prio < CSP_RX_QUEUES; prio++) { + while (csp_queue_dequeue(conn->rx_queue[prio], &packet, 0) == CSP_QUEUE_OK) + if (packet != NULL) + csp_buffer_free(packet); + } + + /* Flush event queue */ +#ifdef CSP_USE_QOS + int event; + while (csp_queue_dequeue(conn->rx_event, &event, 0) == CSP_QUEUE_OK); +#endif + + return CSP_ERR_NONE; + +} + +csp_conn_t * csp_conn_allocate(csp_conn_type_t type) { + + int i, j; + static uint8_t csp_conn_last_given = 0; + csp_conn_t * conn; + + if (csp_bin_sem_wait(&conn_lock, 100) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to lock conn array"); + return NULL; + } + + /* Search for free connection */ + i = csp_conn_last_given; + i = (i + 1) % CSP_CONN_MAX; + + for (j = 0; j < CSP_CONN_MAX; j++) { + conn = &arr_conn[i]; + if (conn->state == CONN_CLOSED) + break; + i = (i + 1) % CSP_CONN_MAX; + } + + if (conn->state == CONN_OPEN) { + csp_log_error("No more free connections"); + csp_bin_sem_post(&conn_lock); + return NULL; + } + + conn->idin.ext = 0; + conn->idout.ext = 0; + conn->socket = NULL; + conn->timestamp = 0; + conn->type = type; + conn->state = CONN_OPEN; + + csp_conn_last_given = i; + csp_bin_sem_post(&conn_lock); + + return conn; + +} + +csp_conn_t * csp_conn_new(csp_id_t idin, csp_id_t idout) { + + /* Allocate connection structure */ + csp_conn_t * conn = csp_conn_allocate(CONN_CLIENT); + + if (conn) { + /* No lock is needed here, because nobody else * + * has a reference to this connection yet. */ + conn->idin.ext = idin.ext; + conn->idout.ext = idout.ext; + conn->timestamp = csp_get_ms(); + + /* Ensure connection queue is empty */ + csp_conn_flush_rx_queue(conn); + } + + return conn; + +} + +int csp_close(csp_conn_t * conn) { + + if (conn == NULL) { + csp_log_error("NULL Pointer given to csp_close"); + return CSP_ERR_INVAL; + } + + if (conn->state == CONN_CLOSED) { + csp_log_protocol("Conn already closed"); + return CSP_ERR_NONE; + } + +#ifdef CSP_USE_RDP + /* Ensure RDP knows this connection is closing */ + if (conn->idin.flags & CSP_FRDP || conn->idout.flags & CSP_FRDP) + if (csp_rdp_close(conn) == CSP_ERR_AGAIN) + return CSP_ERR_NONE; +#endif + + /* Lock connection array while closing connection */ + if (csp_bin_sem_wait(&conn_lock, 100) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to lock conn array"); + return CSP_ERR_TIMEDOUT; + } + + /* Set to closed */ + conn->state = CONN_CLOSED; + + /* Ensure connection queue is empty */ + csp_conn_flush_rx_queue(conn); + + if (conn->socket && (conn->type == CONN_SERVER) && (conn->opts & (CSP_SO_CONN_LESS | CSP_SO_INTERNAL_LISTEN))) { + csp_queue_remove(conn->socket); + conn->socket = NULL; + } + + /* Reset RDP state */ +#ifdef CSP_USE_RDP + if (conn->idin.flags & CSP_FRDP) + csp_rdp_flush_all(conn); +#endif + + /* Unlock connection array */ + csp_bin_sem_post(&conn_lock); + + return CSP_ERR_NONE; +} + +csp_conn_t * csp_connect(uint8_t prio, uint8_t dest, uint8_t dport, uint32_t timeout, uint32_t opts) { + + /* Force options on all connections */ + opts |= CSP_CONNECTION_SO; + + /* Generate identifier */ + csp_id_t incoming_id, outgoing_id; + incoming_id.pri = prio; + incoming_id.dst = csp_get_address(); + incoming_id.src = dest; + incoming_id.sport = dport; + incoming_id.flags = 0; + outgoing_id.pri = prio; + outgoing_id.dst = dest; + outgoing_id.src = csp_get_address(); + outgoing_id.dport = dport; + outgoing_id.flags = 0; + + /* Set connection options */ + if (opts & CSP_O_NOCRC32) { + opts &= ~CSP_O_CRC32; + } + + if (opts & CSP_O_RDP) { +#ifdef CSP_USE_RDP + incoming_id.flags |= CSP_FRDP; + outgoing_id.flags |= CSP_FRDP; +#else + csp_log_error("Attempt to create RDP connection, but CSP was compiled without RDP support"); + return NULL; +#endif + } + + if (opts & CSP_O_HMAC) { +#ifdef CSP_USE_HMAC + outgoing_id.flags |= CSP_FHMAC; + incoming_id.flags |= CSP_FHMAC; +#else + csp_log_error("Attempt to create HMAC authenticated connection, but CSP was compiled without HMAC support"); + return NULL; +#endif + } + + if (opts & CSP_O_XTEA) { +#ifdef CSP_USE_XTEA + outgoing_id.flags |= CSP_FXTEA; + incoming_id.flags |= CSP_FXTEA; +#else + csp_log_error("Attempt to create XTEA encrypted connection, but CSP was compiled without XTEA support"); + return NULL; +#endif + } + + if (opts & CSP_O_CRC32) { +#ifdef CSP_USE_CRC32 + outgoing_id.flags |= CSP_FCRC32; + incoming_id.flags |= CSP_FCRC32; +#else + csp_log_error("Attempt to create CRC32 validated connection, but CSP was compiled without CRC32 support"); + return NULL; +#endif + } + + /* Find an unused ephemeral port */ + csp_conn_t * conn = NULL; + + /* Wait for sport lock - note that csp_conn_new(..) is called inside the lock! */ + if (csp_bin_sem_wait(&sport_lock, 1000) != CSP_SEMAPHORE_OK) + return NULL; + + const uint8_t start = sport; + while (++sport != start) { + if (sport > CSP_ID_PORT_MAX) + sport = CSP_MAX_BIND_PORT + 1; + + outgoing_id.sport = sport; + incoming_id.dport = sport; + + /* Match on destination port of _incoming_ identifier */ + if (csp_conn_find(incoming_id.ext, CSP_ID_DPORT_MASK) == NULL) { + /* Break - we found an unused ephemeral port + allocate connection while locked to mark port in use */ + conn = csp_conn_new(incoming_id, outgoing_id); + break; + } + } + + /* Post sport lock */ + csp_bin_sem_post(&sport_lock); + + if (conn == NULL) + return NULL; + + /* Set connection options */ + conn->opts = opts; + +#ifdef CSP_USE_RDP + /* Call Transport Layer connect */ + if (outgoing_id.flags & CSP_FRDP) { + /* If the transport layer has failed to connect + * deallocate connection structure again and return NULL */ + if (csp_rdp_connect(conn, timeout) != CSP_ERR_NONE) { + csp_close(conn); + return NULL; + } + } +#endif + + /* We have a successful connection */ + return conn; + +} + +inline int csp_conn_dport(csp_conn_t * conn) { + + return conn->idin.dport; + +} + +inline int csp_conn_sport(csp_conn_t * conn) { + + return conn->idin.sport; + +} + +inline int csp_conn_dst(csp_conn_t * conn) { + + return conn->idin.dst; + +} + +inline int csp_conn_src(csp_conn_t * conn) { + + return conn->idin.src; + +} + +inline int csp_conn_flags(csp_conn_t * conn) { + + return conn->idin.flags; + +} + +#ifdef CSP_DEBUG +void csp_conn_print_table(void) { + + int i; + csp_conn_t * conn; + + for (i = 0; i < CSP_CONN_MAX; i++) { + conn = &arr_conn[i]; + printf("[%02u %p] S:%u, %u -> %u, %u -> %u, sock: %p\r\n", + i, conn, conn->state, conn->idin.src, conn->idin.dst, + conn->idin.dport, conn->idin.sport, conn->socket); +#ifdef CSP_USE_RDP + if (conn->idin.flags & CSP_FRDP) + csp_rdp_conn_print(conn); +#endif + } +} + +int csp_conn_print_table_str(char * str_buf, int str_size) { + + int i, start = 0; + csp_conn_t * conn; + char buf[100]; + + /* Display up to 10 connections */ + if (CSP_CONN_MAX - 10 > 0) + start = CSP_CONN_MAX - 10; + + for (i = start; i < CSP_CONN_MAX; i++) { + conn = &arr_conn[i]; + snprintf(buf, sizeof(buf), "[%02u %p] S:%u, %u -> %u, %u -> %u, sock: %p\n", + i, conn, conn->state, conn->idin.src, conn->idin.dst, + conn->idin.dport, conn->idin.sport, conn->socket); + + strncat(str_buf, buf, str_size); + if ((str_size -= strlen(buf)) <= 0) + break; + } + + return CSP_ERR_NONE; +} +#endif + +const csp_conn_t * csp_conn_get_array(size_t * size) +{ + *size = CSP_CONN_MAX; + return arr_conn; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_conn.h b/gomspace/libgscsp/lib/libcsp/src/csp_conn.h new file mode 100644 index 00000000..3fa0ff52 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_conn.h @@ -0,0 +1,112 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CONN_H_ +#define _CSP_CONN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include +#include + +/** @brief Connection states */ +typedef enum { + CONN_CLOSED = 0, + CONN_OPEN = 1, +} csp_conn_state_t; + +/** @brief Connection types */ +typedef enum { + CONN_CLIENT = 0, + CONN_SERVER = 1, +} csp_conn_type_t; + +typedef enum { + RDP_CLOSED = 0, + RDP_SYN_SENT, + RDP_SYN_RCVD, + RDP_OPEN, + RDP_CLOSE_WAIT, +} csp_rdp_state_t; + +/** @brief RDP Connection header + * @note Do not try to pack this struct, the posix sem handle will stop working */ +typedef struct { + csp_rdp_state_t state; /**< Connection state */ + uint16_t snd_nxt; /**< The sequence number of the next segment that is to be sent */ + uint16_t snd_una; /**< The sequence number of the oldest unacknowledged segment */ + uint16_t snd_iss; /**< The initial send sequence number */ + uint16_t rcv_cur; /**< The sequence number of the last segment received correctly and in sequence */ + uint16_t rcv_irs; /**< The initial receive sequence number */ + uint16_t rcv_lsa; /**< The last sequence number acknowledged by the receiver */ + uint32_t window_size; + uint32_t conn_timeout; + uint32_t packet_timeout; + uint32_t delayed_acks; + uint32_t ack_timeout; + uint32_t ack_delay_count; + uint32_t ack_timestamp; + csp_bin_sem_handle_t tx_wait; + csp_queue_handle_t tx_queue; + csp_queue_handle_t rx_queue; +} csp_rdp_t; + +/** @brief Connection struct */ +struct csp_conn_s { + csp_conn_type_t type; /* Connection type (CONN_CLIENT or CONN_SERVER) */ + csp_conn_state_t state; /* Connection state (CONN_OPEN or CONN_CLOSED) */ + csp_mutex_t lock; /* Connection structure lock */ + csp_id_t idin; /* Identifier received */ + csp_id_t idout; /* Identifier transmitted */ +#ifdef CSP_USE_QOS + csp_queue_handle_t rx_event; /* Event queue for RX packets */ +#endif + csp_queue_handle_t rx_queue[CSP_RX_QUEUES]; /* Queue for RX packets */ + csp_queue_handle_t socket; /* Socket to be "woken" when first packet is ready */ + uint32_t timestamp; /* Time the connection was opened */ + uint32_t opts; /* Connection or socket options */ +#ifdef CSP_USE_RDP + csp_rdp_t rdp; /* RDP state */ +#endif +}; + +int csp_conn_lock(csp_conn_t * conn, uint32_t timeout); +int csp_conn_unlock(csp_conn_t * conn); +int csp_conn_enqueue_packet(csp_conn_t * conn, csp_packet_t * packet); +int csp_conn_init(void); +csp_conn_t * csp_conn_allocate(csp_conn_type_t type); +csp_conn_t * csp_conn_find(uint32_t id, uint32_t mask); +csp_conn_t * csp_conn_new(csp_id_t idin, csp_id_t idout); +void csp_conn_check_timeouts(void); +int csp_conn_get_rxq(int prio); + +const csp_conn_t * csp_conn_get_array(size_t * size); // for test purposes only! + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_CONN_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_crc32.c b/gomspace/libgscsp/lib/libcsp/src/csp_crc32.c new file mode 100644 index 00000000..8bf2145f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_crc32.c @@ -0,0 +1,140 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include +#include + +#include + +#ifdef CSP_USE_CRC32 + +#ifdef __AVR__ +#include +static const uint32_t crc_tab[256] PROGMEM = { +#else +static const uint32_t crc_tab[256] = { +#endif + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 }; + +uint32_t csp_crc32_memory(const uint8_t * data, uint32_t length) { + uint32_t crc; + + crc = 0xFFFFFFFF; + while (length--) +#ifdef __AVR__ + crc = pgm_read_dword(&crc_tab[(crc ^ *data++) & 0xFFL]) ^ (crc >> 8); +#else + crc = crc_tab[(crc ^ *data++) & 0xFFL] ^ (crc >> 8); +#endif + + return (crc ^ 0xFFFFFFFF); +} + +int csp_crc32_append(csp_packet_t * packet, bool include_header) { + + uint32_t crc; + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + /* Calculate CRC32, convert to network byte order */ + if (include_header) { + crc = csp_crc32_memory((uint8_t *) &packet->id, packet->length + sizeof(packet->id)); + } else { + crc = csp_crc32_memory(packet->data, packet->length); + } + crc = csp_hton32(crc); + + /* Copy checksum to packet */ + memcpy(&packet->data[packet->length], &crc, sizeof(uint32_t)); + packet->length += sizeof(uint32_t); + + return CSP_ERR_NONE; + +} + +int csp_crc32_verify(csp_packet_t * packet, bool include_header) { + + uint32_t crc; + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + if (packet->length < sizeof(uint32_t)) + return CSP_ERR_INVAL; + + /* Calculate CRC32, convert to network byte order */ + if (include_header) { + crc = csp_crc32_memory((uint8_t *) &packet->id, packet->length + sizeof(packet->id) - sizeof(uint32_t)); + } else { + crc = csp_crc32_memory(packet->data, packet->length - sizeof(uint32_t)); + } + crc = csp_hton32(crc); + + /* Compare calculated checksum with packet header */ + if (memcmp(&packet->data[packet->length] - sizeof(uint32_t), &crc, sizeof(uint32_t)) != 0) { + /* CRC32 failed */ + return CSP_ERR_INVAL; + } else { + /* Strip CRC32 */ + packet->length -= sizeof(uint32_t); + return CSP_ERR_NONE; + } + +} + +#endif // CSP_USE_CRC32 diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_debug.c b/gomspace/libgscsp/lib/libcsp/src/csp_debug.c new file mode 100644 index 00000000..2e710cb3 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_debug.c @@ -0,0 +1,133 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#ifdef __AVR__ +#include +#endif + +/* CSP includes */ +#include + +#include + +/* Custom debug function */ +csp_debug_hook_func_t csp_debug_hook_func = NULL; + +/* Debug levels */ +static bool csp_debug_level_enabled[] = { + [CSP_ERROR] = true, + [CSP_WARN] = true, + [CSP_INFO] = false, + [CSP_BUFFER] = false, + [CSP_PACKET] = false, + [CSP_PROTOCOL] = false, + [CSP_LOCK] = false, +}; + +/* Some compilers do not support weak symbols, so this function + * can be used instead to set a custom debug hook */ +void csp_debug_hook_set(csp_debug_hook_func_t f) +{ + csp_debug_hook_func = f; +} + +void do_csp_debug(csp_debug_level_t level, const char *format, ...) +{ + int color = COLOR_RESET; + va_list args; + + /* Don't print anything if log level is disabled */ + if (level > CSP_LOCK || !csp_debug_level_enabled[level]) + return; + + switch(level) { + case CSP_INFO: + color = COLOR_GREEN | COLOR_BOLD; + break; + case CSP_ERROR: + color = COLOR_RED | COLOR_BOLD; + break; + case CSP_WARN: + color = COLOR_YELLOW | COLOR_BOLD; + break; + case CSP_BUFFER: + color = COLOR_MAGENTA; + break; + case CSP_PACKET: + color = COLOR_GREEN; + break; + case CSP_PROTOCOL: + color = COLOR_BLUE; + break; + case CSP_LOCK: + color = COLOR_CYAN; + break; + default: + return; + } + + va_start(args, format); + + /* If csp_debug_hook symbol is defined, pass on the message. + * Otherwise, just print with pretty colors ... */ + if (csp_debug_hook_func) { + csp_debug_hook_func(level, format, args); + } else { + csp_sys_set_color(color); +#ifdef __AVR__ + vfprintf_P(stdout, format, args); +#else + vprintf(format, args); +#endif + printf("\r\n"); + csp_sys_set_color(COLOR_RESET); + } + + va_end(args); +} + +void csp_debug_set_level(csp_debug_level_t level, bool value) +{ + if (level > CSP_LOCK) + return; + csp_debug_level_enabled[level] = value; +} + +int csp_debug_get_level(csp_debug_level_t level) +{ + if (level > CSP_LOCK) + return 0; + return csp_debug_level_enabled[level]; +} + +void csp_debug_toggle_level(csp_debug_level_t level) +{ + if (level > CSP_LOCK) { + printf("Max level is 6\r\n"); + return; + } + csp_debug_level_enabled[level] = (csp_debug_level_enabled[level]) ? false : true; + printf("Level %u: value %u\r\n", level, csp_debug_level_enabled[level]); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_dedup.c b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.c new file mode 100644 index 00000000..d263c7a4 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.c @@ -0,0 +1,66 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "csp_dedup.h" + +/* Check the last CSP_DEDUP_COUNT packets for duplicates */ +#define CSP_DEDUP_COUNT 16 + +/* Only consider packet a duplicate if received under CSP_DEDUP_WINDOW_MS ago */ +#define CSP_DEDUP_WINDOW_MS 1000 + +/* Store packet CRC's in a ringbuffer */ +static uint32_t csp_dedup_array[CSP_DEDUP_COUNT] = {}; +static uint32_t csp_dedup_timestamp[CSP_DEDUP_COUNT] = {}; +static int csp_dedup_in = 0; + +bool csp_dedup_is_duplicate(csp_packet_t *packet) +{ + /* Calculate CRC32 for packet */ + uint32_t crc = csp_crc32_memory((const uint8_t *) &packet->id, packet->length + sizeof(packet->id)); + + /* Check if we have received this packet before */ + for (int i = 0; i < CSP_DEDUP_COUNT; i++) { + + /* Check for match */ + if (crc == csp_dedup_array[i]) { + + /* Check the timestamp */ + if (csp_get_ms() < csp_dedup_timestamp[i] + CSP_DEDUP_WINDOW_MS) + return true; + } + } + + /* If not, insert packet into duplicate list */ + csp_dedup_array[csp_dedup_in] = crc; + csp_dedup_timestamp[csp_dedup_in] = csp_get_ms(); + csp_dedup_in = (csp_dedup_in + 1) % CSP_DEDUP_COUNT; + + return false; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_dedup.h b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.h new file mode 100644 index 00000000..75a3f124 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.h @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_DEDUP_H_ +#define CSP_DEDUP_H_ + +/** + * Check for a duplicate packet + * @param packet pointer to packet + * @return false if not a duplicate, true if duplicate + */ +bool csp_dedup_is_duplicate(csp_packet_t *packet); + +#endif /* CSP_DEDUP_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_endian.c b/gomspace/libgscsp/lib/libcsp/src/csp_endian.c new file mode 100644 index 00000000..6d0ef226 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_endian.c @@ -0,0 +1,204 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* CSP includes */ +#include +#include + +/* Convert 16-bit number from host byte order to network byte order */ +inline uint16_t __attribute__ ((__const__)) csp_hton16(uint16_t h16) { +#ifdef CSP_BIG_ENDIAN + return h16; +#else + return (((h16 & 0xff00) >> 8) | + ((h16 & 0x00ff) << 8)); +#endif +} + +/* Convert 16-bit number from network byte order to host byte order */ +inline uint16_t __attribute__ ((__const__)) csp_ntoh16(uint16_t n16) { + return csp_hton16(n16); +} + +/* Convert 32-bit number from host byte order to network byte order */ +inline uint32_t __attribute__ ((__const__)) csp_hton32(uint32_t h32) { +#ifdef CSP_BIG_ENDIAN + return h32; +#else + 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 */ +inline uint32_t __attribute__ ((__const__)) csp_ntoh32(uint32_t n32) { + return csp_hton32(n32); +} + +/* Convert 64-bit number from host byte order to network byte order */ +inline uint64_t __attribute__ ((__const__)) csp_hton64(uint64_t h64) { +#ifdef CSP_BIG_ENDIAN + return h64; +#else + 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 */ +inline uint64_t __attribute__ ((__const__)) csp_ntoh64(uint64_t n64) { + return csp_hton64(n64); +} + +/* Convert 16-bit number from host byte order to big endian byte order */ +inline uint16_t __attribute__ ((__const__)) csp_htobe16(uint16_t h16) { + return csp_hton16(h16); +} + +/* Convert 16-bit number from host byte order to little endian byte order */ +inline uint16_t __attribute__ ((__const__)) csp_htole16(uint16_t h16) { +#ifdef CSP_LITTLE_ENDIAN + return h16; +#else + return (((h16 & 0xff00) >> 8) | + ((h16 & 0x00ff) << 8)); +#endif +} + +/* Convert 16-bit number from big endian byte order to little endian byte order */ +inline uint16_t __attribute__ ((__const__)) csp_betoh16(uint16_t be16) { + return csp_ntoh16(be16); +} + +/* Convert 16-bit number from little endian byte order to host byte order */ +inline uint16_t __attribute__ ((__const__)) csp_letoh16(uint16_t le16) { + return csp_htole16(le16); +} + +/* Convert 32-bit number from host byte order to big endian byte order */ +inline uint32_t __attribute__ ((__const__)) csp_htobe32(uint32_t h32) { + return csp_hton32(h32); +} + +/* Convert 32-bit number from little endian byte order to host byte order */ +inline uint32_t __attribute__ ((__const__)) csp_htole32(uint32_t h32) { +#ifdef CSP_LITTLE_ENDIAN + return h32; +#else + 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 */ +inline uint32_t __attribute__ ((__const__)) csp_betoh32(uint32_t be32) { + return csp_ntoh32(be32); +} + +/* Convert 32-bit number from little endian byte order to host byte order */ +inline uint32_t __attribute__ ((__const__)) csp_letoh32(uint32_t le32) { + return csp_htole32(le32); +} + +/* Convert 64-bit number from host byte order to big endian byte order */ +inline uint64_t __attribute__ ((__const__)) csp_htobe64(uint64_t h64) { + return csp_hton64(h64); +} + +/* Convert 64-bit number from host byte order to little endian byte order */ +inline uint64_t __attribute__ ((__const__)) csp_htole64(uint64_t h64) { +#ifdef CSP_LITTLE_ENDIAN + return h64; +#else + 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 */ +inline uint64_t __attribute__ ((__const__)) csp_betoh64(uint64_t be64) { + return csp_ntoh64(be64); +} + +/* Convert 64-bit number from little endian byte order to host byte order */ +inline uint64_t __attribute__ ((__const__)) csp_letoh64(uint64_t le64) { + return csp_htole64(le64); +} + + +/* Convert float from host byte order to network byte order */ +inline float __attribute__ ((__const__)) csp_htonflt(float f) { +#ifdef CSP_BIG_ENDIAN + return f; +#else + union v { + float f; + uint32_t i; + }; + union v val; + val.f = f; + val.i = csp_hton32(val.i); + return val.f; +#endif +} + +/* Convert float from host byte order to network byte order */ +inline float __attribute__ ((__const__)) csp_ntohflt(float f) { + return csp_htonflt(f); +} + +/* Convert double from host byte order to network byte order */ +inline double __attribute__ ((__const__)) csp_htondbl(double d) { +#ifdef CSP_BIG_ENDIAN + return d; +#else + union v { + double d; + uint64_t i; + }; + union v val; + val.d = d; + val.i = csp_hton64(val.i); + return val.d; +#endif +} + +/* Convert float from host byte order to network byte order */ +inline double __attribute__ ((__const__)) csp_ntohdbl(double d) { + return csp_htondbl(d); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_hex_dump.c b/gomspace/libgscsp/lib/libcsp/src/csp_hex_dump.c new file mode 100644 index 00000000..af0a2660 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_hex_dump.c @@ -0,0 +1,55 @@ +#include +#include + +void csp_hex_dump (const char *desc, void *addr, int len) +{ + int i; + unsigned char buff[17]; + unsigned char *pc = (unsigned char*)addr; + + // Output description if given. + if (desc != NULL) + printf ("%s:\r\n", desc); + + if (len == 0) { + printf(" ZERO LENGTH\r\n"); + return; + } + if (len < 0) { + printf(" NEGATIVE LENGTH: %i\r\n",len); + return; + } + + // Process every byte in the data. + for (i = 0; i < len; i++) { + // Multiple of 16 means new line (with line offset). + + if ((i % 16) == 0) { + // Just don't print ASCII for the zeroth line. + if (i != 0) + printf (" %s\r\n", buff); + + // Output the offset. + printf (" %p ", addr + i); + } + + // Now the hex code for the specific character. + printf (" %02x", pc[i]); + + // And store a printable ASCII character for later. + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + // Pad out last line if not exactly 16 characters. + while ((i % 16) != 0) { + printf (" "); + i++; + } + + // And print the final ASCII bit. + printf (" %s\r\n", buff); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_iflist.c b/gomspace/libgscsp/lib/libcsp/src/csp_iflist.c new file mode 100644 index 00000000..2bfef422 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_iflist.c @@ -0,0 +1,100 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* Interfaces are stored in a linked list*/ +static csp_iface_t * interfaces = NULL; + +csp_iface_t * csp_iflist_get_by_name(const char *name) { + csp_iface_t *ifc = interfaces; + while(ifc) { + if (strncmp(ifc->name, name, 10) == 0) + break; + ifc = ifc->next; + } + return ifc; +} + +void csp_iflist_add(csp_iface_t *ifc) { + + /* Add interface to pool */ + if (interfaces == NULL) { + /* This is the first interface to be added */ + interfaces = ifc; + ifc->next = NULL; + } else { + /* One or more interfaces were already added */ + csp_iface_t * i = interfaces; + while (i != ifc && i->next) + i = i->next; + + /* Insert interface last if not already in pool */ + if (i != ifc && i->next == NULL) { + i->next = ifc; + ifc->next = NULL; + } + } + +} + +csp_iface_t * csp_iflist_get(void) +{ + return interfaces; +} + +#ifdef CSP_DEBUG +static int csp_bytesize(char *buf, int len, unsigned long int n) { + char postfix; + double size; + + if (n >= 1048576) { + size = n/1048576.0; + postfix = 'M'; + } else if (n >= 1024) { + size = n/1024.; + postfix = 'K'; + } else { + size = n; + postfix = 'B'; + } + + return snprintf(buf, len, "%.1f%c", size, postfix); +} + +void csp_iflist_print(void) { + csp_iface_t * i = interfaces; + char txbuf[25], rxbuf[25]; + + while (i) { + csp_bytesize(txbuf, 25, i->txbytes); + csp_bytesize(rxbuf, 25, i->rxbytes); + printf("%-5s tx: %05"PRIu32" rx: %05"PRIu32" txe: %05"PRIu32" rxe: %05"PRIu32"\r\n" + " drop: %05"PRIu32" autherr: %05"PRIu32 " frame: %05"PRIu32"\r\n" + " txb: %"PRIu32" (%s) rxb: %"PRIu32" (%s)\r\n\r\n", + i->name, i->tx, i->rx, i->tx_error, i->rx_error, i->drop, + i->autherr, i->frame, i->txbytes, txbuf, i->rxbytes, rxbuf); + i = i->next; + } + +} +#endif + diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_io.c b/gomspace/libgscsp/lib/libcsp/src/csp_io.c new file mode 100644 index 00000000..3d7f614a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_io.c @@ -0,0 +1,502 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "csp_io.h" +#include "csp_port.h" +#include "csp_conn.h" +#include "csp_route.h" +#include "csp_promisc.h" +#include "csp_qfifo.h" +#include "transport/csp_transport.h" + +/** CSP address of this node */ +static uint8_t csp_my_address; + +/* Hostname, model and build revision */ +static const char *csp_hostname = NULL; +static const char *csp_model = NULL; +static const char *csp_revision = GIT_REV; + +#ifdef CSP_USE_PROMISC +extern csp_queue_handle_t csp_promisc_queue; +#endif + +void csp_set_address(uint8_t addr) +{ + csp_my_address = addr; +} + +uint8_t csp_get_address(void) +{ + return csp_my_address; +} + +void csp_set_hostname(const char *hostname) +{ + csp_hostname = hostname; +} + +const char *csp_get_hostname(void) +{ + return csp_hostname; +} + +void csp_set_model(const char *model) +{ + csp_model = model; +} + +const char *csp_get_model(void) +{ + return csp_model; +} + +void csp_set_revision(const char *revision) +{ + csp_revision = revision; +} + +const char *csp_get_revision(void) +{ + return csp_revision; +} + +int csp_init(unsigned char address) { + + int ret; + + /* Initialize CSP */ + csp_set_address(address); + + ret = csp_conn_init(); + if (ret != CSP_ERR_NONE) + return ret; + + ret = csp_port_init(); + if (ret != CSP_ERR_NONE) + return ret; + + ret = csp_qfifo_init(); + if (ret != CSP_ERR_NONE) + return ret; + + /* Loopback */ + csp_iflist_add(&csp_if_lo); + + /* Register loopback route */ + csp_route_set(csp_get_address(), &csp_if_lo, CSP_NODE_MAC); + + /* Also register loopback as default, until user redefines default route */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_lo, CSP_NODE_MAC); + + return CSP_ERR_NONE; + +} + +csp_socket_t * csp_socket(uint32_t opts) { + + /* Validate socket options */ +#ifndef CSP_USE_RDP + if (opts & CSP_SO_RDPREQ) { + csp_log_error("Attempt to create socket that requires RDP, but CSP was compiled without RDP support"); + return NULL; + } +#endif + +#ifndef CSP_USE_XTEA + if (opts & CSP_SO_XTEAREQ) { + csp_log_error("Attempt to create socket that requires XTEA, but CSP was compiled without XTEA support"); + return NULL; + } +#endif + +#ifndef CSP_USE_HMAC + if (opts & CSP_SO_HMACREQ) { + csp_log_error("Attempt to create socket that requires HMAC, but CSP was compiled without HMAC support"); + return NULL; + } +#endif + +#ifndef CSP_USE_CRC32 + if (opts & CSP_SO_CRC32REQ) { + csp_log_error("Attempt to create socket that requires CRC32, but CSP was compiled without CRC32 support"); + return NULL; + } +#endif + + /* Drop packet if reserved flags are set */ + if (opts & ~(CSP_SO_RDPREQ | CSP_SO_XTEAREQ | CSP_SO_HMACREQ | CSP_SO_CRC32REQ | CSP_SO_CONN_LESS)) { + csp_log_error("Invalid socket option"); + return NULL; + } + + /* Use CSP buffers instead? */ + csp_socket_t * sock = csp_conn_allocate(CONN_SERVER); + if (sock == NULL) + return NULL; + + /* If connectionless, init the queue to a pre-defined size + * if not, the user must init the queue using csp_listen */ + if (opts & CSP_SO_CONN_LESS) { + sock->socket = csp_queue_create(CSP_CONN_QUEUE_LENGTH, sizeof(csp_packet_t *)); + if (sock->socket == NULL) { + csp_close(sock); + return NULL; + } + } else { + sock->socket = NULL; + } + sock->opts = opts; + + return sock; + +} + +csp_conn_t * csp_accept(csp_socket_t * sock, uint32_t timeout) { + + if (sock == NULL) + return NULL; + + if (sock->socket == NULL) + return NULL; + + csp_conn_t * conn; + if (csp_queue_dequeue(sock->socket, &conn, timeout) == CSP_QUEUE_OK) + return conn; + + return NULL; + +} + +csp_packet_t * csp_read(csp_conn_t * conn, uint32_t timeout) { + + csp_packet_t * packet = NULL; + + if (conn == NULL || conn->state != CONN_OPEN) + return NULL; + +#ifdef CSP_USE_QOS + int prio, event; + if (csp_queue_dequeue(conn->rx_event, &event, timeout) != CSP_QUEUE_OK) + return NULL; + + for (prio = 0; prio < CSP_RX_QUEUES; prio++) + if (csp_queue_dequeue(conn->rx_queue[prio], &packet, 0) == CSP_QUEUE_OK) + break; +#else + if (csp_queue_dequeue(conn->rx_queue[0], &packet, timeout) != CSP_QUEUE_OK) + return NULL; +#endif + +#ifdef CSP_USE_RDP + /* Packet read could trigger ACK transmission */ + if (conn->idin.flags & CSP_FRDP && conn->rdp.delayed_acks) + csp_rdp_check_ack(conn); + +#endif + + return packet; + +} + +int csp_send_direct(csp_id_t idout, csp_packet_t * packet, csp_iface_t * ifout, uint32_t timeout) { + + if (packet == NULL) { + csp_log_error("csp_send_direct called with NULL packet"); + goto err; + } + + if ((ifout == NULL) || (ifout->nexthop == NULL)) { + csp_log_error("No route to host: %#08x", idout.ext); + goto err; + } + + csp_log_packet("OUT: S %u, D %u, Dp %u, Sp %u, Pr %u, Fl 0x%02X, Sz %u VIA: %s", + idout.src, idout.dst, idout.dport, idout.sport, idout.pri, idout.flags, packet->length, ifout->name); + + /* Copy identifier to packet (before crc, xtea and hmac) */ + packet->id.ext = idout.ext; + +#ifdef CSP_USE_PROMISC + /* Loopback traffic is added to promisc queue by the router */ + if (idout.dst != csp_get_address() && idout.src == csp_get_address()) { + packet->id.ext = idout.ext; + csp_promisc_add(packet); + } +#endif + + /* Only encrypt packets from the current node */ + if (idout.src == csp_get_address()) { + /* Append HMAC */ + if (idout.flags & CSP_FHMAC) { +#ifdef CSP_USE_HMAC + /* Calculate and add HMAC (does not include header for backwards compatability with csp1.x) */ + if (csp_hmac_append(packet, false) != 0) { + /* HMAC append failed */ + csp_log_warn("HMAC append failed!"); + goto tx_err; + } +#else + csp_log_warn("Attempt to send packet with HMAC, but CSP was compiled without HMAC support. Discarding packet"); + goto tx_err; +#endif + } + + /* Append CRC32 */ + if (idout.flags & CSP_FCRC32) { +#ifdef CSP_USE_CRC32 + /* Calculate and add CRC32 (does not include header for backwards compatability with csp1.x) */ + if (csp_crc32_append(packet, false) != 0) { + /* CRC32 append failed */ + csp_log_warn("CRC32 append failed!"); + goto tx_err; + } +#else + csp_log_warn("Attempt to send packet with CRC32, but CSP was compiled without CRC32 support. Sending without CRC32r"); + idout.flags &= ~(CSP_FCRC32); +#endif + } + + if (idout.flags & CSP_FXTEA) { +#ifdef CSP_USE_XTEA + /* Create nonce */ + uint32_t nonce, nonce_n; + nonce = (uint32_t)rand(); + nonce_n = csp_hton32(nonce); + memcpy(&packet->data[packet->length], &nonce_n, sizeof(nonce_n)); + + /* Create initialization vector */ + uint32_t iv[2] = {nonce, 1}; + + /* Encrypt data */ + if (csp_xtea_encrypt(packet->data, packet->length, iv) != 0) { + /* Encryption failed */ + csp_log_warn("Encryption failed! Discarding packet"); + goto tx_err; + } + + packet->length += sizeof(nonce_n); +#else + csp_log_warn("Attempt to send XTEA encrypted packet, but CSP was compiled without XTEA support. Discarding packet"); + goto tx_err; +#endif + } + } + + /* Store length before passing to interface */ + uint16_t bytes = packet->length; + uint16_t mtu = ifout->mtu; + + if (mtu > 0 && bytes > mtu) + goto tx_err; + + if ((*ifout->nexthop)(ifout, packet, timeout) != CSP_ERR_NONE) + goto tx_err; + + ifout->tx++; + ifout->txbytes += bytes; + return CSP_ERR_NONE; + +tx_err: + ifout->tx_error++; +err: + return CSP_ERR_TX; + +} + +int csp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) { + + int ret; + + if ((conn == NULL) || (packet == NULL) || (conn->state != CONN_OPEN)) { + csp_log_error("Invalid call to csp_send"); + return 0; + } + +#ifdef CSP_USE_RDP + if (conn->idout.flags & CSP_FRDP) { + if (csp_rdp_send(conn, packet, timeout) != CSP_ERR_NONE) { + csp_iface_t * ifout = csp_rtable_find_iface(conn->idout.dst); + if (ifout != NULL) + ifout->tx_error++; + csp_log_warn("RDP send failed!"); + return 0; + } + } +#endif + + csp_iface_t * ifout = csp_rtable_find_iface(conn->idout.dst); + ret = csp_send_direct(conn->idout, packet, ifout, timeout); + + return (ret == CSP_ERR_NONE) ? 1 : 0; + +} + +int csp_send_prio(uint8_t prio, csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) { + conn->idout.pri = prio; + return csp_send(conn, packet, timeout); +} + +int csp_transaction_persistent(csp_conn_t * conn, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen) { + + int size = (inlen > outlen) ? inlen : outlen; + csp_packet_t * packet = csp_buffer_get(size); + if (packet == NULL) + return 0; + + /* Copy the request */ + if (outlen > 0 && outbuf != NULL) + memcpy(packet->data, outbuf, outlen); + packet->length = outlen; + + if (!csp_send(conn, packet, timeout)) { + csp_buffer_free(packet); + return 0; + } + + /* If no reply is expected, return now */ + if (inlen == 0) + return 1; + + packet = csp_read(conn, timeout); + if (packet == NULL) + return 0; + + if ((inlen != -1) && ((int)packet->length != inlen)) { + csp_log_error("Reply length %u expected %u", packet->length, inlen); + csp_buffer_free(packet); + return 0; + } + + memcpy(inbuf, packet->data, packet->length); + int length = packet->length; + csp_buffer_free(packet); + return length; + +} + +int csp_transaction(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen) { + return csp_transaction2(prio, dest, port, timeout, outbuf, outlen, inbuf, inlen, 0); +} + +int csp_transaction2(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen, uint32_t opts) { + + csp_conn_t * conn = csp_connect(prio, dest, port, 0, opts); + if (conn == NULL) + return 0; + + int status = csp_transaction_persistent(conn, timeout, outbuf, outlen, inbuf, inlen); + + csp_close(conn); + + return status; + +} + +csp_packet_t * csp_recvfrom(csp_socket_t * socket, uint32_t timeout) { + + if ((socket == NULL) || (!(socket->opts & CSP_SO_CONN_LESS))) + return NULL; + + csp_packet_t * packet = NULL; + csp_queue_dequeue(socket->socket, &packet, timeout); + + return packet; + +} + +int csp_sendto(uint8_t prio, uint8_t dest, uint8_t dport, uint8_t src_port, uint32_t opts, csp_packet_t * packet, uint32_t timeout) { + + packet->id.flags = 0; + + if (opts & CSP_O_RDP) { + csp_log_error("Attempt to create RDP packet on connection-less socket"); + return CSP_ERR_INVAL; + } + + if (opts & CSP_O_HMAC) { +#ifdef CSP_USE_HMAC + packet->id.flags |= CSP_FHMAC; +#else + csp_log_error("Attempt to create HMAC authenticated packet, but CSP was compiled without HMAC support"); + return CSP_ERR_NOTSUP; +#endif + } + + if (opts & CSP_O_XTEA) { +#ifdef CSP_USE_XTEA + packet->id.flags |= CSP_FXTEA; +#else + csp_log_error("Attempt to create XTEA encrypted packet, but CSP was compiled without XTEA support"); + return CSP_ERR_NOTSUP; +#endif + } + + if (opts & CSP_O_CRC32) { +#ifdef CSP_USE_CRC32 + packet->id.flags |= CSP_FCRC32; +#else + csp_log_error("Attempt to create CRC32 validated packet, but CSP was compiled without CRC32 support"); + return CSP_ERR_NOTSUP; +#endif + } + + packet->id.dst = dest; + packet->id.dport = dport; + packet->id.src = csp_get_address(); + packet->id.sport = src_port; + packet->id.pri = prio; + + csp_iface_t * ifout = csp_rtable_find_iface(dest); + if (csp_send_direct(packet->id, packet, ifout, timeout) != CSP_ERR_NONE) + return CSP_ERR_NOTSUP; + + return CSP_ERR_NONE; + +} + +int csp_sendto_reply(csp_packet_t * request_packet, csp_packet_t * reply_packet, uint32_t opts, uint32_t timeout) { + if (request_packet == NULL) + return CSP_ERR_INVAL; + + return csp_sendto(request_packet->id.pri, request_packet->id.src, request_packet->id.sport, request_packet->id.dport, opts, reply_packet, timeout); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_io.h b/gomspace/libgscsp/lib/libcsp/src/csp_io.h new file mode 100644 index 00000000..6ea8dfec --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_io.h @@ -0,0 +1,47 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IO_H_ +#define _CSP_IO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** + * Function to transmit a frame without an existing connection structure. + * This function is used for stateless transmissions + * @param idout 32bit CSP identifier + * @param packet pointer to packet, + * @param ifout pointer to output interface + * @param timeout a timeout to wait for TX to complete. NOTE: not all underlying drivers supports flow-control. + * @return returns 1 if successful and 0 otherwise. you MUST free the frame yourself if the transmission was not successful. + */ +int csp_send_direct(csp_id_t idout, csp_packet_t * packet, csp_iface_t * ifout, uint32_t timeout); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_IO_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_port.c b/gomspace/libgscsp/lib/libcsp/src/csp_port.c new file mode 100644 index 00000000..2a4ac2a9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_port.c @@ -0,0 +1,105 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include + +#include +#include +#include + +#include "csp_port.h" +#include "csp_conn.h" + +/* Allocation of ports */ +static csp_port_t ports[CSP_MAX_BIND_PORT + 2]; + +csp_socket_t * csp_port_get_socket(unsigned int port) { + + csp_socket_t * ret = NULL; + + if (port >= CSP_ANY) + return NULL; + + /* Match dport to socket or local "catch all" port number */ + if (ports[port].state == PORT_OPEN) + ret = ports[port].socket; + else if (ports[CSP_ANY].state == PORT_OPEN) + ret = ports[CSP_ANY].socket; + + return ret; + +} + +int csp_port_init(void) { + + memset(ports, PORT_CLOSED, sizeof(csp_port_t) * (CSP_MAX_BIND_PORT + 2)); + + return CSP_ERR_NONE; + +} + +int csp_listen(csp_socket_t * socket, size_t conn_queue_length) { + + if (socket == NULL) + return CSP_ERR_INVAL; + + socket->socket = csp_queue_create(conn_queue_length, sizeof(csp_conn_t *)); + if (socket->socket == NULL) + return CSP_ERR_NOMEM; + + socket->opts |= CSP_SO_INTERNAL_LISTEN; + + return CSP_ERR_NONE; + +} + +int csp_bind(csp_socket_t * socket, uint8_t port) { + + if (socket == NULL) + return CSP_ERR_INVAL; + + if (port > CSP_ANY) { + csp_log_error("Only ports from 0-%u (and CSP_ANY for default) are available for incoming ports", CSP_ANY); + return CSP_ERR_INVAL; + } + + /* Check if port number is valid */ + if (ports[port].state != PORT_CLOSED) { + csp_log_error("Port %d is already in use", port); + return CSP_ERR_USED; + } + + csp_log_info("Binding socket %p to port %u", socket, port); + + /* Save listener */ + ports[port].socket = socket; + ports[port].state = PORT_OPEN; + + return CSP_ERR_NONE; + +} + + diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_port.h b/gomspace/libgscsp/lib/libcsp/src/csp_port.h new file mode 100644 index 00000000..d2ec06e9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_port.h @@ -0,0 +1,55 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_PORT_H_ +#define _CSP_PORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** @brief Port states */ +typedef enum { + PORT_CLOSED = 0, + PORT_OPEN = 1, +} csp_port_state_t; + +/** @brief Port struct */ +typedef struct { + csp_port_state_t state; // Port state + csp_socket_t * socket; // New connections are added to this socket's conn queue +} csp_port_t; + +/** + * Init ports array + */ +int csp_port_init(void); + +csp_socket_t * csp_port_get_socket(unsigned int dport); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_PORT_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_promisc.c b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.c new file mode 100644 index 00000000..5f156c33 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.c @@ -0,0 +1,82 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#ifdef CSP_USE_PROMISC + +static csp_queue_handle_t csp_promisc_queue = NULL; +static int csp_promisc_enabled = 0; + +int csp_promisc_enable(unsigned int buf_size) { + + /* If queue already initialised */ + if (csp_promisc_queue != NULL) { + csp_promisc_enabled = 1; + return CSP_ERR_NONE; + } + + /* Create packet queue */ + csp_promisc_queue = csp_queue_create(buf_size, sizeof(csp_packet_t *)); + + if (csp_promisc_queue == NULL) + return CSP_ERR_INVAL; + + csp_promisc_enabled = 1; + return CSP_ERR_NONE; + +} + +void csp_promisc_disable(void) { + csp_promisc_enabled = 0; +} + +csp_packet_t * csp_promisc_read(uint32_t timeout) { + + if (csp_promisc_queue == NULL) + return NULL; + + csp_packet_t * packet = NULL; + csp_queue_dequeue(csp_promisc_queue, &packet, timeout); + + return packet; + +} + +void csp_promisc_add(csp_packet_t * packet) { + + if (csp_promisc_enabled == 0) + return; + + if (csp_promisc_queue != NULL) { + /* Make a copy of the message and queue it to the promiscuous task */ + csp_packet_t *packet_copy = csp_buffer_clone(packet); + if (packet_copy != NULL) { + if (csp_queue_enqueue(csp_promisc_queue, &packet_copy, 0) != CSP_QUEUE_OK) { + csp_log_error("Promiscuous mode input queue full"); + csp_buffer_free(packet_copy); + } + } + } + +} + +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_promisc.h b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.h new file mode 100644 index 00000000..be62edda --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.h @@ -0,0 +1,30 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_PROMISC_H_ +#define CSP_PROMISC_H_ + +/** + * Add packet to promiscuous mode packet queue + * @param packet Packet to add to the queue + */ +void csp_promisc_add(csp_packet_t * packet); + +#endif /* CSP_PROMISC_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.c b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.c new file mode 100644 index 00000000..9329b2ca --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.c @@ -0,0 +1,149 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "csp_qfifo.h" + +static csp_queue_handle_t qfifo[CSP_ROUTE_FIFOS]; +#ifdef CSP_USE_QOS +static csp_queue_handle_t qfifo_events; +#endif + +int csp_qfifo_init(void) { + int prio; + + /* Create router fifos for each priority */ + for (prio = 0; prio < CSP_ROUTE_FIFOS; prio++) { + if (qfifo[prio] == NULL) { + qfifo[prio] = csp_queue_create(CSP_FIFO_INPUT, sizeof(csp_qfifo_t)); + if (!qfifo[prio]) + return CSP_ERR_NOMEM; + } + } + +#ifdef CSP_USE_QOS + /* Create QoS fifo notification queue */ + qfifo_events = csp_queue_create(CSP_FIFO_INPUT, sizeof(int)); + if (!qfifo_events) + return CSP_ERR_NOMEM; +#endif + + return CSP_ERR_NONE; + +} + +int csp_qfifo_read(csp_qfifo_t * input) { + +#ifdef CSP_USE_QOS + int prio, found, event; + + /* Wait for packet in any queue */ + if (csp_queue_dequeue(qfifo_events, &event, FIFO_TIMEOUT) != CSP_QUEUE_OK) + return CSP_ERR_TIMEDOUT; + + /* Find packet with highest priority */ + found = 0; + for (prio = 0; prio < CSP_ROUTE_FIFOS; prio++) { + if (csp_queue_dequeue(qfifo[prio], input, 0) == CSP_QUEUE_OK) { + found = 1; + break; + } + } + + if (!found) { + csp_log_warn("Spurious wakeup: No packet found"); + return CSP_ERR_TIMEDOUT; + } +#else + if (csp_queue_dequeue(qfifo[0], input, FIFO_TIMEOUT) != CSP_QUEUE_OK) + return CSP_ERR_TIMEDOUT; +#endif + + return CSP_ERR_NONE; + +} + +void csp_qfifo_write(csp_packet_t * packet, csp_iface_t * interface, CSP_BASE_TYPE * pxTaskWoken) { + + int result; + + if (packet == NULL) { + if (pxTaskWoken == NULL) { // Only do logging in non-ISR context + csp_log_warn("csp_new packet called with NULL packet"); + } + return; + } else if (interface == NULL) { + if (pxTaskWoken == NULL) { // Only do logging in non-ISR context + csp_log_warn("csp_new packet called with NULL interface"); + } + if (pxTaskWoken == NULL) + csp_buffer_free(packet); + else + csp_buffer_free_isr(packet); + return; + } + + csp_qfifo_t queue_element; + queue_element.interface = interface; + queue_element.packet = packet; + +#ifdef CSP_USE_QOS + int fifo = packet->id.pri; +#else + int fifo = 0; +#endif + + if (pxTaskWoken == NULL) + result = csp_queue_enqueue(qfifo[fifo], &queue_element, 0); + else + result = csp_queue_enqueue_isr(qfifo[fifo], &queue_element, pxTaskWoken); + +#ifdef CSP_USE_QOS + static int event = 0; + + if (result == CSP_QUEUE_OK) { + if (pxTaskWoken == NULL) + csp_queue_enqueue(qfifo_events, &event, 0); + else + csp_queue_enqueue_isr(qfifo_events, &event, pxTaskWoken); + } +#endif + + if (result != CSP_QUEUE_OK) { + if (pxTaskWoken == NULL) { // Only do logging in non-ISR context + csp_log_warn("ERROR: Routing input FIFO is FULL. Dropping packet."); + } + interface->drop++; + if (pxTaskWoken == NULL) + csp_buffer_free(packet); + else + csp_buffer_free_isr(packet); + } + +} + +void csp_qfifo_wake_up(void) { + csp_qfifo_t queue_element; + queue_element.interface = NULL; + queue_element.packet = NULL; + csp_queue_enqueue(qfifo[0], &queue_element, 0); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.h b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.h new file mode 100644 index 00000000..2910c48d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.h @@ -0,0 +1,54 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_QFIFO_H_ +#define CSP_QFIFO_H_ + +#ifdef CSP_USE_RDP +#define FIFO_TIMEOUT 100 //! If RDP is enabled, the router needs to awake some times to check timeouts +#else +#define FIFO_TIMEOUT CSP_MAX_DELAY //! If no RDP, the router can sleep untill data arrives +#endif + +/** + * Init FIFO/QOS queues + * @return CSP_ERR type + */ +int csp_qfifo_init(void); + +typedef struct { + csp_iface_t * interface; + csp_packet_t * packet; +} csp_qfifo_t; + +/** + * Read next packet from router input queue + * @param input pointer to router queue item element + * @return CSP_ERR type + */ +int csp_qfifo_read(csp_qfifo_t * input); + +/** + * Wake up any task (e.g. router) waiting on messages. + * For testing. + */ +void csp_qfifo_wake_up(void); + +#endif /* CSP_QFIFO_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_route.c b/gomspace/libgscsp/lib/libcsp/src/csp_route.c new file mode 100644 index 00000000..627dfdc8 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_route.c @@ -0,0 +1,347 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include +#include + +#include +#include + +#include +#include + +#include "csp_port.h" +#include "csp_conn.h" +#include "csp_io.h" +#include "csp_promisc.h" +#include "csp_qfifo.h" +#include "csp_dedup.h" +#include "transport/csp_transport.h" +#include "csp_buffer.h" + +/** + * Check supported packet options + * @param interface pointer to incoming interface + * @param packet pointer to packet + * @return CSP_ERR_NONE is all options are supported, CSP_ERR_NOTSUP if not + */ +static int csp_route_check_options(csp_iface_t *interface, csp_packet_t *packet) +{ +#ifndef CSP_USE_XTEA + /* Drop XTEA packets */ + if (packet->id.flags & CSP_FXTEA) { + csp_log_error("Received XTEA encrypted packet, but CSP was compiled without XTEA support. Discarding packet"); + interface->autherr++; + return CSP_ERR_NOTSUP; + } +#endif + +#ifndef CSP_USE_HMAC + /* Drop HMAC packets */ + if (packet->id.flags & CSP_FHMAC) { + csp_log_error("Received packet with HMAC, but CSP was compiled without HMAC support. Discarding packet"); + interface->autherr++; + return CSP_ERR_NOTSUP; + } +#endif + +#ifndef CSP_USE_RDP + /* Drop RDP packets */ + if (packet->id.flags & CSP_FRDP) { + csp_log_error("Received RDP packet, but CSP was compiled without RDP support. Discarding packet"); + interface->rx_error++; + return CSP_ERR_NOTSUP; + } +#endif + return CSP_ERR_NONE; +} + +/** + * Helper function to decrypt, check auth and CRC32 + * @param security_opts either socket_opts or conn_opts + * @param interface pointer to incoming interface + * @param packet pointer to packet + * @return -1 Missing feature, -2 XTEA error, -3 CRC error, -4 HMAC error, 0 = OK. + */ +static int csp_route_security_check(uint32_t security_opts, csp_iface_t * interface, csp_packet_t * packet) { + +#ifdef CSP_USE_XTEA + /* XTEA encrypted packet */ + if (packet->id.flags & CSP_FXTEA) { + /* Read nonce */ + uint32_t nonce; + memcpy(&nonce, &packet->data[packet->length - sizeof(nonce)], sizeof(nonce)); + nonce = csp_ntoh32(nonce); + packet->length -= sizeof(nonce); + + /* Create initialization vector */ + uint32_t iv[2] = {nonce, 1}; + + /* Decrypt data */ + if (csp_xtea_decrypt(packet->data, packet->length, iv) != 0) { + /* Decryption failed */ + csp_log_error("Decryption failed! Discarding packet"); + interface->autherr++; + return CSP_ERR_XTEA; + } + } else if (security_opts & CSP_SO_XTEAREQ) { + csp_log_warn("Received packet without XTEA encryption. Discarding packet"); + interface->autherr++; + return CSP_ERR_XTEA; + } +#endif + + /* CRC32 verified packet */ + if (packet->id.flags & CSP_FCRC32) { +#ifdef CSP_USE_CRC32 + if (packet->length < 4) + csp_log_error("Too short packet for CRC32, %u", packet->length); + /* Verify CRC32 (does not include header for backwards compatability with csp1.x) */ + if (csp_crc32_verify(packet, false) != 0) { + /* Checksum failed */ + csp_log_error("CRC32 verification error! Discarding packet"); + interface->rx_error++; + return CSP_ERR_CRC32; + } + } else if (security_opts & CSP_SO_CRC32REQ) { + csp_log_warn("Received packet without CRC32. Accepting packet"); +#else + /* Strip CRC32 field and accept the packet */ + csp_log_warn("Received packet with CRC32, but CSP was compiled without CRC32 support. Accepting packet"); + packet->length -= sizeof(uint32_t); +#endif + } + +#ifdef CSP_USE_HMAC + /* HMAC authenticated packet */ + if (packet->id.flags & CSP_FHMAC) { + /* Verify HMAC (does not include header for backwards compatability with csp1.x) */ + if (csp_hmac_verify(packet, false) != 0) { + /* HMAC failed */ + csp_log_error("HMAC verification error! Discarding packet"); + interface->autherr++; + return CSP_ERR_HMAC; + } + } else if (security_opts & CSP_SO_HMACREQ) { + csp_log_warn("Received packet without HMAC. Discarding packet"); + interface->autherr++; + return CSP_ERR_HMAC; + } +#endif + +#ifdef CSP_USE_RDP + /* RDP packet */ + if (!(packet->id.flags & CSP_FRDP)) { + if (security_opts & CSP_SO_RDPREQ) { + csp_log_warn("Received packet without RDP header. Discarding packet"); + interface->rx_error++; + return CSP_ERR_INVAL; + } + } +#endif + + return CSP_ERR_NONE; + +} + +int csp_route_work(uint32_t timeout) { + + csp_qfifo_t input; + csp_packet_t * packet; + csp_conn_t * conn; + csp_socket_t * socket; + +#ifdef CSP_USE_RDP + /* Check connection timeouts (currently only for RDP) */ + csp_conn_check_timeouts(); +#endif + + /* Get next packet to route */ + if (csp_qfifo_read(&input) != CSP_ERR_NONE) + return -1; + + packet = input.packet; + if (!packet) + return -1; + + csp_log_packet("INP: S %u, D %u, Dp %u, Sp %u, Pr %u, Fl 0x%02X, Sz %"PRIu16" VIA: %s", + packet->id.src, packet->id.dst, packet->id.dport, + packet->id.sport, packet->id.pri, packet->id.flags, packet->length, input.interface->name); + + /* Here there be promiscuous mode */ +#ifdef CSP_USE_PROMISC + csp_promisc_add(packet); +#endif + +#ifdef CSP_USE_DEDUP + /* Check for duplicates */ + if (csp_dedup_is_duplicate(packet)) { + /* Discard packet */ + csp_log_packet("Duplicate packet discarded"); + input.interface->drop++; + csp_buffer_free(packet); + return 0; + } +#endif + + /* Now we count the message (since its deduplicated) */ + input.interface->rx++; + input.interface->rxbytes += packet->length; + + /* If the message is not to me, route the message to the correct interface */ + if ((packet->id.dst != csp_get_address()) && (packet->id.dst != CSP_BROADCAST_ADDR)) { + + /* Find the destination interface */ + csp_iface_t * dstif = csp_rtable_find_iface(packet->id.dst); + + /* If the message resolves to the input interface, don't loop it back out */ + if ((dstif == NULL) || ((dstif == input.interface) && (input.interface->split_horizon_off == 0))) { + csp_buffer_free(packet); + return 0; + } + + /* Otherwise, actually send the message */ + if (csp_send_direct(packet->id, packet, dstif, 0) != CSP_ERR_NONE) { + csp_log_warn("Router failed to send"); + csp_buffer_free(packet); + } + + /* Next message, please */ + return 0; + } + + /* Discard packets with unsupported options */ + if (csp_route_check_options(input.interface, packet) != CSP_ERR_NONE) { + csp_buffer_free(packet); + return 0; + } + + /* The message is to me, search for incoming socket */ + socket = csp_port_get_socket(packet->id.dport); + + /* If the socket is connection-less, deliver now */ + if (socket && (socket->opts & CSP_SO_CONN_LESS)) { + if (csp_route_security_check(socket->opts, input.interface, packet) < 0) { + csp_buffer_free(packet); + return 0; + } + if (csp_queue_enqueue(socket->socket, &packet, 0) != CSP_QUEUE_OK) { + csp_log_error("Conn-less socket queue full"); + csp_buffer_free(packet); + return 0; + } + return 0; + } + + /* Search for an existing connection */ + conn = csp_conn_find(packet->id.ext, CSP_ID_CONN_MASK); + + /* If this is an incoming packet on a new connection */ + if (conn == NULL) { + + /* Reject packet if no matching socket is found */ + if (!socket) { + csp_buffer_free(packet); + return 0; + } + + /* Run security check on incoming packet */ + if (csp_route_security_check(socket->opts, input.interface, packet) < 0) { + csp_buffer_free(packet); + return 0; + } + + /* New incoming connection accepted */ + csp_id_t idout; + idout.pri = packet->id.pri; + idout.src = csp_get_address(); + + idout.dst = packet->id.src; + idout.dport = packet->id.sport; + idout.sport = packet->id.dport; + idout.flags = packet->id.flags; + + /* Create connection */ + conn = csp_conn_new(packet->id, idout); + + if (!conn) { + csp_log_error("No more connections available"); + csp_buffer_free(packet); + return 0; + } + + /* Store the socket queue and options */ + conn->socket = socket->socket; + conn->opts = socket->opts; + + /* Packet to existing connection */ + } else { + + /* Run security check on incoming packet */ + if (csp_route_security_check(conn->opts, input.interface, packet) < 0) { + csp_buffer_free(packet); + return 0; + } + + } + +#ifdef CSP_USE_RDP + /* Pass packet to RDP module */ + if (packet->id.flags & CSP_FRDP) { + csp_rdp_new_packet(conn, packet); + return 0; + } +#endif + + /* Pass packet to UDP module */ + csp_udp_new_packet(conn, packet); + return 0; +} + +static CSP_DEFINE_TASK(csp_task_router) { + + /* Here there be routing */ + while (1) { + csp_route_work(FIFO_TIMEOUT); + } + + return CSP_TASK_RETURN; + +} + +int csp_route_start_task(unsigned int task_stack_size, unsigned int priority) { + + static csp_thread_handle_t handle_router; + int ret = csp_thread_create(csp_task_router, "RTE", task_stack_size, NULL, priority, &handle_router); + + if (ret != 0) { + csp_log_error("Failed to start router task"); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_route.h b/gomspace/libgscsp/lib/libcsp/src/csp_route.h new file mode 100644 index 00000000..2a20f49f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_route.h @@ -0,0 +1,24 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_ROUTE_H_ +#define _CSP_ROUTE_H_ + +#endif // _CSP_ROUTE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_service_handler.c b/gomspace/libgscsp/lib/libcsp/src/csp_service_handler.c new file mode 100644 index 00000000..0090afc1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_service_handler.c @@ -0,0 +1,334 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "csp_route.h" + +#define CSP_RPS_MTU 196 + +/** + * The CSP CMP mempy function is used to, override the function used to + * read/write memory by peek and poke. + */ +#ifdef __AVR__ +static uint32_t wrap_32bit_memcpy(uint32_t to, const uint32_t from, size_t size) { + return (uint32_t) (uintptr_t) memcpy((void *) (uintptr_t) to, (const void *) (uintptr_t) from, size); +} +static csp_memcpy_fnc_t csp_cmp_memcpy_fnc = wrap_32bit_memcpy; +#else +static csp_memcpy_fnc_t csp_cmp_memcpy_fnc = (csp_memcpy_fnc_t) memcpy; +#endif + + +void csp_cmp_set_memcpy(csp_memcpy_fnc_t fnc) { + csp_cmp_memcpy_fnc = fnc; +} + +static int do_cmp_ident(struct csp_cmp_message *cmp) { + + /* Copy revision */ + strncpy(cmp->ident.revision, csp_get_revision(), CSP_CMP_IDENT_REV_LEN); + cmp->ident.revision[CSP_CMP_IDENT_REV_LEN - 1] = '\0'; + + /* Copy compilation date */ + strncpy(cmp->ident.date, __DATE__, CSP_CMP_IDENT_DATE_LEN); + cmp->ident.date[CSP_CMP_IDENT_DATE_LEN - 1] = '\0'; + + /* Copy compilation time */ + strncpy(cmp->ident.time, __TIME__, CSP_CMP_IDENT_TIME_LEN); + cmp->ident.time[CSP_CMP_IDENT_TIME_LEN - 1] = '\0'; + + /* Copy hostname */ + strncpy(cmp->ident.hostname, csp_get_hostname(), CSP_HOSTNAME_LEN); + cmp->ident.hostname[CSP_HOSTNAME_LEN - 1] = '\0'; + + /* Copy model name */ + strncpy(cmp->ident.model, csp_get_model(), CSP_MODEL_LEN); + cmp->ident.model[CSP_MODEL_LEN - 1] = '\0'; + + return CSP_ERR_NONE; + +} + +static int do_cmp_route_set(struct csp_cmp_message *cmp) { + + csp_iface_t *ifc = csp_iflist_get_by_name(cmp->route_set.interface); + if (ifc == NULL) + return CSP_ERR_INVAL; + + if (csp_route_set(cmp->route_set.dest_node, ifc, cmp->route_set.next_hop_mac) != CSP_ERR_NONE) + return CSP_ERR_INVAL; + + return CSP_ERR_NONE; + +} + +static int do_cmp_if_stats(struct csp_cmp_message *cmp) { + + csp_iface_t *ifc = csp_iflist_get_by_name(cmp->if_stats.interface); + if (ifc == NULL) + return CSP_ERR_INVAL; + + cmp->if_stats.tx = csp_hton32(ifc->tx); + cmp->if_stats.rx = csp_hton32(ifc->rx); + cmp->if_stats.tx_error = csp_hton32(ifc->tx_error); + cmp->if_stats.rx_error = csp_hton32(ifc->rx_error); + cmp->if_stats.drop = csp_hton32(ifc->drop); + cmp->if_stats.autherr = csp_hton32(ifc->autherr); + cmp->if_stats.frame = csp_hton32(ifc->frame); + cmp->if_stats.txbytes = csp_hton32(ifc->txbytes); + cmp->if_stats.rxbytes = csp_hton32(ifc->rxbytes); + cmp->if_stats.irq = csp_hton32(ifc->irq); + + return CSP_ERR_NONE; +} + +static int do_cmp_peek(struct csp_cmp_message *cmp) { + + cmp->peek.addr = csp_hton32(cmp->peek.addr); + if (cmp->peek.len > CSP_CMP_PEEK_MAX_LEN) + return CSP_ERR_INVAL; + + /* Dangerous, you better know what you are doing */ + csp_cmp_memcpy_fnc((csp_memptr_t) (uintptr_t) cmp->peek.data, (csp_memptr_t) (unsigned long) cmp->peek.addr, cmp->peek.len); + + return CSP_ERR_NONE; + +} + +static int do_cmp_poke(struct csp_cmp_message *cmp) { + + cmp->poke.addr = csp_hton32(cmp->poke.addr); + if (cmp->poke.len > CSP_CMP_POKE_MAX_LEN) + return CSP_ERR_INVAL; + + /* Extremely dangerous, you better know what you are doing */ + csp_cmp_memcpy_fnc((csp_memptr_t) (unsigned long) cmp->poke.addr, (csp_memptr_t) (uintptr_t) cmp->poke.data, cmp->poke.len); + + return CSP_ERR_NONE; + +} + +static int do_cmp_clock(struct csp_cmp_message *cmp) { + + cmp->clock.tv_sec = csp_ntoh32(cmp->clock.tv_sec); + cmp->clock.tv_nsec = csp_ntoh32(cmp->clock.tv_nsec); + + if ((cmp->clock.tv_sec != 0) && (clock_set_time != NULL)) { + clock_set_time(&cmp->clock); + } + + if (clock_get_time != NULL) { + clock_get_time(&cmp->clock); + } + + cmp->clock.tv_sec = csp_hton32(cmp->clock.tv_sec); + cmp->clock.tv_nsec = csp_hton32(cmp->clock.tv_nsec); + return CSP_ERR_NONE; + +} + +/* CSP Management Protocol handler */ +static int csp_cmp_handler(csp_conn_t * conn, csp_packet_t * packet) { + + int ret = CSP_ERR_INVAL; + struct csp_cmp_message * cmp = (struct csp_cmp_message *) packet->data; + + /* Ignore everything but requests */ + if (cmp->type != CSP_CMP_REQUEST) + return ret; + + switch (cmp->code) { + case CSP_CMP_IDENT: + ret = do_cmp_ident(cmp); + packet->length = CMP_SIZE(ident); + break; + + case CSP_CMP_ROUTE_SET: + ret = do_cmp_route_set(cmp); + packet->length = CMP_SIZE(route_set); + break; + + case CSP_CMP_IF_STATS: + ret = do_cmp_if_stats(cmp); + packet->length = CMP_SIZE(if_stats); + break; + + case CSP_CMP_PEEK: + ret = do_cmp_peek(cmp); + break; + + case CSP_CMP_POKE: + ret = do_cmp_poke(cmp); + break; + + case CSP_CMP_CLOCK: + ret = do_cmp_clock(cmp); + break; + + default: + ret = CSP_ERR_INVAL; + break; + } + + cmp->type = CSP_CMP_REPLY; + + return ret; +} + +void csp_service_handler(csp_conn_t * conn, csp_packet_t * packet) { + + switch (csp_conn_dport(conn)) { + + case CSP_CMP: + /* Pass to CMP handler */ + if (csp_cmp_handler(conn, packet) != CSP_ERR_NONE) { + csp_buffer_free(packet); + return; + } + break; + + case CSP_PING: + /* A ping means, just echo the packet, so no changes */ + csp_log_info("SERVICE: Ping received"); + break; + + case CSP_PS: { + /* Sanity check on request */ + if ((packet->length != 1) || (packet->data[0] != 0x55)) { + /* Sanity check failed */ + csp_buffer_free(packet); + /* Clear the packet, it has been freed */ + packet = NULL; + break; + } + /* Start by allocating just the right amount of memory */ + int task_list_size = csp_sys_tasklist_size(); + char * pslist = csp_malloc(task_list_size); + /* Check for malloc fail */ + if (pslist == NULL) { + /* Send out the data */ + strcpy((char *)packet->data, "Not enough memory"); + packet->length = strlen((char *)packet->data); + /* Break and let the default handling send packet */ + break; + } + + /* Retrieve the tasklist */ + csp_sys_tasklist(pslist); + int pslen = strnlen(pslist, task_list_size); + + /* Split the potentially very long string into packets */ + int i = 0; + while(i < pslen) { + + /* Allocate packet buffer, if need be */ + if (packet == NULL) + packet = csp_buffer_get(CSP_RPS_MTU); + if (packet == NULL) + break; + + /* Calculate length, either full MTU or the remainder */ + packet->length = (pslen - i > CSP_RPS_MTU) ? CSP_RPS_MTU : (pslen - i); + + /* Send out the data */ + memcpy(packet->data, &pslist[i], packet->length); + i += packet->length; + if (!csp_send(conn, packet, 0)) + csp_buffer_free(packet); + + /* Clear the packet reference when sent */ + packet = NULL; + + } + csp_free(pslist); + break; + } + + case CSP_MEMFREE: { + uint32_t total = csp_sys_memfree(); + + total = csp_hton32(total); + memcpy(packet->data, &total, sizeof(total)); + packet->length = sizeof(total); + + break; + } + + case CSP_REBOOT: { + uint32_t magic_word; + memcpy(&magic_word, packet->data, sizeof(magic_word)); + + magic_word = csp_ntoh32(magic_word); + + /* If the magic word is valid, reboot */ + if (magic_word == CSP_REBOOT_MAGIC) { + csp_sys_reboot(); + } else if (magic_word == CSP_REBOOT_SHUTDOWN_MAGIC) { + csp_sys_shutdown(); + } + + + + csp_buffer_free(packet); + return; + } + + case CSP_BUF_FREE: { + uint32_t size = csp_buffer_remaining(); + size = csp_hton32(size); + memcpy(packet->data, &size, sizeof(size)); + packet->length = sizeof(size); + break; + } + + case CSP_UPTIME: { + uint32_t time = csp_get_s(); + time = csp_hton32(time); + memcpy(packet->data, &time, sizeof(time)); + packet->length = sizeof(time); + break; + } + + default: + csp_buffer_free(packet); + return; + } + + if (packet != NULL) { + if (!csp_send(conn, packet, 0)) + csp_buffer_free(packet); + } + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_services.c b/gomspace/libgscsp/lib/libcsp/src/csp_services.c new file mode 100644 index 00000000..5392cb82 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_services.c @@ -0,0 +1,233 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include +#include +#include + +#include + +int csp_ping(uint8_t node, uint32_t timeout, unsigned int size, uint8_t conn_options) { + + unsigned int i; + uint32_t start, time, status = 0; + + /* Counter */ + start = csp_get_ms(); + + /* Open connection */ + csp_conn_t * conn = csp_connect(CSP_PRIO_NORM, node, CSP_PING, timeout, conn_options); + if (conn == NULL) + return -1; + + /* Prepare data */ + csp_packet_t * packet; + packet = csp_buffer_get(size); + if (packet == NULL) + goto out; + + /* Set data to increasing numbers */ + packet->length = size; + for (i = 0; i < size; i++) + packet->data[i] = i; + + /* Try to send frame */ + if (!csp_send(conn, packet, 0)) + goto out; + + /* Read incoming frame */ + packet = csp_read(conn, timeout); + if (packet == NULL) + goto out; + + /* Ensure that the data was actually echoed */ + for (i = 0; i < size; i++) + if (packet->data[i] != i % (0xff + 1)) + goto out; + + status = 1; + +out: + /* Clean up */ + if (packet != NULL) + csp_buffer_free(packet); + csp_close(conn); + + /* We have a reply */ + time = (csp_get_ms() - start); + + if (status) { + return time; + } else { + return -1; + } + +} + +void csp_ping_noreply(uint8_t node) { + + /* Prepare data */ + csp_packet_t * packet; + packet = csp_buffer_get(1); + + /* Check malloc */ + if (packet == NULL) + return; + + /* Open connection */ + csp_conn_t * conn = csp_connect(CSP_PRIO_NORM, node, CSP_PING, 0, 0); + if (conn == NULL) { + csp_buffer_free(packet); + return; + } + + packet->data[0] = 0x55; + packet->length = 1; + + printf("Ping ignore reply node %u.\r\n", (unsigned int) node); + + /* Try to send frame */ + if (!csp_send(conn, packet, 0)) + csp_buffer_free(packet); + + csp_close(conn); + +} + +void csp_reboot(uint8_t node) { + uint32_t magic_word = csp_hton32(CSP_REBOOT_MAGIC); + csp_transaction(CSP_PRIO_NORM, node, CSP_REBOOT, 0, &magic_word, sizeof(magic_word), NULL, 0); +} + +void csp_shutdown(uint8_t node) { + uint32_t magic_word = csp_hton32(CSP_REBOOT_SHUTDOWN_MAGIC); + csp_transaction(CSP_PRIO_NORM, node, CSP_REBOOT, 0, &magic_word, sizeof(magic_word), NULL, 0); +} + +void csp_ps(uint8_t node, uint32_t timeout) { + + /* Open connection */ + csp_conn_t * conn = csp_connect(CSP_PRIO_NORM, node, CSP_PS, 0, 0); + if (conn == NULL) + return; + + /* Prepare data */ + csp_packet_t * packet; + packet = csp_buffer_get(95); + + /* Check malloc */ + if (packet == NULL) + goto out; + + packet->data[0] = 0x55; + packet->length = 1; + + printf("PS node %u: \r\n", (unsigned int) node); + + /* Try to send frame */ + if (!csp_send(conn, packet, 0)) + goto out; + + while(1) { + + /* Read incoming frame */ + packet = csp_read(conn, timeout); + if (packet == NULL) + break; + + /* We have a reply, add our own NULL char */ + packet->data[packet->length] = 0; + printf("%s", packet->data); + + /* Each packet from csp_read must to be freed by user */ + csp_buffer_free(packet); + } + + printf("\r\n"); + + /* Clean up */ +out: + if (packet != NULL) + csp_buffer_free(packet); + csp_close(conn); + +} + +void csp_memfree(uint8_t node, uint32_t timeout) { + + uint32_t memfree; + + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_MEMFREE, timeout, NULL, 0, &memfree, sizeof(memfree)); + if (status == 0) { + printf("Network error\r\n"); + return; + } + + /* Convert from network to host order */ + memfree = csp_ntoh32(memfree); + + printf("Free Memory at node %u is %"PRIu32" bytes\r\n", (unsigned int) node, memfree); + +} + +void csp_buf_free(uint8_t node, uint32_t timeout) { + + uint32_t size = 0; + + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_BUF_FREE, timeout, NULL, 0, &size, sizeof(size)); + if (status == 0) { + printf("Network error\r\n"); + return; + } + size = csp_ntoh32(size); + printf("Free buffers at node %u is %"PRIu32"\r\n", (unsigned int) node, size); + +} + +void csp_uptime(uint8_t node, uint32_t timeout) { + + uint32_t uptime = 0; + + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_UPTIME, timeout, NULL, 0, &uptime, sizeof(uptime)); + if (status == 0) { + printf("Network error\r\n"); + return; + } + uptime = csp_ntoh32(uptime); + printf("Uptime of node %u is %"PRIu32" s\r\n", (unsigned int) node, uptime); + +} + +int csp_cmp(uint8_t node, uint32_t timeout, uint8_t code, int membsize, struct csp_cmp_message * msg) { + msg->type = CSP_CMP_REQUEST; + msg->code = code; + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_CMP, timeout, msg, membsize, msg, membsize); + if (status == 0) + return CSP_ERR_TIMEDOUT; + + return CSP_ERR_NONE; +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_sfp.c b/gomspace/libgscsp/lib/libcsp/src/csp_sfp.c new file mode 100644 index 00000000..96ef36e1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_sfp.c @@ -0,0 +1,170 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include "csp_conn.h" + +typedef struct __attribute__((__packed__)) { + uint32_t offset; + uint32_t totalsize; +} sfp_header_t; + +/** + * SFP Headers: + * The following functions are helper functions that handles the extra SFP + * information that needs to be appended to all data packets. + */ +static sfp_header_t * csp_sfp_header_add(csp_packet_t * packet) { + sfp_header_t * header = (sfp_header_t *) &packet->data[packet->length]; + packet->length += sizeof(sfp_header_t); + memset(header, 0, sizeof(sfp_header_t)); + return header; +} + +static sfp_header_t * csp_sfp_header_remove(csp_packet_t * packet) { + sfp_header_t * header = (sfp_header_t *) &packet->data[packet->length-sizeof(sfp_header_t)]; + packet->length -= sizeof(sfp_header_t); + return header; +} + +int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout, void * (*memcpyfcn)(void *, const void *, size_t)) { + + int count = 0; + while(count < totalsize) { + + /* Allocate packet */ + csp_packet_t * packet = csp_buffer_get(mtu); + if (packet == NULL) + return -1; + + /* Calculate sending size */ + int size = totalsize - count; + if (size > mtu) + size = mtu; + + /* Print debug */ + csp_debug(CSP_PROTOCOL, "Sending SFP at %x size %u", data + count, size); + + /* Copy data */ + (*memcpyfcn)(packet->data, data + count, size); + packet->length = size; + + /* Set fragment flag */ + conn->idout.flags |= CSP_FFRAG; + + /* Add SFP header */ + sfp_header_t * sfp_header = csp_sfp_header_add(packet); + sfp_header->totalsize = csp_hton32(totalsize); + sfp_header->offset = csp_hton32(count); + + /* Send data */ + if (!csp_send(conn, packet, timeout)) { + csp_buffer_free(packet); + return -1; + } + + /* Increment count */ + count += size; + + } + + return 0; + +} + +int csp_sfp_send(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout) { + return csp_sfp_send_own_memcpy(conn, data, totalsize, mtu, timeout, &memcpy); +} + +int csp_sfp_recv_fp(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout, csp_packet_t * first_packet) { + + unsigned int last_byte = 0; + + *dataout = NULL; /* Allow caller to assume csp_free() can always be called when dataout is non-NULL */ + + /* Get first packet from user, or from connection */ + csp_packet_t * packet = NULL; + if (first_packet == NULL) { + packet = csp_read(conn, timeout); + if (packet == NULL) + return -1; + } else { + packet = first_packet; + } + + do { + + /* Check that SFP header is present */ + if ((packet->id.flags & CSP_FFRAG) == 0) { + csp_debug(CSP_ERROR, "Missing SFP header"); + csp_buffer_free(packet); + return -1; + } + + /* Read SFP header */ + sfp_header_t * sfp_header = csp_sfp_header_remove(packet); + sfp_header->offset = csp_ntoh32(sfp_header->offset); + sfp_header->totalsize = csp_ntoh32(sfp_header->totalsize); + + csp_debug(CSP_PROTOCOL, "SFP fragment %u/%u", sfp_header->offset + packet->length, sfp_header->totalsize); + + if (sfp_header->offset > last_byte + 1) { + csp_debug(CSP_ERROR, "SFP missing %u bytes", sfp_header->offset - last_byte); + csp_buffer_free(packet); + return -1; + } else { + last_byte = sfp_header->offset + packet->length; + } + + /* Allocate memory */ + if (*dataout == NULL) + *dataout = csp_malloc(sfp_header->totalsize); + if (*dataout == NULL) { + csp_debug(CSP_ERROR, "No dyn-memory for SFP fragment"); + csp_buffer_free(packet); + return -1; + } + + /* Copy data to output */ + *datasize = sfp_header->totalsize; + memcpy(*dataout + sfp_header->offset, packet->data, packet->length); + + if (sfp_header->offset + packet->length >= sfp_header->totalsize) { + csp_debug(CSP_PROTOCOL, "SFP complete"); + csp_buffer_free(packet); + return 0; + } else { + csp_buffer_free(packet); + } + + } while((packet = csp_read(conn, timeout)) != NULL); + + return -1; + +} + +int csp_sfp_recv(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout) { + return csp_sfp_recv_fp(conn, dataout, datasize, timeout, NULL); +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/drivers/can/can_socketcan.c b/gomspace/libgscsp/lib/libcsp/src/drivers/can/can_socketcan.c new file mode 100644 index 00000000..7d12f184 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/drivers/can/can_socketcan.c @@ -0,0 +1,201 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* SocketCAN driver */ +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef CSP_HAVE_LIBSOCKETCAN +#include +#endif + +static struct can_socketcan_s { + int socket; + csp_iface_t interface; +} socketcan[1] = { + { + .interface = { + .name = "CAN", + .nexthop = csp_can_tx, + .mtu = CSP_CAN_MTU, + .driver = &socketcan[0], + }, + }, +}; + +static void * socketcan_rx_thread(void * parameters) +{ + struct can_frame frame; + int nbytes; + + while (1) { + /* Read CAN frame */ + nbytes = read(socketcan[0].socket, &frame, sizeof(frame)); + if (nbytes < 0) { + csp_log_error("read: %s", strerror(errno)); + continue; + } + + if (nbytes != sizeof(frame)) { + csp_log_warn("Read incomplete CAN frame"); + continue; + } + + /* Frame type */ + if (frame.can_id & (CAN_ERR_FLAG | CAN_RTR_FLAG) || !(frame.can_id & CAN_EFF_FLAG)) { + /* Drop error and remote frames */ + csp_log_warn("Discarding ERR/RTR/SFF frame"); + continue; + } + + /* Strip flags */ + frame.can_id &= CAN_EFF_MASK; + + /* Call RX callbacsp_can_rx_frameck */ + csp_can_rx(&socketcan[0].interface, frame.can_id, frame.data, frame.can_dlc, NULL); + } + + /* We should never reach this point */ + pthread_exit(NULL); +} + + +int csp_can_tx_frame(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc) +{ + struct can_frame frame; + int i, tries = 0; + memset(&frame, 0, sizeof(frame)); + if (dlc > 8) + return -1; + + /* Copy identifier */ + frame.can_id = id | CAN_EFF_FLAG; + + /* Copy data to frame */ + for (i = 0; i < dlc; i++) + frame.data[i] = data[i]; + + /* Set DLC */ + frame.can_dlc = dlc; + + /* Send frame */ + while (write(socketcan[0].socket, &frame, sizeof(frame)) != sizeof(frame)) { + if (++tries < 1000 && errno == ENOBUFS) { + /* Wait 10 ms and try again */ + usleep(10000); + } else { + csp_log_error("write: %s", strerror(errno)); + break; + } + } + + return 0; +} + +csp_iface_t * csp_can_socketcan_init(const char * ifc, int bitrate, int promisc) +{ + struct ifreq ifr; + struct sockaddr_can addr; + pthread_t rx_thread; + + printf("Init can interface %s\n", ifc); + +#ifdef CSP_HAVE_LIBSOCKETCAN + /* Set interface up */ + if (bitrate > 0) { + can_do_stop(ifc); + can_set_bitrate(ifc, bitrate); + can_set_restart_ms(ifc, 100); + can_do_start(ifc); + } +#endif + + /* Create socket */ + if ((socketcan[0].socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + csp_log_error("socket: %s", strerror(errno)); + return NULL; + } + + /* Locate interface */ + strncpy(ifr.ifr_name, ifc, IFNAMSIZ - 1); + if (ioctl(socketcan[0].socket, SIOCGIFINDEX, &ifr) < 0) { + csp_log_error("ioctl: %s", strerror(errno)); + return NULL; + } + memset(&addr, 0, sizeof(addr)); + /* Bind the socket to CAN interface */ + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + if (bind(socketcan[0].socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + csp_log_error("bind: %s", strerror(errno)); + return NULL; + } + + /* Set filter mode */ + if (promisc == 0) { + + struct can_filter filter; + filter.can_id = CFP_MAKE_DST(csp_get_address()); + filter.can_mask = CFP_MAKE_DST((1 << CFP_HOST_SIZE) - 1); + + if (setsockopt(socketcan[0].socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)) < 0) { + csp_log_error("setsockopt: %s", strerror(errno)); + return NULL; + } + + } + + /* Create receive thread */ + if (pthread_create(&rx_thread, NULL, socketcan_rx_thread, NULL) != 0) { + csp_log_error("pthread_create: %s", strerror(errno)); + return NULL; + } + + csp_iflist_add(&socketcan[0].interface); + + return &socketcan[0].interface; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_linux.c b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_linux.c new file mode 100644 index 00000000..c4ceeb27 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_linux.c @@ -0,0 +1,254 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int fd; +usart_callback_t usart_callback = NULL; + +static void *serial_rx_thread(void *vptr_args); + +int getbaud(int ifd) { + struct termios termAttr; + int inputSpeed = -1; + speed_t baudRate; + tcgetattr(ifd, &termAttr); + /* Get the input speed. */ + baudRate = cfgetispeed(&termAttr); + switch (baudRate) { + case B0: + inputSpeed = 0; + break; + case B50: + inputSpeed = 50; + break; + case B110: + inputSpeed = 110; + break; + case B134: + inputSpeed = 134; + break; + case B150: + inputSpeed = 150; + break; + case B200: + inputSpeed = 200; + break; + case B300: + inputSpeed = 300; + break; + case B600: + inputSpeed = 600; + break; + case B1200: + inputSpeed = 1200; + break; + case B1800: + inputSpeed = 1800; + break; + case B2400: + inputSpeed = 2400; + break; + case B4800: + inputSpeed = 4800; + break; + case B9600: + inputSpeed = 9600; + break; + case B19200: + inputSpeed = 19200; + break; + case B38400: + inputSpeed = 38400; + break; + case B57600: + inputSpeed = 57600; + break; + case B115200: + inputSpeed = 115200; + break; + case B230400: + inputSpeed = 230400; + break; +#ifndef CSP_MACOSX + case B460800: + inputSpeed = 460800; + break; + case B500000: + inputSpeed = 500000; + break; + case B576000: + inputSpeed = 576000; + break; + case B921600: + inputSpeed = 921600; + break; + case B1000000: + inputSpeed = 1000000; + break; + case B1152000: + inputSpeed = 1152000; + break; + case B1500000: + inputSpeed = 1500000; + break; + case B2000000: + inputSpeed = 2000000; + break; + case B2500000: + inputSpeed = 2500000; + break; + case B3000000: + inputSpeed = 3000000; + break; + case B3500000: + inputSpeed = 3500000; + break; + case B4000000: + inputSpeed = 4000000; + break; +#endif + } + + return inputSpeed; + +} + +void usart_init(struct usart_conf * conf) { + + struct termios options; + pthread_t rx_thread; + + fd = open(conf->device, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fd < 0) { + printf("Failed to open %s: %s\r\n", conf->device, strerror(errno)); + return; + } + + int brate = 0; + switch(conf->baudrate) { + case 4800: brate=B4800; break; + case 9600: brate=B9600; break; + case 19200: brate=B19200; break; + case 38400: brate=B38400; break; + case 57600: brate=B57600; break; + case 115200: brate=B115200; break; + case 230400: brate=B230400; break; +#ifndef CSP_MACOSX + case 460800: brate=B460800; break; + case 500000: brate=B500000; break; + case 576000: brate=B576000; break; + case 921600: brate=B921600; break; + case 1000000: brate=B1000000; break; + case 1152000: brate=B1152000; break; + case 1500000: brate=B1500000; break; + case 2000000: brate=B2000000; break; + case 2500000: brate=B2500000; break; + case 3000000: brate=B3000000; break; + case 3500000: brate=B3500000; break; + case 4000000: brate=B4000000; break; +#endif + default: + printf("Unsupported baudrate requested, defaulting to 500000, requested baudrate=%u\n", conf->baudrate); + brate=B500000; + break; + } + + tcgetattr(fd, &options); + cfsetispeed(&options, brate); + cfsetospeed(&options, brate); + options.c_cflag |= (CLOCAL | CREAD); + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; + options.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); + options.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); + options.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST); + options.c_cc[VTIME] = 0; + options.c_cc[VMIN] = 1; + tcsetattr(fd, TCSANOW, &options); + if (tcgetattr(fd, &options) == -1) + perror("error setting options"); + fcntl(fd, F_SETFL, 0); + + /* Flush old transmissions */ + if (tcflush(fd, TCIOFLUSH) == -1) + printf("Error flushing serial port - %s(%d).\n", strerror(errno), errno); + + if (pthread_create(&rx_thread, NULL, serial_rx_thread, NULL) != 0) + return; + +} + +void usart_set_callback(usart_callback_t callback) { + usart_callback = callback; +} + +void usart_insert(char c, void * pxTaskWoken) { + printf("%c", c); +} + +void usart_putstr(char * buf, int len) { + if (write(fd, buf, len) != len) + return; +} + +void usart_putc(char c) { + if (write(fd, &c, 1) != 1) + return; +} + +char usart_getc(void) { + char c; + if (read(fd, &c, 1) != 1) return 0; + return c; +} + +static void *serial_rx_thread(void *vptr_args) { + unsigned int length; + uint8_t * cbuf = malloc(100000); + + // Receive loop + while (1) { + length = read(fd, cbuf, 300); + if (length <= 0) { + perror("Error: "); + exit(1); + } + if (usart_callback) + usart_callback(cbuf, length, NULL); + } + return NULL; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_windows.c b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_windows.c new file mode 100644 index 00000000..91ffe87d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_windows.c @@ -0,0 +1,230 @@ +#include +#include +#include + +#include +#include + +static HANDLE portHandle = INVALID_HANDLE_VALUE; +static HANDLE rxThread = INVALID_HANDLE_VALUE; +static CRITICAL_SECTION txSection; +static LONG isListening = 0; +static usart_callback_t usart_callback = NULL; + +static void prvSendData(char *buf, int bufsz); +static int prvTryOpenPort(const char* intf); +static int prvTryConfigurePort(const struct usart_conf*); +static int prvTrySetPortTimeouts(void); +static const char* prvParityToStr(BYTE paritySetting); + +#ifdef CSP_DEBUG +static void prvPrintError(void) { + char *messageBuffer = NULL; + DWORD errorCode = GetLastError(); + DWORD formatMessageRet; + formatMessageRet = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&messageBuffer, + 0, + NULL); + + if( !formatMessageRet ) { + csp_log_error("FormatMessage error, code: %lu", GetLastError()); + return; + } + csp_log_error("%s", messageBuffer); + LocalFree(messageBuffer); +} +#endif + +#ifdef CSP_DEBUG +#define printError() prvPrintError() +#else +#define printError() do {} while(0) +#endif + +static int prvTryOpenPort(const char *intf) { + portHandle = CreateFileA( + intf, + GENERIC_READ|GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if( portHandle == INVALID_HANDLE_VALUE ) { + DWORD errorCode = GetLastError(); + if( errorCode == ERROR_FILE_NOT_FOUND ) { + csp_log_error("Could not open serial port, because it didn't exist!"); + } + else + csp_log_error("Failure opening serial port! Code: %lu", errorCode); + return 1; + } + return 0; +} + +static int prvTryConfigurePort(const struct usart_conf * conf) { + DCB portSettings = {0}; + portSettings.DCBlength = sizeof(DCB); + if(!GetCommState(portHandle, &portSettings) ) { + csp_log_error("Could not get default settings for open COM port! Code: %lu", GetLastError()); + return -1; + } + portSettings.BaudRate = conf->baudrate; + portSettings.Parity = conf->paritysetting; + portSettings.StopBits = conf->stopbits; + portSettings.fParity = conf->checkparity; + portSettings.fBinary = TRUE; + portSettings.ByteSize = conf->databits; + if( !SetCommState(portHandle, &portSettings) ) { + csp_log_error("Error when setting COM port settings! Code:%lu", GetLastError()); + return 1; + } + + GetCommState(portHandle, &portSettings); + + csp_log_info("Port: %s, Baudrate: %lu, Data bits: %d, Stop bits: %d, Parity: %s", + conf->device, conf->baudrate, conf->databits, conf->stopbits, prvParityToStr(conf->paritysetting)); + return 0; +} + +static const char* prvParityToStr(BYTE paritySetting) { + static const char *parityStr[] = { + "None", + "Odd", + "Even", + "N/A" + }; + char const *resultStr = NULL; + + switch(paritySetting) { + case NOPARITY: + resultStr = parityStr[0]; + break; + case ODDPARITY: + resultStr = parityStr[1]; + break; + case EVENPARITY: + resultStr = parityStr[2]; + break; + default: + resultStr = parityStr[3]; + }; + return resultStr; +} + +static int prvTrySetPortTimeouts(void) { + COMMTIMEOUTS timeouts = {0}; + + if( !GetCommTimeouts(portHandle, &timeouts) ) { + csp_log_error("Error gettings current timeout settings"); + return 1; + } + + timeouts.ReadIntervalTimeout = 5; + timeouts.ReadTotalTimeoutMultiplier = 1; + timeouts.ReadTotalTimeoutConstant = 5; + timeouts.WriteTotalTimeoutMultiplier = 1; + timeouts.WriteTotalTimeoutConstant = 5; + + if(!SetCommTimeouts(portHandle, &timeouts)) { + csp_log_error("Error setting timeouts!"); + return 1; + } + + return 0; +} + +unsigned WINAPI prvRxTask(void* params) { + DWORD bytesRead; + DWORD eventStatus; + uint8_t recvBuffer[24]; + SetCommMask(portHandle, EV_RXCHAR); + + while(isListening) { + WaitCommEvent(portHandle, &eventStatus, NULL); + if( !(eventStatus & EV_RXCHAR) ) { + continue; + } + if( !ReadFile(portHandle, recvBuffer, 24, &bytesRead, NULL)) { + csp_log_warn("Error receiving data! Code: %lu", GetLastError()); + continue; + } + if( usart_callback != NULL ) + usart_callback(recvBuffer, (size_t)bytesRead, NULL); + } + return 0; +} + +static void prvSendData(char *buf, int bufsz) { + DWORD bytesTotal = 0; + DWORD bytesActual; + if( !WriteFile(portHandle, buf, bufsz-bytesTotal, &bytesActual, NULL) ) { + csp_log_error("Could not write data. Code: %lu", GetLastError()); + return; + } + if( !FlushFileBuffers(portHandle) ) { + csp_log_warn("Could not flush write buffer. Code: %lu", GetLastError()); + } +} + +void usart_shutdown(void) { + InterlockedExchange(&isListening, 0); + CloseHandle(portHandle); + portHandle = INVALID_HANDLE_VALUE; + if( rxThread != INVALID_HANDLE_VALUE ) { + WaitForSingleObject(rxThread, INFINITE); + rxThread = INVALID_HANDLE_VALUE; + } + DeleteCriticalSection(&txSection); +} + +void usart_listen(void) { + InterlockedExchange(&isListening, 1); + rxThread = (HANDLE)_beginthreadex(NULL, 0, &prvRxTask, NULL, 0, NULL); +} + +void usart_putstr(char* buf, int bufsz) { + EnterCriticalSection(&txSection); + prvSendData(buf, bufsz); + LeaveCriticalSection(&txSection); +} + +void usart_insert(char c, void *pxTaskWoken) { + /* redirect debug output to stdout */ + printf("%c", c); +} + +void usart_set_callback(usart_callback_t callback) { + usart_callback = callback; +} + +void usart_init(struct usart_conf * conf) { + if( prvTryOpenPort(conf->device) ) { + printError(); + return; + } + + if( prvTryConfigurePort(conf) ) { + printError(); + return; + } + + if( prvTrySetPortTimeouts() ) { + printError(); + return; + } + + InitializeCriticalSection(&txSection); + + /* Start receiver thread */ + usart_listen(); +} + + diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can.c new file mode 100644 index 00000000..5add8334 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can.c @@ -0,0 +1,279 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* CAN frames contains at most 8 bytes of data, so in order to transmit CSP + * packets larger than this, a fragmentation protocol is required. The CAN + * Fragmentation Protocol (CFP) header is designed to match the 29 bit CAN + * identifier. + * + * The CAN identifier is divided in these fields: + * src: 5 bits + * dst: 5 bits + * type: 1 bit + * remain: 8 bits + * identifier: 10 bits + * + * Source and Destination addresses must match the CSP packet. The type field + * is used to distinguish the first and subsequent frames in a fragmented CSP + * packet. Type is BEGIN (0) for the first fragment and MORE (1) for all other + * fragments. Remain indicates number of remaining fragments, and must be + * decremented by one for each fragment sent. The identifier field serves the + * same purpose as in the Internet Protocol, and should be an auto incrementing + * integer to uniquely separate sessions. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "csp_if_can_pbuf.h" + +/* CFP Frame Types */ +enum cfp_frame_t { + CFP_BEGIN = 0, + CFP_MORE = 1 +}; + +int csp_can_rx(csp_iface_t *interface, uint32_t id, const uint8_t *data, uint8_t dlc, CSP_BASE_TYPE *task_woken) +{ + csp_can_pbuf_element_t *buf; + uint8_t offset; + + /* Random packet loss */ +#if 0 + int random = rand(); + if (random < RAND_MAX * 0.00005) { + csp_log_warn("Dropping frame"); + return; + } +#endif + + /* Bind incoming frame to a packet buffer */ + buf = csp_can_pbuf_find(id, CFP_ID_CONN_MASK); + + /* Check returned buffer */ + if (buf == NULL) { + if (CFP_TYPE(id) == CFP_BEGIN) { + buf = csp_can_pbuf_new(id, task_woken); + if (buf == NULL) { + //csp_log_warn("No available packet buffer for CAN"); + interface->rx_error++; + return CSP_ERR_NOMEM; + } + } else { + //csp_log_warn("Out of order id 0x%X remain %u", CFP_ID(id), CFP_REMAIN(id)); + interface->frame++; + return CSP_ERR_INVAL; + } + } + + /* Reset frame data offset */ + offset = 0; + + switch (CFP_TYPE(id)) { + + case CFP_BEGIN: + + /* Discard packet if DLC is less than CSP id + CSP length fields */ + if (dlc < sizeof(csp_id_t) + sizeof(uint16_t)) { + //csp_log_warn("Short BEGIN frame received"); + interface->frame++; + csp_can_pbuf_free(buf, task_woken); + break; + } + + /* Check for incomplete frame */ + if (buf->packet != NULL) { + /* Reuse the buffer */ + //csp_log_warn("Incomplete frame"); + interface->frame++; + } else { + /* Allocate memory for frame */ + if (task_woken == NULL) { + buf->packet = csp_buffer_get(interface->mtu); + } else { + buf->packet = csp_buffer_get_isr(interface->mtu); + } + if (buf->packet == NULL) { + //csp_log_error("Failed to get buffer for CSP_BEGIN packet"); + interface->frame++; + csp_can_pbuf_free(buf, task_woken); + break; + } + } + + /* Copy CSP identifier and length*/ + memcpy(&(buf->packet->id), data, sizeof(csp_id_t)); + buf->packet->id.ext = csp_ntoh32(buf->packet->id.ext); + memcpy(&(buf->packet->length), data + sizeof(csp_id_t), sizeof(uint16_t)); + buf->packet->length = csp_ntoh16(buf->packet->length); + + /* Reset RX count */ + buf->rx_count = 0; + + /* Set offset to prevent CSP header from being copied to CSP data */ + offset = sizeof(csp_id_t) + sizeof(uint16_t); + + /* Set remain field - increment to include begin packet */ + buf->remain = CFP_REMAIN(id) + 1; + + /* FALLTHROUGH */ + + case CFP_MORE: + + /* Check 'remain' field match */ + if (CFP_REMAIN(id) != buf->remain - 1) { + //csp_log_error("CAN frame lost in CSP packet"); + csp_can_pbuf_free(buf, task_woken); + interface->frame++; + break; + } + + /* Decrement remaining frames */ + buf->remain--; + + /* Check for overflow */ + if ((buf->rx_count + dlc - offset) > buf->packet->length) { + //csp_log_error("RX buffer overflow"); + interface->frame++; + csp_can_pbuf_free(buf, task_woken); + break; + } + + /* Copy dlc bytes into buffer */ + memcpy(&buf->packet->data[buf->rx_count], data + offset, dlc - offset); + buf->rx_count += dlc - offset; + + /* Check if more data is expected */ + if (buf->rx_count != buf->packet->length) + break; + + /* Data is available */ + csp_qfifo_write(buf->packet, interface, task_woken); + + /* Drop packet buffer reference */ + buf->packet = NULL; + + /* Free packet buffer */ + csp_can_pbuf_free(buf, task_woken); + + break; + + default: + //csp_log_warn("Received unknown CFP message type"); + csp_can_pbuf_free(buf, task_woken); + break; + + } + + return CSP_ERR_NONE; +} + +int csp_can_tx(csp_iface_t *interface, csp_packet_t *packet, uint32_t timeout) +{ + + /* CFP Identification number */ + static volatile int csp_can_frame_id = 0; + + /* Get local copy of the static frameid */ + int ident = csp_can_frame_id++; + + uint16_t tx_count; + uint8_t bytes, overhead, avail, dest; + uint8_t frame_buf[8]; + + /* Calculate overhead */ + overhead = sizeof(csp_id_t) + sizeof(uint16_t); + + /* Insert destination node mac address into the CFP destination field */ + dest = csp_rtable_find_mac(packet->id.dst); + if (dest == CSP_NODE_MAC) + dest = packet->id.dst; + + /* Create CAN identifier */ + uint32_t id = 0; + id |= CFP_MAKE_SRC(packet->id.src); + id |= CFP_MAKE_DST(dest); + id |= CFP_MAKE_ID(ident); + id |= CFP_MAKE_TYPE(CFP_BEGIN); + id |= CFP_MAKE_REMAIN((packet->length + overhead - 1) / 8); + + /* Calculate first frame data bytes */ + avail = 8 - overhead; + bytes = (packet->length <= avail) ? packet->length : avail; + + /* Copy CSP headers and data */ + uint32_t csp_id_be = csp_hton32(packet->id.ext); + uint16_t csp_length_be = csp_hton16(packet->length); + + memcpy(frame_buf, &csp_id_be, sizeof(csp_id_be)); + memcpy(frame_buf + sizeof(csp_id_be), &csp_length_be, sizeof(csp_length_be)); + memcpy(frame_buf + overhead, packet->data, bytes); + + /* Increment tx counter */ + tx_count = bytes; + + /* Send first frame */ + if (csp_can_tx_frame(interface, id, frame_buf, overhead + bytes)) { + //csp_log_warn("Failed to send CAN frame in csp_tx_can"); + interface->tx_error++; + return CSP_ERR_DRIVER; + } + + /* Send next frames if not complete */ + while (tx_count < packet->length) { + /* Calculate frame data bytes */ + bytes = (packet->length - tx_count >= 8) ? 8 : packet->length - tx_count; + + /* Prepare identifier */ + id = 0; + id |= CFP_MAKE_SRC(packet->id.src); + id |= CFP_MAKE_DST(dest); + id |= CFP_MAKE_ID(ident); + id |= CFP_MAKE_TYPE(CFP_MORE); + id |= CFP_MAKE_REMAIN((packet->length - tx_count - bytes + 7) / 8); + + /* Increment tx counter */ + tx_count += bytes; + + /* Send frame */ + if (csp_can_tx_frame(interface, id, packet->data + tx_count - bytes, bytes)) { + //csp_log_warn("Failed to send CAN frame in Tx callback"); + interface->tx_error++; + return CSP_ERR_DRIVER; + } + } + + csp_buffer_free(packet); + + return CSP_ERR_NONE; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.c new file mode 100644 index 00000000..65f18de9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.c @@ -0,0 +1,77 @@ +/* + * csp_if_can_pbuf.c + * + * Created on: Feb 3, 2017 + * Author: johan + */ + +#include +#include "csp_if_can_pbuf.h" + +/* Number of packet buffer elements */ +#define PBUF_ELEMENTS CSP_CONN_MAX + +/* Buffer element timeout in ms */ +#define PBUF_TIMEOUT_MS 1000 + +static csp_can_pbuf_element_t csp_can_pbuf[PBUF_ELEMENTS] = {}; + +int csp_can_pbuf_free(csp_can_pbuf_element_t *buf, CSP_BASE_TYPE *task_woken) +{ + /* Free CSP packet */ + if (buf->packet != NULL) { + if (task_woken == NULL) { + csp_buffer_free(buf->packet); + } else { + csp_buffer_free_isr(buf->packet); + } + } + + /* Mark buffer element free */ + buf->packet = NULL; + buf->rx_count = 0; + buf->cfpid = 0; + buf->last_used = 0; + buf->remain = 0; + buf->state = BUF_FREE; + + return CSP_ERR_NONE; +} + +csp_can_pbuf_element_t *csp_can_pbuf_new(uint32_t id, CSP_BASE_TYPE *task_woken) +{ + uint32_t now = csp_get_ms(); + + for (int i = 0; i < PBUF_ELEMENTS; i++) { + + /* Perform cleanup in used pbufs */ + if (csp_can_pbuf[i].state == BUF_USED) { + if (now - csp_can_pbuf[i].last_used > PBUF_TIMEOUT_MS) + csp_can_pbuf_free(&csp_can_pbuf[i], task_woken); + } + + if (csp_can_pbuf[i].state == BUF_FREE) { + csp_can_pbuf[i].state = BUF_USED; + csp_can_pbuf[i].cfpid = id; + csp_can_pbuf[i].remain = 0; + csp_can_pbuf[i].last_used = now; + return &csp_can_pbuf[i]; + } + + } + + return NULL; + +} + +csp_can_pbuf_element_t *csp_can_pbuf_find(uint32_t id, uint32_t mask) +{ + for (int i = 0; i < PBUF_ELEMENTS; i++) { + if ((csp_can_pbuf[i].state == BUF_USED) && ((csp_can_pbuf[i].cfpid & mask) == (id & mask))) { + csp_can_pbuf[i].last_used = csp_get_ms(); + return &csp_can_pbuf[i]; + } + } + return NULL; +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.h b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.h new file mode 100644 index 00000000..3e71c26c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.h @@ -0,0 +1,31 @@ +/* + * csp_if_can_pbuf.h + * + * Created on: Feb 3, 2017 + * Author: johan + */ + +#ifndef LIB_CSP_SRC_INTERFACES_CSP_IF_CAN_PBUF_H_ +#define LIB_CSP_SRC_INTERFACES_CSP_IF_CAN_PBUF_H_ + +/* Packet buffers */ +typedef enum { + BUF_FREE = 0, /* Buffer element free */ + BUF_USED = 1, /* Buffer element used */ +} csp_can_pbuf_state_t; + +typedef struct { + uint16_t rx_count; /* Received bytes */ + uint32_t remain; /* Remaining packets */ + uint32_t cfpid; /* Connection CFP identification number */ + csp_packet_t *packet; /* Pointer to packet buffer */ + csp_can_pbuf_state_t state; /* Element state */ + uint32_t last_used; /* Timestamp in ms for last use of buffer */ +} csp_can_pbuf_element_t; + +int csp_can_pbuf_free(csp_can_pbuf_element_t *buf, CSP_BASE_TYPE *task_woken); +csp_can_pbuf_element_t *csp_can_pbuf_new(uint32_t id, CSP_BASE_TYPE *task_woken); +csp_can_pbuf_element_t *csp_can_pbuf_find(uint32_t id, uint32_t mask); +void csp_can_pbuf_cleanup(CSP_BASE_TYPE *task_woken); + +#endif /* LIB_CSP_SRC_INTERFACES_CSP_IF_CAN_PBUF_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_i2c.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_i2c.c new file mode 100644 index 00000000..c5d105df --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_i2c.c @@ -0,0 +1,116 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +static int csp_i2c_handle = 0; + +int csp_i2c_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + /* Cast the CSP packet buffer into an i2c frame */ + i2c_frame_t * frame = (i2c_frame_t *) packet; + + /* Insert destination node into the i2c destination field */ + if (csp_rtable_find_mac(packet->id.dst) == CSP_NODE_MAC) { + frame->dest = packet->id.dst; + } else { + frame->dest = csp_rtable_find_mac(packet->id.dst); + } + + /* Save the outgoing id in the buffer */ + packet->id.ext = csp_hton32(packet->id.ext); + + /* Add the CSP header to the I2C length field */ + frame->len += sizeof(packet->id); + frame->len_rx = 0; + + /* Some I2C drivers support X number of retries + * CSP don't care about this. If it doesn't work the first + * time, don'y use time on it. + */ + frame->retries = 0; + + /* enqueue the frame */ + if (i2c_send(csp_i2c_handle, frame, timeout) != E_NO_ERR) + return CSP_ERR_DRIVER; + + return CSP_ERR_NONE; + +} + +/** + * When a frame is received, cast it to a csp_packet + * and send it directly to the CSP new packet function. + * Context: ISR only + * @param frame + */ +void csp_i2c_rx(i2c_frame_t * frame, void * pxTaskWoken) { + + static csp_packet_t * packet; + + /* Validate input */ + if (frame == NULL) + return; + + if ((frame->len < 4) || (frame->len > I2C_MTU)) { + csp_if_i2c.frame++; + csp_buffer_free_isr(frame); + return; + } + + /* Strip the CSP header off the length field before converting to CSP packet */ + frame->len -= sizeof(csp_id_t); + + /* Convert the packet from network to host order */ + packet = (csp_packet_t *) frame; + packet->id.ext = csp_ntoh32(packet->id.ext); + + /* Receive the packet in CSP */ + csp_qfifo_write(packet, &csp_if_i2c, pxTaskWoken); + +} + +int csp_i2c_init(uint8_t addr, int handle, int speed) { + + /* Create i2c_handle */ + csp_i2c_handle = handle; + if (i2c_init(csp_i2c_handle, I2C_MASTER, addr, speed, 10, 10, csp_i2c_rx) != E_NO_ERR) + return CSP_ERR_DRIVER; + + /* Register interface */ + csp_iflist_add(&csp_if_i2c); + + return CSP_ERR_NONE; + +} + +/** Interface definition */ +csp_iface_t csp_if_i2c = { + .name = "I2C", + .nexthop = csp_i2c_tx, +}; diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_kiss.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_kiss.c new file mode 100644 index 00000000..fe5707f6 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_kiss.c @@ -0,0 +1,260 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define KISS_MTU 256 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define TNC_DATA 0x00 +#define TNC_SET_HARDWARE 0x06 +#define TNC_RETURN 0xFF + +static int kiss_lock_init = 0; +static csp_bin_sem_handle_t kiss_lock; + +/* Send a CSP packet over the KISS RS232 protocol */ +static int csp_kiss_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + if (interface == NULL || interface->driver == NULL) + return CSP_ERR_DRIVER; + + /* Add CRC32 checksum */ + csp_crc32_append(packet, false); + + /* Save the outgoing id in the buffer */ + packet->id.ext = csp_hton32(packet->id.ext); + packet->length += sizeof(packet->id.ext); + + /* Lock */ + csp_bin_sem_wait(&kiss_lock, 1000); + + /* Transmit data */ + csp_kiss_handle_t * driver = interface->driver; + driver->kiss_putc(FEND); + driver->kiss_putc(TNC_DATA); + for (unsigned int i = 0; i < packet->length; i++) { + if (((unsigned char *) &packet->id.ext)[i] == FEND) { + ((unsigned char *) &packet->id.ext)[i] = TFEND; + driver->kiss_putc(FESC); + } else if (((unsigned char *) &packet->id.ext)[i] == FESC) { + ((unsigned char *) &packet->id.ext)[i] = TFESC; + driver->kiss_putc(FESC); + } + driver->kiss_putc(((unsigned char *) &packet->id.ext)[i]); + } + driver->kiss_putc(FEND); + + /* Free data */ + csp_buffer_free(packet); + + /* Unlock */ + csp_bin_sem_post(&kiss_lock); + + return CSP_ERR_NONE; +} + +/** + * When a frame is received, decode the kiss-stuff + * and eventually send it directly to the CSP new packet function. + */ +void csp_kiss_rx(csp_iface_t * interface, uint8_t * buf, int len, void * pxTaskWoken) { + + /* Driver handle */ + csp_kiss_handle_t * driver = interface->driver; + + while (len--) { + + /* Input */ + unsigned char inputbyte = *buf++; + + /* If packet was too long */ + if (driver->rx_length > interface->mtu) { + //csp_log_warn("KISS RX overflow"); + interface->rx_error++; + driver->rx_mode = KISS_MODE_NOT_STARTED; + driver->rx_length = 0; + } + + switch (driver->rx_mode) { + + case KISS_MODE_NOT_STARTED: + + /* Send normal chars back to usart driver */ + if (inputbyte != FEND) { + if (driver->kiss_discard != NULL) + driver->kiss_discard(inputbyte, pxTaskWoken); + break; + } + + /* Try to allocate new buffer */ + if (driver->rx_packet == NULL) { + if (pxTaskWoken == NULL) { + driver->rx_packet = csp_buffer_get(interface->mtu); + } else { + driver->rx_packet = csp_buffer_get_isr(interface->mtu); + } + } + + /* If no more memory, skip frame */ + if (driver->rx_packet == NULL) { + driver->rx_mode = KISS_MODE_SKIP_FRAME; + break; + } + + /* Start transfer */ + driver->rx_length = 0; + driver->rx_mode = KISS_MODE_STARTED; + driver->rx_first = 1; + break; + + case KISS_MODE_STARTED: + + /* Escape char */ + if (inputbyte == FESC) { + driver->rx_mode = KISS_MODE_ESCAPED; + break; + } + + /* End Char */ + if (inputbyte == FEND) { + + /* Accept message */ + if (driver->rx_length > 0) { + + /* Check for valid length */ + if (driver->rx_length < CSP_HEADER_LENGTH + sizeof(uint32_t)) { + //csp_log_warn("KISS short frame skipped, len: %u", driver->rx_length); + interface->rx_error++; + driver->rx_mode = KISS_MODE_NOT_STARTED; + break; + } + + /* Count received frame */ + interface->frame++; + + /* The CSP packet length is without the header */ + driver->rx_packet->length = driver->rx_length - CSP_HEADER_LENGTH; + + /* Convert the packet from network to host order */ + driver->rx_packet->id.ext = csp_ntoh32(driver->rx_packet->id.ext); + + /* Validate CRC */ + if (csp_crc32_verify(driver->rx_packet, false) != CSP_ERR_NONE) { + //csp_log_warn("KISS invalid crc frame skipped, len: %u", driver->rx_packet->length); + interface->rx_error++; + driver->rx_mode = KISS_MODE_NOT_STARTED; + break; + } + + /* Send back into CSP, notice calling from task so last argument must be NULL! */ + csp_qfifo_write(driver->rx_packet, interface, pxTaskWoken); + driver->rx_packet = NULL; + driver->rx_mode = KISS_MODE_NOT_STARTED; + break; + + } + + /* Break after the end char */ + break; + } + + /* Skip the first char after FEND which is TNC_DATA (0x00) */ + if (driver->rx_first) { + driver->rx_first = 0; + break; + } + + /* Valid data char */ + ((char *) &driver->rx_packet->id.ext)[driver->rx_length++] = inputbyte; + + break; + + case KISS_MODE_ESCAPED: + + /* Escaped escape char */ + if (inputbyte == TFESC) + ((char *) &driver->rx_packet->id.ext)[driver->rx_length++] = FESC; + + /* Escaped fend char */ + if (inputbyte == TFEND) + ((char *) &driver->rx_packet->id.ext)[driver->rx_length++] = FEND; + + /* Go back to started mode */ + driver->rx_mode = KISS_MODE_STARTED; + break; + + case KISS_MODE_SKIP_FRAME: + + /* Just wait for end char */ + if (inputbyte == FEND) + driver->rx_mode = KISS_MODE_NOT_STARTED; + + break; + + } + + } + +} + +void csp_kiss_init(csp_iface_t * csp_iface, csp_kiss_handle_t * csp_kiss_handle, csp_kiss_putc_f kiss_putc_f, csp_kiss_discard_f kiss_discard_f, const char * name) { + + /* Init lock only once */ + if (kiss_lock_init == 0) { + csp_bin_sem_create(&kiss_lock); + kiss_lock_init = 1; + } + + /* Register device handle as member of interface */ + csp_iface->driver = csp_kiss_handle; + csp_kiss_handle->kiss_discard = kiss_discard_f; + csp_kiss_handle->kiss_putc = kiss_putc_f; + csp_kiss_handle->rx_packet = NULL; + csp_kiss_handle->rx_mode = KISS_MODE_NOT_STARTED; + + /* Set default MTU if not given */ + if (csp_iface->mtu == 0) { + csp_iface->mtu = KISS_MTU; + } + + /* Setup other mandatories */ + csp_iface->nexthop = csp_kiss_tx; + csp_iface->name = name; + + /* Regsiter interface */ + csp_iflist_add(csp_iface); + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_lo.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_lo.c new file mode 100644 index 00000000..f3e81b15 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_lo.c @@ -0,0 +1,61 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* CSP includes */ +#include +#include +#include +#include + +#include +#include + +#include "../csp_route.h" + +/** + * Loopback interface transmit function + * @param packet Packet to transmit + * @param timeout Timout in ms + * @return 1 if packet was successfully transmitted, 0 on error + */ +static int csp_lo_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + /* Drop packet silently if not destined for us. This allows + * blackhole routing addresses by setting their nexthop to + * the loopback interface. + */ + if (packet->id.dst != csp_get_address()) { + /* Consume and drop packet */ + csp_buffer_free(packet); + return CSP_ERR_NONE; + } + + /* Send back into CSP, notice calling from task so last argument must be NULL! */ + csp_qfifo_write(packet, &csp_if_lo, NULL); + + return CSP_ERR_NONE; + +} + +/* Interface definition */ +csp_iface_t csp_if_lo = { + .name = "LOOP", + .nexthop = csp_lo_tx, +}; diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_zmqhub.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_zmqhub.c new file mode 100644 index 00000000..5292663b --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_zmqhub.c @@ -0,0 +1,165 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* CSP includes */ +#include +#include +#include +#include +#include +#include + +/* ZMQ */ +#include + +static void * context; +static void * publisher; +static void * subscriber; +static csp_bin_sem_handle_t tx_wait; + +/** + * Interface transmit function + * @param packet Packet to transmit + * @param timeout Timeout in ms + * @return 1 if packet was successfully transmitted, 0 on error + */ +int csp_zmqhub_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + /* Send envelope */ + uint8_t dest = csp_rtable_find_mac(packet->id.dst); + if (dest == CSP_NODE_MAC) + dest = packet->id.dst; + + uint16_t length = packet->length; + uint8_t * destptr = ((uint8_t *) &packet->id) - sizeof(dest); + memcpy(destptr, &dest, sizeof(dest)); + csp_bin_sem_wait(&tx_wait, CSP_INFINITY); /* Using ZMQ in thread safe manner*/ + int result = zmq_send(publisher, destptr, length + sizeof(packet->id) + sizeof(dest), 0); + csp_bin_sem_post(&tx_wait); /* Release tx semaphore */ + if (result < 0) + csp_log_error("ZMQ send error: %u %s\r\n", result, strerror(result)); + + csp_buffer_free(packet); + + return CSP_ERR_NONE; + +} + +CSP_DEFINE_TASK(csp_zmqhub_task) { + + while(1) { + zmq_msg_t msg; + assert(zmq_msg_init_size(&msg, 1024) == 0); + + /* Receive data */ + if (zmq_msg_recv(&msg, subscriber, 0) < 0) { + zmq_msg_close(&msg); + csp_log_error("ZMQ: %s", zmq_strerror(zmq_errno())); + continue; + } + + int datalen = zmq_msg_size(&msg); + if (datalen < 5) { + csp_log_warn("ZMQ: Too short datalen: %u", datalen); + while(zmq_msg_recv(&msg, subscriber, ZMQ_NOBLOCK) > 0) + zmq_msg_close(&msg); + continue; + } + + /* Create new csp packet */ + csp_packet_t * packet = csp_buffer_get(256); + if (packet == NULL) { + zmq_msg_close(&msg); + continue; + } + + /* Copy the data from zmq to csp */ + uint8_t * destptr = ((uint8_t *) &packet->id) - sizeof(*destptr); + memcpy(destptr, zmq_msg_data(&msg), datalen); + packet->length = datalen - sizeof(packet->id) - sizeof(*destptr); + + /* Queue up packet to router */ + csp_qfifo_write(packet, &csp_if_zmqhub, NULL); + + zmq_msg_close(&msg); + } + + return CSP_TASK_RETURN; + +} + +int csp_zmqhub_init(uint8_t addr, const char * host) { + char url_pub[100]; + char url_sub[100]; + + sprintf(url_pub, "tcp://%s:6000", host); + sprintf(url_sub, "tcp://%s:7000", host); + + return csp_zmqhub_init_w_endpoints(addr, url_pub, url_sub); +} + +int csp_zmqhub_init_w_endpoints(uint8_t addr, const char * publisher_endpoint, + const char * subscriber_endpoint) { + + context = zmq_ctx_new(); + assert(context); + + csp_log_info("INIT ZMQ with addr %u to servers %s / %s\r\n", + addr, publisher_endpoint, subscriber_endpoint); + + /* Publisher (TX) */ + publisher = zmq_socket(context, ZMQ_PUB); + assert(publisher); + assert(zmq_connect(publisher, publisher_endpoint) == 0); + + /* Subscriber (RX) */ + subscriber = zmq_socket(context, ZMQ_SUB); + assert(subscriber); + assert(zmq_connect(subscriber, subscriber_endpoint) == 0); + + if (addr == CSP_NODE_MAC) { // == 255 + assert(zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, NULL, 0) == 0); + } else { + assert(zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, &addr, 1) == 0); + } + /* ZMQ isn't thread safe, so we add a binary semaphore to wait on for tx */ + if (csp_bin_sem_create(&tx_wait) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to initialize semaphore in csp_zmqhub_init_w_endpoints"); + return CSP_ERR_NOMEM; + } + /* Start RX thread */ + static csp_thread_handle_t handle_subscriber; + int ret = csp_thread_create(csp_zmqhub_task, "ZMQ", 20000, NULL, 0, &handle_subscriber); + csp_log_info("Task start %d\r\n", ret); + + /* Register interface */ + csp_iflist_add(&csp_if_zmqhub); + + return CSP_ERR_NONE; + +} + +/* Interface definition */ +csp_iface_t csp_if_zmqhub = { + .name = "ZMQHUB", + .nexthop = csp_zmqhub_tx, +}; diff --git a/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_cidr.c b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_cidr.c new file mode 100644 index 00000000..5758dc3c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_cidr.c @@ -0,0 +1,233 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include + +/* Local typedef for routing table */ +typedef struct __attribute__((__packed__)) csp_rtable_s { + uint8_t address; + uint8_t netmask; + uint8_t mac; + csp_iface_t * interface; + struct csp_rtable_s * next; +} csp_rtable_t; + +/* Routing entries are stored in a linked list*/ +static csp_rtable_t * rtable = NULL; + +static csp_rtable_t * csp_rtable_find(uint8_t addr, uint8_t netmask, uint8_t exact) { + + /* Remember best result */ + csp_rtable_t * best_result = NULL; + uint8_t best_result_mask = 0; + + /* Start search */ + csp_rtable_t * i = rtable; + while(i) { + + /* Look for exact match */ + if (i->address == addr && i->netmask == netmask) { + best_result = i; + break; + } + + /* Try a CIDR netmask match */ + if (!exact) { + uint8_t hostbits = (1 << (CSP_ID_HOST_SIZE - i->netmask)) - 1; + uint8_t netbits = ~hostbits; + //printf("Netbits %x Hostbits %x\r\n", netbits, hostbits); + + /* Match network addresses */ + uint8_t net_a = i->address & netbits; + uint8_t net_b = addr & netbits; + //printf("A: %hhx, B: %hhx\r\n", net_a, net_b); + + /* We have a match */ + if (net_a == net_b) { + if (i->netmask >= best_result_mask) { + //printf("Match best result %u %u\r\n", best_result_mask, i->netmask); + best_result = i; + best_result_mask = i->netmask; + } + } + + } + + i = i->next; + + } + +#if 0 + if (best_result) + csp_debug(CSP_PACKET, "Using routing entry: %u/%u dev %s m:%u\r\n", best_result->address, best_result->netmask, best_result->interface->name, best_result->mac); +#endif + + return best_result; + +} + +void csp_rtable_clear(void) { + for (csp_rtable_t * i = rtable; (i);) { + void * freeme = i; + i = i->next; + csp_free(freeme); + } + rtable = NULL; + + /* Set loopback up again */ + csp_rtable_set(csp_get_address(), CSP_ID_HOST_SIZE, &csp_if_lo, CSP_NODE_MAC); + +} + +static int csp_rtable_parse(const char * buffer, int dry_run) { + + int valid_entries = 0; + + /* Copy string before running strtok */ + char * str = alloca(strlen(buffer) + 1); + memcpy(str, buffer, strlen(buffer) + 1); + + /* Get first token */ + str = strtok(str, ","); + + while ((str) && (strlen(str) > 1)) { + int address = 0, netmask = 0, mac = 255; + char name[10] = {}; + if (sscanf(str, "%u/%u %s %u", &address, &netmask, name, &mac) != 4) { + if (sscanf(str, "%u/%u %s", &address, &netmask, name) != 3) { + csp_log_error("Parse error %s", str); + return -1; + } + } + //printf("Parsed %u/%u %u %s\r\n", address, netmask, mac, name); + csp_iface_t * ifc = csp_iflist_get_by_name(name); + if (ifc) { + if (dry_run == 0) + csp_rtable_set(address, netmask, ifc, mac); + } else { + csp_log_error("Unknown interface %s", name); + return -1; + } + valid_entries++; + str = strtok(NULL, ","); + } + + return valid_entries; +} + +void csp_rtable_load(const char * buffer) { + csp_rtable_parse(buffer, 0); +} + +int csp_rtable_check(const char * buffer) { + return csp_rtable_parse(buffer, 1); +} + +int csp_rtable_save(char * buffer, int maxlen) { + int len = 0; + for (csp_rtable_t * i = rtable; (i); i = i->next) { + if (i->mac != CSP_NODE_MAC) { + len += snprintf(buffer + len, maxlen - len, "%u/%u %s %u, ", i->address, i->netmask, i->interface->name, i->mac); + } else { + len += snprintf(buffer + len, maxlen - len, "%u/%u %s, ", i->address, i->netmask, i->interface->name); + } + } + return len; +} + +csp_iface_t * csp_rtable_find_iface(uint8_t id) { + csp_rtable_t * entry = csp_rtable_find(id, CSP_ID_HOST_SIZE, 0); + if (entry == NULL) + return NULL; + return entry->interface; +} + +uint8_t csp_rtable_find_mac(uint8_t id) { + csp_rtable_t * entry = csp_rtable_find(id, CSP_ID_HOST_SIZE, 0); + if (entry == NULL) + return 255; + return entry->mac; +} + +int csp_rtable_set(uint8_t _address, uint8_t _netmask, csp_iface_t *ifc, uint8_t mac) { + + if (ifc == NULL) + return CSP_ERR_INVAL; + + /* Set default route in the old way */ + int address, netmask; + if (_address == CSP_DEFAULT_ROUTE) { + netmask = 0; + address = 0; + } else { + netmask = _netmask; + address = _address; + } + + /* Fist see if the entry exists */ + csp_rtable_t * entry = csp_rtable_find(address, netmask, 1); + + /* If not, create a new one */ + if (!entry) { + entry = csp_malloc(sizeof(csp_rtable_t)); + if (entry == NULL) + return CSP_ERR_NOMEM; + + entry->next = NULL; + /* Add entry to linked-list */ + if (rtable == NULL) { + /* This is the first interface to be added */ + rtable = entry; + } else { + /* One or more interfaces were already added */ + csp_rtable_t * i = rtable; + while (i->next) + i = i->next; + i->next = entry; + } + } + + /* Fill in the data */ + entry->address = address; + entry->netmask = netmask; + entry->interface = ifc; + entry->mac = mac; + + return CSP_ERR_NONE; +} + +#ifdef CSP_DEBUG +void csp_rtable_print(void) { + + for (csp_rtable_t * i = rtable; (i); i = i->next) { + if (i->mac == 255) { + printf("%u/%u %s\r\n", i->address, i->netmask, i->interface->name); + } else { + printf("%u/%u %s %u\r\n", i->address, i->netmask, i->interface->name, i->mac); + } + } + +} +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_static.c b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_static.c new file mode 100644 index 00000000..ea993027 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_static.c @@ -0,0 +1,128 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* Local typedef for routing table */ +typedef struct __attribute__((__packed__)) csp_rtable_s { + csp_iface_t * interface; + uint8_t mac; +} csp_rtable_t; + +/* Static storage context for routing table */ +static csp_rtable_t routes[CSP_ROUTE_COUNT] = {}; + +/** + * Find entry in static routing table + * This is done by table lookup with fallback to the default route + * The reason why the csp_rtable_t struct is not returned directly + * is that we wish to hide the storage format, mainly because of + * the alternative routing table storage (cidr). + * @param id Node + * @return pointer to found routing entry + */ +static csp_rtable_t * csp_rtable_find(uint8_t id) { + + if (routes[id].interface != NULL) { + return &routes[id]; + } else if (routes[CSP_DEFAULT_ROUTE].interface != NULL) { + return &routes[CSP_DEFAULT_ROUTE]; + } + return NULL; + +} + +csp_iface_t * csp_rtable_find_iface(uint8_t id) { + csp_rtable_t * route = csp_rtable_find(id); + if (route == NULL) + return NULL; + return route->interface; +} + +uint8_t csp_rtable_find_mac(uint8_t id) { + csp_rtable_t * route = csp_rtable_find(id); + if (route == NULL) + return 255; + return route->mac; +} + +void csp_rtable_clear(void) { + memset(routes, 0, sizeof(routes[0]) * CSP_ROUTE_COUNT); +} + +void csp_route_table_load(uint8_t route_table_in[CSP_ROUTE_TABLE_SIZE]) { + memcpy(routes, route_table_in, sizeof(routes[0]) * CSP_ROUTE_COUNT); +} + +void csp_route_table_save(uint8_t route_table_out[CSP_ROUTE_TABLE_SIZE]) { + memcpy(route_table_out, routes, sizeof(routes[0]) * CSP_ROUTE_COUNT); +} + +int csp_rtable_set(uint8_t node, uint8_t mask, csp_iface_t *ifc, uint8_t mac) { + + /* Don't add nothing */ + if (ifc == NULL) + return CSP_ERR_INVAL; + + /** + * Check if the interface has been added. + * + * NOTE: For future implementations, interfaces should call + * csp_route_add_if in its csp_if__init function, instead + * of registering at first route_set, in order to make the interface + * available to network based (CMP) route configuration. + */ + csp_iflist_add(ifc); + + /* Set route */ + if (node <= CSP_DEFAULT_ROUTE) { + routes[node].interface = ifc; + routes[node].mac = mac; + } else { + csp_log_error("Failed to set route: invalid node id %u", node); + return CSP_ERR_INVAL; + } + + return CSP_ERR_NONE; + +} + +void csp_rtable_load(const char * buffer) { +} + +int csp_rtable_check(const char * buffer) { + return -1; +} + +#ifdef CSP_DEBUG +void csp_rtable_print(void) { + int i; + printf("Node Interface Address\r\n"); + for (i = 0; i < CSP_DEFAULT_ROUTE; i++) + if (routes[i].interface != NULL) + printf("%4u %-9s %u\r\n", i, + routes[i].interface->name, + routes[i].mac == CSP_NODE_MAC ? i : routes[i].mac); + printf(" * %-9s %u\r\n", routes[CSP_DEFAULT_ROUTE].interface->name, routes[CSP_DEFAULT_ROUTE].mac); + +} +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/transport/csp_rdp.c b/gomspace/libgscsp/lib/libcsp/src/transport/csp_rdp.c new file mode 100644 index 00000000..e19968e2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/transport/csp_rdp.c @@ -0,0 +1,1102 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + * This is a implementation of the seq/ack handling taken from the Reliable Datagram Protocol (RDP) + * For more information read RFC 908/1151. The implementation has been extended to include support for + * delayed acknowledgments, to improve performance over half-duplex links. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "../csp_port.h" +#include "../csp_conn.h" +#include "../csp_io.h" +#include "csp_transport.h" + +#ifdef CSP_USE_RDP + +#define RDP_SYN 0x01 +#define RDP_ACK 0x02 +#define RDP_EAK 0x04 +#define RDP_RST 0x08 + +static uint32_t csp_rdp_window_size = 4; +static uint32_t csp_rdp_conn_timeout = 10000; +static uint32_t csp_rdp_packet_timeout = 1000; +static uint32_t csp_rdp_delayed_acks = 1; +static uint32_t csp_rdp_ack_timeout = 1000 / 4; +static uint32_t csp_rdp_ack_delay_count = 4 / 2; + +/* Used for queue calls */ +static CSP_BASE_TYPE pdTrue = 1; + +typedef struct __attribute__((__packed__)) { + /* The timestamp is placed in the padding bytes */ + uint8_t padding[CSP_PADDING_BYTES - 2 * sizeof(uint32_t)]; + uint32_t quarantine; // EACK quarantine period + uint32_t timestamp; // Time the message was sent + uint16_t length; // Length field must be just before CSP ID + csp_id_t id; // CSP id must be just before data + uint8_t data[]; // This just points to the rest of the buffer, without a size indication. +} rdp_packet_t; + +typedef struct __attribute__((__packed__)) { + union __attribute__((__packed__)) { + uint8_t flags; + struct __attribute__((__packed__)) { +#if defined(CSP_BIG_ENDIAN) && !defined(CSP_LITTLE_ENDIAN) + unsigned int res : 4; + unsigned int syn : 1; + unsigned int ack : 1; + unsigned int eak : 1; + unsigned int rst : 1; +#elif defined(CSP_LITTLE_ENDIAN) && !defined(CSP_BIG_ENDIAN) + unsigned int rst : 1; + unsigned int eak : 1; + unsigned int ack : 1; + unsigned int syn : 1; + unsigned int res : 4; +#else + #error "Must define one of CSP_BIG_ENDIAN or CSP_LITTLE_ENDIAN in csp_platform.h" +#endif + }; + }; + uint16_t seq_nr; + uint16_t ack_nr; +} rdp_header_t; + +/** + * RDP Headers: + * The following functions are helper functions that handles the extra RDP + * information that needs to be appended to all data packets. + */ +static rdp_header_t * csp_rdp_header_add(csp_packet_t * packet) { + rdp_header_t * header = (rdp_header_t *) &packet->data[packet->length]; + packet->length += sizeof(rdp_header_t); + memset(header, 0, sizeof(rdp_header_t)); + return header; +} + +static rdp_header_t * csp_rdp_header_remove(csp_packet_t * packet) { + rdp_header_t * header = (rdp_header_t *) &packet->data[packet->length-sizeof(rdp_header_t)]; + packet->length -= sizeof(rdp_header_t); + return header; +} + +static rdp_header_t * csp_rdp_header_ref(csp_packet_t * packet) { + rdp_header_t * header = (rdp_header_t *) &packet->data[packet->length-sizeof(rdp_header_t)]; + return header; +} + +/* Functions for comparing wrapping sequence numbers and timestamps */ + +/* Return 1 if seq is between start and end (both inclusive) */ +static inline int csp_rdp_seq_between(uint16_t seq, uint16_t start, uint16_t end) { + return (uint16_t)(end - start) >= (uint16_t)(seq - start); +} + +/* Return 1 if seq is before cmp */ +static inline int csp_rdp_seq_before(uint16_t seq, uint16_t cmp) { + return (int16_t)(seq - cmp) < 0; +} + +/* Return 1 if seq is after cmp */ +static inline int csp_rdp_seq_after(uint16_t seq, uint16_t cmp) { + return csp_rdp_seq_before(cmp, seq); +} + +/* Return 1 if time is between start and end (both inclusive) */ +static inline int csp_rdp_time_between(uint32_t time, uint32_t start, uint32_t end) { + return (uint32_t)(end - start) >= (uint32_t)(time - start); +} + +/* Return 1 if time is before cmp */ +static inline int csp_rdp_time_before(uint32_t time, uint32_t cmp) { + return (int32_t)(time - cmp) < 0; +} + +/* Return 1 if time is after cmp */ +static inline int csp_rdp_time_after(uint32_t time, uint32_t cmp) { + return csp_rdp_time_before(cmp, time); +} + +/** + * CONTROL MESSAGES + * The following function is used to send empty messages, + * with ACK, SYN or RST flag. + */ +static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, int seq_nr, int ack_nr) { + + csp_id_t idout; + + /* Generate message */ + if (!packet) { + packet = csp_buffer_get(20); + if (!packet) + return CSP_ERR_NOMEM; + packet->length = 0; + } + + /* Add RDP header */ + rdp_header_t * header = csp_rdp_header_add(packet); + header->seq_nr = csp_hton16(seq_nr); + header->ack_nr = csp_hton16(ack_nr); + header->ack = (flags & RDP_ACK) ? 1 : 0; + header->eak = (flags & RDP_EAK) ? 1 : 0; + header->syn = (flags & RDP_SYN) ? 1 : 0; + header->rst = (flags & RDP_RST) ? 1 : 0; + + /* Send copy to tx_queue, before sending packet to IF */ + if (flags & RDP_SYN) { + rdp_packet_t * rdp_packet = csp_buffer_clone(packet); + if (rdp_packet == NULL) return CSP_ERR_NOMEM; + rdp_packet->timestamp = csp_get_ms(); + if (csp_queue_enqueue(conn->rdp.tx_queue, &rdp_packet, 0) != CSP_QUEUE_OK) + csp_buffer_free(rdp_packet); + } + + /* Send control messages with high priority */ + idout = conn->idout; + idout.pri = conn->idout.pri < CSP_PRIO_HIGH ? conn->idout.pri : CSP_PRIO_HIGH; + + /* Send packet to IF */ + csp_iface_t * ifout = csp_rtable_find_iface(idout.dst); + if (csp_send_direct(idout, packet, ifout, 0) != CSP_ERR_NONE) { + csp_log_error("INTERFACE ERROR: not possible to send"); + csp_buffer_free(packet); + return CSP_ERR_BUSY; + } + + /* Update last ACK time stamp */ + if (flags & RDP_ACK) { + conn->rdp.rcv_lsa = ack_nr; + conn->rdp.ack_timestamp = csp_get_ms(); + } + + return CSP_ERR_NONE; + +} + +/** + * EXTENDED ACKNOWLEDGEMENTS + * The following function sends an extended ACK packet + */ +static int csp_rdp_send_eack(csp_conn_t * conn) { + + /* Allocate message */ + csp_packet_t * packet_eack = csp_buffer_get(100); + if (packet_eack == NULL) return CSP_ERR_NOMEM; + packet_eack->length = 0; + + /* Loop through RX queue */ + int i, count; + csp_packet_t * packet; + count = csp_queue_size(conn->rdp.rx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from rx_queue in queue deliver"); + break; + } + + /* Add seq nr to EACK packet */ + rdp_header_t * header = csp_rdp_header_ref(packet); + packet_eack->data16[packet_eack->length/sizeof(uint16_t)] = csp_hton16(header->seq_nr); + packet_eack->length += sizeof(uint16_t); + csp_log_protocol("Added EACK nr %u", header->seq_nr); + + /* Requeue */ + csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + + } + + return csp_rdp_send_cmp(conn, packet_eack, RDP_ACK | RDP_EAK, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + +} + + +/** + * SYN Packet + * The following function sends a SYN packet + */ +static int csp_rdp_send_syn(csp_conn_t * conn) { + + /* Allocate message */ + csp_packet_t * packet = csp_buffer_get(100); + if (packet == NULL) return CSP_ERR_NOMEM; + + /* Generate contents */ + packet->data32[0] = csp_hton32(csp_rdp_window_size); + packet->data32[1] = csp_hton32(csp_rdp_conn_timeout); + packet->data32[2] = csp_hton32(csp_rdp_packet_timeout); + packet->data32[3] = csp_hton32(csp_rdp_delayed_acks); + packet->data32[4] = csp_hton32(csp_rdp_ack_timeout); + packet->data32[5] = csp_hton32(csp_rdp_ack_delay_count); + packet->length = 6 * sizeof(uint32_t); + + return csp_rdp_send_cmp(conn, packet, RDP_SYN, conn->rdp.snd_iss, 0); + +} + +static inline int csp_rdp_receive_data(csp_conn_t * conn, csp_packet_t * packet) { + + /* Remove RDP header before passing to userspace */ + csp_rdp_header_remove(packet); + + /* Enqueue data */ + if (csp_conn_enqueue_packet(conn, packet) < 0) { + csp_log_warn("Conn RX buffer full"); + return CSP_ERR_NOBUFS; + } + + return CSP_ERR_NONE; + +} + +static inline void csp_rdp_rx_queue_flush(csp_conn_t * conn) { + + /* Loop through RX queue */ + int i, count; + csp_packet_t * packet; + +front: + count = csp_queue_size(conn->rdp.rx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from rx_queue in queue deliver"); + break; + } + + rdp_header_t * header = csp_rdp_header_ref(packet); + csp_log_protocol("RX Queue deliver matching Element, seq %u", header->seq_nr); + + /* If the matching packet was found: */ + if (header->seq_nr == (uint16_t)(conn->rdp.rcv_cur + 1)) { + csp_log_protocol("Deliver seq %u", header->seq_nr); + csp_rdp_receive_data(conn, packet); + conn->rdp.rcv_cur++; + /* Loop from first element again */ + goto front; + + /* Otherwise, requeue */ + } else { + csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + } + + } + +} + +static inline bool csp_rdp_seq_in_rx_queue(csp_conn_t * conn, uint16_t seq_nr) { + + /* Loop through RX queue */ + int i, count; + rdp_packet_t * packet; + count = csp_queue_size(conn->rdp.rx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from rx_queue in queue exists"); + break; + } + + csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + + rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *) packet); + csp_log_protocol("RX Queue exists matching Element, seq %u", header->seq_nr); + + /* If the matching packet was found, deliver */ + if (header->seq_nr == seq_nr) { + csp_log_protocol("We have a match"); + return true; + } + + } + + return false; + +} + +static inline int csp_rdp_rx_queue_add(csp_conn_t * conn, csp_packet_t * packet, uint16_t seq_nr) { + + if (csp_rdp_seq_in_rx_queue(conn, seq_nr)) + return CSP_QUEUE_ERROR; + return csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + +} + +static void csp_rdp_flush_eack(csp_conn_t * conn, csp_packet_t * eack_packet) { + + /* Loop through TX queue */ + int i, j, count; + rdp_packet_t * packet; + count = csp_queue_size(conn->rdp.tx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue(conn->rdp.tx_queue, &packet, 0) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from tx_queue in flush EACK"); + break; + } + + rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *) packet); + csp_log_protocol("EACK compare element, time %u, seq %u", packet->timestamp, csp_ntoh16(header->seq_nr)); + + /* Look for this element in EACKs */ + int match = 0; + for (j = 0; j < (int)((eack_packet->length - sizeof(rdp_header_t)) / sizeof(uint16_t)); j++) { + if (csp_ntoh16(eack_packet->data16[j]) == csp_ntoh16(header->seq_nr)) + match = 1; + + /* Enable this if you want EACK's to trigger retransmission */ + if (csp_ntoh16(eack_packet->data16[j]) > csp_ntoh16(header->seq_nr)) { + uint32_t time_now = csp_get_ms(); + if (csp_rdp_time_after(time_now, packet->quarantine)) { + packet->timestamp = time_now - conn->rdp.packet_timeout - 1; + packet->quarantine = time_now + conn->rdp.packet_timeout / 2; + } + } + } + + if (match == 0) { + /* If not found, put back on tx queue */ + csp_queue_enqueue(conn->rdp.tx_queue, &packet, 0); + } else { + /* Found, free */ + csp_log_protocol("TX Element %u freed", csp_ntoh16(header->seq_nr)); + csp_buffer_free(packet); + } + + } + +} + +static inline bool csp_rdp_should_ack(csp_conn_t * conn) { + + /* If delayed ACKs are not used, always ACK */ + if (!conn->rdp.delayed_acks) { + return true; + } + + /* ACK if time since last ACK is greater than ACK timeout */ + uint32_t time_now = csp_get_ms(); + if (csp_rdp_time_after(time_now, conn->rdp.ack_timestamp + conn->rdp.ack_timeout)) + return true; + + /* ACK if number of unacknowledged packets is greater than delay count */ + if (csp_rdp_seq_after(conn->rdp.rcv_cur, conn->rdp.rcv_lsa + conn->rdp.ack_delay_count)) + return true; + + return false; + +} + +void csp_rdp_flush_all(csp_conn_t * conn) { + + if ((conn == NULL) || conn->rdp.tx_queue == NULL) { + csp_log_error("Null pointer passed to rdp flush all"); + return; + } + + rdp_packet_t * packet; + + /* Empty TX queue */ + while (csp_queue_dequeue_isr(conn->rdp.tx_queue, &packet, &pdTrue) == CSP_QUEUE_OK) { + if (packet != NULL) { + csp_log_protocol("RDP %p: Flush TX Element, time %u, seq %u", conn, packet->timestamp, csp_ntoh16(csp_rdp_header_ref((csp_packet_t *) packet)->seq_nr)); + csp_buffer_free(packet); + } + } + + /* Empty RX queue */ + while (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) == CSP_QUEUE_OK) { + if (packet != NULL) { + csp_log_protocol("RDP %p: Flush RX Element, time %u, seq %u", conn, packet->timestamp, csp_ntoh16(csp_rdp_header_ref((csp_packet_t *) packet)->seq_nr)); + csp_buffer_free(packet); + } + } + +} + + +int csp_rdp_check_ack(csp_conn_t * conn) { + + /* Check all RX queues for spare capacity */ + int prio, avail = 1; + for (prio = 0; prio < CSP_RX_QUEUES; prio++) { + if (CSP_RX_QUEUE_LENGTH - csp_queue_size(conn->rx_queue[prio]) <= 2 * (int32_t)conn->rdp.window_size) { + avail = 0; + break; + } + } + + /* If more space available, only send after ack timeout or immediately if delay_acks is zero */ + if (avail && csp_rdp_should_ack(conn)) + csp_rdp_send_cmp(conn, NULL, RDP_ACK, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + + return CSP_ERR_NONE; + +} + +static inline bool csp_rdp_is_conn_ready_for_tx(csp_conn_t * conn) +{ + // Check Tx window (messages waiting for acks) + if (csp_rdp_seq_after(conn->rdp.snd_nxt, conn->rdp.snd_una + conn->rdp.window_size - 1)) { + return false; + } + return true; +} + +/** + * This function must be called with regular intervals for the + * RDP protocol to work as expected. This takes care of closing + * stale connections and retransmitting traffic. A good place to + * call this function is from the CSP router task. + */ +void csp_rdp_check_timeouts(csp_conn_t * conn) { + + rdp_packet_t * packet; + + /** + * CONNECTION TIMEOUT: + * Check that connection has not timed out inside the network stack + * */ + uint32_t time_now = csp_get_ms(); + if (conn->socket != NULL) { + if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { + csp_log_warn("RDP %p: Found a lost connection (now: %u, ts: %u, to: %u), closing now", + conn, time_now, conn->timestamp, conn->rdp.conn_timeout); + csp_close(conn); + return; + } + } + + /** + * CLOSE-WAIT TIMEOUT: + * After waiting a while in CLOSE-WAIT, the connection should be closed. + */ + if (conn->rdp.state == RDP_CLOSE_WAIT) { + if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { + csp_log_protocol("RDP %p: CLOSE_WAIT timeout", conn); + csp_close(conn); + } + return; + } + + /** + * MESSAGE TIMEOUT: + * Check each outgoing message for TX timeout + */ + int i, count; + count = csp_queue_size(conn->rdp.tx_queue); + for (i = 0; i < count; i++) { + + if ((csp_queue_dequeue_isr(conn->rdp.tx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) || packet == NULL) { + csp_log_warn("Cannot dequeue from tx_queue in check timeout"); + break; + } + + /* Get header */ + rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *) packet); + + /* If acked, do not retransmit */ + if (csp_rdp_seq_before(csp_ntoh16(header->seq_nr), conn->rdp.snd_una)) { + csp_log_protocol("TX Element Free, time %u, seq %u, una %u", packet->timestamp, csp_ntoh16(header->seq_nr), conn->rdp.snd_una); + csp_buffer_free(packet); + continue; + } + + /* Check timestamp and retransmit if needed */ + if (csp_rdp_time_after(time_now, packet->timestamp + conn->rdp.packet_timeout)) { + csp_log_protocol("TX Element timed out, retransmitting seq %u", csp_ntoh16(header->seq_nr)); + + /* Update to latest outgoing ACK */ + header->ack_nr = csp_hton16(conn->rdp.rcv_cur); + + /* Send copy to tx_queue */ + packet->timestamp = csp_get_ms(); + csp_packet_t * new_packet = csp_buffer_clone(packet); + csp_iface_t * ifout = csp_rtable_find_iface(conn->idout.dst); + if (csp_send_direct(conn->idout, new_packet, ifout, 0) != CSP_ERR_NONE) { + csp_log_warn("Retransmission failed"); + csp_buffer_free(new_packet); + } + + } + + /* Requeue the TX element */ + csp_queue_enqueue_isr(conn->rdp.tx_queue, &packet, &pdTrue); + + } + + /** + * ACK TIMEOUT: + * Check ACK timeouts, if we have unacknowledged segments + */ + if (conn->rdp.delayed_acks) { + csp_rdp_check_ack(conn); + } + + /* Wake user task if connection is open and additional Tx can be done */ + if ((conn->rdp.state == RDP_OPEN) && csp_rdp_is_conn_ready_for_tx(conn)) { + csp_log_protocol("RDP %p: Wake Tx task (check timeouts)", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); + } +} + +void csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { + + /* Get RX header and convert to host byte-order */ + rdp_header_t * rx_header = csp_rdp_header_ref(packet); + rx_header->ack_nr = csp_ntoh16(rx_header->ack_nr); + rx_header->seq_nr = csp_ntoh16(rx_header->seq_nr); + + csp_log_protocol("RDP %p: Received in S %u: syn %u, ack %u, eack %u, " + "rst %u, seq_nr %5u, ack_nr %5u, packet_len %u (%u)", + conn, conn->rdp.state, rx_header->syn, rx_header->ack, rx_header->eak, + rx_header->rst, rx_header->seq_nr, rx_header->ack_nr, + packet->length, packet->length - sizeof(rdp_header_t)); + + /* If a RESET was received. */ + if (rx_header->rst) { + + if (rx_header->ack) { + /* Store current ack'ed sequence number */ + conn->rdp.snd_una = rx_header->ack_nr + 1; + } + + if (conn->rdp.state == RDP_CLOSE_WAIT || conn->rdp.state == RDP_CLOSED) { + csp_log_protocol("RDP %p: RST received in CLOSE_WAIT or CLOSED. Now closing connection", conn); + goto discard_close; + } else { + csp_log_protocol("RDP %p: Got RESET in state %u", conn, conn->rdp.state); + + if (rx_header->seq_nr == (uint16_t)(conn->rdp.rcv_cur + 1)) { + csp_log_protocol("RDP %p: RESET in sequence, no more data incoming, reply with RESET", conn); + conn->rdp.state = RDP_CLOSE_WAIT; + conn->timestamp = csp_get_ms(); + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + goto discard_close; + } else { + csp_log_protocol("RDP %p: RESET out of sequence, keep connection open", conn); + goto discard_open; + } + } + } + + /* The BIG FAT switch (state-machine) */ + switch(conn->rdp.state) { + + /** + * STATE == CLOSED + */ + case RDP_CLOSED: { + + /* No SYN flag set while in closed. Inform by sending back RST */ + if (!rx_header->syn) { + csp_log_protocol("Not SYN received in CLOSED state. Discarding packet"); + csp_rdp_send_cmp(conn, NULL, RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + goto discard_close; + } + + csp_log_protocol("RDP: SYN-Received"); + + /* Setup TX seq. */ + conn->rdp.snd_iss = (uint16_t)rand(); + conn->rdp.snd_nxt = conn->rdp.snd_iss + 1; + conn->rdp.snd_una = conn->rdp.snd_iss; + + /* Store RX seq. */ + conn->rdp.rcv_cur = rx_header->seq_nr; + conn->rdp.rcv_irs = rx_header->seq_nr; + conn->rdp.rcv_lsa = rx_header->seq_nr; + + /* Store RDP options */ + conn->rdp.window_size = csp_ntoh32(packet->data32[0]); + conn->rdp.conn_timeout = csp_ntoh32(packet->data32[1]); + conn->rdp.packet_timeout = csp_ntoh32(packet->data32[2]); + conn->rdp.delayed_acks = csp_ntoh32(packet->data32[3]); + conn->rdp.ack_timeout = csp_ntoh32(packet->data32[4]); + conn->rdp.ack_delay_count = csp_ntoh32(packet->data32[5]); + csp_log_protocol("RDP: Window Size %u, conn timeout %u, packet timeout %u", + conn->rdp.window_size, conn->rdp.conn_timeout, conn->rdp.packet_timeout); + csp_log_protocol("RDP: Delayed acks: %u, ack timeout %u, ack each %u packet", + conn->rdp.delayed_acks, conn->rdp.ack_timeout, conn->rdp.ack_delay_count); + + /* Connection accepted */ + conn->rdp.state = RDP_SYN_RCVD; + + /* Send SYN/ACK */ + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_SYN, conn->rdp.snd_iss, conn->rdp.rcv_irs); + + goto discard_open; + + } + break; + + /** + * STATE == SYN-SENT + */ + case RDP_SYN_SENT: { + + /* First check SYN/ACK */ + if (rx_header->syn && rx_header->ack) { + + conn->rdp.rcv_cur = rx_header->seq_nr; + conn->rdp.rcv_irs = rx_header->seq_nr; + conn->rdp.rcv_lsa = rx_header->seq_nr - 1; + conn->rdp.snd_una = rx_header->ack_nr + 1; + conn->rdp.ack_timestamp = csp_get_ms(); + conn->rdp.state = RDP_OPEN; + + csp_log_protocol("RDP: NP: Connection OPEN"); + + /* Send ACK */ + csp_rdp_send_cmp(conn, NULL, RDP_ACK, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + + /* Wake TX task */ + csp_log_protocol("RDP %p: Wake Tx task (ack)", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); + + goto discard_open; + } + + /* If there was no SYN in the reply, our SYN message hit an already open connection + * This is handled by sending a RST. + * Normally this would be followed up by a new connection attempt, however + * we don't have a method for signaling this to the user space. + */ + if (rx_header->ack) { + csp_log_error("Half-open connection found, sending RST"); + csp_rdp_send_cmp(conn, NULL, RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + csp_log_protocol("RDP %p: Wake Tx task (rst)", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); + + goto discard_open; + } + + /* Otherwise we have an invalid command, such as a SYN reply to a SYN command, + * indicating simultaneous connections, which is not possible in the way CSP + * reserves some ports for server and some for clients. + */ + csp_log_error("Invalid reply to SYN request"); + goto discard_close; + + } + break; + + /** + * STATE == OPEN + */ + case RDP_SYN_RCVD: + case RDP_OPEN: + { + + /* SYN or !ACK is invalid */ + if (rx_header->syn || !rx_header->ack) { + if (rx_header->seq_nr != conn->rdp.rcv_irs) { + csp_log_error("Invalid SYN or no ACK, resetting!"); + goto discard_close; + } else { + csp_log_protocol("Ignoring duplicate SYN packet!"); + goto discard_open; + } + } + + /* Check sequence number */ + if (!csp_rdp_seq_between(rx_header->seq_nr, conn->rdp.rcv_cur + 1, conn->rdp.rcv_cur + conn->rdp.window_size * 2)) { + csp_log_protocol("Invalid sequence number! %"PRIu16" not between %"PRIu16" and %"PRIu16, + rx_header->seq_nr, conn->rdp.rcv_cur + 1, conn->rdp.rcv_cur + 1 + conn->rdp.window_size * 2); + /* If duplicate SYN received, send another SYN/ACK */ + if (conn->rdp.state == RDP_SYN_RCVD) + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_SYN, conn->rdp.snd_iss, conn->rdp.rcv_irs); + /* If duplicate data packet received, send EACK back */ + if (conn->rdp.state == RDP_OPEN) + csp_rdp_send_eack(conn); + + goto discard_open; + } + + /* Check ACK number */ + if (!csp_rdp_seq_between(rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1)) { + csp_log_error("Invalid ACK number! %u not between %u and %u", + rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1); + goto discard_open; + } + + /* Check SYN_RCVD ACK */ + if (conn->rdp.state == RDP_SYN_RCVD) { + if (rx_header->ack_nr != conn->rdp.snd_iss) { + csp_log_error("SYN-RCVD: Wrong ACK number"); + goto discard_close; + } + csp_log_protocol("RDP: NC: Connection OPEN"); + conn->rdp.state = RDP_OPEN; + + /* If a socket is set, this message is the first in a new connection + * so the connection must be queued to the socket. */ + if (conn->socket != NULL) { + + /* Try queueing */ + if (csp_queue_enqueue(conn->socket, &conn, 0) == CSP_QUEUE_FULL) { + csp_log_error("ERROR socket cannot accept more connections"); + goto discard_close; + } + + /* Ensure that this connection will not be posted to this socket again + * and remember that the connection handle has been passed to userspace + * by setting the socket = NULL */ + conn->socket = NULL; + } + + } + + /* Store current ack'ed sequence number */ + conn->rdp.snd_una = rx_header->ack_nr + 1; + + /* We have an EACK */ + if (rx_header->eak) { + if (packet->length > sizeof(rdp_header_t)) + csp_rdp_flush_eack(conn, packet); + goto discard_open; + } + + /* If no data, return here */ + if (packet->length <= sizeof(rdp_header_t)) + goto discard_open; + + /* If message is not in sequence, send EACK and store packet */ + if (rx_header->seq_nr != (uint16_t)(conn->rdp.rcv_cur + 1)) { + if (csp_rdp_rx_queue_add(conn, packet, rx_header->seq_nr) != CSP_QUEUE_OK) { + csp_log_protocol("Duplicate sequence number"); + csp_rdp_check_ack(conn); + goto discard_open; + } + csp_rdp_send_eack(conn); + goto accepted_open; + } + + /* Store sequence number before stripping RDP header */ + uint16_t seq_nr = rx_header->seq_nr; + + /* Receive data */ + if (csp_rdp_receive_data(conn, packet) != CSP_ERR_NONE) + goto discard_open; + + /* Update last received packet */ + conn->rdp.rcv_cur = seq_nr; + + /* Only ACK the message if there is room for a full window in the RX buffer. + * Unacknowledged segments are ACKed by csp_rdp_check_timeouts when the buffer is + * no longer full. */ + csp_rdp_check_ack(conn); + + /* Flush RX queue */ + csp_rdp_rx_queue_flush(conn); + + goto accepted_open; + + } + break; + + case RDP_CLOSE_WAIT: + + /* Ignore SYN or !ACK */ + if (rx_header->syn || !rx_header->ack) { + csp_log_protocol("Invalid SYN or no ACK in CLOSE-WAIT"); + goto discard_open; + } + + /* Check ACK number */ + if (!csp_rdp_seq_between(rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1)) { + csp_log_error("Invalid ACK number! %u not between %u and %u", + rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1); + goto discard_open; + } + + /* Store current ack'ed sequence number */ + conn->rdp.snd_una = rx_header->ack_nr + 1; + + /* Send back a reset */ + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + + goto discard_open; + + default: + csp_log_error("RDP: ERROR default state!"); + goto discard_close; + } + +discard_close: + /* If user-space has received the connection handle, wake it up, + * by sending a NULL pointer, user-space should close connection */ + if (conn->socket == NULL) { + csp_log_protocol("RDP %p: Waiting for userspace to close", conn); + csp_conn_enqueue_packet(conn, NULL); + } else { + csp_close(conn); + } + +discard_open: + csp_buffer_free(packet); +accepted_open: + return; + +} + +int csp_rdp_connect(csp_conn_t * conn, uint32_t timeout) { + + int retry = 1; + + conn->rdp.window_size = csp_rdp_window_size; + conn->rdp.conn_timeout = csp_rdp_conn_timeout; + conn->rdp.packet_timeout = csp_rdp_packet_timeout; + conn->rdp.delayed_acks = csp_rdp_delayed_acks; + conn->rdp.ack_timeout = csp_rdp_ack_timeout; + conn->rdp.ack_delay_count = csp_rdp_ack_delay_count; + conn->rdp.ack_timestamp = csp_get_ms(); + +retry: + csp_log_protocol("RDP %p: Active connect, conn state %u", conn, conn->rdp.state); + + if (conn->rdp.state == RDP_OPEN) { + csp_log_error("RDP %p: Connection already open", conn); + return CSP_ERR_ALREADY; + } + + /* Randomize ISS */ + conn->rdp.snd_iss = (uint16_t)rand(); + + conn->rdp.snd_nxt = conn->rdp.snd_iss + 1; + conn->rdp.snd_una = conn->rdp.snd_iss; + + csp_log_protocol("RDP %p: AC: Sending SYN", conn); + + /* Ensure semaphore is busy, so router task can release it */ + csp_bin_sem_wait(&conn->rdp.tx_wait, 0); + + /* Send SYN message */ + conn->rdp.state = RDP_SYN_SENT; + if (csp_rdp_send_syn(conn) != CSP_ERR_NONE) + goto error; + + /* Wait for router task to release semaphore */ + csp_log_protocol("RDP %p: AC: Waiting for SYN/ACK reply...", conn); + int result = csp_bin_sem_wait(&conn->rdp.tx_wait, conn->rdp.conn_timeout); + + if (result == CSP_SEMAPHORE_OK) { + if (conn->rdp.state == RDP_OPEN) { + csp_log_protocol("RDP %p: AC: Connection OPEN", conn); + return CSP_ERR_NONE; + } else if(conn->rdp.state == RDP_SYN_SENT) { + if (retry) { + csp_log_warn("RDP %p: Half-open connection detected, RST sent, now retrying", conn); + csp_rdp_flush_all(conn); + retry = 0; + goto retry; + } else { + csp_log_error("RDP %p: Connection stayed half-open, even after RST and retry!", conn); + goto error; + } + } + } else { + csp_log_protocol("RDP %p: AC: Connection Failed", conn); + goto error; + } + +error: + conn->rdp.state = RDP_CLOSE_WAIT; + return CSP_ERR_TIMEDOUT; + +} + +int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) { + + if (conn->rdp.state != RDP_OPEN) { + csp_log_error("RDP: ERROR cannot send, connection reset"); + return CSP_ERR_RESET; + } + + while ((conn->rdp.state == RDP_OPEN) && (csp_rdp_is_conn_ready_for_tx(conn) == false)) { + csp_log_protocol("RDP %p: Waiting for window update before sending seq %u", conn, conn->rdp.snd_nxt); + if ((csp_bin_sem_wait(&conn->rdp.tx_wait, conn->rdp.conn_timeout)) != CSP_SEMAPHORE_OK) { + csp_log_error("RDP %p: Timeout during send", conn); + return CSP_ERR_TIMEDOUT; + } + } + + if (conn->rdp.state != RDP_OPEN) { + csp_log_error("RDP: ERROR cannot send, connection reset"); + return CSP_ERR_RESET; + } + + /* Add RDP header */ + rdp_header_t * tx_header = csp_rdp_header_add(packet); + tx_header->ack_nr = csp_hton16(conn->rdp.rcv_cur); + tx_header->seq_nr = csp_hton16(conn->rdp.snd_nxt); + tx_header->ack = 1; + + /* Send copy to tx_queue */ + rdp_packet_t * rdp_packet = csp_buffer_clone(packet); + if (rdp_packet == NULL) { + csp_log_error("Failed to allocate packet buffer"); + return CSP_ERR_NOMEM; + } + + rdp_packet->timestamp = csp_get_ms(); + rdp_packet->quarantine = 0; + if (csp_queue_enqueue(conn->rdp.tx_queue, &rdp_packet, 0) != CSP_QUEUE_OK) { + csp_log_error("No more space in RDP retransmit queue"); + csp_buffer_free(rdp_packet); + return CSP_ERR_NOBUFS; + } + + csp_log_protocol("RDP: Sending in S %u: syn %u, ack %u, eack %u, " + "rst %u, seq_nr %5u, ack_nr %5u, packet_len %u (%u)", + conn->rdp.state, tx_header->syn, tx_header->ack, tx_header->eak, + tx_header->rst, csp_ntoh16(tx_header->seq_nr), csp_ntoh16(tx_header->ack_nr), + packet->length, packet->length - sizeof(rdp_header_t)); + + conn->rdp.snd_nxt++; + return CSP_ERR_NONE; + +} + +int csp_rdp_allocate(csp_conn_t * conn) { + + csp_log_protocol("RDP: Creating RDP queues for conn %p", conn); + + /* Set initial state */ + conn->rdp.state = RDP_CLOSED; + conn->rdp.conn_timeout = csp_rdp_conn_timeout; + conn->rdp.packet_timeout = csp_rdp_packet_timeout; + + /* Create a binary semaphore to wait on for tasks */ + if (csp_bin_sem_create(&conn->rdp.tx_wait) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to initialize semaphore"); + return CSP_ERR_NOMEM; + } + + /* Create TX queue */ + conn->rdp.tx_queue = csp_queue_create(CSP_RDP_MAX_WINDOW, sizeof(csp_packet_t *)); + if (conn->rdp.tx_queue == NULL) { + csp_log_error("Failed to create TX queue for conn"); + csp_bin_sem_remove(&conn->rdp.tx_wait); + return CSP_ERR_NOMEM; + } + + /* Create RX queue */ + conn->rdp.rx_queue = csp_queue_create(CSP_RDP_MAX_WINDOW * 2, sizeof(csp_packet_t *)); + if (conn->rdp.rx_queue == NULL) { + csp_log_error("Failed to create RX queue for conn"); + csp_bin_sem_remove(&conn->rdp.tx_wait); + csp_queue_remove(conn->rdp.tx_queue); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} + +/** + * @note This function may only be called from csp_close, and is therefore + * without any checks for null pointers. + */ +int csp_rdp_close(csp_conn_t * conn) { + + if (conn->rdp.state == RDP_CLOSED) + return CSP_ERR_NONE; + + /* If message is open, send reset */ + if (conn->rdp.state != RDP_CLOSE_WAIT) { + conn->rdp.state = RDP_CLOSE_WAIT; + conn->timestamp = csp_get_ms(); + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + csp_log_protocol("RDP %p: Close, sent RST", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); // wake up any pendng Tx + return CSP_ERR_AGAIN; + } + + csp_log_protocol("RDP %p: Close in CLOSE_WAIT, now closing", conn); + conn->rdp.state = RDP_CLOSED; + return CSP_ERR_NONE; + +} + +/** + * RDP Set socket options + * Controls important parameters of the RDP protocol. + * These settings will be applied to all new outgoing connections. + * The settings are global, so be sure no other task are conflicting with your settings. + */ +void csp_rdp_set_opt(unsigned int window_size, unsigned int conn_timeout_ms, + unsigned int packet_timeout_ms, unsigned int delayed_acks, + unsigned int ack_timeout, unsigned int ack_delay_count) { + csp_rdp_window_size = window_size; + csp_rdp_conn_timeout = conn_timeout_ms; + csp_rdp_packet_timeout = packet_timeout_ms; + csp_rdp_delayed_acks = delayed_acks; + csp_rdp_ack_timeout = ack_timeout; + csp_rdp_ack_delay_count = ack_delay_count; +} + +void csp_rdp_get_opt(unsigned int * window_size, unsigned int * conn_timeout_ms, + unsigned int * packet_timeout_ms, unsigned int * delayed_acks, + unsigned int * ack_timeout, unsigned int * ack_delay_count) { + + if (window_size) + *window_size = csp_rdp_window_size; + if (conn_timeout_ms) + *conn_timeout_ms = csp_rdp_conn_timeout; + if (packet_timeout_ms) + *packet_timeout_ms = csp_rdp_packet_timeout; + if (delayed_acks) + *delayed_acks = csp_rdp_delayed_acks; + if (ack_timeout) + *ack_timeout = csp_rdp_ack_timeout; + if (ack_delay_count) + *ack_delay_count = csp_rdp_ack_delay_count; +} + +#ifdef CSP_DEBUG +void csp_rdp_conn_print(csp_conn_t * conn) { + + if (conn == NULL) + return; + + printf("\tRDP: State %"PRIu16", rcv %"PRIu16", snd %"PRIu16", win %"PRIu32"\r\n", + conn->rdp.state, conn->rdp.rcv_cur, conn->rdp.snd_una, conn->rdp.window_size); + +} +#endif + +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/transport/csp_transport.h b/gomspace/libgscsp/lib/libcsp/src/transport/csp_transport.h new file mode 100644 index 00000000..7fcda3dc --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/transport/csp_transport.h @@ -0,0 +1,46 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_TRANSPORT_H_ +#define _CSP_TRANSPORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** ARRIVING SEGMENT */ +void csp_udp_new_packet(csp_conn_t * conn, csp_packet_t * packet); +void csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet); + +/** RDP: USER REQUESTS */ +int csp_rdp_connect(csp_conn_t * conn, uint32_t timeout); +int csp_rdp_allocate(csp_conn_t * conn); +int csp_rdp_close(csp_conn_t * conn); +void csp_rdp_conn_print(csp_conn_t * conn); +int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout); +int csp_rdp_check_ack(csp_conn_t * conn); +void csp_rdp_check_timeouts(csp_conn_t * conn); +void csp_rdp_flush_all(csp_conn_t * conn); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_TRANSPORT_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/transport/csp_udp.c b/gomspace/libgscsp/lib/libcsp/src/transport/csp_udp.c new file mode 100644 index 00000000..61732703 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/transport/csp_udp.c @@ -0,0 +1,49 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../csp_port.h" +#include "../csp_conn.h" +#include "csp_transport.h" + +void csp_udp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { + + /* Enqueue */ + if (csp_conn_enqueue_packet(conn, packet) < 0) { + csp_log_error("Connection buffer queue full!"); + csp_buffer_free(packet); + return; + } + + /* Try to queue up the new connection pointer */ + if (conn->socket != NULL) { + if (csp_queue_enqueue(conn->socket, &conn, 0) != CSP_QUEUE_OK) { + csp_log_warn("Warning socket connection queue full"); + csp_close(conn); + return; + } + + /* Ensure that this connection will not be posted to this socket again */ + conn->socket = NULL; + } + +} + diff --git a/gomspace/libgscsp/lib/libcsp/utils/cfpsplit.py b/gomspace/libgscsp/lib/libcsp/utils/cfpsplit.py new file mode 100644 index 00000000..9a350e3e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/utils/cfpsplit.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +# Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +# Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Split CFP header in protocol fields + +import sys + +def usage(): + print("usage: cfpsplit.py HEADER") + +def main(): + try: + hdr = sys.argv[1] + except: + usage() + sys.exit(-1) + + try: + hdrhex = int(hdr, 16) + except: + print("HEADER must be in hexadecimal format") + sys.exit(-1) + + if hdrhex > 0x1fffffff: + print("HEADER is not a valid CFP header") + sys.exit(-1) + + print("Source: {0}".format((hdrhex >> 24) & 0x1f)) + print("Destination: {0}".format((hdrhex >> 19) & 0x1f)) + print("Type: {0}".format("MORE" if ((hdrhex >> 18) & 0x01) else "BEGIN")) + print("Remain: {0}".format((hdrhex >> 10) & 0xff)) + print("Identifier: {0}".format((hdrhex >> 0) & 0x3ff)) + +if __name__ == "__main__": + main() diff --git a/gomspace/libgscsp/lib/libcsp/utils/cspsplit.py b/gomspace/libgscsp/lib/libcsp/utils/cspsplit.py new file mode 100644 index 00000000..f4ed942f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/utils/cspsplit.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +# Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +# Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Split CSP header in protocol fields + +import sys + +def usage(): + print("usage: cspsplit.py HEADER") + +def main(): + try: + hdr = sys.argv[1] + except: + usage() + sys.exit(-1) + + try: + hdrhex = int(hdr, 16) + except: + print("HEADER must be in hexadecimal format") + sys.exit(-1) + + print("Priotity: {0}".format((hdrhex >> 30) & 0x03)) + print("Source: {0}".format((hdrhex >> 25) & 0x1f)) + print("Destination: {0}".format((hdrhex >> 20) & 0x1f)) + print("Destination port: {0}".format((hdrhex >> 14) & 0x3f)) + print("Source port: {0}".format((hdrhex >> 8) & 0x3f)) + print("HMAC: {0}".format("Yes" if ((hdrhex >> 3) & 0x01) else "No")) + print("XTEA: {0}".format("Yes" if ((hdrhex >> 2) & 0x01) else "No")) + print("RDP: {0}".format("Yes" if ((hdrhex >> 1) & 0x01) else "No")) + print("CRC32: {0}".format("Yes" if ((hdrhex >> 0) & 0x01) else "No")) + +if __name__ == "__main__": + main() diff --git a/gomspace/libgscsp/lib/libcsp/waf b/gomspace/libgscsp/lib/libcsp/waf new file mode 100644 index 00000000..4b322f1a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/waf @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# encoding: ISO8859-1 +# Thomas Nagy, 2005-2016 + +""" +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. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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. +""" + +import os, sys, inspect + +VERSION="1.8.19" +REVISION="b1fc8f7baef51bd2db4c2971909a568d" +GIT="22213cd8abbd141bda40667f7ca2a48f2d6ad785" +INSTALL='' +C1='#5' +C2='#/' +C3='#,' +cwd = os.getcwd() +join = os.path.join + + +WAF='waf' +def b(x): + return x +if sys.hexversion>0x300000f: + WAF='waf3' + def b(x): + return x.encode() + +def err(m): + print(('\033[91mError: %s\033[0m' % m)) + sys.exit(1) + +def unpack_wafdir(dir, src): + f = open(src,'rb') + c = 'corrupt archive (%d)' + while 1: + line = f.readline() + if not line: err('run waf-light from a folder containing waflib') + if line == b('#==>\n'): + txt = f.readline() + if not txt: err(c % 1) + if f.readline() != b('#<==\n'): err(c % 2) + break + if not txt: err(c % 3) + txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) + + import shutil, tarfile + try: shutil.rmtree(dir) + except OSError: pass + try: + for x in ('Tools', 'extras'): + os.makedirs(join(dir, 'waflib', x)) + except OSError: + err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) + + os.chdir(dir) + tmp = 't.bz2' + t = open(tmp,'wb') + try: t.write(txt) + finally: t.close() + + try: + t = tarfile.open(tmp) + except: + try: + os.system('bunzip2 t.bz2') + t = tarfile.open('t') + tmp = 't' + except: + os.chdir(cwd) + try: shutil.rmtree(dir) + except OSError: pass + err("Waf cannot be unpacked, check that bzip2 support is present") + + try: + for x in t: t.extract(x) + finally: + t.close() + + for x in ('Tools', 'extras'): + os.chmod(join('waflib',x), 493) + + if sys.hexversion<0x300000f: + sys.path = [join(dir, 'waflib')] + sys.path + import fixpy2 + fixpy2.fixdir(dir) + + os.remove(tmp) + os.chdir(cwd) + + try: dir = unicode(dir, 'mbcs') + except: pass + try: + from ctypes import windll + windll.kernel32.SetFileAttributesW(dir, 2) + except: + pass + +def test(dir): + try: + os.stat(join(dir, 'waflib')) + return os.path.abspath(dir) + except OSError: + pass + +def find_lib(): + src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) + base, name = os.path.split(src) + + #devs use $WAFDIR + w=test(os.environ.get('WAFDIR', '')) + if w: return w + + #waf-light + if name.endswith('waf-light'): + w = test(base) + if w: return w + err('waf-light requires waflib -> export WAFDIR=/folder') + + dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) + for i in (INSTALL,'/usr','/usr/local','/opt'): + w = test(i + '/lib/' + dirname) + if w: return w + + #waf-local + dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) + w = test(dir) + if w: return w + + #unpack + unpack_wafdir(dir, src) + return dir + +wafdir = find_lib() +sys.path.insert(0, wafdir) + +if __name__ == '__main__': + + from waflib import Scripting + Scripting.waf_entry_point(cwd, VERSION, wafdir) + +#==> +#BZh91AY&SYmEõKQ#/ÿÿÿ°#,Óÿÿÿÿÿÿÿÿÿÿÿ„ Y Â#%H4#,`(bÜrû}Ñ#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,ÕÓ­ul]ZåÍë}i{l4­´ï±®í_pÝlkLϱ÷dzvúݺ{¾]òxì¾ó¥Ô´©…Rúøq楯½¯y\ÃÛe… h«Ì÷g¡éEm.Ç»u­w¦×ºÔë]2]ôõyk=·{»º¼å·[ž®×™½×ywÞ@=ggÅíkWe÷œ}}À}ìowÕãNzî#,#,#,è#, yì#,x->€è#,ûØÜ<¼À*©©{¸WLåÜû·a¦ƒOl¥Ø5nûÛ§FƒËÊ#,·m6Ð ØÑÛPP½ºu(QBª#5 JJöeGc@#,¤’ P6bªîàïA—nâûîׯZ>ÝÃãH½ž'»{´½íp#/ÍKØÔvPÊZ6îîûÞtÃjÒ>©·Þ·£¾{šûzèÓÝl›»_7Zíôìíëéß}ÙÛ¡¦MZûÚõlíζˮ83oM<€k=bWGÓªU¤Ž rz5O{½ÏyÞå뽇 ôz#/o3ÕPKh—F;ÛÝæ·Ûl5­#5ªvê½Ù¦‡¯f­î{©%žöëÕŽÚ»sg¼RÃË’î}¹Ÿ}ÞÐ#/#//·ZÅ"õîiµÍÀŸç@[ßsÇ­·®Øèì^s +ŽÞ€>û½îBqŸ*ÓKïxÓG5_/»=ÝÐ=ª£¾Ü'Ø}Â>9¶¦¶ë¯½÷e…åÏ6íëžÝÓmõvuÛnúåëÎãï{›E³{«·6·u.#/Ýö»ÎÛêï·îNî»^FM>ŽO}¤ÓˆßX¥­áÍL|R¡ZÛu.9ñ.÷;ìí«/nûÝÞÛ¯7rúçµj;<è¼ßF¾ù¹¾öogƒŸnžïj½zÛ|`uï¾uòú-íèúy=gÔ#, J=°#@˜S{0)ÚRO½Ôó5^‡¯^ñN²>·3îeÝÖk|½ÚðöP•Ü§;{¸Š‰ïUíØ)¨+×Kßw^|#,=Üu #,=ïŸ^—|ívõÞ§Ãv¾Ÿqw{ß]8¾ŽtÓz#¸§í×HÛ˜×@ÓÄåÎÙì^vÛlÒÈλ÷¼zÞ‹›g¾|å6ÝÛÀ%W ¢ëî»ïW×ݶûêx};ìßešô>æ&¾Ž{o=—Þ}î»{¶oŸkÛÝÞ:Þ{}»‡ÛkL}½>û>ya—Û½¾vòÛÀÖŠï°£®šqM_^/…ó:è5í„éx_-ê»IÚžÛíïynOµ@}o³+LUS¯4ºŸZ:û=àuîžæî­a+ïW{ëS¥¾ofw[{½ï\î7Ïvò_|î÷\VŠ€:Õ/±¡Û̳ÏnR„+Åw¨Ñ†#/×_wÑãÛ@mg #,#,ùíÜ:;¶5èkGv»sÉ®Wß"}½Àt@õïxõõèÞê“6ù·wuÏN×Vvû¤½­Îíº‹F’HÍÎ.î‰õ‡Sy½çUr^ßN/FϧÝîO®·Ð¦çq«^;N!ßyNû¬·£à´Ä“NŽí¸{›ovŒkwV¾]ÜÚÛv™òzÉ÷ßskí´ÙNÛ®Ûo6¾÷ÏSæo‡#|w M @#,É Ó#/ ™iODžˆ¥=CjA‰‘êx JhD@„Äi2hÔj6™Sð£D4òÓ'¨#,#,#,4#,#,A!! A¢bh' Sòh ž©6Dõ='©é6 Ð7©#,#,#,#,#,'ªQI4j<ši‰¤ôSf”ôÑ=OHz@#/#,Ш#,3I¡ #,#,#,#,$ˆ#, i0M#,§¡¦‰¦"{I“I‚O$z4Ð#,#,#,#,j"#, Ñ #5™é=SCÑ<©ížò‡Šzž§ê˜€ Ð#,È×ý+m«ÿîbcú†Ö¹µ¹uÉ~ÆÛS°“)#5=¶®º3&ª-5³åV­WêêmV¾Èþ#òþ'—òüµû #Îo{ÉÃMòÎg5ÌÇœ¦o9­Ì¦ç.înç9£šs-Ïð´èJ}B-~¨Z¯¦ÛRªßkæÈD&Tƒ%ŠªÆ(°xT)U\óâãóÏEÓ<š7^øIË̶ëXI³&>rµŒÕÞVª«L*ØiH ?#/ÕõŠ7ÀR ÀYeË©jÒµZ*Ú1µm#,-ÕB¨IX(¤È‚ ‚¡!„[*E±#,|à…R‚ Õ F±V«m¯¿kjµL‚@¡ ı™¦ &D4c6™QSR%Œ‰%(ÒŠm¤¨EL’X XMSQT”DJ “4M”-”ÆJÌ £I,hM0¥F’)6"ÑJmIJZS"Ô@ %–’#5’`˜2’)lQm“T#/A”²¢–&¥()%Ò‰Yš‹F¢´«iU©I6¢dÌ™4lUZ•¬f˜fÌÓ6¤±šm°KM±5-M2jadS$š djF•¤ÖB*M&ƒL’ZH-¦±’²h˜!`£3M1šlb`•4°HA˜†31#/†I‘(‰(l›R@‘KŠÉ¡’#2‘„–f–)2A´˜c%4–ÒTZ#/¦5*ECI†Š4Òl(’Q&È¥±1Pl™M¢’e¤2’&ƒF”ŒÉRŠ)™6ÈT2(–E’BH…6"ÒX5 ¨…"J*5D”…3b $Ô–)1H‰”Œd™3Fa-™S ØJ‚ˆ‰!*M‰ ’M%`Ä”–,³0lÚ#M#FÂJ!e$ ¤Í‘)š¥›,¥Ë0É¥’ji”h#/¥ŠK264ed&bIZ––HXCeA¦Ì Æ£f‘±†SKS[M†!3H¢ÌDjL”š@ÓIK-,©QQD‚Š#/‚&$Ȉ’”Æ4 #$4ÌP¢!•3-¤EaJ¤!”©&B”…6’ÒQl‘¬i&I% E’šÈ¶F̆LцÉ&Ê“L¦¢1Ë*™¥1 P©“DTѳSb“(²2bJE*I!e2,Ji‚´¤É²’f&É£&B¡¤`B5$Ë)YI†ÐY6J@“!%“(ÈE3A’2™HË0–’•Mš*6‹BeÈ”‘ÔbØÒLÖE#/&60’FTi#/*%k,˜Q¬eš™"f‹&–DJÅ,€5JJ2ŒÒ5µ6Ûh(¶0IS@´”ÊHÑb™)’cI£l–”³F˜¤Ìe3K*M”±jdcal’#5›(ÊFhÓe¯Úmv4…•¤¤%J2SX«EbÑQ²IšTDÓ%KAQ°Š’V¦"†Æ–!™e) RF³*Ô&Ú,Šk&Cc)–•E¡bd¦5-*c$&,32•Y£e”#k*ER©X¦Ê$²£)µdÔˆÖɦVD©5 [%YYJSL+f(i‘5£KMD–’%ƱXÕ3h¶ÛH3`±ªÂF¤Ñ¶Æѱ²Qª3LÄ5E!¨4jÅQ°ÍHmbY0¢5³Rhe1%‚h±„5(”Ú–Äm$šÆ5!b´’šµ³VÓA,#ei6F&³l‰3)©¥#5I–Õ6¢–’”Ô²¤¶š¦ÒZ¦˜“²¦Rɶ–m¦ÌZ4f¶k32I¬±¡›¤ÛKYRSJ(ˆ C5J4‰ŒQ°b„- 3–ѵ$JME2š£iI¥FLš#/%0ŠE,¨ŠAieH€i¢˜)’e6hbÄ„¤TZ,É b•š’ÀclË3K1š`©¨™Ñ2FLlBÌK!I´lI¤£&h4‹2‹S2TÅ4Rma3`Ñ#5TXÌ1S6 RÉBlE†54Ú3 3h¨±±±F„hÆÉ©¡"“aK-“$¤¦L¦Á’Ø…3R‰2I²ÒˆJ¨²Ô™¢6”ÒlÆÒei+Ä#”dfM”“BTš“)FÌÓj[,Ø´”³I²lbTQfŠJfdÑ©–´Œ”¥¢ÁH‘›$FY‘1¢Òš#@ÖY6Jhbb&“I F¢5BTmmˆ­1¤ÃI2ÄÊ &‘h¦•É K#5¥e$š *5“0²ie24´›mIkhЙfÚ‚H´–ÅA£ ,#/›R”JQliIš¨Ù&k2*H’£I"Ë*ÔƆMEc&&VJˆ¦”¡Q¥%4ET±*Je)FÛ`µ$Qd£dª0Ê6Kd¤Ô›j”ÑIEJjµ&ÅCI3d†RTXiH‹- ””ÆhÁ´˜#5‰3BÁª”FÈÊV&#JKEhƲEd„6%`Š1”¶#h¬¤”T€h1E& “V”ZST¦Òj-±¬VÍ1Y-f¢©š’‹Å(#(1³B$¡4l”Á£VÄm‹hØÒšÖHe¢#5Æ&¢Ñ­6Ë6£h´TZÆI5©fÍÒÊĪ1h³,UMIHÛ(Ë)DÒFÊÅ`’Æ’Ù-d¢Ô–±I´¦eM¢Â‘QIF‹kT­´ŒeCI¢$©”Z66† ÒZ[$ÛIŒÉl‚›%Eµ$¶ŠÑ­‹FÛhÚ¡K&ÖÄZ¦¥e´J2ŠbFÒL`©lMšlT‘²È¨ ™‰1$™I2$¡jM±fV¦?‹çý5üKý&¼ |ü©ŒRžÜÐZdI‹©±‡þå©-Iû²Ò/E¹±ò»´–^ï|¿¦¿¤ë×ô£Æ°Tԥƪ?ðÖã,Sý^‰b¨ O¦ò¢ób¬M4ºd7%‚•Ä3³m´5T;%ëvþ>zãyg÷îÐÿƒÌ³'ù£Ÿ,¯uc/eÕLŠYÝ]ÈZ1±, òt–u÷ë-²'«êÆÚe)‰QéjoÎQmå¦Ðn¥ØГfN½1j—qÈqÖ=c,„Â’¦6Ýr¸6=)`ó¨S‚¢5Š¨ªìÃKJÈÒ|9ëW®Y×[#¿Êní·ei”-M(¥@Ñ ¸_ñQ eRËFƶh&«ìºòO9Q ‰¸‘6Y6?óÀÝ€4Í5ȤU5vZ±ËQ­îX?ùhÿÀ¦ãaRµ^Ô›2Æ*Šžê2ØÁdX¿ë¹aßÝZ3#,©Ñ#57ªAOS(C (`)UAÒ¼®—é­u›ërÖæå‹òöêÞKŠež f>ÐD¹Ó§‘&²–9©%C™E¤¹æ%Š¿žªùOUòmº˜šƒ@ìQ†EÇÛ{zY]J4}]¢­0n(i¥JÂn@rpeË’nºìNºéŠƒ}K[ËÎÿlàUŠ ,D¬ÐÑ(Fí¶R·-"‚¿MTFzè­UiŽ´ÿÚz`™`ÿ¦ˆÎ™O-Li6ÂJ°¹@`@hÔ¨OÐVâ<çÕ€˜Ú˜Dã™{Œ’àq¶Ü`Cò“U}Ã]¿l]~>îÙ›ˆ­Vpò…§†o‘;J0Û)¨ãjåýeNôÕ8TãLhµÅ©j¶0æCfB(t\´Ã·µö% v3IêåS—ʉf´0 ÷09!àá­ñÁ±´µºÎš‰™Ú†{¦’z XZ%l²ÜÝ(ÂOa"ÚO– ½:Þ>×åŠ0ö@¨ÎÚÑfÉPe‰J#5WïÉ—RcäÝ¿£ZÁ6–‘»GÕ"_ºø7Ýv›$£#}þ»_zîF: ãe.¶W7"^*®P‹ÿ‰¥ð$cós´WŽLŽVÚiþÎУ²?.ãä]u6Š1£^9™dÞ5Ë–Æ«ù)¨Óï84mëwßêü¯ ™þº¾ïó{_Kâíå¹£UÝ\XŒQd$Äa–“Þ뻩 (hdS£C"¬éÊŒ&/º_†W x«¦¬oW“ñ{ÑJ_7*#5¾óáy›â¸ÌWíMu­»4 ‰"1SJBÌV·b·cX²júOöÅòW#kðº„"ȆùkÁ‡ÏPÐ?íV"0ãKŸEÓlØþz'<• "1Ò¶³d< ™!µõwë¼®mÔß9u#Es”H²sª›»Œ…¦õAu¢–•­Bz’ZÅŽ£”ÈP‚wQ&šÒ›³äšêP¨¬¢ÚTD×¾ï˜XùíÏIà•½raißÝv‘’dªFÓÊÊ90Õ L"ÈòªKv‰ÑÄ®áçˆ6ÊÃm-8ÂÉ2ÔQ¶Ùìᦻ`¶"§#5ôºœûªŒÀ(ò@Þø%€.ê}UÁ b‡dtAí²—ðŒ´Ö3Úá·0÷"+„úÇitˈ!cûœøÏE0Œ«“ꆀáÇ6¿v¹ÎÞ,MP—“$XŠ‚Œb‚ ÝT‚}åuÒV …H±CZ*SHuÅqf?áNåyaðÚ»™:¡¨ã…ufÿ/jiÉÐ80jN §Hï¯Öo/žø9`Î>®¿/,šÁ)Bµ)ÍA¿:—+«v#W*ƒÆ|™u ²&7½DjBQN84ÛüÕK°™&d¦«ý#5*mÏí¯gÓ|¸ÕÍŸQNoø (|‹¡Ù(ÅsÛ_á|¼»ðÄV'rï¾I3Æm¼c+¯-ÍIŸEMT}[×@0ØÏÂŽì$‰hJº¯ZÅX¿|ê*—º&dìFŽÚmÄŽÈŽ0¡üGʯg™kú‰;¼e ÷›¤x Á×ÇœønãoîqØàäñÕOì´÷¿.w’ô‡ž˜7p’3Œ¶=æF¶÷¿½P'ãûµ†HùOÀÌhËhÕq¦{æ2°oïüÒ¿„ѯ%BÒ„–¨¯:?_ÛëñͺC—öGÊŠo‡®.W’wD+:Äy/²"]žòæO{{õ•X*Rpª‘ëkq*(øV1)"­¯MëÝ´ò‹ýfŠì†˜­PcMïôå1­AÄ«pÒ¤V=§Aç=ÓFÐì¾ì¿n.ØÜ=oeŽH!aÝ$™hNü|dm«]-üÑ”ÈLÛ@áò:<¥—¤!iõ—o »h-ˆHAWã‰ûs2±ÃpTªj¹î]¬ð«³ƒ-öN˜H”Š#5Ft—æ¸>ªµ¨;ùÖ”ú?ÝŒv3ÜÜ¥D#PübžÐÓ®ÏZ:Ö4@Iî¥*h­=(<—Ü+Ã4—|õ#5ÇÅþŸšµB—_xá Ÿ>Ä4¯]ÀG§H­Ñ Œ|]#5b"«£.xù›ã•#/.SθÊîta¦aæ”ÚØ”†•7lDݶØ1Õmð¿þü,8HÑ“èºr.ÿ›>y#/‘A;÷Éí âo͘«HjEá¤Aƒhlù½W—â(¥ôvÝë\—évê0‚1:éeˆ9Jö0¯¢ÑG’·I”Ü•óåþÕéçþ§[Oïï¼û6±º{0)º¡J‰jUª×’ÒÙ.éaðg¸müÌAÖg­@I#5ÇBÞ‰¼`ÑG¡àaé-†{_#/™[á%Îs¶­OÏ>Ñ»5²F3Ù1õ «8 9ºùý71ÝF¿m€§zä¦(§-|4°²øbÌdj~.£áøQ’ʤX€Š=_©žoŽxpîð§çÌU5øøïWìýÊ,饌„{‰#ŒÖ³Õ'‘Tiç¯<P8°)‰:YßmMî£b‰w@ ‚Á@a”ãƒÓŒ=x³Õ¥dÀ}øëy”Ð+MuÞøb…#5nê3›°èM6~ŠR-äœÙ¡‡Žó\æòx@ëpÔ8\Ê6ir¢t{î¦P¤ª¡AFšòh„»ÜäSC°¡ã1ºb™RüŽÓ`^©š;4õªâÍl)W»µZ›ívE€Àe³CÌL¢ˆ²šèóª¤;&#/F‘Ò§»t^yyÊxq¦.K•ö3¸ë·á˜ËÌèQIŒ:d$Z¨g§7‹ ”†B©É=0JŠBJq¢}öÉé†>4ßi¶DÛ%RR„Ç’Â"[àCþõçoqE1×íàä‚ØáÚ{~Yƒ>«ÉökZ™;xZoÚ¤Lhø‰êÂ×VRŠÄ9³HÉ`œMuôÆÅxë²7cÌOrJD2ÇÓ: uÁv˜ôOîrÊêe“¦DeÿØ$Ï:3Îw׈X© *B²‹Ÿ,žöJø¾â¡œð½Úõˆ^ï0s‡wÒ‚åÓX‹F¶;¢ûn^Ÿß‡Ž›\ÍÈT#5p€Lt"Ý3ÃtíÝé8§Ç§„ÊóÑšMÀ¨§ƒHœ(£1ÅBP0ŒA¢Äßøºj¯f¯†§…Ó»}s[Õ{C©Ý}ÿ>Ï7M/e«å-o‘Œîe‡#5#5Ö¼ ¤dSRª{^{÷ê}i›ƒ¤#,$ ¡NjG÷G5íIÉOµÇÔ?Ë–ç§WègLäãóg]EÇòü*Ô;Þþù» ðzþ¨/o/b‘œwÒA«^?%g%ÛÓê÷ªzn©÷|ö.?#×}$éäüq=^E¤nñí¥¤Ö ìFcïv–f¦¶,ù#/³,ðgîg›œ!³¾#5~Š«ªPžâiå:: Ðš:䲦‰ïQi±WýÐÊ9ªÃÝkdUWk¢™B#/IMvýõgØøé(:0Ì"ðp8®H±/Fý“à@ô:Jæg‘ÿºÃª Uüבä¢e;tÊ™á‡óúÎ3†ÙöÛ¹ðÐC~ÛÍêÜôzM,„ÂçöÂÁT®œw6—j†)Ú!¥kûçövñ|­Cï£ÒPðˆ—.QËËÀGòB|‚rÆ=*D2•þ,Ñ®wÀ ÊÈßà›3•ÏÏÒìâý©ÕÈá´J?JwHMÂSÖ¥ÒXTñj,‡)< Ó'iÆ~ÈØG÷u>’wºÒ9×w5‘¬ú¹Lf8ë$„¿Z‹LÅfã4ž.çÒœLVsR÷¼`ÆY ALjg½)ÀÅ“BÏj~oJ“4„³}vZZÉU):èg {·£uü8P¹¨x󘳜ï³leüÊ:o]éùÙìÞÍÄîß<ï{ÔǽY/”?3Çðé5œÝà_µÊÐÓ1)4º:mË¿–šCuŒ›¥»¥ Ì „(b1#»/4ÜÂc-Å;Ä2Ž…Ç£«1T]†ÆcFzÏf¼ñ…YÕ#5FmUSZ6I»+dU½^¶S#/›4ãßO ËD=ô[¡©ÝqŒ1ùÚÕ¶UOÛÖ±Zä~ë3"Y·þ‚c|4oZ–0åÁËú]y‚üQ›‡ÛQâûÿFÃãó뼟#/6#/ùAA“Á©zø`e°¶÷¢—TT•˜,„UQ²–PûzÝkLCh¢%jV•MP-R‰¦˜.ð€èÔdNdƒMÙdJ¿•ù¢ç;­Ži]š\òQ?†8ÁUòØ–†Å#/ yJŸµRN5ûZ+H÷5úZ:||jînÖá¹½h§ÂqÖŽÌìëÙ˜ÕaÚtèI«Ý#¶Û‘‘§&ZƒÄ‹#Œå Ñß÷CÙmÃ)ã.#/„6G®it¬„/½iô®µP-§¥Ž¶›~òâZ0ú:¿~®J+JmÇM}î/sÚ»±¸´^i@ÁÑñ©Ç%”ÈCÊAòÍThÝ°Ñû|Æ‹Ç[4¨wpUüÜϧ‡ýú#稺…f¾\bÊÏLc‰Ñç¯B°Vü8éqæ÷ë²^©À€¡ Æ&[>¼Üµk6ª«Ž˜ñãl<ø9^K#/ÎÌSm/××;)ÂøtýVÖE×,×iýëþÖw-#5õÄ4&á7ÂߺutßÅdwçi#/>÷ÅZÙ~ŸÓ™¡©]w«DÚñ¥ï¨û|œ»}>˜ê®#uŸ¤†òñ´ÎÉáMgYYRóªó«TY¼q‰f¤Üбœ*qoj å›®¥ñ“f+­?Éæg—mº‡Ký¸Ç&Lã·C}W$3Ó«·4àe)„Þ«­býŒÕ“|×Æ©Ú²ÂÐ>u?……#/$î‡Ìã–æÂçŽi¤[Sß¹åÔ‰¦Ì“æƒïù¯|Ó #/‰é®Sw?„ëë›Ù{œùFÃÂ.Ó_Y$ŒøFïl;timâøv¬¨CÛÇO”+)ŽÐ~:*‰ºx­d!žÆ4W'߶Q1üÖðoØ„÷IëúÂfOÏËz6½Š²k.&Ü‚õwsŠð$D981…2â®Í»±í´øåsÊ#¬ŽOn6ÖØ›*3לÊ[LÖhùW’"¹#1˜|α#,TªME‹ºMqéªoävs6¸]–/œqŽ\ø<$öAó©C}¾%è•©LÑÉáSéM$ÔV9Ÿl{§šzk;öO~)tW¦\¹kר›¥§Uo‰˜–¬LÏ|^Ó‘s›¥žsÙe¶»¹ÖÎÝ›ìanÊ‘ üj‘@z•A6‹ßøý¼†ö´3Ž°¬·‚W{x‘a$ý++o}ÝZ¢@þ¨³e]Ò¡ºà•3¹äúT?Ù…S¨ÛU5ºÚaŽãnæ~ØbPI𥠮Ž¿/;#5ä‘ú ¦ÞªR“9ØK8ǣü¼9·Ùѽ(ÃKhœ-¡ºGsÂ(Ùk8Aqj wÇ”„Uü° s¢Ç“ÏŒÚãî‰Uy_¡'·~Â+Ž~ÕXgÔÒ]©I•â#/Lx¢ùBw3Â>gGè•B²BÇq×]!©¨w]ßçÏœ]vÆg›ëNßM4ô¾À§aBú9æsÒ5xöí¨£ÞsµG ’†Y¡Y†µ£$ÔÃîz´”¹@¾ì3š^Gt—+Î%y ݽ,Ù|ÛõR¡T¼$¥¼´IëCî—ÁYÛ;gX²$…MëÄäóÇC ÿ‰§Ù¢ªvgÑdKÍ9oI×V³…™ëÅ]ŸJÌvsȵ¤ŒPRÑ®¶Œa“¿÷3QÙ¬C Œ>©½–Bû÷ìÙ0´·¥òTUBšˆEeƒµ¶([¡(ùhí†G„\ìÙII“Þª‹K®D+²QÃášÀ«YÍÑæf®_m#/žìt};ôx#¡ZÍÎÖ·øÆ>cÿ8K|MPÓÃýPññå’ŒDÂd!,›I¶KƒÊæƒeN–ÇMÑbˆdÜ]‡ç;ê>¢ÉUò!¾yzwîgÓãòç¿ÐªPÛî[Õ@ðú?š@“L$NLjÿU­£¤PX´[7åä³ev4ú®P©iÏ›iyÐI'iq v†Êyº#/z¬¨¤8è‡Zõjòú¾›ž½¦Æ†þ Éîæwñz{&|I¾ÝZáÐX‰˜¼DÛFì Þy‰ÉT,cšÉ˜@#,˜`>únãùªfk­ÚøX~8Â~%·e“¨ócº¨šÝäÝÿ<ÿ¦æÒÛ Uí·i€Û À‰G’oŽ ÿ_;#/¿[´²Da©wà¨Õ  "Å‹>gΤÇÙµ]'•neÂfqU Å‘©z='U.aš6|½¥v•…O…¼¹süÓ¤QîüåÜÉšo$ !ãñU³,ÉU«}Z#W`åÕu”8Ò0HÈE[„^ÛÜ`//«]º#/tP^(ƒ–¸–wQná…·„VÍ%Ît’bƒ(ÚÌÌám˜Ìݽp“L'o˜“ 5û'ìs¥µË/”ô›Œ@ V„ÈM^%€æ-©ÞîbæaµÃÝ—«©bíð‡¢›ú~øûþâ€cþÕ À¬-Á§ø°t‰š„¯£PŽ#51U·Oìï,…P‡Â&£ïöw̯m¸âÇø?èòb®uYƒ©ëµ®Ê9•TñÏ?ŸM,ýxÁª·ögß ÌÚ¡¯F΃›ãӮݷXG÷ÂíÈÑU^ekuþß'TI²A¦Hk+Í~Fî!I—vsõ×^Å€ã€ÎQçs@õM=¸ä˜#/¤fÿ»¦ÙÕQL"žSL+Ü¡KSiÆÔ£ú sOǃ:…NVʹ”SsAßPT%B'ú? ˜u'.BÝÝ®³é©±ËPõŽÐh`¡¯1ŠÕ¾¤´¥æã@vü©õh7)ÝwÎyî*³Þ‘Á­I$5!ÝÂe‘óÌr&šmïC³C(¸³W&HLÁRÊý’iÚB­YàÓ{­ØNlÍ¡3¡†’)G¡5ç4nÛ@Û;Þ7覧þÊÍ‘‚)¾œuËÌÃVfeš5IDðç”jß„!@¿h¤ŠG4hf6D™X9FF)èÄ»ÝünZC†ÄDKßm£IúüyüÕ¶aÓÒå»4ÁAÀŲ¥Âñ?†¥´¦g² ýX<Ä ÂZ3°áÊÞ´IÊc#/ŸHªc?Œ‹œXXÌúéíåv‘ïìÏðŽ[Ù ¼»Œî*뉣êQÖ°ÆŸ{wN4‰Fè·ÀLdIAÛƒ´ˆŽší3¨×[”˜B¿wE fì°²‡Œ„©8÷p€»VCùô ¦§±#5÷L5i8.^£TéiݬòÇ40öþ\Î×s¶©ì\løîlÿ‹=ÄlêŸp·¯vöµ®M#/µ>ÇùòúàW§{ßl½3㢂̶WTô™ÈÀ×xTs+•¸S´ÀÚ{¥EP¹§~n^&¶þÅPï¦Ê.XZ9™IÑ®¿¥=SF™9ˆg!8ïyùþ—W¡4`˜üÔytÅ1î˜×ó©7,»/+i'}‰†nß”Ùðþî©“–Ðû†¤lï°#Ö:§¬>sñèÒòš§˜ÊÛl;BÞ`–§iùŸ•dŒVÁFú8Úq”ªtyG6¿Ÿ5#/+žÆúo¡E{fð÷w »¬ppÍ(”&d¥ïqÓ­|ŸÃf¸•‚áºÒIÀçË¿1œ åU‹c¿v¨Ø餑&`q|kŒLþ 5XQpùàâj‹‡Ü23(!°Èò¦íæËî–;¤„Aë|»¿Âö¢ãY#5mÜJä:Å!Ó·³¡ñ¦øïûkM³çV1»è¢ÊÇ“’&”˜«aÌ/†'Þóžk¦ùòԉ½ž*ŸNu½ÁD½L‚Á3´‰´µèJß?‡Ðg<á@dCDžÃ#5j3xÚX‹TiˆÒnÀ£:2.î\µÆ ¶Öƒi¤šQccyHà,ÇlÑa%Btš“0úæ?œq§¤ôÌü.£C¿ÿ`‘BÄ=uìç"1vŒ•€‹ ´‡Ðb*póÜÕe…6oçü¿ÈÌÌ¡™´!³¡!&[3F a6?>¹6LÕâå\¢•AuØÍ·¬.ÓªúR|ân [#5 hŒ;2ÌJAÈôSÄ£6.²ì³fÂJf”óêZ%•+æ|bôcÊN¶G.ä¢Óg]w$¯Çû93ÆÙéó?³ÄÉî®Ý5×J7*Wk˜²jrï•¡ ªÑÎ{ÈoóÆ;‹ÓXýZš„ww´Ù·¾ ØnÚ3‹óÄ@ûƒâ\ëÇHfÅ]†ZdjÊ!“â¼v¹5n>OsÔ5›_FjÂÿE[Å7sn×(Dð˜¤Úã¿ã©Û²f}‹ˆo(}Ë¥é/o¢ùÝ3MÔ#/*ô\˜ÒÛS‚c"ðµ]tÇe¢°üL(Dvó5èyÓCêÒeòó´Ky£lÎú!ÂÅ8–Ud˜ùî–Ià ‡0¹6ä`‹œ½B˜àB[‰CÕeØ©xìú\ªN¯ŸXÄ?Ÿ~öŒZWÞxßV8gŒâE/ptv6À¨¿1+Í- ÜKu—–™‡ÂÁ!+²5D7­KaA³Ù¦qšd·Žˆ=3`¢QSO›ú»ÓîŒýËñbYuvL 7´Æ` þ_Ïò3”‘ldÚZ¯®Ü+ÔCä–|’ó9©Ï»ÀªóÞùò{ê}2%\z¸m‘oM[ ‡þjÞ=Ì4å¾q0öÁH¦Y²aµ8B󺻼ó4æ¯;‘˜’,i•¦JH,Š£µ¾XÐLèÖ¿o?Gßœ=úr8üžLÔËe%0Š(5aH­RšÄPÁŒ+š>­{±­¢jED¥EAa zêVå —%YbTÂ}~Zâç 9‹×;® Š´< 9ÅFŸ*ŒP¢“’Oö~º+fcÈÇW%Ÿ®ƒ’莚ù¦‚0þ²HXºÐf¸0íf¼d­ø×Êú£Ç<wòÍT¿uH6\‰²E…0i©¥Ìà]äÉ*ê‚ÐÖD\LÄÀ‚l`Mü6W›ôñŶ•u=&0á‡t……"|"n¾½¨Æ¸‡L™×MoãùvÈ=Eóç'8H£¿ÈSÏ·©¢Œ–!#NÌí^ ýó†Z«¬i[<.ž6P*‰#5¥«7eP¹òƒS†¶ÙßÐ7bPžG®æˆŽRÖŸù2TÆ4`œ@×8jLL¦ŒF“K"áfƒµ™­ ‹å™›#5`g©¡œæÊ-TbB¢Â®¸ÜÑß]Mt:›´Ë®ñ^6ú-ËJöh?­‘¬ù¤1Š¢¶à¨%¢ó0ÝáÑ")œ…" G¿J.ëϯé‘úw½yŽüˆSªICQÄ(`a kï5¼UÉ4[5-Q¤aÉ‹#/XEÂV\ !<î­r×ÉnÍ⯄VêVÉ­š–½Q½é­°åBê£hD‡‹F{Ip×kN:µóŠW9HU’غ¯«U¥.àHªo>µ]Vâ"/z(•~©r#5°«+j¾O AÜÀÎÆîK]›ÈcÙ«×ûj¯v={[vëƒdIÑàj=qÞFRUxoÍ¥Ÿñp UswØU+nnÈ:.öÐÍôz¡êÓÓ–e~ƒ<P¶1öÊ1gMÛ7*â_'‚Ï0úº½ÀJÅ7(’Ù#5™H0Q’車£WË2¬úr«û³Õ“u˜­¨”»ŒªÜ¹hÂÊ) OƒnšÜt¾²I [º_Ž™Ù¿]A8âòVT‡ãP[úÜÔ“™NËù¿Å›¿»qž|tKƒê¹Î[ãðŸyVàWí­÷Íø6††Ná¡—´MŠ<·u,Ư âLÁ³:FßU¦hÞ‡1ü;HÆ‘Œ @ dJ•¦ÛÏ/5Ûòë±±^¸j-ñ/F‚d‹"ÂÈÐ ’ RE—‹¢¢²˜H4›I#/ I³.Îaϳš=u:Äj¢úc³ùô­5¾VR…k/’IÇú¬îvýÞ¼ñÇsÆc«üË©’è#ÒsD»OGÍøM‹F)ÅâH„cpÎ"MUöo¿WáÈu¡uG¾#–œ)˜{žSªd>?«ðr¨dÇr6&foÙ²j[!¬×J­!bSB»%ÖMk6{fþïp#/¶2ѲÞgZwéÊ/ê80n\„9PÁ$ï=®˜Xu,[À&ŽÅŠ­iêŸ ý§/ôËRdOʪ)È(dµûÓ.Z¤ØÆ(Q…E·T(0QµÔϵ’–L·:+wÚŒ‘gwÕ9¼†!ÇJ¯éiKËÈéÛ¥þ²ÙøÔÔs“Äf¨î¼ÜûN~¾\¤Èn€˜6ó$”Á<>)Î.±X‘! ö£&}÷·¿búŽŽ ð’þ2 ×z’lŽëì² ¬v×LT¢‰d$ŒyD}^ò—¶ßgöÎøNÑŒRèuÆ–8ÖT¤ˆ¡Ý®q/òÄ}–þ/)$·´Ø^sfõlÀ‡÷*ŒÛräÊ´‘¢Ì‹ÝU,^;æ\ÓæMt0se#5kã,6EUPq'=q#Ÿ^ºá§D>±à¡lÎF•¾)NB„xSqF†£ü* Y‚$/#/2·™&½¡¿ÙóŸòàû Àܲda=®ÄÝ­,ÕÂ\&ãf*ÑõÈZëÚjô¾½!ÊóŒ×;¤1î9TÆuCBs U.¢¢$ÚŒrÛ\ÚHµÍ¹­ÜÚÝ-¦iÌ÷n½öºDSU<*ì¨.;ð`X&Écñâ¤àèŽfÖ¡“ ¸n‰d©LNk”Ëë8ÇHQ¨rö>ÓÚzÂeâªhS30gÐcÕó`L#š~m!ô·#Íí#Ç/¿ÊFÝTŸoÇÝï]ýltËQ~×PÌbæiŽ«ê±­ #/„¬Q÷þõm¨ã¤ÿaëÑÕÚ-;ݾ´ÇêÞíÒŒ[ÓC%ߊãÊÉIŠ¼ÔÝåå«çÖjÖ<êyuåB?j§«`Áœ?Á½ÆhW,¿ëh¯ÖŽ2Îÿ>rã#íåoû«"å'RúÎôï ïPxÅ—²W–Ç+Û“Ë©­iÈ"#]“¼ 1û³BF]ÍãÈŒ²Ä½4†Ÿ=9üxOÏsÂzú_ìû;áDUãâÓ}><ûáË/1¤ÊÞ2n­ÿG&iŸ×LCk¸bÔƒ¨Òuüß«/5µÓÖ֓*“Ì:rëìÙ•·¯¡_ÑÄñ^+Ëùëûª•Òó]ÐZ!ê>Ì£Ù*6mçõ¿ðÍôcÙð>N¾+ú÷~Þ>ï·òųæ3öf_ž:Í´™Ø½gDøK;¹#5ì¡ ×‰E¦Qx Õ4{hyOçfßñì²Eʼnb|•ãŸ®_'¯AC9<Õ9³zt¢“FB”Ó–¨P!õ×­¿õL²k;±ù×Óöwé·•¶ ¾Ü#5ÇiéöY÷ÂŽÁ5‰V¡GW3à«Éa1¦”4Ü åÞQcziÉIP%4QhÕ¨%B¾ïßF¿Ä*†ÚhÿÒªB-ôÁä‡IWïü¿Nš¹™™‚–X0; ]ËÂâùsWŠùÿ{wëßÈsœ¡™ÄÜ"Ä[ø¶‰A™!!…Bb3âv´Lµ%“øÊæLHfÊ–%Iùf&’Ò£kúÿŽvÝlQY\Üvq0Ι ’ Þ&¹1 ¬=Å°ÝÖ^[-ŸÌÄ? MÃnzM¦v¶‰e4.Z)M!t¿}^Mzߣϻæù1ãDF"whª #52’ØO€b¢‘S†‚&H'{÷_ôLô®ÖxŽA‡N+Qó¡Ù ÈdD>žŸ{ý]L‡v0ŽQ£ˆ0›$3~ër½¿¿®¶¿B½EXŒ°ÚŒx¾gáb™d¿é«Hu¢„dY¼#5gEkò«Öõ·K^Í]J½ImóÑ\ˆ%%„Qñ.?äùÜúS¡Vª%´ÉlÞ"–eHhÀ¦#5[*‚‰àù]Ã11þCHOÑŸ0xjgüø/¦$(—ãoð³¿%Œ_ù#5»U0}q–_ùé§ï^™?3üììÙ:¦Ú [4À6?¸ ž³S»Lë[ž13£ŒN´?á½$y×%’1@z‰»qÁ„ ÓÎö ¥mú"Õz_ ΢׸n"=°áxM *¨èÎ3 뚣IU+«ÏN_F,Ýø#âI@ÿ·*"Ãû.N§#5è’‘P‰CdìQŒ.ð±q.CÛÒçúBkÝ…gâÖbkßùØdE=l|öDU)I÷0¤ý JÃõ´#·Mj°šëjú†Þâ–füÝuý‚å£W¨ê·/ÆÅ$㇮¥‰ƒCá?‹òÕÓåÛë>Tbß-ß»Ëñ§®¿¹Ú¸sÇž>8[Â%hæïÏïŸyù*ýÿMý"MÅy—JûŸNm…>‰Ö¿*™Ë匩E¬ÄÆi|ÿXçËgáÍa×vö1•t‡=¦ŽÏßH‡:ƒo­–ƒ˜–Rô<5áî{-ºÚG"j  Q‚¯•þ³&jçû(áÐ*ú°»#,ÀJo¹|ezmûf~áàµÅê¾?Ãþœ)í†ÌýúÓõé ¬»”M¹õÂ''ßÝV7q™°¹,RveS»wð28É/œVÄídîð$/@T\cLï¡îª´3cv¾»-E±Ø£L>ÝÀ4#,Ô%ñ3#5Oóô=Q[MW9ÙetŽ˜Æ=cñõ_[*[åØ´å§ÍEÍœÕÁ²Ï¥¯è—zµd ¦ã¯‘a¤ÃB#5LÜý445è>K·ñW*–¤ßYÜz¡=„¥ÆRLÜíã?Äܲ\*™wÂyÿ~ïÂ|ŒÎ¬ÜN& ^Û ?.¹©ÝT8#/ZÅ5ý­{ò€¤êÍÄ…¤c‘°o0€ÛNÂsüþ ›:Òß9t û „"uúO|ÈU¼&;éì·ïl}ðèÞIÈ~³ù¡Þš“°G-²DǯŠ‘,îìïå ßÜñ‹ýoÒÈo½Îc|ÈÑÙHxžG¤¯ÔDy¤!&B1d8ÐÁüóù Wð_¶?uŽÙ“fñ!’*„ùO@‰Ö˜|‡+N/•©ù?ºuôgYèÏn¥›y^9ÌœûS:gwi‡³ƒóDáIM~‰ˆÓ4ÑŠ »LFÚŸµè{!V4 A2IbçBÉpVl­à#ƒýYà.½Üât’‡cšH’LJ·×~µ ¦ÿüÁØòû»Õz0:* #5|ÀK‰¥TR#,Ý TDDEWåîñ† ÝÔîöLoÏ—M‰þeaüµúå”Ò%'ô©_ããÖ\Zñn2Sh2PÉ,ƒ ÎAÀ1¥ €ÒŒ]îoFƒåÖi31-´t¡1ûzŠÉøÓÔ¿ŸÍ³fîùfïû{{¸×êòâÇÛê>th|´tÜ»XöÝg»KzÿuÞÝGÈqü·Þsé#/xT{sÝ ìÀÛ3Gš¹ÆšðÆ/çPïêî?^½tiÇöíʼrÕÕùgÝQŸ£uÖBáUüèòá»~Tr«Žò¿úOœ¦~—½CÈ·x¹éþ8÷OMµ­³ –¼wkrùãWÔó‡DÍøÆ_)À§ÃuŸ^ýÞxú—ͧwÞDá¶ÍÝÍŸ<÷Ó»ö–†ˆözsËŸ²Û=méòñ5™°;²õz4ž\ú5žž^-mÓø7¯ùü~}7VGãi²f¶ÆœףɻÅ#/íæ×+%BÜ{bÚqóöž9N+ôÐŽ:àÞ‚¹í,9Ç?§fãÃg=÷í|ÝóÏú©–ª³Å»«–È×#/¯J¯ÌGFÎjàu~~=vû²l?¥0†zJäOtªå§Vy×l«4S\‘,‚ñZºö´Ipç=%Ò>^û.ðSž]Z·U³´UÒ:k²;9ÿ]1‚öâÜÕߤƎ|vø¡nj·gmJÊy çÝ—IÍ®Ìô°ù\ÀÆ2+ׯ‰#5U¨<8œ„äŠãô<¡÷üpm~òÝ:ê~Eüæë|°¸Ù·Ô´éJÝCø‘?wŲîû´úyüv–CŒ2ݳD'år.gmQ+õao.]¬ãÍeû=WUZeB+ÏÒ¦ÞZ¡#5•kôòvPÑþØÜ©ºþQÉ®S†Ùuü¾&¶7Ó3û¿ >¡´Iëæìüg™ïª’ѽSIŒ‹ÍR¯ß$£¹ÿMx~O6{‹!׿?ÎyÈß- ®+òÃdž_vÛ:¬È'ªÿò=Ê¢ï€AáPe Êß^»óÚlËuîŽßoš>ä¿n«—×áåáJbíÑüÌšÚÛ£þ÷×ðÚÕŸ»gËüý¸òÕ›B‘\çýœz*Ñ‘õzÆ¿W51ø/U[dXhÆݹ»gWó=™9µYgº€^ÞY÷}’¸³O]Ó/n¸{kÓÏÏüvëøº+¶ÉæMrôádÜ„ÿZK×\6¹þ8éjË“qpGtK³”×°Ž3öfÙ¶a²È—ú|ÿÎË~læmÙºOy·ý¿ËwÃäïúáXo?[ïæÛ|ñÏÓMB-Ñ^Ÿ]>&æª>ÚDò¼Ä(ŽøhðúwsþZ¿IºPØòÅè¢FFU!dÔú9Þå—ÒS“åáqC°H¤"œ‹L$⟎Ï_Å *l‰¦>@E ÃZ˜¶25˜fÚG˜—Ùòi÷ž ß»´«Ÿ´éê×óùù_ËÁ—¯òù~z÷öè#ð9‡œèòô°spšìúX¿oë­¼ñì;Ïô÷ño¯òþãfoš­Ñlí¯WEÞê›CTÚ|ú_Ó~˳s+\û~_x~;tÆ~£Iaæ&Ü^–¾7Í«/=f8|ÛEW.¾vÕ®ÉðÒ|ìº'ëÓi¿ðêÌS|²·•Rzì¹Û»låøرuû+êZtš§­­Pg•±JüíP9¡[ÈVË1qB T/;WRØž•¼/“Ý©tåÇTë²™^—Ç®œÞîeJKNWõf»–TäSsv'k»òÉ)Ês³—Ù Q¸ÂÜÍÍ)ÙýÜcÆy#E’I'!i!¹Ž¢Zƒ9/ÜÛ?#/^k#5óíõ ²¾>­_˜Ãâáâê¥;žÀÝ*kMê”;þ‘:ª'¶S©¨f‰Å<)ñí„>8Âœrìçß„1ds…óÂhœ!rÐ˦~߬rl}÷5ŠÕ½¹µwTb©<èäPÈØíÁõvDN¢Ô½ãvïŠ~¾UÞ±Þ·!žZ‡ŒüÒPéZ\ZgW ËDK¢’áBQx 3d¸YfHg¿ˆ—äöYp¸hâ×a-³Òf*•ùËrϯ˜ªÉ™l¶üÓ›™½[;âh÷üòÕauÀ‹bq&„ÑÆ%£–É„zV€Ë1 V­„±}2+†ó I–ºW k-ìÞ{ÿfë„'¦o›}†üµ=uÛ9c{bgîצ^cf·×mv·8›ôBP”+çšP[ÂiÃËMùõ>(c“9§HJíYŠ¿ÙàW§=‡ùK¼`¾—üÔ²y»5íÝÍò|•V¤r]:’®—vw×Ê—Ùëëü§GÃ/¨Ú#“tð5/ÓêÙ#/p<ël{ƒîŠ²Iÿ_/d¾Œºnå‹Ó¶žÉF Ùy!ÝÓ4"«ÎÞ6Ý}F8#/ÛôÇÕÓîóú¨cê4™G¼26©tÈÔsyJ\³Ó×Q`š„ùd`Žï>p#ûIȃU^^J¾Š¾©…×ê­õ.EwË»/¢ÜÕøý$¾ŸËWõ­a\q´ZŒÌ¹‘²¶ä%`Ïž*;GªAºÖÇuöÃta­¬¤°ŽƒšVŠ•$Ç´Cc†E„‚ª(8Ü#5*"ªjDÚF 6ΞšÛ[k[‰Ò `ÒŽ ÈLXµ‚V“a¦HhÄYSI¤ÝU X–š’¤0­Ò#@ÝÓ¶–k¨¨,›Ä06…ûóüY‹Ã8h Ô$m÷E¡=ø§M§X¾îóÈÞ³!¹À‚ŸìuòX~Õ1©&~˜­Zj]'åþáhkQ˜XÁàñغ9ˆˆ¢13S²%”R(Èùªæº t{¢t/9h#08Ž$ø™”ˆŽJ:QFk1}KIPc 0i`QV‹† M@„4í Dé Aà<Èwoì‡fIˆ”xS-ÇÔgoþl,î)~½ÿŸt»gÒ·Ÿ1‡ôëÂËóQ¨ñ/4W¥hÑÙÓ?¾«9¶Y¤æül¦Zµ%û½CÌ·þŸÉæÍ÷a_#/úz~Ó®ŒDÕ‹u~½-2üÔß]Ÿ%Ë<ãÙâ{n¹w;åmxû'š”ÓæýóÌ=ÇÏiäÐ1¯¥¬i5B·”}#5>fÅ®>ãÉÕêý_ƒÿ…^­ZýišöBB°ãÊ DÒ8%k½ÿw÷­?AfoºwqáÓ×¢}Y?¦ú‘³ñ…Ñþ’iIå)B1x×^@Àäêñã?åÛáO%>ÿ?㪥Íá4N=ßÜùÏ/ÉguÉ~>w祑ùkêøºÿºþ®þš»ïüìݸÝû«ü5zë(¨–‡î¤à}†spçGÁÊ|¢®õ?È~Aˆøo{Í8VšÏHm0ÿaÌcb!…hô22Ã!I¥n0KJÑÊØ¥ß%IH Ó@ÓHÁ˜2¬)#,i³ÜuF Ó¦$©Z¹LÚ›&ÈÉ®Ýôó^šö÷ÀÄŽ˜–™SÍ)jŠ€ÄÀr7h(UX¥¡`È£¡ÚHŒ*V&šefeX6<+-iNþüF„<Ç+Ú“{†º†Í„f#/² £IºàEƒ©‰‘AÆÚN…iÑÅv“yåN=Q˜R2ŠŠ9‰Á£DI°n€ÄB° ›pcK J ¸‘Š)"Éó†%ºæ¡¶„¦ˆ2)@lƃ2#/e&:EÕ‘'(<)‘LRÛŠX›-7օ̤«J#5%F#50¬ (älRÍ>7³Mi¡²ˆ‚Æ—s6„B§ˆ‘îÀÇ⢷f“ /öZU™¨¨V¦œ®Â jPï‚ÀÇX`- ed ?@¡ÐóÈQ§Â'Ѹ˜.j.DÝÊGºìqrº¡hs#/ü¸1”>=j0}'t’Pƒ»¼0ïsòM¬8…Ù#/¤$Ò¾«š0,ô.e‹/³ùÕå¹óš~hsÌÀ7ïQÉá8m5_:ˆñô{þdã·ltþTmg"éø•vS“Rìi— ²v÷I¢n7]#/CŒŠThb¬ƒÖ0£«ÊI¯DðZª^1¢ìfe!™i?Ï#8þ^[©50î!Ý5ô;ÖÿŒýÓôø›£<(.™£ÉÄ#äv;³)™ ·¢€¸ÄÐ|oà¨2{»ÙËþ\þûn¼XôÆ–TµMå Ú­EUµ6Ç?ðíS§Qá×±ÿfhèïÜTU¼Â[wBëµÛ¤/ÑÜž¼âBBåWÙ&Ž/^ša6¼yõ›¶MšºR—±FåºjnþHÉ$·o½•«hqØÏ#/<Ú”%­èµµ£&¯­¢r¶ý;_É56„UòȇÐVç1Ê'}˜}CŽnLé €ø •DÛÿ®±¦Ú¤ld„q‘!¨—ðb¶?Ó—MìÒ60¬Î8;"Ê·LÐ!ÅO7³éÕôztÝów—ÎõøOþ¿¦_DÉ‹cꀠˆTH(¤QŒèkš°ôÉ(VôMÁëLê ]Æ‚^©†1‘¡¥aÃ[ÌÃTu¢šùè] ÒÞà†r‚Ì2Æ¢Iæ¥f˜äq|(£x8V‘Šã3ƒˆ]»” Ò*0be¨°#5¾§¡è‘MÊx›X0¯g½«Èâ­–êŽÞÚÁgáó1@¼aå°Ù²=°¬h´;_ÞFC4FµÝ£*%ié†'äeCÑK9ntlV ‰ž™’Æ‘à:iëC9ª*‡·§iâ3‹b4‚ƒPK³5¾j&JQ@z+F ,#Q6ʵ´kï`bäJ.×^Î )iÄêvº‘rŒ‰6Q¨k0wçc+z;EA³¾hk ·ÄZ4”!÷îõ5M)Ž4ܦˆæšÑ ¦ŸüŸéž&ÿï?}éçʪ½Ñú;úéC£êÊÈô÷S^_kwž3^c¿ÏýÞrìº*>äV¿¿ˆü)úØmâËÎI ‘tÒHÜØêluÚ~&|f7¡TΘ77lÓ€ãDc#5çDŒx»S &´21· 1›l£'bF–åÖ“qÄB2š‹õ’,Ter¢Ö9q¼UºöT²ÀÐœj¸¹¦²dÄ…”9¨´SLŽÄËÌ…’™A ZaϬ|C;öÒ®½-j†‹´ªÁôÞŒ¥¤e]Jß1§×@l*œâCr¦[‚Çã9¼P@™è… àtДC0]#áŒfØûÓ³AqC­I*V÷Á“&sbÓ-Œ^,\ņÊy¹`tœZiAȤ ‚ŠGb“vHƈé”OR¿ºkYþkÓ¿©wóõä½üÅïŸïTGò†ÿgÇ«Ûë{ç1J6OøÅ)ÕãWj¶63sr²Ž„Z™ÓÀ˜0É©4NX°#I(9NÆÉ‹³»ÌlßGqªVdþ¸»­ÚcºLåpr¦v‰ˆa”G›Q#5šÐ&àð¼Ó‹0œ‹Ô„¿L¤—ô#fÌýí£#/êüj̳˜ ë;d3ÄÐãž,X« n¹bŒI”ÈòQ”pÔ.6ó²ÇâA±£a©¼l±·#f*U¼Ã2ÊeÃs]×›ËÍçW]9wP$$¡°`ÁÝšÙbЧ…ÂÍV¾þMw+^ïæÂ" 7¾+š±]š](Ò†‘ÔÀ¤Š¥R —mdÓ×GóÕ¤ÆúÿtD˜(4 x8)#5Ó°ã —È’4û5ÄlËŒH™˜¡#/§ao|›!ŽñBiNb™Ì!8˜‘±—#/’ÎoàDJÃö°Þ[áË€™%À‰Uº;åœ3˜aŒ/Í8kÒ fA#/¨í;æ~doa™]7›8:›ÙÉ°Ê`”ÂBLìívP¾|l¬kåóñx ¶Ð“ZôãÍ©u4„¯fù}’Ê]¤6ªºÉ²¡°,™&M+ˆwònÁµÛ#/à ƒˆ8,·äF Á‚÷r7Q¸J-âœÎ-“vmk­tQ6v5#5',ˆÇjŽÚÔÃv+zâ΃®©à+GÉcvs¦ÁÒ´ÝXÎÐqšW/€š!³X:XÆ1–¨qïìºÍfÙÂn¹ª\†¥HÕÐkƒ… µ‘Æ»\ëÙœL‹O¹®psnÝÔç\“..L“Òª™Zµ–ç%'frf“€ODHåaô;ôì÷Ž#/bûõL^…vîóØ\“#/ç<ÓLEç¥Æ#ÀŠ5x^éö'>7®±å->6ÚW¤òßoÌùëµôSƒ-¹ò~ÂÄoƒFqRæ2>De^ÌF/K©± ·}`²ÿÔsD!ô!à˜ÎÉŠÄç£ÿÉ&û|¾=mêw%²êOçÀõ¹£~k¿ †§µæesQ»?"ë0òJ»ˆØ+Uny\(·˜›qUF̳ÁSl¨·ï©ÆËês¶Œ]$„¥:dsu§‡å%ýš÷̧_#5sûL†3Ï‹{7Ó›fïÇ|2SDï錙49õÐèõi[ã>•&™0™2@×ÄrC’™wì®ï·ñÞfŸË™ŠÖYmsD¨ìz;A‡ËÃÍõ:vYÁ—Ášˆ˜1bð°]2ÔøÈCæDv!ÌRoñèqb|¥)iÅpg“¾«ŒÓ'šZlÿ¯GL·Û#/{Oíq„g=ùo¥‹X[èÃ¥¬@c«‡‹„CY·2Kdu£é}ÞˆêýÕk·;9Œ5[þ½§\A|+/éwÇè£b $Ú-2 exìÁ2f*Ÿ)ŽÑ—-üqÈØ2ô’½²\3°bB„b`•E£ú¦àoêf/\dÄ#/$]‘ ÉPq©A;HÁˆ¸;€Å3i»ø˜ô3žÏµ®³¾@˜)à‰DËùÛœsª¡àLï”þ¦íTãÑ¢¯´Ò©H©‘¤Jœ“ð&á=Så^;Üy¹…3«€’ƒÔž…K¿‘Ó#/ù²ôa¼#¶ßJ(¿¤ð©'ŽôZen· Zoí¢£Ü²[¹ƒ^o(5úÅd÷o]§h=Oh’G·&{¥ü#5dýZ…ðúgHÑzëºìCZ$V¢ð¼fS%¦Ã{i› n˧ª(“ ½‚0GË«ãšßÀ™)'¿|YÀôxÇhv¤™‚!Ƽ1ç׌ìÏsFÍ[HÜÑý½œuh劥ûÅÄxŽZ_)éúD©iœ¦jL<¹ ÖyÌc†Ì¯E¦²OóON4Z”ãæ&NFÛèÀ»C¹ÀeÑË5Üß$?ääS¯?ÜD3ÊŽ¦ÿN6ø¯³ v‡«.å:¼èÆ!€Hßã<ˆ]27|jƒ$Ó‘î¨0ÜÎTé$=Ì“,aÇgG>ãôDÞ‰á@'û.'Øøñ£_%æš·‡ ýUgn=-šåMŠroš^bZk£œÓI«&˜t:pé²2¬-{n»}nû10ÙÕcb(€–æº_FÕ±í£+ÅV›-÷´[}ʲ³PíŠÌ©¥ λ‰¡ÜëÂG‘Üv¨¨3^Í1'bøŸ˜ö/M˜ùöÑ„níevè›;ôJª®åÖ)l¹Ê—qµ‹w–‘¸Ìv]έ–j–f»ãÄ;ÿåQ_'.=”Ó‹÷¹”+Ãçå³ßá~ÃÝ‘œj°Òaß‹ˆg—Œ÷ÊXÆßXl´#5¬ya(\CÒv»±Å¿w¶í3Þqf2N"^JHç—æÊØ!þûÖ‡ø¬¦ìîË‚ÞSØà‰¹'*…Î {æç\•¥Ùq#/3”Ó†_gÀ’P—]ê<¸˜þ_Ê篵äúÞø¯Yï§:pùÁ"&¯-P&Ð5*¦÷¨Z¦–¼øL‰Ê™¶á™€ÚET4 Ë©:G¶ºXØ_sBÆ;2וZ~gÑqwF¸GFãÎ"[1”jjáuIý&™çà®V÷Ú›zC¿‡lg* ®¤]ƒ Û]jP‘Pí-Ç~Â#/Sa£â›[ëf<òÒ‹àrª9–b’ÐÐvÍzg¦­°¯\ë|õéQ’ h!ý^MÜmûcÎËÛ¸‹.Hn%ÓV3û—¬O¤ñNc3ßi{=Ûæ¿ÉÕ¬{ÙÛ|Á6ÓR„ÐâZôoÍcž.±ìÎäi.Ù(Ûa–U%ÎE;Å·¾aÐQŸ&Ò-FL4aØW*µ$MÚVÀƒ ¦!ÉæIˆ9Gתªúã§bDÝdÀ:Þ¤¥-WjjWÈè^6ôìUðäéÝ;§·KWñ×UvQ¬·.¡„*àóJ#,Ü‘$†s\’8¨±Ê'ø {ǧéÌ¿=sœ.ƒø/奊8<³W”¾=¦Z8l`ï;1)qŸZ·zΨœ{¹ÕQƒÂ‘ÌN'â{#,ï“ß®FâÄ›nØÑhùÇ;mñ1Mé²÷#œËŽ™q(¸VU qCáêÂ%I<ŽÎÍ™4s¶äH“°‡¿œË51€–$1¾È'lLq½-wqVaé¢4í&n½2/½¸œù§Go\ïµ\²t“÷—%øó¶ ìÖÉù*£ß2Š‡ðÜ÷ÎL$mð,¨‚<#5ëÆS2Ê[Áïs}ÌË¡¾Ïûï¬ Añ×R&9ðrD·,Ö(ƒKÉ賨Ê!L½FªFv³¦Â²P!f¾òynÌi&•îëaÁF,Î=x)“Re¦Þ¹=5"^#?#eóO•Ç-½eÀQ€~²,Äx-¸Lª ôÈlgW‡ÕHþÃ×—ÎÌ£}Š°˜ÜˆI n±hš{a¢ÐŒ¹ØšTéyV驵ú0ÞåèúÂÃýaãþvD^fÝßü~îMQN„ÉAÝWG~ïÕóÎÝŽÍQæ3]Ÿ)û#/»lÒîô\ñ|Þÿ•ùöH—ç.Øõ×f|`éøkÌhd7ˆ}g­gXVÖX%AE¯ÊŽÎV≟ £}ï õ/A ±ˆFõsÅÕY~’‰aG6âq¬¶©ªð;ŽÝJ*8…ê¦9íûüBºÊ—7o¿zí<œ}*ÏÉ´Q¦m¢ŽqI«ò8í8ZTD„¿ËúÁ¶[CNÒVF·¥üÅܳk%E^VÏ4Ä[w \3:“ñ¦jüRk)rP௼m9©¢Ûáe7ßÆ«ÌKpWW>ê]4÷‚ÂvÝ2YósÆuJ*rºO$¯œpí´Àž®zµÞÿ¯ñÝðßoÂý¸³°±ÛƼv'®’ŒfÊ”œ¾#ñ·H¾™ýUèi_Âëì}ØÅH¹÷D²ÌøÆšêãc1óI=ÝR¿™ùƒYSeY8=ùóXlY$®­]œ|•t”xJÍeÄ´N ÌŽ<Äckº\y!ý³ú<’2×–°ÓTNzw“^ÙPRiˆK»?…¡79ƒÞÏó{kõYX~›Æêãºmª³á'ámYªÂ™ðŒo€ˆ‘ "í¿ì®…e5,¶·½[—ÊP±µyo,­ž/ m±ë4ßH"$!LÚ&tŠWsW•èf5ð5fH‰l*;Ú®LOt3dcu r®‚#5a ô‰I<%Ç(G¦Uöø¶êªÚê.x—‰ô®Ô’—Òqðƒûf=b¹oG*0'ÍŒÄe{ÃË,bDŠDîÝÚ) ¼òB=‹¾Qe…£ÃG9l5až¼üÕ«¼s*+ó§¤ÊËçÎ̉{æàöþJâîÁìEiˆ‰díÊ£²Ço c[¥¾ —÷>ˆ6h_TvtÞ¿/>JÚ1†û"•Už5(÷ónšÜ2â*KCz×_[¡bú–QA]#èºfqiׇžUyNOS©çžq• ˆ7¹¡u×ø'Ò#5Ñy$ü rdòç53„å®äûÁÞúv{’ÜÉ-q¾1©4L“tpºo¢ß6YÍÏu¿"˜Ë2óWçØΪ9½VBâ§cÆ[Ù#5U;elóÌaWê[Sk\òVôËd.¯‡§§ÏaÌn5Ä“ß$‹Åó5j‡wǺ¬&6ßKxª®kk7q×î…ƒÓ¶d"õäö¬`|¯H²E°§ˆ©3êäÎ÷=k}ZǾéàŒ3÷NµŒóóßïX^XŽžÑ\Ü$Ξ‡Hi%ÙF裣ü|«<ãD U1Ÿœ{uÎ?o!¸Õü7t¯+Ô¿hÜôñkÓq3Wùbbüó-Æêxš…D:hvUƒÄ:s¿ãÄ÷?jZQ¸ŸG=y{ðîºâºã8­òúÏo~$èF øó'·7à 4³Ç ßWÇžkÏni’´9ùêÿ5Á£#5ªÆC¯5ò9çm±¡h3?¥lMAòÄصÜ`[U5¾<Ãœ¹[Unü±>Ü3bÑg\#uNI{®ýü秢Ïw>ÅñÛÓëY7ÎàNƒ‹•ënåçu6Lþ55Ê…vYª nª¨GF±®9Ú©¢Õ†’)¡¢.žÍ<9秊ãZ”YîôyBÇÙã™yj¬µÅrOíÎpe{#ç³êùé°ž•b\ãàA<`STO­ùžÙJ÷YŒL.g´šƒBV•Ae®ÜU‰®¸bU2²Õno’Š¯]`ªD{ÜTõ‹©wjuûD…4ð<梺âcŸ.ç”´}O>k¾úJb~÷n9ðFI럿›ŸOÎ#YÇB*4/9ž2ÿWö^“¬my$œýL{7½D°Œ.É¢98‘}ÑÇ—Æú#à¯:HóÑû®™Jk‡a›”#/û¥ÀòÍó‚¡/wéñã»]3¨mWQUpÕͦý÷™d”¢ÚE¨Ó^£sñ¤Ÿš¼ìÇÃzš0EüþŸ-ü+÷aöï­M‘kÕÝ¢›!Jë7YOß+O++^´A— ¬Úõ& SØžQ#/Ô2ÑG¯#5BAÓhêwlÍ*ìN§n-z×!å>+­5S¨§]S5Oš¶À°Õt#5ÚîTt>ڸϺSŽP×…ÇÚêz>7ñŸ4î·©ž"úxí0µÝ¯æ çzºÐXnß9~]ëVƒîÓro.˜iÇG¥\\\¨$y«…ȼÒAðwÉ2Š‡ TÜ‹´ N“çW4f²F¼Ù¿WÛéJÉå–Ëéﺙaf¯ñZF–Å zº±™$?CÀÍYײÖÌeÅùw$ ¾|µ<.‰_£æa«ŸÂ¹ãXGãF¾dªàx!ç+xóÔë#5B:&adΓ RÞª« hR#5DMüíÚhDc7,O=P®Ã1¿e0¥Ó|›e’ɇUlêÞžøü"¸í^UàPÔ›¿z¸nŒŸ¿=c&÷Ë—ÅM%çß”;º>^X­Ðß4}ÕÅsIñÚ¹¦¼§G{Õ"B×Åå¶o.ZµcJÅš&±š·Bd[× }άù!#5¬¯”%›šPËÍVŠo¡çòWA-«\c#,¶½1ˆD^æü¡§½‹ë¢Šny–„z_„NÛn/¹vÕÔtÞåÏw~ørÎkš$Îcy»ãªcüoÍ`]]¾~.÷ᄦ¯Å–2±†£äãÔøO­›ÄÕ×oÄ£52©Ãö¿âøò¦9ß#/ùÁ€KôŸ·ßÇÕ%½æyÜŸZ×Ãàð<â’¾ªeˆšƒB;s†ò‰n¼i[%§uÕ‘Ððœu¯DzÙÔÔ D]S1áËà ë +FËa  àìÖ#Ç1ú騃4uá2#/óÍôÍw¸@kS.‹s²Ïžwµ¥W‘Ct­p/Yk²Í—xGb;¬;«Ž.;EÇ´wΡ¦;a”'/[Ê© µÝÜ·Bvð«Ç‘(cÛ|7㟨]õæ¦bÖ‰C§d¦™YTc<7WÓ‹éÙUw=Zë0¼ÐǶþþknDqqZ7ÆPlÅR¨jsÕÃTõÕ¿Iï,ù³e šäÆÄ5ª1f÷nM<šÌ¢YdÍèIŒÂŠ³u˜žµ[\­gW…nïƒóV$QÍî>Õ¥÷ž¼TÕÉbåL/éˆb0›é׈֟ŽO³á÷]Mzà™î©²¼nKÒ¿Ôý»ôéÓªéËäܼ äy†—ÃÓW‡ˆAüFŸ.#5{ç®/~ä »ÈúÉmqoG:7—Rì<áá:ߣ2µcÚAuÒ^ºNš°!§CÚŠ\Œ³Ä€ C EÂ|HY9(îKÊ¿%ç#/'ýF/;Ìúj>`Ë_d¥º¨G’2/®^•Ûó‚úÄÈáÞ3—ò›=‘úðóÛÍŽÚ®üëÉtåìùl5J»ÎúúöÉãP?ßïHK×Øá×3¥œO2³ÝY<…òáT LâÊcÙø¡þï1. ËáѤü#/¿kUò„ž—š’ÞAøÄ ÑÈ’ÀÕG½ÉÖšèdÌ·éÅš–»âá3 ø£j±ˆ-1†7ÄŠÑh†K•BÚÕåqy§®ÆOG|`xO Qb(D£ª´\ÿd,¦ò XˆWõÔliï½2뉷íOØ•írqIÍzÎWÖ»,©õãD´Žö‡V·Å¶ûzþ³¦¦ÊÄoƒ]¹9ZbÔNŽoqÚäÂQç·l‘¨¹Ñ‘;¦Ó}óëUC‡Ôܧä -:úÕcó‰TsV씣§C¢Ñøm…dî«„œGlç*:¸IÊyH~Ž|,ðŠé†=Ð?‡n,OŠˆGv‚È–• vHø=Þ’1¾x3!>*Ý»L¡¾¡«µÞl™72éR šÁo¦(§LbåÖÿ5~CµâNá·w©{Eöº.“ƒH^:ì;Š™Žæ’tˆB—zéš³Þäç<&ÉTcKÚ#/vÙGÜPf¯e¿®f|ßÄzm$Ð爄rÿ{Žpr>ËISÀyYCdÅ5)œcÑ‹Æ<ÇxdÖ›Œæíû‘qˆ[¸)’RÔ'¢çË1È?¬·2¨é¬r”T·göl8ÈǤm›É0Ï–<ùËìf&&qÐà˜Ì¯…6e„œ34ëÑ7m+Ó‡ËrRÁ ï³}Pë.dž?g@ºÍ^,嫶cŒŠ:M:ÿ¦ëîÛÕ#,pöÿin-camvÙÒ™¦›ËŽ!hèÅJ¼3m׆Š„¿oŽñ~³›oA1 [Fy]}Œ¹gÒ1ÙS®Šïô;¤ÜI“vÑÂQßæƆ–ú#/»Ôü~¥î5Ù„°ƒÀ»TDJv®Þ_,pcÛê~Nö©ŸÚŽt×ô"ww__§n‚4N-uF:µWø½Úâñ½|~gû†÷bkKÀ#³¹ÐC»–¢— ât£ùÿJàú”äpðx})–) A.ç¢È„t3ïQ¨ÔðP„ó©uÔô,óÂ~;[Mc5)hº,ý§ñoš«nŸÌI|\!ë#h¹,ý§Ç¨V47£Á‹èõíôFãÎ<ý›Q¾9êNs_»)Áëìlîs+[-Ö¼¼3×li2ÂÚ6ý¦ÌJ:nW)C©øý>{­îñ´°ûå®R\¬Ù­3Dɾd7¥µ¿F?H¼\Zœ Hï\ uï?5–Ud!z!Ò˜µI§ëñhµN±#/$Û˜„†tÈöaùáqä³l1¶VÂèd¿¨í‘>¯ûÅCŠ0J¤‘Dm"¯§ò«‰˜+&Ö-X(¥Ìöf†#% !D è1È&çâæH+†›ÂP¦iÑQc7¡`«; sÅJm¥bÑ ØÒUòÖ­U"ª¥ƒÆù@1,>*«q“—pÝÛ¹‹'ª§jWµZ´R»4?ÆXlàFÒH‡Nì/ÈFT†¼ü‘…­é y›œlWƒ#/ïù2ÍÎ_Db´0©Ïí=ÚßKE*ªì<¾éýYT‘VqHTA|ÒÛÿeeÛF\P…äOô³7(Y~^êš½òøÃFº|Ý×[Mh­(÷¸Púö7fî„¿Çôzf{!P¦ÑLí1Ê”9C„5ð9ø›CS¾ÏUÞ|‚¿.fŽwûÎ1ü×Á¿'ÚÝÄgs¬ÏÁ+M-¹=Zqµ®îéjm}êŽ~ÊDŠkÐÃÌR$þI¼ ÓJ¯P|Ñ÷÷™úBm&2Q´û@°”ûÎ’ü¤;¬dØæR'ð~÷òUtˆb›ìDÂI«s’vCÙ¥JEP ÜhQû„c6AäQOê«žó‰6з='ïdXùÎYø}öíöýéo•n~ª3}é¡éS*ïBÒéìÒÖ˜r¿{o³Æì=ž›tÎúͶÅÁa#/J*#4«àWÁ¤¶0ˆS×F—q˜ß‡J6HGÚmÖ…ÏðÇ•Îè=#ÝÅ€V†Æ{‡y¢`‚wð©_“(Ìtñ³‚“b¸ò¬G¯•rÓX|Ulão,þï;²g„ðãxa»D,°ç#5¯¿JÏ>GòwG#îÜ$Ѓ-#,xÍ1w@P¾[wöp øIðúüÿé¬M=SÍRMÝ¡Aô¦à¼~q×Å1¦8D7‰àCœõÿVKÊmðí¡Õ¼N̓íªaì*R¡hÈlW¦f®ñ“QF¥á=Ú¦9Ë.wì:†3t)I†gRó ¢F¨ÝpÀцVÞL®Mvúa5#ª ?3ëιîTÖ‚q‘‚±BR‰½Ì7tñ©)–˜ÒâàmÀG(eA0€ï ¡Ãyßg üåµDŽß”_’ÕY*k~²+Q_Š©ê®*KNýæïN÷JË=L™õ û4¾q×RhíÝK«nf=`{ºàNV5ÊätÏr~]Oø•#@ªtFö«CãP È4 &“"Ó¢{t×H†wüìê¤ÚâòÏaÙPøÞ7vÊ[JŽª/y]‡T˜M¢8áÍL„ Ÿ-1¹×>ƒ“çêžêh°fÑk­bR‚MdR‹sðÓš§Ãmex:×#/µ¦^ž‘,…ðíòó„‘¢Aè{îÆ%ëdõ‚„ÍyóЪ] WžþÒYñ0kNRÉâ¯F9Wdo‡aKÑ|X"´ ‚2CÉ”¬PQìM ~‚«ã™´×a#5bÃï‰I#5Wc§ÁŽj9Åe¼¥÷[n cVÀù‚ßËñÔtÎSr‘72A¹‚Þ‰Ÿ²!t~¡®» ÄT%I³iL©±¾Y-Ù³t£tiq\;’Ño®âçN¹ÇNØ/+#/äÊzñ¥ÕTR8 )#/#/ˆ_#/—#/z—óÞÐ1/g§g–sºÐÔƒž(£ lâ%£·¾ÚsÄÚðuš…pIë—Y GA6Êx=ÌWà#«" Ô;MNü]ìàß‘¬;Ì|K4È!²/Ÿ@ä”CËC´µWW|3bÑ–Šóë­ˆ71žÍ|1€ýË6©–¡¦c.«æÌ<5oŽ OÀCI»fÎÙ÷Ï;¾#)Åa,~~X›Œ3q¨ÝvÀXÀgj3+ÅÕœŠñÑ;Îs’ˆ1âsÆO¥u¶ãÃÁ'Gø·NBæåˆË¬ŽG?£½ÂšßQ˜Á,]ôÀÄXL§I„þØ,¢uH§|i¸8††J(M¥ƒ˜–æ}«ž²ÄwÎÎûAº»˜/Wl#/s•3•GJ+Mã Ù %L¡œ\hå©LU2åkïÍlgß&ji±ÃXòLšPõÏiè÷ñVq!ÖÇüìo8s +#/ZלLVN‹<ðÇEÞH­¦Y(A¦\.{jÙŽWšNvµÝinæJÓŒÈ)«‰º6øÝtÔ5vjvãBg¢$ÐÔ #/#/—`’° 3ßvUo¯#5f›ÞväTšÙ9aï féÃ,}k¶¬ÚÖ©e&†r­ƒnx…¬R0!¡¹àÓØhŒ,í¬f]³>†|f´Ã±òÜ}ÜæÝ/7M¾ãAª•ò)(c5iE¤‹Ic#5m9øëòò9ʉ”xm4Ül4j†A8ÐÀ–MùûAÂ#5hÓ·«Ò·p³à&ºVî Õ&’¸E&‡©u¨<"Jl =>M|ØúÜ4"Rüås¾z§ï´þÈH?WúXRÏ.ï„O.Zè MP0ÿ¼…¿ÝJaÜ"ŠÍ6»._‘»o/<«1•*I ŸßüßÕãaʪTz¡ú”3þ¥6ï;M©ÅX}c! ¿ D&†aùº¸¿ók›V~tÓ¬ ¾0ø|eƒ„\gn$„¡›#5˜q’È:3þcÃ|½I&ŽD?fró¬Öºd9 BMtÃÜ¿u«-ÔÇ}8A^ZO÷¦£¿Ãã ð`x¡C†QõˆV-@‘Fè‹€);‘ Hb…˜{˜¢cÙˆ_c…9E’Ý#,ƒZÙI¥€Ø @ÓEL0î*"¯àï½Å.%%°R ÈçzG1”×éiEJ”Iá¨D*ΖÍ­‚í3<6f"È,²`‘¡Rè`Á…ÈSuï‚&g·X@’'ô q(&æ52ëI|]îëúóìóc‹/c—3³|šÿ!³Ù¹ì<²†"³ãöüfDÿ—Ë]q?ãÝlú6¿½%ýÏÊ[ûá…ÒÈ’Ä#/^!¸j 9q‰öŸ—ó®LŒý±=ŸMüÕðó¨(¸òéa‰épRjMŽTZ˜XxŒÌÁª™e1âcHIŠ~(´É{^£œÄz-#5ë?èÕGJ‹(9¸ÖÞˆ¶ˆ?£–r¨gCiŒõ¶>.sÎ`Œ¥é?äQ¦;#Ýçλ´ŒÌcr®·w’ŒyÖÝÉo Þiî«“VÈ,Ñ;mGJïlò) ]Ú,x3˜»AûÇ##/%iˆß!’aÇÀñ«Hl…uÎ#58 sV¢;Ê8–P±‘7(•¼hšèWÆPa)‹—/zU#/Ù;Æhê¨Õe#5OöÝïÊå3Å¡ ùü5!ä#¡ÄFŽíoÕ¿eCö[Ü:12µº ýp:Dì>F͇ðš(wQ–ëgaÌü(0Å¡{zƒ¼Õ+«ÂBCõð0óÎÇQ‰Eø®jøu×n#/,YÑ $dŒ‰^æõO39:Ñi¦¨©H¬PYET,í·¦³ q˜<ç3¹5wæm³GµÕºÖ&â’ð#/3p²»V`îƒ “IŒ¨$ˆ#5ñ*€¤Ý*”P!*¡Y_ŒA®D´Ýéó×õŽ}´#/B<»XÔ”î Â"B(T{#/Õ‘Œyõ†#/Pyög® 0ìÁ[’F pÎ8Ûmymp΢»H]!º ¶ƒÃ?9r':”"‚ÛMP:1H¸7 †Ó0´Nl`;(ÊΆ´qÌ£PKD}|ŽßUµÆ&>ul*ºŒ4¤TÛ¬à¡%Ýq­Äk^ø80 <'¬OH<,u³#ÛÊ)µ¹³·<ÎÄO}FÆpW|´yÓëÞdLÈó¼¬dd¨ªNÝÎéD8¦3¥ë#‘¶!ΉÎ4E„€gÒE8T„#5¢«­%WWMµí‘çö7UŒB¤ „D N<èdPF-©KfP‰"GÏ꯷/+Hå5I—té¡D–•¡/TÑÇ·€ˆmGšNG•t×¼ÈuJn1-Ȫ”%•É•37yzI ,jzüS&|YóÚNj…ÕOªê!äÑåR—(ÏŒ«òó¨s1ß>+#ÔÁ€‰Hˆ Õ\^âÞ¦„*pEëÎN>>ôŠ#L(ô1Œ#(.UÔ¸O{ɹÓ5D¢VÂ% ¡R] (׬Øó^Y¦O sУS7Å›`–C‘wÈ¢*ᩈØwÅ„NÖq°V4Ï=¬9ÚP’ ôa=J˜JúèC#,õÑAˆH=ùaఠȼHÐ}›=QÕ‚ )¨ÉÁ†tâæEÚékag=LÑ+¸ ÀMä¾ÂÞÃ)€Dˆ©Nï$[—”$!!&A… ¶àûº#xK"¸æÍú,M°¨Ïƒ3rçÑr4&z8éëJ VÉ›XXž­ºã#[•€`¢Z;Õ#5„dñÖótSO+k¸s!u~H£óíWZuË' e*:*ôÂ#5·ùÅóCr 2'o h(:¹ôÎϲ`L™²ÀwÝ@ ‚‡,7–÷ákW*ã/Ü-SÛÓKUB¼ÀZ#5±SB†Ò6é¾ÿ$çW(ŠÁ¹M‚Î%éCËÌùð8éÄÎ/ƒ:ì¢f‹ºêgÕæR‘þ<Ú#/1³Thõzõ½Ž…Ê®©ÑÉelUdà‹ÝÝßœB’“D¢H$ mTK=hŠA0i*zšëMË[ufr­7 63PA™ã“Ý9² N®ú-ŽX󹌊t;‡T±ñöÙPË1ømÝó³2RH(£XxM‚üŠ(”–!2ÉJöœ9ƒ7>®SXøžÎÛäc$õÆÅT#èær•8"p«ºÜyK}³ÊÛhŠG4!…Ä'–˜Ë)Õ)d °ÏÖùtó-vEØ…ª…[â÷ÙrCÙÛq[¤aÒ¸2.¦Ë8¨#/…(Ê6 mžJöU÷ùKf»QÂLï¸:kÁ’ȳö‹>‡~N•\&µÍ¿2BV~c‘˜ÛË7¯­¥ùãnü¦#/½­¸›üæÉŒy¢“ÄCØœ›4 øF¬ŠDA†'¥iT¦„Ù6¿èº8|#52Å"šê),(5#)Pö¢p ô9QòHmšt¯‡Çú,ë‘\tváv–‰ÛG.K} vCŽÞEâ«ÜÎŽ1£d4>?P=ˆ¾¶•M!5H«©Sà÷»Ñ|zë¹ÃÆÊ~x!Ë£»4­7ãíŸF'é’qŸ0ƒêìÎt,#Ñ÷¶ØðÖ[†A1,Xwl¥áý#5›ƒ{Ø‹KK9l£‡wÜ·#,ÝëÁ™¹!×I'D:u”ó!zÐÖs,ëÃ×ykåälö;ºÃ@#5L«qž½ÅZE µÅÙÍø̬#/¡uò‡;²)ÄCnûôõuÀófµ´5W´ˆó~£áÆç"&à±Î9Õ)Ò#ØA„-3aüFðÃ@J2ˆ÷}šyÃÁÎB;Zv`äYÔƒ¨­‘m„”ÒÅ£Öº©,É%)š1D5$:±0ðlT¥ªL—¨Øš57ˆq•‚¨=š%$7š#,zˆÞÛ˜“õÄ4—õzm‡SêS‹ß†t;ûùËJ,Á¯ìâuBuR!¿"¶Tï‘Iȃߣ5~#y5‡–$!¹t$à‡cò¯C™òlzEu]Åu&fr²Ëò^»j`…KJ ÐE#,Èôò–‘Ñ6z~ç ­œ›¾M‰:êƒÛ A2›ÉAÇÏNí»vlÃ1vkB>ÖÜ€–£–œ¾êسµÍp?tL“7‰1Æ÷2>9¢X’˜Öã+s¯:Å€°x~ÏϘdcDú>þXüœvÌ~ÿB¨e’¨o{l¾P3Š\yÑEºzC~à”äUdM"aùi¸K£š—ý¬ÌÁÓ(ò£ËËSû&R–iâf챇ùmåëÇÙÐÌâOt ½<#5VûZ:ï•»»\œXJ”ùžääÄÚ%o|©¾~À&b º“A+¸«: @Ï#50‹Y™ƒæ3­»ztÿa/da1A_âýã¤?Ô‘!!Êa/à ¨ù¾ÞtÙ]a¡¶&»e‘‚x@=æI®ûXt›õ ÂÌQBòüׂ“áeuÒ¯ÇÊ¥³éñÐ>Úôbý³Óm¹EoõÖOê•ÝÓwYmuwþ®'.pQ̆!3só÷œ»sñð»„#u1¥ygW[Ç#ùØsaI þ$¤¤(HŒ‘B¤?£%6¥ L¥þOyaýŸíÂ1÷úZ¨DoH˜j/½Øúañ|bB/ç‚P2w3ä£ur¡ ¡Ã¦;ÁqÓHq³äKD¥–•[Ž8V¾Þ±«˜öYŸ¤›~•ØÛGÔî#5šÖ2y_–Šçö~eÙÒõóÿÔÒïÞuø™†‹]fW—x€ Œ/®éîý÷ôá}•?ƒ30_¾}R«6-bj¬y‹;#/’`o§~VíÖ‡(uD×Y_s Îj&8Úc_¿yŽÌèfø¢e¡®G4·‡ÊŠ$Ôzj¯Îø1rVaD£ìkZWGó¾-·çg2mjˆ•Š¸gÕë}agæ+ª%ž®Þ³ê6açãwï1­rN–›Ns˜5311Pú`ŽKt¤yº¸æ!èóËÇwoëÅ‘`NÌ1/ú(KôÄkøÞ*²>²Š$>@ªˆ«ù~1[Þ+óÿ9RqXÌ°Ìr…§#,&éùúµs¹¥Cõ¢R"é&H<ìLjñã·¯œ‘º{ú(Ø».qÑAãºÉÂû¿Ñöc?‰.îöhD d(ù|mû7óê#/@OÖÈmLoO`•^.Þ²]ép0ÕÒûttð†WGš)xt áy(kÈóaöÙwu+°uÀ-‘³jß®Ï(ñ¿‡šÞÑ:l ^ÔuyãÉ¢Ë`Y-ŸAßêÜm z5ÄìØy½òT‹à禇˫çÑ¥"ß— ÌÁ'á9´äiÏ#5¡–´ÖÎÎÃ|©›fÇ"ÎÞ¸] ¶1z¬ë1[ĺ•|ý®®Z µ¤Ó’"kmõY‡lkø=xû|÷¾DYe-bBfGC\¦"ËÉ.}yfnûýÍÞ½ÿ5“Ïœûvúc1#,…† £E˜b"®P»–æ>!#ÚÞ³Ïí9NgÅ®¦ÓLOpÞ¶f`ý ¢'ÞÆã‹eå3šÒæÙh õæ„€’„)9⬣u4œ9À¯ãßsª"➺_ÂMÿÙåöÞ7Öÿ‡5ö¸ÒùLÃ.ì©"¸H¤{áÕup”æU“g†:¶z8]›½™˜}‡ßš2Ûª!Œ·@¼Ç3‰<“Nf?¿œi•A¨àÅ^½# ¼õ©ÉÛ<ï^Å¥ÎÞhmöÞ-4î¼3#,¬ßÕ½#/ÒüNªÞ,ÿW?]®É߈!Vâ šæ ‹¼Ãáà=PÌI êEš­/l×À’0iQýñ¿é–õû³”´wÕ""ª‡JæþöâB™PKE¿yŸžùçµæßþ¹®>Ó'#5|#/y²‹ð—;ÇÓb·sŸ*Ž/›öΠC÷xëƒñŒ’ýo‡×.^NÊ”:2t¸7·ä¸õ²OĆ!8KÁÛ’¥r„¥i‰(<öA³aNbìñ®SòþëÊw5Ú/ã¬Ow•Ë<‘0üÞ„z*Û/1IÊNyàÛõ¥w4Oª¡Ë^]¼/ ©/„»úÚðLz Ð M–óPÐòF×ù$sžHDŽO\°OQv¾/Úáªuñ\̉zà}Ûð‡˜ƒåÔ“¯Í¶N§Ž'¬âÇzžñ&]›ÇG:ñÄq£®ë~|Á83M0Ŷ-^\{uïŽñ×z+îs>ÛˆMgÙUùb6vLtOt*dƒdtºŠóõæèj·C‰~ÈyRãÈX¯~Lw$êéjãΫJÏœf.(ó|v5|cw1†”YùÝ%;ó‹;vÖ{ç]ëSª0¥“¨{ý#5™O~ªìù–aÁSúÞFÒº}“#5 SSa:›; bî4Í‘ÓM0–vñ®•8èL±ßðu_z<ÍQCØYª-™_¢eœö“"¡Ï¯# §ÒðÄ*‚Òð6¦yI¼GTWUñöò¬·/dzI#;~÷?N€ÀXó‰gíÖ©‹åðÃ2ô´ª3·kÓÀ…˜¹­1Ý›è›W®Ã¿.’ýQµlUŸõ}à«¢àu#ù6‘oÖé”ùÃZ¤î|üeyš¹áŒ|ÓÃ?u#/äˆyÌž0yÉ e²Á©}?6ÃöKžk›öò6^÷Ò$d›žÓ¬ú¡¬õ}lªŸAd0­•÷)¹eÓš*ŽñbfŽ\=~]H3ÑËÁGY'œ{l;üÅ8,J»Åž¤c.ê`ó—gÃG©½êÁ„çƒ÷¿ö«3õqà[Ci30öÔŠFª^;ÎlËškF½µíáøíwX56ZËY}.ˆsŸœušÿVÌŒsç9§wÃWi&yZ_ycé'-ê0ñÛÏ̽%êí¤uàÏAž«¬t?whlŸl?CUÀÓôÙl™ s¨#Ê·š¬§dÐRé.)NÈøõ}­¦r“öGüi©Ÿo‡6#¥Å)CýW_™ègß. ì,O/óD(-_ZÉA*9ìæZÜŠpÔìñ‚­þQàÌçÛeÁ×$n9µw;­-k1æ#/Ò·ÃùÐåÅÄ1èSñû2}j—Ç07ß?†ïŸmKó¢Iîìì‘lÝKpû‰Ä—æçÓÃÁÝú¹µømÛ¿Jç‚©£Fú%ü²ú3Ÿ)m5S0W–‚k(êN>vZ´9J =–âT*õ;ñRƒþkÀÍ á–¦·R^4ÞpÑðóï#5~§zHßÚžØ$ÇO¾p÷|œ-a{#õâaöýPt΢‘j+Vß3¯ZmY·EÛHÂDÚÛ´Bò…ñ¦dBi]×k%ôKÊ~âfQxô & Û´P“Å1¥8ƒÑGâ ´Å$¿äjêÅ‹ò‘à%ÄÎ{‡“éj£øYB®DÂ^Äi/¦ê8Ä0(O¬DãÔ,|–-‹$ʽ³—­.kstÓ‘Mâœ@Æ/(ì‹ðåñ 4(@vχ$ß㦔葠9#Æ_#5…“ôÌtMô4aþ¡G.ÚqÇò"Iêƒ";¼ÆøZó~”±Ë°#5±¹Ë£]õ5äÛ/èCKK–S¾üdûiÁxjÓ!ÒëqÄ ¦mäAìŽV-Ñ#b0ÿ9¢P“õUphÑñn‘kYB.Åã'Aˆly! ­ýw¬ÚmA/Ò³4ŒÞ¿Yþw6¿1…1>o„7?\/ŠM„‚ˆý±<#5³g‡‘ã"ÿ«îêÍÑ ìMSfé‡ZtžY´>õ”µ"ÙY:uè©×í7R§#Ö9D‰›/;¿+ü:ÈLœ20ÞÊ;ñ¿.ÎRýUß]§ì>öüM•cNnIï»ô#/OÐËâëøë峿ž,MÂ61ú¹§ù“Ë}^õ™¬'[XŒç*« J¼ßæ|wªŠsiùHÞ´½Å V’<°>ì³Oáð›Â+¸7þNE…»‹È„œ*jýI@ùJ„Hà8;׬×sº“uÁÂå¡É 0bî.ÊT÷À¼èôÀºSÖtǪ¦J+3½N°dl%ùðfg kD×Iú!{Hë_wçÛ†™Ï¶>ØéÎÇeâã(nvç"ìÊ<îB.&¥tÔwtô˜ëCYLæs¸™ª­$Ýh¨›±e—de^V©“ÊÿÇÝŸZ«Ûku$$Gpq3 Ý€Hb0PëÚ‹V#,°;«ÃO­=zò&°¨ã¾ZÊe2‰š7ˆ„Ý&q!þ±ãY}.+ÐWæ/b\îñ œn&݈vWdH% š=2ƒú®¹†!ð‚-Î5·6÷Ál÷ïÆCÜÅOèÉ@&àÕ=Js†¨Ï#,y]a>”VmÚ@ÂÊÕ{­y •OßAׄyáÄõ‚„× \ÖÞvæ,ÖTö²Ùá§N-e{ed:*gsàC;º¸Ü'ÆùJý~lŠHg Í®ð¸E¨‰W7‡ŽúŠžu+l¥­uSf½MÆYÔL`fe‡#/k8î± uŒq°å‡›9,èŸx$YG±©ä‚C¹ßMp­@üpÍkI·_luÌÇPaw=µ„H– w>õÉ©;qx,ÔñÈÂCÊX°L!T2šj‹vúˆeÛ.l:¼Á Úesj“5Æ2"X’~¦·O—¢ÉØ{{aϧy¼}á˜Û£ßÑ·›nÒVKJ3ÏlõʺÍ5N³¾Ò–¾Ú1,iª¬Ú{lÓ…åÚ³ u¯c³Ni0äÅtl_µ6ÔΚŽà#{³“AÁg©í¿<Šˆ{]ÉÓ^ÈsüS™)ZãÂÃij›ÞŒÄ“ú¸@è]ݤۻ¡}Ù¿<0UÆ&„Òþ¾¿7¼õßµ7È„u¯0$þÿÂ/ ˆÒ*‚g¿¶s§¦…áš¾}+˜óŒ 9ð7Ÿ‹®uüE4×J ï­±eóûú¬OÖ"&ܵ*£¤NžÓa•›ÄšcUÈ:bðÌÖxæÞxí*G¢¬¨sf¾&#/f2ÆJ&fŒ­·EVú‘ªfE*ä8R¦|¹çZV2©r¿«„Ý8Mý®Í«¢K¦p„þ•`©} 7É'˵U³QßùyØÞ9åͦQ#/(8z»X°Ìi¯KçÎÐÒe(U”W•0„~³ãøyÿ^¢ü(:zKk¬Ç’ ðŒdÀðV¨jÇŠ™û¾Õ~ënõÞNÍàã•ÊE^ÔúPù4ÆŽ„#5žBGeS‰Îá ¸éÆäiØ)ÏlO>hfëjÜj„ô¥f )•t~×3ߘŸ é BS/£j–n¬+²oº%ºCK›,¬†™k¿UE.ýЇ©šH”VéÃD/$ÏÛ»ÅD|¤îvá×Ý>}³ 1¯(Ç»àÆqš·¿EÙ°¹e% uÚ2öž§µ•ëÙi˜ÎÅÕ,Ѹ.'g±•b¹9€™YV#5ßk¯6ï#5êø*tòž0å#/2ÐRÀJ²Ÿ~Gó`jM½]å‹beýqKAJ`ñb¼­¼¿i8±!#/wî=„+-ÖØW8#,žp²CûÛˆÒÅ\wñ_Çc]îgÑ¿Ùõä_t…Ýy¼Õ×êßÝñÓC“9YH°fôQ¥þ5Œ´(ÛsŸ­š”›Í6ö8e6Äîè…;iøÑÁDvü7:L²í•ÓŠƒÍ:C¾ßÃŽï}y!!æT}£E#/înE ïQ†×ÔÒ&GÈaç´õ Ý#/=Ì4!´Kkúiÿ•†™7ô§s;cQ(Ò>=9\ÌeVŒé,¬,Y9µÜi@{}6ß_ßôÓ¶ëw0RDXìyð0Îh©ê~ú¯=O¶¥6Ê{k1¨÷|=9ñ©XÊƶØd{<³È½ˆRÔi##5;7¿ÑñP­‹R«”*&vl¸VO^5Ö5Žæq&ïèYè_f_ÑÆá‡óãÆ#5mÉÊÚ•äÒþÝ+ß FoÓœpÙŠvcgïøUôÈÈr°R~¹ÛyÜ"ij!”4f¬ä‹¢’d’K°™mí@(wÊaá›=§„õÇ“³®ã&öéQÑ\÷ú'âñÐ9Ý‚-ïß·®²GlM,]ÑÍä_Âyý?d¾÷CW“6#/úénP<¼å¹ú¯€Š¶W•ï6ì­²)†î9÷.¬9>sl:颅\†`ÒúgvÕ‡º9í>žª®ì¢YŒ0¡kË|`ÞÉ6´ÞéßìÕU¨%bÔ[ya·>ûé,ðÙq+Œ­Ci1b©œêíyØë3?TïñÆ..ˆ>Àè)®ýèÍxqûuÏz®Iûúo‡ßÄ`Ö5qŽYøç"­c0¬¦‡c5¶û´-à]¡àl„jÄ1Ï{bÜ…éÙšcçc‡GN=ýˆwä“Fz^`Å#5ç9HªÔmºj­Tœ ÕõDÈùNÛñÖóÆò#~¾Œù<Žù„&}<§åpÞ:òRÝQŽÏ4¬Œ3?–åœÙ½rÕFøðçómwÊ5èÚ@#<Ô–éPúS&79\Ó*E6HÜc>±n. E–gÛÀ: %“È{û°ä½(Ôa©#/Ó±8kŠ™‡nXÔÅ–ÌÀœS4AT(q»«Ýž2­Ÿ¯^M}PÀºm͵L‡túUø#3émk$íØä‚„¹üÆ¿Ç¡v˜Èº_=¶77˜ ‹¦Äån–âÛü æA ÈÒØFQ’›WÆbr%ºŒ)Øpr17ü çXÛŸÏ~è:«Sì~Ê:æíµük¶QË´88öÌ„¥q®0§kÕþ1Æ3ÄÉ%¿ué0#Pè!ÂÛ(¿##œÛn«jëŸf]š(ñÐh0r½tæ^ÃDYÑU{á‰ÛU »jz e~0Ž’^+c´x’ŽXuFOUY’Ëbpwù û*¬¯wIásÏ Z.7³A&ÐATiÆ$óÔtç‘ny[š«­ˆ_qsóK®·k*ºüœ9§ê†ÃÆ&Û`äe´>U\7ì¨Â&ØûËGI8å…û~_¹´€M’¬Y‡·Áù+•YcQTákC’+Ðg.ËÃUò­óÕ˜Ž.ñº4Ý5¨×œ¬gµÊê™S{w3ï¢ß¶ ­™©¦nªÔ·U@Ó\¸±l#5Zõ,Ô,nûdÆÆÔqr¯KŸŸ[߈jî[›%Ÿ&ºøLPIªä~býºaûŠú-ÍÈç#±¹€×¶,Pæ÷s®€Á0æÑeØ^8¹;=À'•¬=a Ó£MÛÔý9Y‘l˜V= Ê;ù˜®J͸j¸ººVÆ}ûg¶~·Ïfˆ;†ÄÚÆPË>¾wŠ¸Ò맶è™ðìÏù£ZëGÕVOA@%¿ùþ[?GÇñžÞóÛõxQÒÇD>ˆ¿ÒF¿”L+Й‡›ÿ›ÓDð˜ûâúìBµÝq< wõWÁ9äs¶¬ÖVj¢ªÄàÊ<ÎS¹9›-[61øØÓ_íëSïòóY{ŠûKãràr—é÷Û{yc‹v¾<ðSŸÉYâæsyV‡Õžâšr_e’ÌòÏûŸ§Ço›—Óù|–~—ól|Š²ð ÷5†ê¼c¦÷~‚ÖoÖg?ZÇVœ‰²°¶Ò#/ü ÏÇ–Z~¿ Xƒ¼áôËö&ÉÓeÑá@ªvH¡9” Ç ™–[_ì$÷~àt¿»6}ž/©\ ö°~ €ßb#,_`éÆ#/ο°`M™BI?ä/õÓÁ#,!ߢ¾™°€åa#±#/é‚6%Vá pU",#,þÇü¥ ËŽÝ³ü_Ôyk«·fDO+‘h›k öÁØ­™?Î=á6˜À Œyu£ íéÞn4LØv7*pÄÔ2“_vC„JÝn5 B"N¶¨Ö˜IµEœoRO—Å 3¹ Èz1~Ĉj á‚U1–º¢¼÷ò[§`áâÓѱîbv½çï°è|}þ Åú—Ú}®h£™¤=¨;!œ#,ÿdE, Þ.àø“­LÒËHæÆåî}ÖÉ52U¶u¨Yþ'צ* í ÞôÂêéú69¦‰èܼÇäÄAO´Oñ.‡‡øwwà¦ôé“âXñg\ÿMZ%Zß²mǸ°FG$-~AæþLéyOø/²í8Ô;:#/¼Úy >²1ò}~G×6ý¯SØ„ÑA`­'šü'ÕjY£›‚A"a‘KQ=eksý=ZŠ>'Ì2†ë¡ÌÈÙŸò“ Y*v}Ð7Wæ=¸¸;µçÃcUŒ%b.qJžº“XŽ\@"âØj.6IN‡Œ$6$ÇRg˜R±Du€Oä†AŸÑ<Š¼Þ¤?ËG¨bÿALxü*UVªa‰‘|O¢˜ÖR<¸€G°7®Ãknà#/€Çï$êÕ5îFÕïSqÜè‡#P-"†Ðñ²ÁŠ#/” )"b€N²cè{Ø\`pRƒK$aì-“"ÆÐ:\ÄNkÌ×"G:•°»°„,dLXÂT­J#/p/hÂQh:†÷çïH+P#/ Ðûâñ#5Ý™y‚ÝÕ¿õràÌ‘DÜã¡@Þ’¡€x͉¿.tJ‚†QÃBÆa2D‡ýd¹ÿ©ƒÝ ò€€xû‹—˜’#5ÒV}xT‚1òÿ+í‡ß\kƒgDÆ‹FhU<è…ÇCßá{‚JXÿÉq¯Õ¿Ï:‚ ˜(8£û<³êÎ&'[_ñÌõ³}fÖD»8RHç»?¨¸û]Á; ¨F#¸·yó4I“O}r$Õ’Ù9J>¥»äpææm9 Pþ{•T†ðüÏ)LHð§qDBD"S.‰Å¹Ÿ´ÚàáØëSHB3B= 8?ê—#/©Ä²í7ŽÿÝysµRTõ4ˆ„ €Sä4§äõþž‚ÕR„ÉƳ¼¥rl™ßm4ªUQÖ«lÕm ÏŸgzâŽý¸ag?q¸8àáæò8Ø?½÷ñqàGৈzNs‹,Xï×iå uÐÕ9”r4  îw`ä]Ñ,Û͈ÃôC­ú¾û-ë~“âF*^ƒä=ŠCÈ$%¯Rff{¿–š´6nÁkÝKIk}/òeŒʆº(ú&î?”û¤)#,ZÓ`ùT×ÐSÂû®3|s§V¸L[¡WwtÄ$N”Î êÒµ~à¤aA|‘¨Ÿñãäû!ƒ»KÓ4—iž#,,±*!¡5@´DÙ‡Ûgpøï]éá8„Ô‚Xd#åþè‰e¦JÓ¥aü”E"E‹|ƒ„¤4ìÕ:#/ú_¥Qqi,BT<Ú@Ò#,‹¼6?i`>|vîÞ#óaGÑ䘮dC1 Ž¬>¡J ¾ï»þ·46}¡öðð"*’™çÖ¦Ór‹Nñ‰Å³¸ÝP ëÒKÓb]ñÄ; #,GŽöŒD6™ø¥Ÿá±d6Q¿¡bÀûf(h©¾=ôÞé4¶xÄ—9°ˆŽF)R 0ñ‘Ã2‚Á!ÌIY"ÑÄÀ{ŽOwy_#,ÅÈšÄ>#×äଠ²‘Fm­Ïð~n5UB"{iÀâœ{Ty7, ?_ƒØ9¸Xf&f4e°³ØºS‘u0bØ.„ Xr!ù_¼ì7œûGû˜=/#,taû¿«÷÷l” `ÿ6…Ïß#$3<£¬m¡F’XÐß =.Y%„êüz¸Ž«ª¿#5Íùnî„»ìU[Ãu<ôhqkÈrªÍI ÕF³5„+Œ|‘ä|¥Ç·§HÞáI®T«|˜U3z5›–#ù¨‰z™Z›&`™±æ\ÈS©XD"“¦ìx N‡Ä£uEÈ÷iVeCMf°»D!cGùX@8l|߯èü‘¨,¨2"Hª~x¯Ù”ã,"s0þFŸó3Í®¤þ¿·ëõXö>ír6ýí÷W³—ÍU¾¦¿¾tvë€μ‚wCÅ¢$‰G$’I––ÆÉ#Ÿ`~ò xäa>Ÿ¯ô£ùDyý¨q§‡#,Þu(KCî!ÏÀ´%±°pxŠ‹_³G«K•ù—·7?h(ûãw|Ë4™fÈ‚Êñd“B¬Þ‹³XÄÄé•}Aá¸ÉɃ8)ÓID¾8ânòHàîât’HßUå]äúO<é'Wpë))¬éBŒCxÆ6ˆ,M^#yk#OFü#/Sfœ%Î;âSÜ÷‡-Q„Søï× ÷ÖgäÇ#,qô¾œ„ÉOHPÑFã›ä!dã²€½ø~•ö²Ð0¨‘#/ú/Ï\èn©¦æ5š6š6»¶×Ÿºû™ fï@xÖ$ü´ºü™Xƒ^æXö½€)g×H#,QT¹°CÔPô>ÞƒðC××÷?3ªOˆ›š±Z}ƒ@pH¾9¥¹‘#+ñ9„ ~Ç~¯pm>Þ ^óùè€F4äw_øŸ¬Óêlªr  $þËý({]›;Óñ7ˆpaÞ¬{E;#Dc‚;€àøx$Âd´lÊ6¤AÓç]qñwžAÅÜÑ€¶}¨Ïg"´DÊ<#,çá;ø”Ú4ÿŒê| ·pœ-ý9ðžÑPõ‚‚Š$!!|á4ȳ֧öKpx('™‰B&ÏEŽˆx'7ËðÌÜðäAjXúG®tŒ"± :dñ‡ÕÅCâp¯¬#5…ú»¿ã )úüt€_d„cÙù+ò }’e¯¾×Vñ=ÁàŸ(¶í~t3yxœÝähŸ *(kŒ-A[0ôº™6 b’z™$’y€\U=@f{8ÿ-´@µÊqͲ4dˆž¿GëñH¢^}ã3DI­VW½´£Ç»?¡>¾†  òå–Û¦rÜ«k|[çäÿ òÁnzŒã$ÙØF$ö·Añ‰`(2½0„*±u4Q“ŠÊÇf#5BËß…44ʽ‹…~ºTN;:šÔ)1J³ÛíøÈf &a™êQÈ$Šª&$0buq’ãÆñ#&Ën¾O5›˜NǨǠm}i§-î#,ˆ¼ÄöOg{®C1 ÷xX#/Ð_Ϭï<Á*B2† ôv;ŒDùÄj¶©vª­Kµg«³ˆ¦'¥'úÏX€ß0>¬žðö+Û¿„èêASÄ#,î6‡nÆŠrt`9+ÀR‹>SÅþAv%¿V^}MLÂÞ7 ‚iŒ3¯Ò·ôýÉ?¼Â)*Ÿç»¶'úmYCû^cCh:ÖiÆÍH#/3D"G[Ú¾#/B31tÔF®¤(wfC‡·OÍã«jç6¥­r„ˆîy­ƒf-`"÷•Ø®[ #,9RÕ|;ªªÙ,p…\À£þq6{˼Ññ ìó”*{€¡ÄcçxÛ°Ùô}FÓ`§0ô‹"¢)*F˜ >òÉÔý#,dû1EÉŠn‡e)Ô}a2}¹ÈÌ„9”hhX±ƒô\ì¹ÀðõWî»÷lËð1¯CÀWÄlŒhãYy°Ì홤w„NÒA$D"DŒQ ˆD˜Ð]È~™b!ï¢.Ì…ÌÎQgÕ}ÞáˆMwÒÕç uœ5@ŠbIrI2qÆöºDfä̦‚‚BLHzѽ`.~´P+g“P}BâQp=èþ„Á"#5° ÄP~ ôXŒ€ŒŒ2bÙ`’hXPÕèR@D0ƒ!#,báE&I¨‚dzý€¡Áƒšj4jZüÎì`Ro<ò÷¡âfFÆG‡Ž›Êø™&AB<A#ˆ°Ü«JQØÂÂõ_j4OP~1)Ù¼ÑLˆ}9šJ#5„IØ­ ÀˆGõ(þ¿ïøþèG>aöCÂï5AÈ#Uçì+)'µ¨£QÒ¡@ ±’m&S3oŸy«çï[;µskÎÜZÕÙB#bA#eSmw^xê³Ë®[Q‹{{>!œ9üíÓè«$Û:ü`x˜ÌÁ¡£Z]kuýà”ÝpËâÌA>²@%®Ð|Š( BùúÞ#,ÃUØÄ4´Ã°Úýú)’^ ”8‰l†•B‚ÁB) #56 ‚%›1Ä„:hf…È|è⥅¬”ñoòª²»ùÍ㜠=ïéveÌ^à„âE¥ÄyÑ¢A ›7'^äýSí¢–q®»´2œú¬‚m8…0-®Â‡lB¶É£Ÿ4{³ž^Æw[©š˜ÛÈFã#,Úf’³ƒ˜ÃÑm=ÿ zÈwwXÊ•®!^òÔÑ$¸Ù;Ág¶¯"ظn骔¬UQ|7¬+ýÀ?nÞÃéC .Ij^TÙHØÛên˜…}`À‡³cbåôoZýßœþé1†Ñ6}âØ«KL© ä â„‚#,ö$÷ &¸’O·êq0Y½¹‚ J<äÎ$ …Ô2¹”Àd£³#”T¢ƒ@’sì}Ýh¨€"r#ÕGSBPJfXâŽu2rŒrÙ±H&*7¦˜ "?P>'°¼ýžïп#¬¼˜“áÂÊô;OºZpìÙÕ!b÷Äã›$!·½Ï #äUóx÷37^Å7SÉ°®¡øï%by寧 bȱ;Ûõ¡PFKdZ˜*ví¥Š$™že¥ìÃxÉÉzOgÄGê#5MÉÁ ì.JGDÅþ|ÞôÌ¢¿RNÊ»Ë%ÙŠ+.ÀÄ©%#/ `ÄÀi1;½…&¥ø(­ÆrE„2–½Û# þq8D\èk\qÙõxø¦eŒ~¤`9•hT¥ŠÈ‰Ì\#…æy¼0nxho„H‡AL““#,T7»Ô#/‘ ¡l÷í-À#,@Óc¸?R(ØC·ÙöÙ.ŒL-ÈúzÎt fÌǨ u×¾øMœä" ‡ê²¦#5.€DìÕ û½X9ØrÄ:æP•×ˆ{ú“È;JéEX`“&@€YØoñ³›¹Ç¿4¬^m@ª1K ˜Ë I2mN/´ü›á2Eh®0­ÜŽ(¤Ùë^‰ûåI}²,€Ü#/ÑÞçP€‚ $4) zj™&“D);&$M¦ì/é×9À£¡ Ñ£D&èä$ZfÕÌ1úñ2:§6A¬å‰<—÷ˆþ¼£e“ë‡vÕrlwìC5s<%j¼™fgv÷§' ò´Öm¶3Dì=çëî”ðów]Ž£S£Ö{RD]‡#5â¿&Î.Aƒˆ+Ū9Wa¤Ö—^¢„Ì¥úÊ#Ú]§@ø$Ä å<“â—ëÇPIë§øeI$ÃŽÄ ;”­l h7àän\ÿV·· L‘ÓP‹É• è~‚(êgrò7·F0ŽrF* àF¯@l¤r7ÞŠà~xOZ/$ØhW§0øb˜¸Ñ ’¦%ª-‚¬LsÝ×ÀáÍíÌìsFEÞQB=õ´ÔÇ\ŸWÜè…³­<³·L¡¼îOÆRù!¨e/¥„ëõ™,<¢h<¾(ÖR,RåÜ#/™ò»w3vA&Ø?¬Þ¿O~÷¢Bâš¾"võÿRƒ3Ðn`ÌB®¥ÑQýñµ}/ =¶×ùc˜^65iÔKVäÜRMΟÎÞGgc›²h“š‚L!0‰IÈyÓvHðGiÕDçi2LÐ/ñµáTÍQþ^G4ìë ™¼Xtà†}p‰‡fì€ D Û|,8ݵ|²Û»ã†Ü3q׋±½?ˆ$ÏÖL@£ÄFL+¦Z÷‡Š4³FëŸ Ýt^ôpBÄ7£AµÁ#5CèÁ-4Hðp\ÆBÀ@è`©IõGãÞ`UîŽÓ LñûÚ(Çìˆ&\ ˆB$` Ÿ¿¨ï°.ªpmKÑèsîL»ÊÀ²à& Z*…ž‡¨àí{šfÐT7|ÿ­ÇÙ Ù=C¡ÍÆþœp„œbùôq¾XÆtL–?ëÐÄŸ€=ŽÒÞTVrG°.µ€\À'Ç8b\ÜÜàpqqÄp C¼È ¨ä•õû‰ªü{¾W€n88£·èJn»ÕG³Ã`Tv°)$+§ªüW¶ ©<ÂL«!{+ò>¿ô¸¤èU–#äˆÄíWt£ŠÅŠ2-µ#5˜ŒíøK•W iÛLY­À_Ù²’#l #¬­ÝŸÅµqíŠns¯ÞT‘¦–ôD·¿ðë?aÈžÔ­µ[i#/†Ú¡+ÒÐ¥f¶f,‹x ¸÷Õsà&öЂA–xno¾+½+'cùorXýºõ½9ŸoÚ—O²ó¨>óû_‰×õD~Gö!W*z&ýÌ*|õÒOö¸[|vKy¿y«¥ÖZæ¦o.qÓ2Ø6 jË A ,«L¢€~A¡€ÚF hàši²nÝ´ÒKÐbDÉŒJ1V4J¨„¨Ò´Ø#4°(‚±P¢¶„ˆ+Ùm·ÂÞÞu­â6ñIXÅ^à‰F ²*¤4ÿ€›Sþ g×#/ŒÜ„+ÿx¿Ôp‚Kf·Á=8 $Fhà BÈ(f¿€~}Bc‹Y$þõ–«Ô&1tõØ4AeòbÊïÝZ¹ôpÁqy :0¢G o~H wt«GRäÜÊô´*Äts† çk *ÅVLv Ä;/¦Ã7ÈLû›‡yêÐì{ȃHDS.W?=ÿ;~c³÷~/ûVñ@è„;ÁZÁ•ßƒlýFúQk¨˜¹Œgáþx»±Ú«úy/Ÿëý3b·Þl§~V’ÀÊe!’/‰ó¦f=¿ r’j‹Ð-"†œšÝý®ØN¯ äW¥¾ïÚ“×2 t“3¾î%š'3@ø@Dá' E·t@?Þcî.Î÷Ó¿´uñQì±—#/7dr‰Š*!9ÌNcÍØqúý‰F®;O7˜ŠBbqaHÌ€íøâ>>tɧ´ƒK×Ý7–1nóµÛì ¤W”P0€™À-PáBB™Ýõ¿Œ$ÍR$’R#5Á¿å‚"Fd(;ówI›Õä¤2dxDr¡øïdÞg¤qÈ!¼w ­¸3g׶£û[ØÔ)‚¶6åhJYšÃDlDÁt¡É#>jä)PÌ Åáù=nÌ7*ÛœˆA'P÷IÊï…ü†%l$Ž°¤„dàü©« 4¤áhîÕ‹@+!§§v‘‘²ç€k»ˆðsZš‹~‚üSºŒð!¿ŽñMO¦NZ3h>"Á#,R]"Aˆ HZig‰#/yš†!ç-üا“p¥Òûù¼Ÿý=ƒ´3‡×U<’mE™^ô:Œ‘EnBø¼É8<Ÿr‚°ÿOƪçÌüËšS¦¶ï©Âó¥Ñ=;iG\•;\×¾f óDœýLŸ¬õßhcÉË ·Ã_ÐãµwsI‘;-”™˜¶dˆ1êË+aïòYáT³GÒµ@ªY¬'îqÿgöŽ`”êhiôA˜"ÈžNÍñ»0¾D4ùµ4ªý|6¸ä¸Iµ&Єîu ‚¹ûàW-ÑÅDéW2Z9{š¼©%Ý$‘AQ)‘Æ|²±šôrE—yÌ!Vî=!#/iÊ:ƒŠaË¡².㼬†Z(}ø›U}¦¶BÎ~\šlG[I–÷+_OJܵÕÙv,´ÅË<ÈÃ4¥ß"¸ú A0Ž\&CïÉÛüôŽ>é’…—£EÛ#qVyO;õ#,‹Ž&¤U^#,ÛV¿#/ ÁA¿õØ/‹¯°øE·¼òi$©ñжœ•4Šõï•Hù,ù¡ü-„ó³+°Ì[„¾´Ñý VFl/žF%¦D©œ¼Œ @jñT#õ_UðÍS$hd8eb½í¯\%:LÔPþKÔÿw¾º~ß×µ’Ú=ÊoRJƒ¸›¢ñÝÏ]<áøEùÇ©}£Ãv‹#ÜøZé®#/­1RmªÄÆ}.ÚÛ•ˆµ¡¼F¸ñ×~Y#/çË›ï}&G;/kº«7å¼Àȸ칳\Œ‹•è¦*LFHzm€þ¹ðÔ¬¦×ë­*Y·^ŒíBå<žP¦Ë°@Mü²¿ ¡G5AsƒÚc Ñj«RsÍM¦óñ™ö¬&®Ü§„ÝqQ=%½‘=Pæ“ö„zt–J6œ^dxñvÓgŒïwHB^ÀíîQ#/´Ðº;œ;‹KÇEIõsyã!`Ôõõ¡ŠÒr6;öp{ëQ³ÕU9U1#01‰QS4h5)+5$ ÏQá­½ÑÆ9³•„¼>ÐfY0ã·nýˆE¥lßì{ä8¬¤ƒŒðÕlZ:¶•{V›*_ô8T50O‘ø~>_ô¯çù«ù.ÞÑœ¸äxô¼`KtymG"­Êk#/‰&³j-‘Ù³Ò¥÷YÐa]¹ïXR»‹ü*Š­Ô,Â$îÐéï«3Q¯GeR;tªá¸ò]>§óêåm§tNxPI_ñ:w‡ÞMgð9éy›ã9Ëƈ+²¬þ7=iÛü®ü˜øô<õÑŒž1é!»ŠÏ…#5ÇÛÁ1âEÙ㓶N¥Zw¾mA„¤ßar•š¡‹“;<邺ß2 !£ƒòŽ(ƒÜO~gÆ“µ¨ãX™ž;“0â !˜sG;Ì)¯`¿ëÎáõ·äûÛ§¶w÷\Ñò‡#/s%yOnËöpȇmdE‡,0;‘%°ü þc-{‰éÄñ¯U•3ì¥G¡ê«ƒ~£ßs‡†|m΋ŠæÜfûËOÁ5qow\;>Ã1Ê „këÃþª…ãéÒc}_Xó‚ÒLÃ:nWWwgºŽòÓËÙL:V€Ï›Èk©û¹ò ãÉ{Þ©­q:n³={r§¨³7 \;=¸ÝÐT ̈†P1‚ëÀó°¡¼ŠØˆƒ”Q#,("¹³0c¹Õ’Ü^7Rá@ãÌ„û¨J€ œPwAßåSŠkwÓûÕ^¦öElUNÙ ÂBïxF’sd4ó(ä†àV¶*r.¦ÙÕñ¼ægÝ]zB‚eÑ® ¡”ËØY˜.¢¬Oš©qªzyî­ó™à°dÈh›µý'¬Ì¬>´Siá(¦a¬b`îñ&@@ó rQäyW:ªÐ¤ðòÈ5Ü’¶e2W²&.ž¢ôÖÖŠcfJ3,xÎÏ~º?Å À<’4˜#ˆº€ÃYXa¡ET€0Á††±†ƒP‹J›®·£A¯ 1ÒQLç{8Š™#,p}郛!‰§¬¹à^^©~c^ÝÄé¸t'¯ÛE³{(,$’ö±!ÉaËmª©®¢a«Tbßj;[—!¼êçtvK£×.}Ú¿‡[µwôÂêì}½ý˜Æ­íøΓöj×CYËé§Ûÿ]¥Ú4Ýß¼]…€ã×M¡Cì7´‡mÊ;Š¨®ÈÕSxšŠl³ëd¼ FnË`((YM" â]•VÓŒýA&LJà/Þ•u¶î[‰5RÕýÏúŸÐø·ÄÍe,æo•Úo-ŠÙ]cU!%¤ª%„ƒòþÇT÷´¯±N!é!Q«»nÏ"¼î ‰¿½þÀz`ÉçÏm̦Ñ6“~¬IÁ| ú0‰„! +¬(ÏÚïyr.z“y ¶KK™cDw}’QµÄáÜ쨑¶Ò&¬;,„ŽCÂøb¶Ùº¼ò±EŽœ ¯\(°B,B&ðÍvȆšÔIg1G¼ÖŽ¦à&‚6Ô)ôàÀ„ÅQ˜Iã„Û'72…ÊYÄŽ{¯oãÚ믉|]T-4d¥_¤±ï.™ 01˳³¥¥­˜vyh¦ztÛ,¾@z¬”Àl qŽLBØ3vÛ ›zÍtj‚ZÙ»ø¡EYß•°ë¹«/¯5­NzʺáŽ.éÔ£)™ömk{U´i;ÊQiL<4êh±Ç6Ïqcã×WPr·Ç–ÎûÌœ"#ÓmÛû!wh”Ú^ÆŽ¼ì\¹Äa±ð[Y·¡èNZ)©lxÒ4S ^ÁÏôds>!ñà4‘“„ôî úÐÏÌb]n&ÆCÂF2îgÈ47„ë—(ÛÝçïØe×¼4,J'j=f)c òº¨MÇ8Mý|9eÝážÝwCF $‰dªIk6l~¹zôÉ«ŠRï¬{!ñ6òR`Šj‚Ó¼'ŒD˜!™½Ô22Þ$.¦3 HQ^‹5#y…ÀÆq3šÓ©²Ä)k°y ñÃzÅ,½|c¨FÄÀ‡»ùwu  ø;#/|4Ö >€·8HkÒÝ¡ì¯9Ç\x:GÕ”jì+¼Ü—7ÙG"&È÷(p³‰éz½cÔ&¦Ç¡  §q¨¡9#s oØë¹s2,©Úm#/Œha›X}ØÀÔƒà’9oVõqëÊwx„¹4ž„z/sÊâÆ9L6mt^”üJ†–$9^Æì’s¨ bˆ;xb\ðeÛþ»÷¨¡Rw»êXê-¾HÞˆ/‹™˜S¨dƒÄå @3‡Ã§ÉÀiº¶ø¯fï׃>çÞòÐOoªq›Âc-“ ,#/MÖ,û~ˆ÷Q¶Ã‰ 6‡ÔiØN<Ò¦2 ‘1ÃêŒÙ´×½d$è„ nyJ¦ÜD¦Î÷{Z$æ°`’ L¹}îšÑ\]Þ/bê`ôØȉÃÞ(‡¸Ü¤&‡QD^÷"àš@g0Gƒhj÷{0e! \(ÊBˆgüÿ@â= IMÊ’ˆÓÆmu±)¤H‘HvŠ›@×xdÙ>d¬j;ÕÉ£‡èÒ`DˆCáÁÉ¡ÃæK-„!™fBAG 1–i¡ÇØLái:àÙ6î/bŠ-jÍ~¢˜`3–3)ÝàJcXŠ¿Ÿ¡Ctˆ3(HÔbè0#,Œ] ¤;•x8Î!=–ÐÖü¦©áäf#5E!SNÙ9æ}ÿ^Ìô1¬¶˜³Ìð#5 !Ñøê`v#/s‘ 3ë©n%%=À{#:ŒŠãžÚ1@ˆ9s(ã ÍSCg³U¡@×,l¸Kd\²SU3Œ¬X7Š12âóæ›àðVp¦ne.S`a‹2=Í‚!ñ{dk2¢ÎÇFzæ9.þ—è„Ð¥aO%PÈ·LÃ#5˜Šd@g‰HzNcƒ”p›˜¬äd#Å:úŒ7¥p)¯¯Û%­ìŒæl9æÀŒ{^ðÂŽº2™–3^„¹´Ž2l#/¡F×q[–*.IÔe©RPY.ûsÙì¡Ý>¡­½‚Åžìcm¶ª]ꮪÚ³Ê]ªªª­yüîv$:†ìWÓáÖ—.DÑES1ÃÀ;Æ|¾8)›Ä5Žs¡åZNòcú¸¾„ʹ>Û žˆ˜CnÆŠdm#5ÆëU$5³Ñ3:g²¬’lf…-Ër%a=¥Š‚MXȵ‘À ÎF%F#, q.&gEÓ;-Ùê(ÊjÌ;2a!l†jŠof÷–rBgHÇ"-3$Ç(Øñ©ý° ®z}&Žž[†úíÛiu®&yüÍÅ‹±]ûDÿÖ„ íçÑ>¶ÍŒë‡ÿ:?ÛA£É!Ú=¯šæ»û0|x®Jü"©‹"¬VEø ÿÚ–,KËI‹NwïÝ]ì#/¤f‚…¤¡îòØ^zîú¸(Ö>%Â÷.X`Á**,F"”Y*–Áô( BQËú^‚‡±ª(h”ö\Š€!œ€˜:.m6#Ÿ9 ¢Ã£Ç¾0È?ÉÃýìÿŒüŪÇý?ôì I©¦5¸ô}Ð* °}ªùzvÛgêç³k³)4”ÕÚ·óճdz³ÑÞ=ÉÝJHlü=-ï)¹#,öiÏ &ýjR“Q²ÚdÛ) M¼ªŠ"HƒAHN QÏlÆ€9J·Ù\‘€Äš±iêüÓFFMlZ)rà3íþO:·Žû9qÛ¹xÀ ¬”“Ô.^Š<,û~ä„‘.ÄT¡¨ˆ4¢=Û('ïk©{¬r²5ì{ìUçýe\s´Œa©½‡‹âÒR|“ô÷ " ÌÊ%Œ A´& ¨üs=$ŠOÂ}òå4Ó#,ŒT²‚f®ªD@7)Ø @;ÐSÙ(hÀBDÈàªÀwûõŠ™—ÔsÒ¥ÂIäÅçy°Ìk1EAPp‰V ±±ŒEõÊ€Ù?AÆ&ŒF71ýí•FèkÙÜp‡”¬LhñÇãcí M`¡©³|„{L‰ÈªM°~eŠh•:üŠBÐH #,=fG@–ï§Ù 2cSàuQhbªÛÇXXÔP3–2Ï[ jFO#, kV–æ[†íû¬þtGÉ€+¦@`àÐ@ $EðÒû¦.ôÂ(A0 $ù'!:É÷ÅaDD$E"âD€W‡È.)åÅ$õ[u·[O.Þåáí×æîÓ'Ž;tUÏÕ©•¶¸ñIlNµƒXk#5iB Ð L”€¥BÉ‘6Sü¡þnW,jw^K{"õoK)%4[Þݼz­ÍìŠö\¹175ÒKË»–5Ç÷žkÇ.‘Ά74••Ýbå>¹ÛÊt»ëåk&±OªR*–‡x ‚hDÀÞ¿¤â32FÂRbkËÍ«ùðmø`\ ˆ§°˜¡q3þ‰K`Z6ôYûÈEEb±ê?)È'²×ð÷OçÈvû+“E’c©Bu‰Q¬C²æ¡È›0åˆÆÑJÒEj+”…‹œjNIA˜,¼íУž¬Ú3œú­´ÌÚZóà$`8Àãºr$ŠšR³¸®ºõU¿ÁÛ‚Ä"ª€qQàvÐ DŒd‰"áF,ŽFt#ÏÛ³–ëUH¯ñA1&öì¹äO ÇÓƒ‘Í_Jµ¼ '‚”àe#ÎTæ0¡É® \;Ò’hEd$rH”JÓntBt›)¥…%A€~,D ˆÇ®i¿‹u.œ0ðZ‡]Æ]Ámr85Ù2/â6ð݃ȓÞ8A4ÕÁRa#5î\K!¥Ž“ÏÁ1í#/#/¦¬8ˆÚ UƒµÞ^Ø!%ÔW¸/ØNÍôòý/Éus@øÄT{0îï徨å(´­õo÷ŸöÉ»nj좈AѳLË—eÌ!¡²ƒÉ™é}gÛ—¯è(Œ!EBŠ”RuvqÜœNå”rA5OM&t¾ˆ#,°‹(´3}ÒD~@ÆÌÉ ÓBéTáØ"jÓÌ¢ê–,è'žI×—Qm¡‘ 9§ö4º¬R¾þsîÒåÚfnKšÙc„@wxîÇumüþ>gaü,ÙeTJ$hªdTwÀ›ƒÀi¨Ý …Ñó #/£m±ªÅÕFEJ$¶*¦¥ZÌ…*’¥ª-’ՆʊÍšµ*Ø‚ˆúN ' òœmôÊ“”8lñ|‘a¨2 ȨœüÅ(VÑ#,@ ôý[Yœ½Lo’§…ˆp†ÚBØßì¸ðÊT`3#/ ±UÖ$ÌŒi(ô-ì;ѱ#,%• öÀÁX8±e:b=s˜z¬#/|÷U_… ÆØðGÕšFLöM6Ž˜Ë7L(âÃsCIFE h ˜6ZEWÉZ£9‰2銯·ëÚ¶D_nõ@V@Û´”ÑSc :³—hg=é9Ò­A‘m*]%]%¤#,”öÜ¿§³Á;ÈÀs ¤D*¨€;6„OÛ¼à<û6·Þ¯¼µd±ŠÍšÌªûÔáª*Jekr®Y+–»JfÅ“E¨¯ÆµÍÕãt-q+å*øÉ+8˜(n‹#, @AÈï]ç}z>Ž±µ¶úŽÿv[Êi£t´‡ü .7|^®­ ëz¤‡*ôù,‡²Ä‚$‡ïN"“Þ}¨ˆ•v>Å:ÿÙ»W¯Ëöýœßßþ6Ú˜9!Žuš.$@„ Îÿ2ü[p˜“,)µŠ’Й@îeî½*„?K ‘#5)Q`¢+åT:¶«n;N9XÉÖhæÐ)Qÿ\X/ç$&ÏÃIeTù¿öiª'T ?‚ï“Å:›x§¬1#/o / (gÂT¿«{ˆhbn#/´bDÖgdç®A²²c¨DH¥Ó¹2l`›zÝž‚ôaWõ\Ü0ÃÚ‚ÿ„Dvvu‘dC!«çq †ˆY‹ð:°°@¨i­ŒÅ¢6ÚÆA¸¿U#/¹‹o{dÖöˆä€¶úxÒ$ U 6$cL¹Ç¬zsE#5PþÇ›ç36pÅBAtʼn²¼–¶ÈÊâaDÜpÖò±ƒ@™ª‰®ËoÑO "¬ $(¾â’¢¡ëñ£¹T;5<€÷Òž1gN\Þ€ÝùÑùØÜ„Ê#ÅÐ~R›…BÖˆ)ª¢‚±eÄb]Vž¿·©æAHŒŠz‚¨¢$¨‚ #5DH ²xü‰2#,¦B†1X#,šd==ävߟsƒMjX3ù vNˆ#5Å÷QHFÅO~Úms`j#/6À#¡}@ô±d#, ² ”`†|ÁÃë²dèììõSö¿¿åŒdoß(ßž$ÛPîÂÕÛ8º“„‚"‚Ö[÷P–O|RMamC²¬. ý™³ãsg£B÷ÄL¤ø¼Ì’ù§ŸƒeZmr´¾#ÈbÒ¤GHJò8,öÒÿG‚tŒ>£Êâ÷òˆã4g¦#xVcmÖ¿[Dõ£DøL.z¦ ò­`tx~{SsÞ{.ž7Ö;g€Öœž×Ø<‘Ô²öýZFà[ÈQ0˜I»ñ~1Ï^zí…öÉT<Œ-ÀƒÌÁ`¡O¡dΙâXÈÇ×Û²X-SdéÒc(‚ˆ: ¿ÅçMÒûrw®ýGÞu£V‡ÏlþèZn uºc:·¼ü¸˜K‡­Ñ=ç–VuAÚ¨ÓFå§j–×J³5C¡!™œ°Ä+ÆB¯Ë¬1¦¶éO;îåò±#5<>?f×ëË»¿¬щ\^6&‰3Xi[²«Œ«}š^šèÅi°¥¾8Ž 66 `ÚŽÅÌ„G¬ê;8¡ÏÅ{Ôò-75¥­ hZšµ]ØzÑä¿®ÍZ[[»Š’'Šã,͵&æfñ¹«Ïf@ J0b¡cŠè©ª&hìÏ ‘‡äþ66mƒMž‹‹÷0äÞx®C¶×Ü"Ó±‚àÎo)žªÐb/ÇòrêïÞ™— /Æfï_#,À‡`À#¦3¾Fk‡¡£ ªk FÇÀf iœÂÉ“õµÂX“ ©,KíÀ/@÷lgèpÙ½œv7Â9ì¢isÙj.uäñC˜èÍqѤ÷†Fa"Ê íL(ËÂ8Aß(2p5S¯(SMUJ,b1d;vó%ÕD%&~×Ô„ÂÈ¢Ò)qæåÝ=DZ öí¾CDA‚F`˜õI=h§–}ïjá#/ÌPðŒwßàL!!“±s‘–DÊÆï˜øéÖÇ‹1;-°à9#,rF¾TÍœ îCGç³ðÿ‘ïxüÅM}`z| jFB‡*)ZP‡ÔÐ|*¦¤o/FGƒHqB:l×N0$—=HDégõ3¿vkï¼²wÁLÕ ÀØqdæõý[” 2!§}›Œc+äv@“¯ÝÚV»„Mþ§AçF6(ç,‘O#/óëÅ©š¦µ¼Úæ)dR›%E®–ìÕÔ³DÎíµuKk»ñ¬æëºë^/àxÞLj9bð¢Ô²! M¼;K˜ ‚SIyË»‹‡y¿ %„l+[óÞÚ¾Òû·ÊhjFRÔÈ«Q5ýS» ÚÃb4Z9˜œ^PmU¼{¯.³€\^øoúêýß›„žt2‰Dó.ËE#U ‰ßˆ±#€áE0žAªuªÂQÎ- YJÉCÞGB!ß#/ÿœÀï£éÀ>#/Ó'QtˆpòÉ#,#,rÛ#ÉåCª °¦C7;ë˜i’Í=h€÷%[»,9­ŒpQ9¡lGJ©„¢æe†¹újÄ3&šîÞµï@i¥ß±M †+e~Ì+‚âŽÿI%KLžÐ*¯©êdž|vž[ïH'$'ÓÐzSê¢bI"•jnÝ]hÖå»;­ÚêÝ­Ýo#CT’7©!Q4•ª#,#/$&H‚ØYöšJ…n¢B¡‚GA8žúXên%µ(.Ždb$ƒP(a áˆ}h¡–6Ï2`4LT5BÞ†ŠÏÙ£ž°šíÀÞI Žû×è<}O¿*¬-¶¶2<[ýY³ÏiÇ"Ëm±mž¢“Ä„‡V#,¥^¸aëš_» ó#/Í\vÈHPÌÃK#,ÓI:ˆ„RD#C·.#X¤7`q/ÇÈ¢éj¡Ö™Þ"ˆ wbwX‘D#v;¤á¶–·¾ÜÐCdXS à¡Øì449íÑD¼Gð¯»C4šËãz¦wÃëøóo5ìmìxÓ30ᤆ‚ÄZît¶6‰èù]ÇOÿdÅ34Q/¯Ï|r2oD”•²,HÈu¢#/±}gžÙ#òc¦³ÅZ’1ùCª¤Þü%à«Ò‘­5)$6‡߀£nÙü!û4ZÊ»I e@¯¹Ì\\6†ëNF¶Èn…$ £äµ)φ»pj²®¥J~Õ³rçÁÓÄÆ6~~–»3ÐС‚׎¨hãÒè„P®s¦úäÃ… jþÊŒ¨4ÌG9û(!¯EUCàì†3Ç7¨ÀÃ7n ­ÃA ´¼Ê/N6Aš ¨Q¸]\è|ò¡ B)L\Âaùƒa°{yÀ…Îl§²j Ð-" ê„„¹†¢Te…,‘9¿ÑÏ[²•üñ¡Ë3äCH&%•V «YAìU*#,…EV Òõ•ªœ$:Ä’ÙAÌ»±ˆF•$*$,„#2¥\c Ã3ØÒÖ“=Ì<šÍ¨Ül÷–ª3¢P A¾50tÖ,-(¸«jÙ‰€I0Ì‹#,Ûo)MD2› l²¶¾F#55`êR“®W¥GŸû'V/¾y”•Ýe•ë‰o¶”Ë‹$…;‹ÖT2‰±¤.; ººP– ã‘O”,HéÆÈ…EÙúDk-ö6™›PPrÌQ'äHéÅ´Íáµ^œSÜ‚Æ}“̶I†MK¹ue% ¼#/OwQÄö9oÌ’þ$áJ•‚ðÌ@ 9:Àq¶i#,,óF(ÐÎ$¡è/ãíÎd°À±~F6óññö÷#ìñâöšpoKž%EýKâF ©T+n‘„ã÷tæN»ô"s-qE’(¡—Yëï?µ ’B+!)ÐDè¥l>Ÿ¯W}ö†&ìÏ™kÖ2¥ž/Ë;Iëï3Ôz¨ñß…šjwHeSR‚Ž»Áp–0úžÛÉ{Óóõˆà¦&ƒìÔq¹áãøÇéˆ"ƒTIÙá„[\“Q êbçD BÍØNKœQPa0Nmj–pvT—sÖ#Þn#/ƒ~s_áò«{‡´`øæsp«í#,öþ³±…*/Z2)MÓzÕw0œâþÍÆñdoéO*TWäð4Ëõã8Ôîñ:Õ%ì(»ÕXÒrª°5ÁŸçgøY3ÒBnc1o|p#/7Ò[sQ¡¦!A9©«.Ë%Zo»-ïÜwJ,i¬#5#,U@W@t`¯?)@M†5B¨É2KäàÈ Ú'2/Z¤K@‘KÀ¹:EÊ ÈÈ –Æ#54ð ITpÇh8ÑÙœ¶ÏO&“‚ÅWMÊ8ZÂó\‰ô˜³a¦,ûŒ…aÆßé#/¶RÙÌOÊKl=ô—a<VN-ÛÅ¿IÍ+pÂ5A´ÐI§Ã$ñ ÖFh>àÕk;5åy¤2z/Ìm”„†Á5áÄÁ›‡±ÛQvb³ûÊ¢'G0Ð c#, Ŧϗ³¡;Ñõí4¶» A6e0!Šˆ¨4‹æ´¤ÏÇ¥ª.ƃ&ÆŽ%Ì”"Ùn±¤3M²IÄ#5i£I#/ #5 E4ðÒãc=íõ[&hˆ„¢ƒö³&úA…Y©b” ‘ ˆÇ@¸Ž#5 €ÞZÂãˆ}d “@¬ì‘¿ih`û^¿6ó€wn§§à™É§U>Õ0G©{`H‚@‹ ŽGYn#,5ïÑÓ«®Mc›=&Ø1¦Æ€wNÿGG"ÿa樌^oÊàq)8÷ä?L¹`—ÞOóAM ’g’©çÙdÿwæÈ!ÒB*B"‰sºvYƉè í­¿’Ðl‹6-oȹžunIKmÛ¯¹jª6Ô„‚ÿ`E´U´E$A¢ #5—iôê÷§¼£#,zˆÈ‘|ëî:¯iÏCÜ$©[¢X7ºz°îЀm…” T“íæu¦Vzž³äqHDî!2@‘†H%2RkHS$Ì–b“lZL´¥ÕÓ2¶˜dŠ6šT!’š#0)6IJÌ‘¯µÛ¦&Mi4h–R›Y¡S)™#0(Ð#/©‰I¡ÙÝD*hR$ŠIIbb Q’PZ4M’’Á(EŠiKQ²(š šFe‘¦£1!¥„”QŠ³Mgz»ö•;À8¦,ú0Ì9.ÛœX&Aò~u=JÎÙŸ·ôf̈ÿx¡=;p%­S*F9.¾ó•e† ì:¡§ºy®ƒ~)W¾/×ÝtÊe#/´îÕ¤•¬D!$Åt2ÒXï-œq1ñ ÐzŸM“Fýìß…Âöï7€y9,碓³¾åì¹·[BJ²G6”þ‰kÒË¥gßÏø¦dþ$¬|>˜/»}!‹…aº« ªhÀ\³#y,P!pÄ ^F¢ü#, @9/rÆcPÕ,ÒSiZMa4•¤¬ËL$$,qpxÖ}miåTx0ƒQÊ™Lø1Ç?büÄχŠ­ F=DØ„}ö“Dˆ•õêÚM³ ! ZÅ¥=ÙþÚäcõÖ;ÛÔµŽÅèõ}"DcqøC‘–Á[oZ}>·rþd‡/F› …Ùt”½*› Jè^á€faúþ‹²H1Õ‹õ‘½ZŽ 1d~æí“{©?å¼WÙÝ­Ûµ-ѵ´cH%µ“#V iô8$Ýiš m¿ ÒöÛMó“(á©ã#´Ê%j7®N.ø""àï3]{ãŽéDÆ°¢›Aa¿rP´OezA¹ÁÞi‚ìÆBÝ|J{Lš feðv^`ÙGäÌÜ$°Y¶ÜKß#5<öÒ@Ø#´ê@íÜeˆ¡ú½’ö?o-tž¼;î¿¢¼Í”‘@†kübØ<<õµc/Ñ´À5kìèrŸÏèÐ!V ÒiϸB½†vW5ƒI²¿5ç†ã-†?mg:eY| ±:YPìžì²ªôS||S%JÓÄ©=ý~õÊŽVpj@¨{% ܬ!¹Ï‘Ð3¯ ôœ²[ÓïäY ¸Y#,7\F0–rÑsÇ'B;Òmˤ7#5ˆÁñÞÉ ,>!C ÄÐ$ÃbaÌÃÔ#/Ôÿ¡¨Í}¤Cog‡ŸZ’ñI–ÓìvƤPû`M2!äûFˆèÖ Ó…-Ì­ÚÆÓÊ;Z#…ê¨Xe$|2’}ÙåÞùÍÖ?£V®lÛfI”Ô†ì)ÔÛÈïZºQW¬Æo a31þMf•.¥2^BåÒy51Á„-,.K¯W”£ÔZ1ìf¬Þž1`9Û©š‰”—ñ¢LKКB$ÎÔm¼JÝ µ6üt’AIÒÊΉÂæ>ùÛw•wws!šYÁŒL\.iäÁ Tø0R0JtÉžçOK7¢©á?óíÄÁz¶¹~#š¢ŒË­;"¥àFá`ÜådçάËì–Ã#5ƒC0jvœÖ ÚcTêX~Ôê¸Ü·1Roø‹g„r¤Øóˆ3%ÃÊ LBì 2´>t#/<;;, ú7žx¦dºCäÓ@Öp5‹¨íUƈڜ‘«y(aÌ\rm†–Âz¡W««2ùZBA¢ ¦³Wo0áë”àÛq’52v¾Ø³/Á§Áuy¾—h ûº;v[Û$ç3¨â3€ËÒÇu,­úfwÕØcbÔRd,vrhÀ L»_;l\;þNq#,X(‚‡cÆáŠ(ŠÕHeûÁ5-1”êqDW;4Xa&(Œ~]çªO÷Ž‡øÞu’ÛŽÇLêÀ¼øãs¸’xíÇ)Þns/x.ï>YÌ㦫 ¤’kL:‡C›Ø‘2Ž6I7= ¼c-l‚vÛ…É%ÅÞ“¥¢*êoNÐdÝΦ—ØÆëD@³ß™9;6.Än%à‘ÓÞÇG=²öâaÝðÜì¿Ün æˇbsÎéÎ'\µŽøRž¦Î]j)XÎÌqMíC“´#÷å¼—qê.gxÕßÃ7­0ln6Ôi•Â¸Ýq-n#ll#/j&¡Ü(]èºÇ&M–7K}KJÆèôRrA‘ƒaÎ`î¦é‹"Œm¾86v¤cpÎLc+ÑÆíÙ\»•ÓZÇsl£ÔÅ\#/e¬S!(òBƒfækKZï½´k**)šjÝ”ˆJfGºVXnÁC#ŸBÞ(BÛ‚ iÐ#5Ñ'Ks­ Á\½ Ï•1GQì±mP¹rIY¨M†1C8ÎÌöC´ù†ÇG2>TP™‘³.e†Ô[ê‡FPaNü+imðg`ÀŠrÐ5×U/V@ÅCd&Æ^‚,ÆÓI53S¥¤ ÓQ‰v9‡ Ta%3 8][ßPJc´$dÂ"méÙKJ¢P‘ê´TÕºùØÔ3‘É 3ø1z®C³mΕ­·Ìõ’`Lƒo´ÚwB\G,A(©Y+”;ßtÓcI¬7rX<ð5iaNF:tÞåt4 ÍËËÕ6ÂXK›e:G #5&$Y<Ú k‰ÈCEñçÃeá—Èã9€·zâòñ³Ñú‰ƒiÈDS‚QHn!¾¬s(a–M…d&°U2LŒTBš@»©O©6f¶RêT–Q§v(bÈ°2’P¹•40òd£zݘphU°-€`ªJº™8T-!–ój¬ YlY)7ªP1šË0`aV‚e©6IO$¤a–`J£€^¡ÓAX6öôÒŒM‡ Œ+ã¥dLÙR’Ù²[,¼Ô±›2evgrx³VE0`õQÇhc¶ögYföΔ:™]ë‰Ö©—ZRðz ‹LiM+ÙÂ3„‰‰w-Í ãÔ/®Ñ‰Ä!+)’e#,òôö ñ8à:H“a ë„<>VàÔ-gnK`lErùÔö×¥ó}™ÄŽí`G½+½è…PèP}R9‚AsI¦Ðî±¢`TiBƒfèÖ9³h'VÙåâ˜m‘#,@ ‡ ”ΆEÁ¶ÁÝÜÚ3#/¦Læ3ÈdçJ×¼ÑLMmH) #/¦á#H. ÑUV8(|WAÈ]G’DX°j(L Œ°:ˆÄõñÉ#,ß±ouš)‚&ßZ7-½m^¶¯%â-OnÛÒL6C„CŒŽÛ…o4ÌXn74§ž"£¯˜Ùä/ë¼ô|…‘æ#-¤J:?åike$‘#3 œ9ð3 £·}hELȆŒ÷ Ç ˜pqD` ¡‡­#/ŒAĶ‘@Š÷ǘ†ØhñÛ²M ÐQ£f&—¬GcrÕV ÒÅÚ•HˆurŽ˜U1Êò.IÀ8ɽÌ0(vî)7†Fk6Â&Â¥$,Ù3£™‰HB*Ùˆ@…À†Çc‘¾Œ8pv¨Ú‚‚x±£Iè,Òóa½Íf‘л/Y‘Øï%Óz`X†‰FÐÁÍuC­T¡k@º ,Ä׫zè6‚/#5úì”&i°ÜŒ`èpWî¬5M‡4f3•‰cžm,OqÞÁ™º5¡·#;pd0Ãr›\A¹Ë„á­Ê¡‰ªJ,&0  "BÁþÆ @‡ø¶”ôBE„M )˜»‰h °RÂìJU#5#/†ŸÞñWòx¿«Õ;€ÚüwV춌Œ‚²­M!¸>cûÌ‚N´TzÊqˆÒ—B«øÀï•#A¢¨³°µ¡²Ó!!ÜApVà^È;U îËKùEòù‡ÓÜÌj4ÈÉëÇÖ31®[>”çq9"ë_óADpX³[dú¡Ç.¦Ð‹¤ ih§¤#5tÄݺ…±RÍŽAiÅÎdÎ-+X¸#‘ƒ†Ûd¦¦k`(o Í[FFK¦ Œ#5ÚïMݧ`b̸«;¡b¨mMšv*†|.ã©´¶Œ÷À8›ˆKœd¹B‘$1ÜýÚСU{nÝ:Çk–emúi-m&ó@„U Œ‘›ïò‚PØ8„C‹ —§®‰€—#/yPåJ–,‚Ъ1Ã:z÷ E¸ª[géý?P±;÷ÇÊq:‡—à ²¯ÒE]¿vÓM+oßkè¼l¼%ɦcI]wfsu‰QmÒþÑy©ü÷#mg¢}Ƨ¦p¡Ô²¤g,rHȳÚÐ"Q#/Fžãò±2Ð>Uñø”Ýq Bá0®#/7>ÓGÞ‡ˆÈa$¶ÅRQ´¦Ú+FÕ%E-35PhÛEŒ#/ €I#, U¯ø&ïaážY‚_h –êSß·«½qLCà]NØ*~M¦½ÂžéØ˦Ÿ¹ç|ÞÉ}9š5v ²KÂÆÙ‰#Mè h$ÝFY•D…¤Ò ro!¬¨{§ŸM@H Šqö:-'„Íe¬ #ÎfÐu”/V*(~ÆRH)#,ÔÉRZ üä;¶#,÷v6hñçÑS¬Xd&A·ßUÆh*1’Ó"F¿#tJ²4&Ði©²#,\ŠÒ%¢ Ä«ÅmXÔ¸óD1–!p„HO“š3pÃLä\ÅÍeº&šEKÀ1€‰dÀfwùÏGÁðbvúÊ#/‚›”]! BÑjÅ l].t§Øè$ Ýq.´ýÄ[YD*"Ix¢ü,†ØŠä(£¹œFXÿB`>šùdé0òð°A |ûÅA¼$&œ ?mFƆ´Â~Ø#¥Ço´] 35o]ÈŠÈæºü!®Ï½ý ñk@nmpF_ol==1–„ɶW¤3ˆyQ„«Kc+ChãÚ la¦oZ4´cX±EzŸÝì°ó:ìҙǥ éVP½´[MÚé×3.º˜)d«Cƒaç¬074ê¥#53©#3“Îðãžfi™ª¿Â&©¨ý;dÎѱÎù!ÝÙÇ6Ü=ˆ.Ù¢“•@°.I›9Ä`5†#/#`†k”¢eL¬;C¬€Ú ÝÒ–S3:g"K`¹ AÍ=:j4Y%¸ºv~Øg3ˆj ‚qÚv†Ì6K:A‰F%‘–XÄ)¥rÔ)ÀÁ\Ød‰ #,ÅÌT°Ò¸0¦L1 ¦ ™C#/‹  †9‡Z$#5YEÇ„03#/Žnh/Ë[棈 ”¸?ã RD^„NFÝáeÓ×Ýë± ³Ùeýjæ°pH±]” £ý'ófn ?Q?T¡pü¡Î °Ÿ»â HÐ5à$éLò°]YG=#,‘Z@ùýPÍ)¥^î d#,ÓNqN˜U4QUUU#5¨4ïFßÛH²[h­’­bÕ¤«Uùµ”Û˜Æ~+ëÄÈ’8}üϸïd­¾h‹s#5}Ë#5ó”‹PꦊüOô`8ã¿›Ü:ubÅr¿½†âa©k²wŽËGº#/Š«d‹‘A“‡,ë§ q¤Ó2enjêˆMªt”’ÂÕÊu…®iubãê;ÞÚÊEF1¯p1¶×Ðèà\PàÄiÊtó©·¡ÁªR ‰1œ¤¥Ö;KÌ4›y¦óYÛ¢ÜÌZ’ž‡¾:Ý­t Ù&)§Už pQßPYbÞÈ…13±³Ë5B"òQ(ÜD е±fKñ-g)\áÎ:;X´3ßääáœBàQ‘2qño#"ì°˜±J|Å!¢ž†”ֵ̕+â;vÕ™§{o(Q4Þø½,m½MŒ/ˆêtQõ6ûe)Ég‰žL˜â}¶`òdCRŽ MNä2Jò¢ƒc`”VwvÍfÜË—\”ä† ¾,,Cˆî— "( ÌÅ9xokeAE¦ #,ƒPJ`Æ ˜¬FŒÍ2<Âå݈ Jte.%¡qžûæÅZóȹ4Rë&¶nÈĉ@K™…œBèÒ…8€Ð…úÙ¦#5:z¶ˆŒ©zÙŸÒ55 ø]ªîÝ’m[}Fù.Kµ³awsK·\f©Î·³s¦]ÎÉÆN»Œ¶ò‰ªéh6ëWi+r»®Ýݤů;¥5;Ow^o5¼jB×-¹­ÚTj“Ch*‘Êëi#/#/ii"Vª†…#,nÛ^62šÓI I‘6V6ÆÔÔ¢™®¥®•¥šX‰¬1b«"CÑå´ÙÀ0Æ fl#/’+[IQY’$AG‰?I«Ñ{%´"Й ö1‰±Œ˜‡}’Ã`¤¨œNM£.Ä¢-"°T†Hl²o‚ób ê;Ï;‰ØL'¬„Ï^‡êaKîZˆYLÃ?y·òb<´@÷Äà#/NíÍtFLJçmz¢×«å¾=›ÏT¯¤Kóéƒy#,ú¨UEÆz,qƒ‚æPî ‡ö¼Ïé’Hð|{†éLûK‚‡8Ú~šêÙL/B`ÁBÑjÔ‚ªÿ~ #5ÃlƒBfh •Z4XRÒ#/BD×d7Q¶Ûfv[MÅ)”a‚Ñû³3ߥ‹}Ù9-ù·XùÄ6kÚP%,jTŽ5\nƒ È3§vŠ'ݬJ¢s\mje2+çÅ@̪3p—¿ zϱ•¹êkÐ#ƒ§l‚Ó r*°+xºg"De“§çvJ9ó“Ýg ë‘t/Ð…ÂÌ(7$ÑtãxŸ§èk¨Ó¡®{ƤØkTcEÚnÍLRY&mÞfè·­Œ‡êøvüc-LªÔ„µrœä¦”Š(–·Ÿ|›[[å­ Þºf²Ò¢Y¦›i›El¤ÕJûKü?ÉÛ_¦Õ_&©¬Óm”µ¾­‘#|)‚D…š– gv¼™J×JƶrÅQm‚”o ÀÄ#/Ôˆ™ÐYÙ5ÜP£ÕH%A@$E 5Å-%&Õñiº0”jIk)¶­6ͦV̵šSE*KbÃ#ELª•±-¥0”PÚl¥2%I›)™´B˜Õ2‰)(¶5&²T•¨5)”Y šJjE(™±%©i²6jɵ4˜,‘T¦IY°•ZPl¥H”$’e’“)3’jdÑK6©¶1ª&Vƒ"FÔ”Ô¦Û*šÖ¥šL–4¦MJUK*Ù*Õ|ªµÝ­²²ÛM¤³i)*ùM­®•6mT¦Ûi*­«á«Í–¢«ÌÕcZÑl¦ØßZªåj‘5jUP‘CIF9Ÿ/HpnÏÀ–NÝoŽ†MYÛH÷e³{°Ù2í›0ñÑcBÝë'~«ÅÈ㸫 ©½»hYá$©=!â^#/)ÂÈÞD‡u/÷€1ؾèž_n'¢£²=PÂÇk´4#5.Ã,=ü½,k³g]¸bÙ 3meÌzË£»>™2Aó1ŽË`yd„E,Žàuõ¥¯¡}8þMCFßl¬ët+÷ŒYE„ Ã¥Q¦(fI QÖšÃM$DD‚jXªÅU#/ û#,;£Ü{dkù‹X'©Üè,š¡é5vî‡K°6Á0ùå&¹Ó’ v¢ª~#,© åžÅåûÐÀƒ1›°ý"|u‚E. y!ŒJ’Š­ÝÓ¯ÀºÆ2âCÛstæ¦!\ \£:q±1®"µàv_´• åïB*75¶0"t^+òA¦#/tøySyjzM‡I!Š“»4ê#ñôÇåo{ºòœÆa^E\^i­éY×O<íF‡ëu¼]ðи±FµÝ qWX¼4×Ñ沶ð·eÍ’¶½ºQW]ƒÙkÏc“B7kéR“ŽæÇ1…qPë6Mù«!ÎÿÌ]"LÅÃþŽHe™EÆZP+®ŒHŒ–Ú¹ÛéÆýƒ’cZPPRJA7+¯è—'±Š+ÚN…7HI!„ØòX;A¡–5g—ZÞÄXí=òª4ÍáGÄÔAFM#/½P*‘*0f=EîdÑhduç§ej®C2)Ò¬„BáPª °aAär+ò0€¢Ld^±zî.5£'[šÑ3D#5GÇx?!Ó¥ãQT­9ÖM°)¥hw)¹_cí#PEêzq–§MKCr¦ UK†QË4`ÅŽŽL·~%cÞPt#"#,ȆÈHÈýËiTÐëlñ¬ZŤŊ ”a&B[e¯Õ›2FŽìe$qJLØù ¯ÅÞöÌh”È#/"#,#¤Òløb#5&’G­*ä‹áÌTcÄÐhŸ,ô­†Þ-:Õ uåÕkQ5; 墬lí4QYTQ˜×¶˜Plv;¼ò´wÇy#/ÔcSfW<|ò:Òx½‚›PìàÓ·rì·NñA4ÒÀb²’tÁG­ R]Sc#Ng#>Ìðn,•D ´é‰B!²L8 ¡Írº$Š¢Jª¢„IÖ¥ ˆ[J¨ÝØY’k¦ëmGÉm½È¶+jòïY>!S®@‰Œ`F‚eAB¢*\¤–ÙB#µÄÄÌ0€}”Û×–Å׃Æd>É·#€ª1@ð3;õxÎ]Ü©q¦¸@åÝÑ"Zº#4Å–ŠW‹‰·ë_x ,À¥™ùúùùBkdÂ+õµ¢šâÿ!Õ€E‡:“A#/b`5|ܤ€(BÙ1UmµrÆͲÍ%6W]6‰×M5ŠÔj¿‘XŠ,I¶kÚQ­½÷«Ùl©hÄú?ª­#,a#,APÂ"PÆ7À°Î!Bå„îa”ü1NaÖ3÷¬ÝBÓçUk´0+Ø"A¡0¬ðf·×6˜Ä¿&®/†``i4‡ßø` `¢ì3›c°0-¡€*ŸŸÐmÃ#,ŒAV¢ª4JÑàÉg?+ŸUú›4ð;㺣,7–"’IÅ ˆψ¹ª‡×>;vI#,%ÖCh#/ý¹;Ç‘éO«Yöýyñ½Ïí½Ï镯 ­vǺ.øuQ³“‰Ûì!ø«6åF†n²àCw7i¨ÎD=wyG˜¢"»ØT"Ûöqô#¬pvv- @¡Y^F#Kô¼j(Ûh b¯XTe[1nŒX„ š¥ßìöoçÈ:Á2 B?PC¨Šq#/ªeÂÕ؃ÏÑ騱â¯Ó=ì_è0[ öúÄ=CmVKb¶¿Rjµ,®mu›U·Kj£i+¬µ¹ZÚ5²ú°ïFŘ¢‚TD$A¢% !&#,3«=wÞÂîËÑýpAW¹ÁVØÀ¹JPe$À" ê%#59#ª:BaXLÚhcAèÐÚÙw°»5 ÈÅ3a OÖsarz“Í(Ñê É8š£ÊÎ%cþQ:øÁGÁU,!ÜHwÑÞ< 1÷¤µH²ØR**+àvúÐÜQø÷hc10*Ä5Ã…hÂômXp[,‡šûä€&¡#/ÇŸq‘ãßM¼+1yåÖ~N,HVRª¡¤Àhxü½ü;;R#¨” 8J<ˆ¨R6USK‹Ç^4R †ÃÓ~¼Ÿ}ì_Œþ=!…„P6QúÉÊ&÷-š[­T&–"`åÕ¼Sh ‡ 1³ØW6¼s«¶î‰(Ù5&*RVѵ Ô×Ê[´i­¦•”öWRk%XªQÛ°-î¹@mßß¿p¥Á;#i“ïvÒEÇ€42£tñJ¶éÆ­…;ˆy³ÌoTŠU7ûmiàУ6ˆøêU&$¨gmÍ(¥8¸àðÝ`ÎqDUå‡Äa3œêèHzmW¹"Á(ª ¢ÅQ!Æêˆ#&Rä+YO’@Áa¸è ]æ’ Ø 1G¹wù '¥íí`u’$’LÀýq±ƒƒíõë¿×÷[ÝÅ é¤ÐqØ%³™¿wRh„"…›hÂ)V›¢`¿äøm#/¹›dŽzÈþzÌö‹ž–¶ün´h5õâR—¦È¬‰Cá :'ª¥t›ûŒÂQPnÊv@às’¾7i5$Ÿ{1#,Ø(2š0Ù†A“ ˆ†#/Ü …F%#,>Ìõý¸ñŸf“Õšà0†n(c‚<ù˜1ЫÌÒrÏõ¢#,¨å¤Ñ*M—ÙóTI%FÁÄ8¶Y0z¦$öi…“¾ñ“òÄt¿Š6ÒuuóObˆ{_zišó®˜‚|ºNµÄ&åßMå~hÖˆ´b-"„¡jM$%"’ªm´µ%¢Ú21™¯Ö› 2hJ‹K?q¶¹µ¦eR¥%XÔQM)i6ÓLKfµJÆ›YM-dÖ‰±ØÚPj•£"ËZQ¦U-*kU’)h5†‡æz”#/i„O÷?6“Žìd¶¤"f#€ƒˆ£ÕáücD‚5$AJ ÍZ£j5¨ÚêkZånÍà[2@$dH¸buC»«~H_r«o³¯£[š¹ÃW(ÆMÔ¿Áꄉù’ ² ùË”Íü¬¯¯uV,š”#/¤K>av\öýÙ#,2Ï~ñ¡: ·(Û°»`ˆÜ§.H€w`‰åßþw0âW„Ah€ÔöDáÏMÂzÓŠÙýÄ A2=‹öP•U*P” •¨ßäQ/j[¤(q XO½œ¯"ØÊ<Þ¿ §ðÌ•´Üm×Bƒ$€ÑÕÐ!=L=|î€âª¥Êh"jD£9–8˜'ëÝ•Å¿2Ð33²›–7ü}‚§¨‰Ú½&Œ$ziDk´ç*©°å4ø †õ@<{GÈ#/gA'1#,bjIÄ·Xj‡P3väž<䱬äN Hw0ãÈàlúḆ©ü\£Ï\»(ÍØH±’ƒŒÄ¡ýk<ÆÍœ„!pIª0Zà~¿ÙgÊ!G¹Ëú «÷Çòùi‹|@¡4´²/âçÝü¤û»My–Ôl&žä"Nçký’À½[FYD0uűŒ[ªd“Bd˜bû©e²¶ö¹Ò^^à07 š…ÅsC’»+Ž_°ÞäÂ} ‡±‡±»­S .Ü6‹i@11Å¥²„øf£&K>^&—×m5ŸFá~¬RE ‘AN䥂(Æ#b„ñKw>ïdŸ«6kG´:htt#,á=_ïÝŒ—¡p2€É¼n¬²ÈH‚‚£c¦TÛ=Ò1ˆÈ,¶’2&š,K¨QC£Ou†n–ê–‹4T$Y”²‚`UD)å-SÆ“¢~„˼S.›€(ÐÛ XcQ“¬qÀ-Ô' ùŠ÷ÁˆÌòAydfe@v`b qLqºß m]*H™ªJêä€Jd¹Ý¡-îÁçtõ¨vÁwÄ"Á\Ý++î6-¿KÍyñòåÕ7<“yrBóÎ¥~I#k,Œ'¢½>ÝÍ•E“\‘P3HoVžLÆ¢"3°ÇwkM´×m*o#/¡'EÚÖÆÆÒq8‹¹‹A-ÂH0„¤”z ]á£ïÒÖ ¤,€ÓŒQU¦¦¼ÌœÅ<eÍŸûB'0rwd§ëµ¸Žjá*Çy²K5^P¸Ç @œ3)ÙïcÞ·Ôº¨WŠ…RÅa0ý¸;§ÃaDȢ߅¶•Êoïó£}g%ø×Ã.³Ì¡T ÔåÊBIª¤‹n¼†È[FRPì#·[Ñ$Y íTiãŠ$økÒ¢µùfúÄ/êvAÎW<r²àˆ›®G$ÆbBÅÞì‰LCh~µ–Å…'6ÌA5F#,Öýäˆ7¤¥‰Oß¼(ÀF[$ï&»ÂZJËÜåóÞöcn½ŒBÛÁª?³ja䥅‹J“k.91-”âa¬†x}L®³Q·#/£ìfξž@¶k€jGR‚†‚РˆÙ±¼º]ª…`B@Fì(‰•iR´@HôE(¨„Ážf´#5¤•Àõw”:S §™±tEvoöο±P1uã±Ä5ç鬙kÈb¤ÄD(u’´ÇvËæð9´t` ¿«ØÜxÄD$6!É›UdE:ö`VAA%Z¬KÆö¶‹¸\ˆÙGèˆeêÉp×^Ó•³j‰©Èàýý°Öúü›Ý<‘}]ÜjÄ~ñhóuµ]v›xPšDÛ^ºò¥ƒQZ›JW 6ìB #,Æ„6’% 1‘¡(2¤*TUF¡ (PH}WC™È¢‚Ø÷P;{kTƒ¾ ˆ‹`©º›SU(ª¨€ªûù^§òW{Ä1\*ð&GaWŽ0­ n(šwŸ##57ôx—nïmëĬcºÂ[p™Œ(À#5Óa;@#:÷/2Þ(’µåÎæ“_R(ô€Ç¦9»ZÉ6ÐIN0ŒJlC*šDh) ›Ñ6¸L±"l˜sEg6f%Y¨–Sd WLë ÆžB]å0ìè›È‰Û­«DÈÂHÙ“Ä/òmZhl0Ò#/ós`Ö¤UD7wD 0ÚÄ1l®¤2Ž{TõEOé8 ÚpãÂB@„u"T#,/˜-ÙÖ—77¡çKïîcI©Ûê´Œ#d=^£š‘ix=KZ…ì#/À›'ùõ6á­•F¿sƒLu8š&ÇÀ:hì¡)#,êƒy, ‡¢*]ZRj™¶ÊZ6eLÖ¨ªRµùUù›[ò"W]¶Ú¹±…!(`°¤)’(£Î€‡Oݸcó!Ò<®Yh¹·£j˜_HqÚ#5€¬==Ö³VB¡ïš1NGŽÂ^uç–\T[”žö«öÑäÝYGÞAÏ ŒC#»:8ŒmNçÒãÓYÂÉŸ÷`â/ $–”kR/ë~ªÀÚE–y'áPÉ<Ö»ùf·˜ÀÚ4Ñ$ØÔÙ'½å2‰¡Ž’Êhå‡8á}“n±ªÓl@‹ŒQ:ÉP#5FõQA4ä'^ Ã#5 1ÉW)«¬ï½ºôë1LÅ‹=•_ ²¦))Š•*l#/r(\4JM¦f†AæwôÒþY’Kjé>Æ#/­µø˜þÜ‘cVÏN}M·ø¦<óMÛò#gz8ò!§Þì2vu¬#/eÁ;|jù`³ßÛDF`(ƒj¿ÕD«–™÷yyž#,zÐOIâ¢Û€Qž”`4jû{~Û‡œ^È”˜„ãÞ…ð dÅ(›N©È”ÕúñtHQ|ÑÑ÷&4Í@{R!ÄÇ*›ÑÀ܈7,cŒ —!Ïë`²|èVcSWn;>³<D’×—ê½õÓÖã„nŽ™àO‡d¿`”:üg„3!š¶#ªTÝ%T@÷DgD47:ÍŸvÁ£ Õ^}ó¯rû žŒGœ·1Ç+>ÇiïrC€Â'*¥YC(•9$—š 2idHAˆ½-"g7{´¿=Ù€¡¸/Bï6.®¯W°°³ý_EÞ+ Q!¶yVÑ`¨ÄÀdvÊ—RÚÕžvëÓ½©«lÍ[z­t֯ћSM_©uҕĈàVxâùÈ›ÍFFRàs±Ó$h1jšø$”¯ü_íbÇõ7ñ¶îÒÙè’¤=È?ìî› ƒÇ•™hæžrõ[F*&ƒkã±xŠ©ÄójV°¸²¡#Ê~HRe¬ç­ulLôеd#/0÷ƒ4â…ÅÁ±å­­Io#|Nz>P 4Ç–" ˆ§R7ø†%C2ämê﹄€G¢#`Â#5í‚H_8FHT¤Õ¨,±\ÝÂØJ€ ·vµ×,ÆÝn³Viµ›6ÔÍi’¥™«lËUu[7e4¼\“Jím}R¦bÚÙ`@E9Øèq`@ß: ë÷ÏDéÏÙñ»oUëÖæ÷<˜ÜÔ³ßÏØÒÙ5DzÌ"µäú*‘‹4„ª‡ÎÓ5!ñ>éMI¹ó†3lÂAëõlõš¾…ÒcZ<«ëÑÄ#/›rÄ£I-¥¦‹ý{¨l­>@ ÈÁkQ ²LU‡MFa°õ8½Ö¬ìbE,…” Æ4ú#/Sž¿Õ¹õ©ßW#/6ÞM»J16æ¸ß¸7plëÁð#/ÈâÊÅ_p6h^²oó®Ãnªø‚,ESøëÒ`ØßÕí[¸`”]Ô¶©#5hŽXæUÕ»°¨Dˆ€A¶ÄÂó RF~ÃVø‚Z*îÖÈ3%Ø÷Hk)JòFÛÐlÈ(3™TqMP90yÍEÁšAuög¡å#/­áTCð~”z,^G™"`÷¿=·<â‘I:~ÇÁï4òOZiƒ[9 ‰PÜäÈù}þg›òÙI0IÐFÿˆK÷#tH²À¬í dZvζâŒî0\7 ŠwnòÐ;#/ì8…‹È® Älô Ò¬ÜÖp#/ÇØ\×yܽ÷ù˜Œ"0ð3Ázõ¶ÛñâiK¸™íÚ(ˆ—IˆöÀ²RaDú•VIu }YK=™.!ö»Ò< áàE„Ö0‚È'!ÔeÁÝ)þ5‚›«1ŽÕÜkÂô Ž¡È3oÅ0ƒ? T7å àÜ„Aú tæ„&Øðº4qü`e3bXFËM£Lö›-¿#/ždl‰ß‚…@rMÓÔ±î£?Â27!áSÑ°»SXÇ•‚´ºÛ¬ÐÊR5vûÃÌø Š‡ÇßÊ|7ÓÜR~"Ä@ž €¨‰™ç;§2€^|7Á2ªº€¡êȵx«Qm¶Š¢¶Å¶£jŒQ!FÒ HP4&0»æû_ò__l'¬a€Ä;HÀ‰@?¿¡Á¹U{ ¸ØŽèØtÛO®¶qqFåœÃØ(nao3[²AD†¬Ì¹ë"1è2•Äšž—-¶ä+|±ÇšpAÌŸî#,í¦ž§˜`8óâŒ!o#˜$†Q[4ÆycP!øÖX¦20üÝ¡ðµ æú4ñý_­óU Q¨(¦‡­O^ýÔª›ÍÔ”IÐ(9†<„£ÄÏB€£`0Pž"ÂdvIOè!·ð©#/[IÀÔ£×ÇyÕ3†Ó}„ˆþ3ÍBçÕÜ|Fo«1=ð<6}Õ æeÖŽž˜Òbè T#/°*ãUÊJª’ƒÉïÁo6Í÷M¾¾å®Å]Ò«!²~Ô¡ˆÀˆ’{ÏaM² dp£AlMH?¿Ýî.xS>Cŧ£1îM|,‘¦Ýøòëd50®n>0†3¿hkľEƒÝFo5qIE.8<<4‰fjiªž ‹4ÍéßcÛC½U³5üïÂÙ”.!CF^ž¢2ónN£méEvÏ>†ùa0cc…¦ó±±—½æ¾¦ºèmi`Q00ƒ!’Ÿ€O¸eSiA›Léõ]-ÊÓ#âý%í»ìç/F 8éØ]T(£¾¬I¼¾ Úñƒ¼ç”ý8.0üa­áæy•žExѭퟧfpuꛧ<‘#5òáÃáŒÔ¤–%áq y³+Òž¤ÒœcÅQMBBÈniúÅI¼·Å‚ø¡d8ΓÙ#5ß<†×ÙÀ®Ù0áH:í@i}᤿5K`S™Ó#/$¦S«ø}aêêü“S.=êqP@o’ƒæ "¨ÌˆØZK‹d²¿8("|hUlºCHMœq­À°¦«2£J±¥ Á.å¾VñÒɼm»*®·6²m»ÇI±Z*òêK][r®íÈÌÝ×h®®ifÍÈÙ±²¥¹Â§vÖ’ÚM³l¬Œ#,´…ˆ BAB¨2ìÀl@¤œPK°ú†G ˆ­;ÑȨÛ£Ó)¡ñ¡Jk©ôaùuó÷¾vÖùµ`J•l¾5YpMZÒ”ºÕ»-¶ZóËlþ8¶P¸7Š‹Â6XjbH2@'%ÍÊÍ|my3D¶[kÊÕÑF.>/=òe5ŠÂ) €Ê„Ë!E´Vñ«©•I? íP%±X2”ض-¢1Y•Æ£TmTÍ££EE3F²Ve$ÈÍR Å´Í%±kf¯vè#5$9 ”A´÷ï°¡òÄÅ:þ,€ÉôÖ­{{#/&Õ„ “ky·í_‚ØúW§|vñm#/O:ˆ´OãÅÍ·’r½Š£]³„×VÃÑP"å#,‰H@Œ“¶rñMš*¯É+~&µ\JÚñ[¥ý[v"V)O7]&©ÝÕÓk·i)µ®&Åm£&²Vî®Ò›V•T¯W[KIQ‚”‹MÿÍ!Ä6^Ÿ™ A~’ŒüjIèJU G&0A´É F;JCay8ä X„EV)!­ÛP\7œøµJW#5#/ÅxaNŠ¦èŒ"ïW]0äÂñÕmúŸhP{ö5>Âo>«ãÆO~#5?^‹HÃ)AˆÃðEƒÞ~ÁÖ•84}& ð¸ëmf,FE[ÃôÙTlcUáŒcÓJ€ÐÒÌ­Y… ö2º$šhÁŒŸÇŠ÷ånµ´ÍáöLc,ÇzÈð³†d·øš˜ÛÇ¿aCsb­†á‹»±-PZ—öR#5¥(P ¼!Ûgꮤ¸MŒ;:>‡$Q8duÃ<^ÑwᲩœù½‹§[¢5]ÉÖó™¢mÜY`¾‚[hÆ‚QŸg{«42Š¸Ó-½?¾ãø“¾â ˆ#ö§Ö;ûñáÀ¬föÖãu»`vH©½O*£•ÄQÃ÷žt?º±ë0T‡!‰çÜLì3Kܺn¨E2ñ-Åõ!ó`y†f@<`«ôMmcTm¡-¾º_}µ¯Ï-äŒHÚGüäL¯J&12ˆÈg#,KEdχQlšÕZ5\Õµåuæê½À•ÜX1ü€nËÇ>pD  <ê0?a?C_¡ÏÙXZQ¶AÕ&´×_·y®ùq¢ÚÊV®––0£%îh€Ö ƒBƒKF¡¾@ÁÛXÍ…£ô½šØ‰Pl4$†ƒÚ¶ïvÕ¾Ø5ª™IŒQ­UãM±oŸ¸Ú¼î݉ð»Æ®¤J(D¦eR{Œ%—!a#‘¤šZawiZ{Ò¥cl¬Â‚àL#5X»‰›¹*Äid-Y* «°ˆ f c €ÂŠ‰(T(±`4½¨t6ÀLÁƒï¥¤µ\E”C2èËôé½q4bàÓ( ¢1‡¸–T„ ò_IP¼æð…#/JK¯¬úpcÒ4a`ªsõ€q•cõfLÜ&†CsÎÎWРèqŒc0Ô–£nÜþòÝš3³˜@9@’zDTQ1#,ò2“v¶ßIùüihqºX=nìšG·o¼7¢Éÿ¶¤²ÂçägƒgHÏíUyì78~X‡C8GG×!HC‘{zðç±úµ:Ô\9úpXÉŸWó𶖬¬äôþ1] ½þ¤q{8ë©qö8é ÖË(΀TáÇPUAà vü›&—ɷ䘎YJè’¤ÓHB eŠ#ÜÄI™˜ŸxÐ×mí Ç2¸¥©#5?U€yÈ,ª)ji-: [G„0÷KÁlÅ@(`-ËYEl8¶<¾€ÂàWH#“‘^yÎéáÏ>Â;u茒ÿi~6a“éã!ØgtÎÇ…ô+ÁæQÃË®6õ4õ5AÝþ÷@>þ¸’AðEUø&+»tî•\Õ¦·[MJ¾ü!VùîwÐP…D¨Þw&Vãû3‹u÷6Ûè×\J•Õ°fÓ±ˆF{‚Í…wŸ£Ù¸’I Ž-U#ÎòóÍ4櫆Iºï<Þe¢S–æ^s JjI’±”ªñmÕ+bÑ ÉcA²[Âk¦´š;¥uy¼GK¶è\·wn‘]/ñµx£ššW——mvœ¶e’uÏ5·U±«m±³MO5Ú“&Ôi6žuÜY.»­Ý”é·E+;®×*êNîÓbÑcš¨¦†(TE‡÷P(ÆÂ)qUö'²pØ`›‚‹Ù·×½UƘ[µ9QüžŠ$;ÔþÞñ-F :Eð„ €H–@S¹ˆ–U—Ú£ä$ëÄËóGÞŠÃx Ø×H7#,|`HŒLÃkÑÒûÙø¦‹õè•ÅÝ°ŒŠÐîõÞš7zòñŸu£á¹?GsÔ›NÀõ‘t•XÖfmQ)™¥ôÕíÌ$ è„#5uZ*Ðb Áp?„#,X(g:½ül*&ŒXEXD’0I†$OaáêPúÕBùòèØ5+¶u×wRÍEE»­w·m¼’ü>ï!ªgá#5ÂØÕ5jL¢·‹ÃÚR¨g ¢ÔA¢έðˆR&HÁz×M!;ŠH¢@0·1ÃB"ŠÌf£o¾uªˆÖ­×m»i³ìkÊŸë«nÕ»Ål¢&4:§žýé˜d'È@k:dÿGöáí)R0 L0)A´(‚#5l`|ÝüSCÙXÔ ú€î@#/ñ¦¶›YfËU4Ö¦ØÛi©ƒ†ð/#,1Û³é0VçQ#,uÌmõí³lšÊ¢¥Ô™›` *ÏÈñ`ÔÕT#¸ú0ÑFÑJRRj˜0²”€'ðP"$P ¹pÃrª¿êƒŽs[S>Šã5‹±\”å,AL•¦~>4Â7H΄ˆx²gW‹Llf-mEúºw÷c~«#,¥¼‘Šb„ BOÄ!{ßá?=¹^"Eå#,úe‡0ƒù³È8ƒÈg„+`_ÓÕ}CyfÊÈHC ÛIÚÔ4ˆ>7€ ÔÀÚz“8blfh J å…¡ü #>,-j#ø6 `j#/$Õ’1W&³hÂÁ8é$B(rA#/…Ú²¨0@ÞÝEd&1<¬¨#5´b#N°ÑM›¨X; •AµM¤‰„Xñ`]ÛL¨žúów•æ·ˆÖ-GŠ®X¼jå“W/‹*µâ¶«ÆW¬5úR¶Ûk;fN=Ñó{njöÖö‰0Ã#h~€bH$ÑG(‹"c\Æ<.ð ²Hk_Æ*-j‹Fž›i¡µ,Œ%£!Q*Ã6ÅîTb¨ÄIE#,ª5AE@‘‰w@ALZ`m6Ô]ƒÆC±Ï<±ÀHA}~…Ýɨ'§bWŒÅå½ù}$µ…ª ”Ž!A¤õ@Œ%(”Р7#/€º±—|ò³±†äOvj)À‘Œ$° ÄyDz·A¢ ²©€D‰(…B¨ ó÷E>A×óÕëX4¯ž6Y7OmwM¢ f#/”bubF(´„ÌèyCó?4YÐ5™á3˜Ag™OøÅ6¼zôþ¥üÔÎàÆ–ûlvAÈ[Ú>pZJ¥¤ÖðÀçÙ*üÍm5ÚFv“°ÃC$MËUÚèˆñt»¼îõÖë‰ùHÓF Á0cKOxÔÌnÉ©]!ýšp#/2ŒÃZ}G‡ý%R¨,¤³N ¢2M«O‘BPZÿ L–ڽϰÜtvc¦#5vbz"‰ù'‘êÔ/#/¦gyêA¼™Â ¯1>¸ìFâˆTWlüÕAœv÷ª ¹FEÆ"€HRD'º†˜ˆ¬¢#,‘#,“˜C¼"XSò#5â`yÓ`ì~ø#…¾Þ¨$ ¼Bﺌí§Pðþ>ödu½ê`B#’4”„§è¶¿nU°››¢ ’‚'ÐoÔ‡®Bx|úŸ¨WÓ—Jö•À."Š5'á±½}å™ÍP9JH”(˜ oÓì#MøÍõ-›¨6œh–Ã%wúÌ°:wýf79 ÕÓ¤’•*ø˜“c¥L°º†Ä@ûÐg¡Ð¢‡\ææiÆ`Ïz\·^è!ö!£[$f”§4XBKš LÈ­*­`&Y4n"Z}_$?/‚x*»ý»Û–&ào÷#/¼h‡¼Hba !ðd;¬_/Q­}h7Êúæ "/:ÜÏxð±Ö/Ë×ÆmàÛ™UT`ûäsž“»¼Ñ6]Y3PµŸÍ{4ÂÖšüÍ—èª;VÊBÝE‹–e0à:ûv„r¨Ã`Ð4#/'8eF©j¬qÛE¤¦‹„b±"À,q­Á#/#+Lé m-0f“Q2ÃìgrI §!Žú²Ù.¼æL;ëúµ HùƒNU”kbf CÕǯFsSÖçØ·÷R'¶ Kâv+Š`Ç”7±={9 lßGf-2>ú#¾Ýº‹xãˆZÞÂœ¡¨x‘ª†¯n¼ÇŸ‘ÞS¿eµI™Îé•|>Ÿõª·ò:lÊP#5Á.ƒÛ^)¥àIò z nÙp£¦ÓLMñéfÿÍß fæ‡ôl‚¥Þ5©^‡.ØÝJud„&ó#5!„3@8‡ )ùÔPhȘz#/QO@'»1ÔÃk:#/Âs>ø;ÁóÊõYZ”*€ðo†vfƒa‘UAÉðìêêÝž@JŸÏ8EuBåœs¦cÈÅš.oŽA°Mı Ô@i©Øp› n"õ¥R¡Û¯†VàeA‰Ð`T#š…pÿîöÔÀ¹@±~“K?¹’|v‘=•ýÞÐ0 Õ iL$DXªÝ–Y„´Œ` Ðò(ôêó®4 m¸¬žÝÍ#5hñ(>ú©¿Ñ1ðm§Ž#/?­üì9óœMI#5BHj¿ŸæÖÃ}_¹5}J#/~‡Ê­ÈÑ­¼šÒnD„ÄF‘ï’FrÙš»“…@ä7®ÇÍ<Ãó‡*„óÙµÉVÓee¿vÂvøxw$NIYúðñð›|h“"Øê½'¯dÐ<õCrÆXµÁ !߈h!2ocnÛìüÞZqpÂD¡ñ·=ØØ–ad1äm”" ¢#,™O2åkp´Î`ì ÒS¾ctà|]ËLõ¦ýW“„ 9Ñ1☖òFqexèô¤@îŽ?fëUÐÒáa¬';ôß¼tN*©&d8œ³ÇFÞ#/­bá=‚u³ž»YfŽ–¡˜ö:Ux1°ì¨HI$«6]ÇÖÂ0á|XBm™a¬Œ3¥|HM®JQ…8åÔæ,ãW7æï÷l—ÆŸÒÐ È9þ¹10H¤%¤"±Dô©ü‰¥a|> _éUƒw¸ˆŠ’H0ˆ²»KUÕ#/ŠŸ+›ägØ~Z}/¥0#/´Mê:·²1ƒÈ4=çÙ‡ä=éÙ.¢ý<ÍÌ4-à˜›Pr<ÎœD1Ûômz»¼Ì-¼¿jö¹ùäqò â admæC\ÕjÌ0¶ØÕ¬Õ­-K‡T\c#ç ŠÁ^%Š¤â#5šI#5–jwáx<ÖCí3ôƒhÇÕlº‰µ&W±Í-þž˜Å7AãÚo@Œ4œPCr‘†A)FP%DE!] þO¯*܆áĬé¼~žøÞÒ¿«·×½m™ Où¡TŠ´ÛÀ¤ê@>Í]`É÷*çÀt[6¸œpbZõõò`¡RÚ¡oB7‹#/]}Ùœf‰ <ŽF[CؘÀ€UUFëa…ß`}¦ì¥‘÷œ(–Æ®-Š ,HÀZÚÈ}x& ¢B·º²#/"PÔ³ÒäÃîË£©t3ìµ¢‘H";r÷ý{à~SH«ë¸î­ËmÜýû­RQ¬c[eRšVjlX–­*@B,êݲÀnxR…÷!ÄÄ ;òøô#5ŸKD‚R‘H(EÝ»ðå4Ó·ùo<ÀdRlÌÈËI&Â#/4”$¦Ä¡´ÊÃ%A²ÊšKHÓ"BÊ”©d)k÷s`Z#,Ç14t„ƒ¬GJf:Óc¡Îž©ËQ¦éá‡uáuž9Î.*v3#/&È„m1¨Œá˜A®¯ ´Ái¯%Q÷:¬Ë+Wwiå#Z¼tÃ+*qÝðif¤ž¸gè,LkzHÅâ ŠËÄ8öŽ \@ !ÞõóŸ)ãI³Ùœ¢»Bº@صYt+t'a¦˜¶@qÇ(£#”T‰¹ù&@b8˜ŽCq£â\Z.=ÛÏxÎ:oS,¢j¶†­õ¢²ö ߎ ÁÒägb5¥œÌ< 1wqáÇ&”¢òDd$‡(íõc_“#ã1ïQkR3„†ð×4V5ý ãÏóOíY;Ô1ZäI ¤þ4DF@ÂÕm¨¬‰–ÁºÆÄ#/24A#I`‚mŠ¶ CbH†Rg~¶®©\º«–›®Õ&·g%Í%æÊJ6Ć˜‰Å²´1¬™p.¶c6ÑŒ‹V#5ÀZ€;nR¡.Ñr!E’ŽË%#,æ8²¢ÚÔf?Ê#,*Â@c 3W¹“n].jw[¥¶Ûu{õ#/'·„ØÌÓšQŒAÌ<ë#,B™‘„#5ÅÓ#,{¨ãZm§ìU+qؼñA¬ ËÉXc)ÿ¼0Çðø›ÀÂB?'Yã ]5Ý]Ñ£È<ØüâWð/#5ŽÑ@Wjš2¥aeŽ(h¤b‚lt¬‡âÚlš(± ³\8l¨´›M ¢¼m¯smžæ-Ë·q‘Jr‰HÄÁ¸¢U =›y#iGM(ÚE¥ “š„qd ¾Dƒ,DCtF“X8¢‰Ë#QI_S³=Ok­{ïÃÂ%{Ž¸M- 6:7&Œ¡+…U§Kd2³ämbB¸ˆX°N5R+%"{2€±d+ 31ÆÒÆ6C ë ˜`ÃRtn(”¥#…IB=$yÔ*`ÝɳcšÖo(žjïzÝŠf;P˜›dÛ.ñæijæfyPŽ"eMž¡²X”™um[¸£ ‘Ƭ•¼#5Õds"X,#ËŒ£2†5€õˆß`ښÊÉïëñk†èh1ÐGC7DAŒj3l3 ™YÚ¨8‡¨Ñ#/j,Œ•Å‘ƒŒoÎàÃR féŒlñ­Y‚n(ë9·4TÖ€5#57× d.vt<Ô`¤yªŽŠ± á´¬f#/ôœ²j4œjŒ#/ ‚b¢Ý#5f“E³(È’Šɦ“bóLÛãÆMœcHB^»>hƒÛqËÙØÚ0‹£X>¥i °Nm(ÐvÖ­$äE‰l2ê’[BÜÁšf‰”,˜D„i4ÀND™…”’$119#,ƒ(`Pè…¤Œä¡¬GäV:Xi°Ç“Fµ}#,Œéáˆcm˜â¢6„˜&0‰ ‚hT‚"m#/,)^ª§FJ&ô{Xg&ÃAÏk´cG6»L~´c´¢¼A*u6(Ì&BÖ-#/4©ªcl[ÈËn“±1›g­M#m>Jash(Ë°Öšû¡ìbÂ…täDT×çѤpìEÐÄUHOID6<¥´ yÃpß4t=(·YeºU$â9¶H|“m'#,÷:hÜF [s(€Ùؘ;0fȈ„i-Æø*@Á’ä4!B†ÐQ bJŠ£"¤Á€4l`Ñö’È^)"¢à4fŸj´ÉÌéÜ‘iì%0±ý­*’ñ…*åM±“w½-åÝ›¤öñºë•ê¼^Mß]öWà´bƒkFª Qd`‘‹!GQ#5Õ?÷w#,YCüBd¼1#,.&ˆOϺýP6” $-Ù#,.vâ¢ê~Oæ2Æ÷¹E@_3#5#/(žª(ÔúˆBŠ2 ¨nÊH B"ˆÈ¢DŒ‹Fhƒ¸Š,™—±î B'¸¢´A9^çHä;æy{ìmò¡# BäR”Ÿ–Ÿ“Å!KFúÃyhÁ…ˆ7wRƒNçÖ‡uדù«xõÅœëcn®®š—¤ *©’Ásj‹G½%Ú¹™‚⋱‚†ótRSRHCö)gœ€víRK†æ¦ì·È˜Ê‰#/0ã§-™÷2cºþo£PDS{n)¥®åÙÞZƸK‘Þ¸k©ô¸–—à!ôÒ`6ùáw/hæÌŒGê]!¯Ú瘙Ií.À±œöÆ6AÈ¿x_Né‚!›Þq; þ°þH®­HD™¹§F*·;2“è×À “¿òyg©‰±!ê"^¸«gŸUݾÌõt48Y©/#/Ðý‡´HÆ"“L(òpDèE‘‚2%tù§`˜†Y‡¼ß{OGºÔwËRÃ*ãmTXÏêúçÖët©²å\T*#,›˜;Š5ÚkíÈ7–9”O.¢|ÂÀø`J¡Ù•[Y Ã¬Ã"ÜÎóh˜…²FbÁ"©TûY‚@ÀhZ¨ÑžæƒSé~'éãÅc«5nZUI'(i¶(°¦0%$Ù«âoƒ}Ì6À—r¾âçöåQB TÜ@´C!Uþ5A”ª¦t ÊKQ¶˜—VÅ×[ ÖOW4mðæ·_n›kžWbW¯®[kÇ5sh£xÜÖï;ZÅɵ!Ín•k`[%²BØ  KVØ#/ép¦Ýýæ·^ud®îå5\¯WÂ’,b#Hƒ¼aN@ð{4TÂØ8¦"|@º\GØ™ûƒÐb·²4c@IŠ¢å#,GÄŠÚ Ò#,z@÷?Ë€âEK´±ƒ»»omWÇ$ñ4§ƒ¾P¯Î!ÌSÎ)JÐ#—å?q @>úà…%Xù)Ô‰‚n"ÈUElïµ”×%­Ù¥®ãm×w#5ì¶tXîï.áB¶—T€§‚%€ÄÑ„pZÅj-¶´–¶íj&Úº„Y$#5j¤K6V#5˜vÉÄÂØ‘DNDQƒ$VHATˆ} #,Í#,t”¯ý¥Põ*†¿š£¥'U÷ÕOF¾áãA°GÞB&Çq–º¾ —ˆ#5©˜½¸³Àô¨!ÜzM‰X=ˆ`¿YÌÅéô”H'J^‡·`dÅï‚„:>B¹bEc„dR@²l¥14Øщ¡³-3DÊJI‰±´2¶ÑV¶Å¶ÅV›J¦U*Å©­cXÚf¢Í^hû‡Ÿik’2µ™¶@ Ð91‚•È‘>ü)pM#/1¨<¡–‘²1HEéd%T°5˜«ÊAh GGŒXÝ"˜W#5)`#/ƒeldE‘¡È1’T:„¨UFÁ‚44D"22*T ­Œ#,„•æfi–L"Ä'Ïõåkª&ô%¡ŠY¥¥Ý;7 ëO®׽^ùPD AF• B c#5FÖ‡h.*W N0é°^ËÅ6¦´“ÝshhyöKkÎ#/«EU)¥^ }#,1 ’´6ÃÃÛ±”!77´qÊà)…2u—ðm:;9øtM­`,Å#5ÎY!‹VF@A1ÄÞ©µ-Ö$<8k”bHµ›†µ’Âe.#,óm“Æááçç$¿Fb‰{¤£Žõç—]5ÕæÒåLÌßrý•ßÛ•F­¨ÛU\Õ\Ö»½¦©+ãkÝìÕÕààØç÷AÊ.qþY ”D³Úû°¹bVUÒã6±dš¤X|?f¸èmŽ8ÖAŒâ凔´}cï€r×BüÎÜâ'óÄË͸=÷PnÝO12R_áë'¸¯òvÛ¸‘´ÖâËϯ¿¶o.‘”‘ ï×eu¨={¬çpïÍl’×iõÜ“ÅÆ;ç:Ý7OKt Œk/ÑÂ)áÎ…-™Š—ÛÉ[ž/}‘äEð„/,L'ü©r‡B¤òŒ‹ôèþ©ºó’9·å]¸tMÐߊkåGW¨tYAÑ3Ï[”5ÑlJèÚ‡ÜEúy»F]Î×!Ý»®éŸÂ%#,âÅÃWN-¿b2 ÖtóßtU°uĶCŒ¯LÕK7ZXn®?“êv¹#,:°éÙN-Q“6ç¶<”h•Ò䮚ž¥°Ïå0¼‡99{ÃV48ŽŽÙ=4å²γÑ|=ÍÍvLç”ù·:8D§Èãwwj%™ ]N•æøkµŒa‚Ñnt{òƒu^9{’×F娄ǟ¥IÔ?9 ~\³½¶Í•Dã숭lÃ>7éŽÌʲéßá*ÔpDÝ5¿]NuD¤¨€ßTSLœEHˆE’æêßmyžú,[¯·ýxvvb–)ªÞWƒÕÛbOãdéb€ºL(«D”Ž—2ùÿS°wÈ´;¿®K×Oy7ËcÒ›c¢P4áÑ*“m…™¨W•Ï’’”ÇÊq[Û¾kÚwy@ûcoÍÑñð{ÃTã¢Ó<#1܇Ä÷Q'wbâü@ë¾½ìâ±ÊŽ¦u~HŠŠÉR×Þù*W˜çO•vé5Ó>ü¤#”F#5wÁTÀá öª'¤ÇRNÐú³†ºÐìÌ rldã5·îù<…ŽeÍëS’]žú=ÃñôÂ3«¦gÂH:&f#|8ßJÙjZœÊJÉÏËÇv0„! ³uŽ‹Šxéǘ!ÝÑÌ®ZœÕQ…sãšQ!Ô8s’„;È+Äu5LÆldR-ߧXRpËf‘(%¸t&!Hìj@"¢ˆÌkè/Nâ~’Ó#núž‰±gëFpðægž&˜Ý»áAÄÈéÐrúçü¾Õ´Ïq©6ºð?qä—™%bLn8ƒY|)®Ø¨ñ¬D¡ÙºøvKxdÍB—3¦zÛàMæ,4C^æ£ÜR£oä~‘¬—2_&O”O¼û˜¬àsÎÁÐç©7ï}ðkÄcŠvòÓ—#³ùônš8ü­øщH…Ë»<|:Ѫí`ƒ¬ºê‡ïD[¿e1›Ú\š»÷Æʤӱﳷ Üì.u«#ÑŸ^$àU†|£3ŸH%LôG›È´­$‰PúžÔªï¢sÇ,¤Ñlu;tm_8Ûð'äésâ]«1Çú`Ü©G„Aï‡#5ë?­å#­ðv(+t>Tú=õ®¾½<ö Aïäx @€2#5Õ‡¦æ¦&Gcª'!c–#/·Xäe“j•¦#/»•*Ô•©ò<óXœªG­_qû”4b8ì;z‹=AÀ~xHôj „qDÃ1#/*Î’v6˜±)‘){¬o¡xÑÇlè4gwò `¨äsÑ—ÅÏ•8Õ…•LbYˆØ-Á\/ILIUãœ)–Íd›mÀ9ÃmRº½Ã”(•‚YãJ^X*'¯$ÂõtÖþx“%S4!2Bð2Їs½le!ÒŠš #+‡—`“3‰—…ÚÊÂ\ºê!l2  ñ9’F£w»PW ¨ÞdÐÏa!HØ6Žã}i*ÔìÍ®“,Ìj]Š¹vmH+‘´¨óËnûd¥lrË-–¼œEŠ¡¹g2PÅj] Aò™–t#, )zè)É(— #5ˆF#,B(D[ðÄÑdÛ«…vmìdz²ÓÄ€îÐèRÏF‘,¨NHéÄÃ-òIÓ ®·@#/Ð3@Ë(ÈOœÔuç öÐÎ>± 9A%A‡š\=ZïëÅ?š•|4G°ª(="…LˆŽÈ»CÏ=Gš/¿ôK¾Ãßž<‡>7p%RÞh¥CÙL²s;Iî‡û‡H)Y.$gá„FÛ\ám°ØÌ>òùÓœ>O룦z“Ò»¢ãÕP{ƒ÷ó´‡M9ëµÉjj?Þ/£#/Û¿·k­¬„7U¨òƒ¹òwF‘®û³e1¶ˆÃw¿1¥6 X'#,Ä öJÇÄ·–Ûu¥¤kL9w*ûGnô |Pz iló0;®8~k¸óé¿F‹„ds$.j¦¸–S½£$2*!‘„’×S’Ú–3o`b‰#4@öæR÷àƒ ý8Dæ‚šyvvã˜b;8=qÀIzÓ¾.TéhünøÉÖáÆA¨Ò¦¼µØÖØq<3Gá¥:¸†ÀùŒ‘éÓM^áO•õÙK»p| 9#/š5ÞUš˜¡ª æË#5ïh*ÆN5²Qu}X5EG’ma¼°áÂÙeœ"5#/ƒ‹â¾—PAÍþüÆ8ýNœµ)é FT³†o—AÓ§\pµ Sh%¨ÔVÕ*ÉRXs›)ºÖfÌVjró¼ÊŒÌ•ÙË|¼ðL’vïlÇÉÀ´*C‚üô_ÊÇm PýX ¨ñÈ=À;ֈ缹e!M,à-²7‡R3±Áíu²œó3Îe²¸‡žy@ó((̈Æg„Py {»–ʤj®¡PÐׂ%öà˜Ì:f,øÒ­&CcÑ®d¥”Ò†#/¶…&„BÂ\ØH©½Ö–²›N,a$ß¾Mnô!Ìà:§ ØŒ ;$7„Ãj¬‘0çËì~€è®ÚòÔjxïédj†l#/~¿¯T~?"úµ¬%š—ò>œä=ÆòLo2±5!£À*€”‚}î·‰“¥D*Òs¬ðf…Ò³½1±fs“W‡D<=Áî^ÒØÈ÷¥£HQU:@ÐZX°æCV‹FѱD»¬ëw*I0 65'mû_±okV-ç–ºÕC0x5EÐØÑ¡Æ”£tZJ²F08ä àD¦@«fÞ­QA¬kšâsmͨõvíœiêGy£YUSq"T'†+·AêB¸`daL‹šbBÌQVd²ÕŒ{†±&0dUC¥›ÌwÓ6Î=>rFža†ßFv¶´O9×"}ª(ÙZ]›`Ú@ÚUÆ#¡DÐj¥†#2Yj2ZÝL3^‡võEÞ““')Œc !b3ä+•„nãËÃxºÍKÌÊ#c7ƒ´lÜA£A¶lȱÖ6¹¬5©Ñ™¼I¹s{)FÛmbm¥jZ#/µH›j¼0‹$ÍË_¦÷u®Œ½ÉÄ*qÔŠwœ#5Fu(3»ãƈŒ)a)ÓpM,(PÊ ©ÓcåƒfHÃSKÅ2¤Ô1wÌ¢ˆxpk3Žâ †iYŽ.Ílo! —R¤Æ±¶–ˆ@Óê+\ФIÛR¶Â7vÕÛmá¦oĨ#/)Âl¤‡æÂ41‰ïlñBMÐÑ¡ƒ[:JÓ6Ôj©je*Š¢…Xa†ñ“LV1·š‹.„ËÛ—KG]ÈŒ3…Ek©Š´š˜:¶†›¤†@aú°wmݳ hq²iƒ2òÈ ¶ûiæqõ<âñ˵aQÓèÃÙ”~n¡£UJ5™b´à£„Bc9GpáF2›¦ Q½åƒUƒ#cDÇF2#/BØR…hl;™1F%Ž´c4-]Fi¶pÂyë+`æ­z¥W¦dpìT¤ŠçaÌá“#jP˲ÆÔ£„rªÛ°ê ƒÐÖ?„… ­GÒ¦fn*£âjˆ‘ˆR-yZ«Œzp§m¥W—”\8@s¹eÇ-:1ês[u¡ñ­½,-™J`qšu6¾i¶Œ.òaZ³ JF™áÜ$go$®Öi‘Ôàè-2“FHÄFK3º*ÁÁ…ÓŒ»!^ž5¤E¤ŸIPÁQ®úµo´#/:Žp0ãG 0%2ÈÛ¾ÂlDqJs;¤*õÇŽ§åó„}+?_èX~×;ˆÒh©4n>õæóξw·¶zê_Ð×ÒŒ3+Ѧ#,B6i±J>°X"B“´*Q)#5UHkø¥2Iâû=µ(Òe!ߧeS¾¬gy­[€i T¦2Û³÷œŠ $UUFJ2ßê*{ÑŸñ/PèEÏó+ÚIŸCªšc‰jó=¾¸CÉûÀþ^°æ‹ëA"qm!–·JÍÈÍPƒ€ñÂ@A²ûµï¿JÙ&a28÷bÍ„Ú[Ýøš§Õ„zÞlº… uq–ÐúðöîF›ºNîäÈ!ݽx÷õ5¼i]tjaQp%-T¨Á³È3=‹g›ÝÆuë2‡^Ð÷¢? ;Q<ÍùT¡“ŒHQˆ á6o-ÕA#ôÏË9ž½Â(Vk{–±´à‘À]‰LÎÈ€ôRƒQ‘a#,X°Aˆ¯)¶¹µ«¦ñ­kË«¶¹¥R«ÍpÝüSòEæE?Aß×ß(Ò}ü$$ŒcPÙQ²’¶Ñ­(ÔF$ØQRRa¶¦VË2j’Ù¦ÅZÆÍ)*‰-&6fdÉ¥–Å)HÊZiR”fJˆ¡Šm¢l”li²Rš,Ô©¥h5"É› 0Œš2[V1­­üëßÒ¿%Þ‰‚ ñÀö¼«hâ/¶%¸PyòŒå¸ûæåteÌÄóÜýOÐ×ó°›ü\÷Õêî¶Ï\ÚD‡ÆsVÑ|’aAdƒpp½ïj–«U¬ZÊ#/5ÂþGŸ§Ç4AóÙÔo³Dó.F#5ä´v‚~Y»zOg†ƒƒM#55n’öÄ 5/öî„ð )"‚‹¬kI­}æÕ®[1¬)4H)-A›_r]¡j•BÅ#5PÅFR>´²eÏ€`'qxå!BŒ*O·Û˜\3W@Ëd}ö^¿°¸j@j" þ˜QjxoCŸŽ]+L"k¦VºŠŒ èËRûŽQÌE4\#/4‹¡w›¢eÙ“$ÂXÃ#,4Ž±w$Âl&‡…1H ªÏmM–ê”$ÅP* ÖXj%í%†¥ñÝ›&õÙcßÍÕõÍz“zÑmF:š¢¬´ò½¥*KHÆ‘#55Q’¥Ñ#5E#,ï8ã'áí5§æá ÙòÅ6FK^΃ҺâP{7¿g_©y»Þ-FÞ­»"’bTi²ƒYDqbÊÿÙÒÈlÐ.g›.j˜R´4•/ ¦d£$1A€`½Qˆ<,1‚:5$m|Ú¬ 8tˆDn`*LCq-ä "I$‘ÁPÙ·‡dœÐʨg˜b¥(¡˜HѨ€7$Ç¡æi;¼ÄMó­#/é\W‡_78ÏÚ•×(,ú/dî"B_òá6ø0 hú½*s¬añ²‰ª£-n]MµA}êÂRЗ0 úT%Ý|È#,ñ‚¿ ¡p˜{ ÄW(4ôçèþ·Œªôn%Ô¢•[«xc%Ö~~>O–ºèjIÛ:ãØ?‘ëSQž‹ñ*ûó)¤ÒÂ:#Êse·ö˜#Ø8…ïúèbáöôaç¦~ÉäX2ŸaÌÈWJñÆÓ­Œz™TZ™ÎÚäÉ64.Œ#51B}©%÷s]kÉ^vœƒ-¾ßaî°i«Ð=v²¾´äœ­r4ié>4"’§< f׸‘©"ø5,:ã»;í˶LÏgÕ&åØ›>f;}J`8§²$DW‘ë5êOñšØ§¦#/‡÷œËEŽç`VŽ@)2g³š Ú611ÿ}XwÄh7!€ì#QýÿTÄ7vuI¨™3>Þ_b7Olbwr>^þ‘BIDI(€Š%çìBöÕ(´jtû´íƒ }'‘n,,)Ÿ¿.¾¯ÏD~ìèGëì~Ç­n~MLáØÎôFB«¦‹ PÆ,+rc¹`Ã6'^ž]hlÅ0ÔU÷¡ƒÑËqQ$îö¡š©*}_m[îÃx`¾fo\e#/1IqˆoÁÚkö‰¼¤þB%„Ž¹°ÙW*Y5ÔX6Gîg|¢ƒ]82¿’Ü1´„–‘™Ÿèw»Þ:*DôyG7p‘l¹ 9R%T:­+ŠŽ\~q5Œæg”à§FÔ²4Ã0ÖÚbj„‚"Bl3Æ 2&#,¤LífÚŸÔZV’ÐÈúˆ¨ª) Ìú—l!Ã4ÈvWº«²™º"¨ih±bE `BFÈ„TIx6;8ØÓâ„Gx£½¡Èk™¡¤’9É¢K7PeÌÓ=™Ø#,˳RƘÈ­h¦RĹ”6ºsÐÙLv‘ƒª³”¬A‡8¦Më¥×'“&èy¶ ÃD…ˆ³Z(`€nQ©ê?i˜` `‚PÈÄ̃Þ"¸7§VŽ¨hͤ•B[ÊBÒ£SÈ4,U±M‚FÌÝîãø¥‘Y-…mÃs=rkš†™Ü±Qš ²dãg[YšÔE`؈‚Ù™ƒNcQ¨œWõúù¾¹Þ ûëgC‹èý #àSAcÕ›f&3ÖF|N.š°cðœ(F®u0ù„õ\ô€Ç;9ç5`Ž¯…í.†êiа;eÛCÀûï&:V3+»ä:ô)˜–t[½C‚;Î’Š[„.êö†5¦9Î'½‡<³‚XÐŽ8-TÑGˆ’Iº›AÊ… °4IAŠ’³#5Ð(„4Ãà˜o³Â^+Þž£ÈFÝøf´kq’Ç—á“SîÃJj—9½Ú›Ê㶠cïLé”hG“×2©á¤AkHÌŽqlcŒ°—Ó`CF”„8íîŠã ÎöêØDI"ÒÆL¨¬2‹™C4 xv´ÚMBÔrç4è…"•j‰D°Î²Òb|ErEbÝc,n#/3AÃkPÁ- A èw ƒ¦¶FH¨åJKy|2”˜LÕÙSÙÚâs\Iu`B2 ëÆÛ#/H³%@òÚ™Þju ©æ>Ír+R–fCÂÐe¶2g=×Ü×mtyBçC‘±Öi ¡f°E2Ç#,Ò7 ƒ@毧̇jXH8ºÎ.Q0tWF;bÑtç±Ócir8ŽMÒ(e#?ê‡h0._Òƒ Ì–‰Ó57“HaÊ< $t3 Sl†tÓi#,s´“Uˆ²P¢& mð±N5:þ[VÆ-ÎPd+‚¶’к0psW)„‡.˜(êã–Kiä¶ 5˜šf…PŒ`dYˆc0’“™Øïú×;ËÓð1 ¸ë\‡Zg;‰Ø0SC#,šÊ©&;XÒ){(zžpÛhK“bK¶èä&º†2Žƒö_ø6˜¶ÎS9’Ô`ôÊŒ¯ƒòö“wLôöx€51X0“Í“¹š£~G36‹ xÑ4õë~F´(ã›ky%\»}ˆGGѼ³Ñ_ k§Žp9kI©X«Ob\8W˜¬L°ˆ3‡f²á G”› ¥£íéxıu»Ú“¶¸ò• #5ÒtÚš–b¿o[£¦ À"l]D¥!Á˜¡*£ó~í¥¥÷ ƹȴš·¿¼7½NP¡Ù¨‚Ê]<ä¹nî7ƒ»åZ íû‚ï¨ò žzbŠd׎(ÍÙ1Ä0ã2ölUñ7’¼–¹¤å£^·*éÁX)ðžäŽtÂfªH›¦ -¬¸1laù¦‡@Íå1¯GYÛv"öÉ,˜;ç„ÎCÉœÉÕZÒ¾Rê41ë~¦XŸŸ•ôÎ0ÑÈ*ÐÆ»ùœ‚#FÎf;&Úlðæd#/ø´3J±v2›§œ¶/5Ê·æF*VøDtCH>4š[ï ’u,w1Œ`¢ÑÌËá¥GBmðX¿cõy­ÓŒÞ0fßE4Q«HˆPÈz!‹ªhEçAFH"¸ªPÝÖMO6÷ÜÉÂÄ;½S¤]Áõë- 2m)\g†`uns…˜®EÌv#DÌ" 3µœúQ4ºÙ"6#/KÀ2ÁÐÃDb¬HªŒbj0¦Iz©#/ÎUñO/wÉHRöYu,««DëJb(‘ÙÅ®ÿ#,Ì1aª"/eHÚ‚žhïCÀø3bi˜RœÊ»ƒÀ CiÁ¨Q Á’pìŒÆá Äaƒ¨bb_BÖŠ*EsI`dl!‚LZÐ6؈‰ ìªXò¨¦&Ý„—²z½}'Ñu”¹F TTEA³GnP]f^®¹=£`Ë*„;E„HH)¨ 5#,æE~íÿÜ[ײöUAÖs—¯}Ø* &ɸ‰U$_°. Äh”"%Q Èvøž@PÈO.Mæ31<Â+"Æ’©bP2Á8âd‚TLc`Ç4kÄÕçxKÍÝbT®[ÞëA±ä! d… È𢢌A¨5%TABJ‹J :éˆã#, >l|si(A¤:ënKßwL¹C®’dˆdÐØ&`¬R, „Nˆ{úÓ³´|û¼B|&>~šíFù ½pà‡ˆ)òáõ´Æ~嚈•Î†Ÿ×3¹FŒPp ‰R‘Ks1ŒµVDD Ð9†Y~И]ÄcaS&¾¨™Jëä%„õuØ'CŸAMìM€ð iÉîŽã¡L’xØÃ)âA!ÜH‹Žû"ÌCÉ©ä±Í#,…0¿‡B°ˆX¥ àïÙϲ؂l‚H„€†a>’6LNpNþ=AÓU7ö#5‡h‚˜|>#, t"qvõl"HÐjá†Tvè®#5dÜ:cD“A‘„\r,É#/ör}°áM‹BÏÌ~eŠ0„a#¥¹#ÏK5 Nè"‚i¢1XY«:ÔEŠñd<¹饤CM"ò£¿!Ý,FÙ‡†a­4¦…ªÂšØ}C¹Ï;PÚC-¶`¯³k9NPïÃ+Zë-sŸ‘‘ƒNÉ ; Ô÷gÛl¨í­õÛ”I°6“jÀ*á,YpèhYhý[¾¸©¼ïðQãU#,E€§¹% 6Ø¢j2‹•1pA-šý–a]Z³š‹06ˆpM Nÿ¬NJJ†RS,Ûs#,Þ&àŠN|P¤¡#,ë:Ûv¬®¾`R)eZ·Ò —Aý‚«! Ó.¾™Âþd|yõl C7Y0Ó®e‰ÖpLCÍ8äP$ E`AAÀU€®ç²=|°Mß#ÆƉòõª×aÈáÐ-@ª!RÈ.B¨6#,R¼„j#5~'»èý½ø$Àce„Yˆž>ߣáQ¼æÅœ+4¢SøÑÈtx×_¢ŠRÉk ‚ß…Ãð?ɨ|>>ïo*ÌŒ>k,"˜ö{M þ3Ah„'ÿÕ·Ä–ŽoN憨m·4kÊ«@(í#,¥$HßÝE‡*oßû©hÚä¾’¿ª ÝZDúbl *|ûÇd,)À_ï€vX.Øbvòر-¸$HÈLÈÅç\¬VaI)"îåŠûžtÞh5xñ^5û’çž^o+Ï|ñM¦«´E°ÙT"6¯uÛ¨à &³KQD™ Q底6Ó6†˜@J” *pþ$Šl¢›OùÊ_w›¼½y$†@!» ¼9›Æ@eÖD«M"¦!ë5ùßHö#,õö‡ok"È,€ÁH1FItIÜ‚Œ‚HKJ“$–*eh²YfKf’m_õãc}ÍWòõô)TC™Qª4še,Òîü°IüOÈEP¨‰Ÿ²–»|Æf7Ò@,)ºî¦I#,àOØc_T½´Ý×6bldÔM¬ÚŠ*Åe¦FÒ{çžjˆ@B {(#,´D"ïŸóD-ÍA5ÿ‡‰˜uw“âú¾¸Á×Iâ¯A5ÕÁ¾Cxtœþ{ŠÊÄ "ñ½WDLp1 ÷¢ž$‹Á¥ÇŠ3˜)–S T‘²Â½´`ÏžfÆð)B°ÆÄ%—óéA¸{ù›iq¢à2 Fû˜ã†1Øž¥MÂÀËCÚXS¢yL@A|Á€A¤Ím6´”„$µÍÉ­R­)6¦Útï–¦‚ Š)sVÛ7“Tì"®J€Ø=Z”`J¦H(HöÚÃe·XOn’ãK¸Ý¨ÕZ¦µÂ} @xÉàRŽ˜ô<ýT-ÙÉiHØœý4#âì:ÆØ£"H›hx’öBVÜÛl Lµ‰Ž„À?,·ïgÍóºÒå†.[6oYhD x˜`næçü¥¿©¶ÕÕúº¾¹}¾:(52&‰RE‘¬SEŠ)(*"Ú2UB•È­úm¯5#5- ÚJ€¦§á•³ñQ –|dK,'Ü2n„ŒD*–QE1cG¢Šh™ññ5± *…¡·`'¥ïŸ\p]»% n»š ´È‚…dCDZ‚´¡¬P›i²ƒ!ƒ$`'‰µòíÌÛæÎíí¥VXjc·Õ¾>øÁW6¾¿9Ž>¢ZØ–0l³(CÝÚmwû š_©ý~C´+CaXe²œÚã-‚#"T²44x d`;ý´:`&ÍlkrÜ’•}}«•¼»¨’Ú*+|®ëÎ˱\ÚZõ©ç[[Æ£E¥™ªÏrGë6³#¸Qý¥d8$-#,Ì,&íOQÏ¢I¨u#œˆr"ĺ3Çü3AZM±¦¬M ( pÈõhX}tfò]JCñ`aÀP®%Li¾pCq5‚u‚]¶ÅaÕ%A‘B0Xs¢k”´nåÕã^o*ëIbÝš™™UÜí´Ò¦Ô¡¯{µÎV+*TÔQ±­çvךû+½¡A#,¦Ac)H&5„h`ÆÈÚ[R•¥4ËfZé·nè”Ò ‚ˆNE¡RDBNεm!‰°éžXK„=¢ºÿÛ—òà«‚Yƒg±¯¤Uòf┘4Ñ8/Ž1†aU3ç qyjI²ë²©Ùíîù"Ö/PXL*&†©J8»÷zÞìñ0lèÑcMû=#/fü…º³ìÛÕB(£Š‡"­yšëÚ%ôÉ!ô^ˆKž‰ BA¢"˜5‰#/ðè:8$ÉQ;’ý 6’_éc MæÿÀZ׎§_¬•¥Çk“Û|å†ýi‹in‘Û¦Jµ ª-ä~?Prg´š¼<ûh_cBQ‘ƒQºô0m­Têð#/(PD>›r8”š_™­ê`×L‹ãõ9ô`¢ÀŸ~úÙ¤q<–ó€’Ešôbä¡HXÜáÛ(Mˉ‚„KÆV#5vÂ9¡4k%Pm¬ýLzŒlŒ¶Œ"À×F\¢é¿HbqK÷M|§hͤ†ÉpdLT“‘=Ic0äd¸(ŒR„¥ €CJâ'#Ãg¥1üÙå—÷#ö^3À!Ç<ª„¤2îz?2P?ø>³Ê¿DÉB6 ʇr³¯y±cÞÀÉï;ݦ"ó¥jIb8h™#ߎ'\¥„Ç:-¥i#,¾e#CûaU°þ¦ŠùŠ¶Ý¿hzž­:µ9´ â(dÿj‡©†È˜2*&½5趙®õmÑ߶…ÆÆF Ú[âôv3óŒõÞäB4Db†'RÖèmI€*™‚ØB#˜BC¡â”_óÄÈé†`– ¯N—À±¤ˆçç"RÚº#,“m2hæaŒ‡/b"fg;Q‰¬ãÛ¬˜‚ö ¬;h-Oî5z¼ìw™hiìºâˆ¤ýØA´ ¨cñ¯Ž+qÎz)\ X¡7 uâÆtù–‚ åþ%)`aá<‘êz¢¾*~LÌÁù]ü7¼žÜ4Ò÷̺‹DSo·\-baúâ.èg_îþ_áò~øÿ»ýÿîÿ»þͶ¿þ^Ïû7¿ÿ‡þ¼?ãþ_÷}ÿþ¹~ÿü<¿ŸüÿúßÿWûçÿ<ßønëÿ—§ðéŸþ?ùYýÿ÷ÿûÿ‡§þ{>ð³þ_ñïÿ‡øËÿχåÍÿøÿËþîÿ¯Ü~O÷þÿ¿7ßùéñ87ÝõõvŸù]†‡ô‡õ«b"jy6Å5M…\?Ð9ßJ<˜«î°B U!œpEÝ!#ZûW‘nóøwïi|§ñyÎî™$Š¢1Œy„ô2IRC²Šl`†éÝG¹¯(Ay4Ú!®z©¨§#,˜6Ç3ø]þꥂƒÜ?îßžÔÓi˜\Ùp–êx†ëqFï„_÷TK´,ã5(„æ)A@µ|nÌ&8ãt§00ÀQa×úèS<šßSÌÿxÿ/ûÞÒÊ?÷yšz6¦ÌÖÐèQþmIŒ‰ôŠ‚Å ºI'ˆÈeŒÌ ¯I?ôÍ@œIÀÜq¿ ÿ•ieqÖ&!3"…2*×r­göòì {­0ªB Ü”Ï&QFišÈ4).¨±ƒ—Y/$³ýùã’ìç1UjUø¦‘2ðÍD)(»p®ýq¡X0š+šANL1#/(=í8C(hškT˜mѤÝEpq1›ˆŒZæá–°ÛÖÓF˜›H†CÄ#/ÿeÉV×#/(k†â¯ÙæIJƒˆ”iåÅÓ%KK¦#/¡²°(R°™ÍŽÆo®'?ì&#/ôçÌö~¨]1ì¾3.ÊbVæ.Èœ¼Óò™àƲÌÌ5¡‘Þø(ÛÚ¿¤—^Íár#%µíÒsVoîÀUÓØÃy…I9óovÿy–#Ûî h1‡¢ #eëÛ³¶5c+KMoÎÃX`­±Œ9¤ž$½CÛ#/V¡¶çB¡<)aó¯´×»á¸ F¼6 ».ˆ¥ÔÏ×·*•ØŘ/S8òÒ`y±3DEa éŠHÎûs#1ïÎËXÍ¡&Hc|·E!Š†Q‹{ňPÑ~––2© E$€¨šy‘¨§J'\3Z€#,À’1-î ü ‰bfi¾ØX(/Õa狸1ågi±ZÒ#‘»#Rý^¼<À¦S ‚ˆ‹ ËJ%B…ATZ²”ʶJÒkL”²C`ÆÊú]ªå#,2Ê€qS#,™?Þ@2ˆÈÁ?ß&<*›(«yÁ-‹'3Ù( %ÂT@¥b(yóúCèïGU¤?ê‘Œ‰ yNð`ò54±Í%2gúF„c!t§“ì#5„Âdx‡‰útttþó‰,¤TG3‡ êú¥#5%±œsøâÓoæ0Ï{BŸûdk»:uNô§VÇë*K~¯xÆ=%ȼ©¾=Í©ò°ööpcõ•]*Se`nL2\aÓs.® #^áÆÙ²Ñ>Ê,7Ÿt›Qyä>–*m4ØšÙîá=(Ýá{.eoÆD'4Š°‡TCVzI”D%%Ý€-W­nm¥•´¥ëk^·ÂÕ}Ëã­IQTZ“D³hŒÆ#/1(мf±Ó¬"5Ýa+O(~^“ÒõÃñ¢ K‡?ìÄø KÔiµ @r19Nac#56œìÁäK–*%*¸”øóèI}0í$&PšÕk##=”´î#5'¯òŸe»".hp ÒH²!!7’æE Ðö‡qFO$v£“íà'‡ŒÿØ ¸zäj*„bECkµÇf•„”ÕË›­«%jF¸jûr¼Ûo%Wؽh›53_•ZüÈ£h€#,ˆäw·Ïá™åóÿ×Ö‚w!=~¨©ãå(ø{f8ò0m¶ÖŸŒx;.‰x§ª#/gȤòû?ñÿ¡EB§ýZþvÌÌ#/l‚Å'ù< Ué6áæçûÔU'ý]¿Å¨£Sõ#/ J®ýá—Ò>ý{pæWi.ò#/¹ÿÜZXßæ§wú¿ì†¯SüþÓúùµµ™ù#¥l³·¢1¡¨zeë¢×Ç¥vñ‰’©œ~®yËAƒSù>¶ÉÓ¶ˆÄÆVJsøzK„pYkϼ#Dâ¢ZÍÆ! ^G¿§,ÀŠÃ$ Œ}ûYOÎÏužº6³8•È'ÇõçÙL*Ï«Ï£o3¸žÍ~ŸWüÕ‘àRsŒâ;Ó¹j¬b/È5Y_ç²V7Ä|¤É{~¯ÿþ.äŠp¡ Ú‹ê– +#<== +#-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v2\n\niQIcBAABCgAGBQJWpU3oAAoJEGelZe39+Q5kv04P/iyvALGAg2Z8oICEDjFkEXWW\nh2CMGLItAhqb3xNeV8WUMMpY4MbRRpN6cU/SPmt+as4oVn2pozca93eWD7yOxukK\n10seOyLTBamS0Wf+BNr6jYXZRQ2N7inc2p6AD75pMOFSg2HeIeQJ0aUIAxNeeojZ\nmUiLYMdtcrF1Kh7KWZAkYSbIAEjJeobLqk2oY7UyqKcODc4RtZJV1InnO4DItEWD\nnd3F5kkVMw4pwYAXaikmCXYBKHXdF5w82KxqEjrAWSoULipX0BVCsSbQ2L0UOs5h\nKXUS4M7AaYKyCcO17E7CnVXaW+vOVyGEECxtSaExWgK5MvYHIGE1OFvb12PkUvUY\nc7CiBxk6X5eZkPyxgxDj20r8zNQVGZ8jDI8Wg08yTAl8+09qCtkE8gGMdNeHYwX8\n2xDH+A3+19022ZZdyO2t5+2AzU6Kkl1qTPKaXJWFRtr8ApD45Y4D3/GAsTNqdOMi\nWeh1XvqQdHjm9rEoJX8aBXShzCMCNhmZalbUhrdzQY6/hnl0PqnlPtyvtkjCvWoF\nXLF6q8YV/ZtqCc36vePZ6lpUQB6FG3g6fhMGraT2VOmT3TROcG17pqIz5y9+85xy\nVSaDc82uHlyzIsZ7vuhV6d9x4yXnFkjMAogCJv6mitFbQsd+LtXYkU+2Zq6wOoEp\ndLLfK0Km4Vs9FYAUbuUi\n=7D7V\n-----END PGP SIGNATURE-----\n diff --git a/gomspace/libgscsp/lib/libcsp/wscript b/gomspace/libgscsp/lib/libcsp/wscript new file mode 100644 index 00000000..7b9cbdba --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/wscript @@ -0,0 +1,346 @@ +#!/usr/bin/env python +# encoding: utf-8 + +# Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +# Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +# Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os + +APPNAME = 'libcsp' +VERSION = '1.5' + +top = '.' +out = 'build' + +def options(ctx): + # Load GCC options + ctx.load('gcc') + + ctx.add_option('--toolchain', default=None, help='Set toolchain prefix') + + # Set libcsp options + gr = ctx.add_option_group('libcsp options') + gr.add_option('--includes', default='', help='Add additional include paths. Separate with comma') + gr.add_option('--install-csp', action='store_true', help='Installs CSP headers and lib') + + gr.add_option('--disable-output', action='store_true', help='Disable CSP output') + gr.add_option('--disable-stlib', action='store_true', help='Build objects only') + gr.add_option('--enable-rdp', action='store_true', help='Enable RDP support') + gr.add_option('--enable-qos', action='store_true', help='Enable Quality of Service support') + gr.add_option('--enable-promisc', action='store_true', help='Enable promiscuous mode support') + gr.add_option('--enable-crc32', action='store_true', help='Enable CRC32 support') + gr.add_option('--enable-hmac', action='store_true', help='Enable HMAC-SHA1 support') + gr.add_option('--enable-xtea', action='store_true', help='Enable XTEA support') + gr.add_option('--enable-bindings', action='store_true', help='Enable Python bindings') + gr.add_option('--enable-python3-bindings', action='store_true', help='Enable Python3 bindings') + gr.add_option('--enable-examples', action='store_true', help='Enable examples') + gr.add_option('--enable-dedup', action='store_true', help='Enable packet deduplicator') + + # Interfaces + gr.add_option('--enable-if-i2c', action='store_true', help='Enable I2C interface') + gr.add_option('--enable-if-kiss', action='store_true', help='Enable KISS/RS.232 interface') + gr.add_option('--enable-if-can', action='store_true', help='Enable CAN interface') + gr.add_option('--enable-if-zmqhub', action='store_true', help='Enable ZMQHUB interface') + + # Drivers + gr.add_option('--enable-can-socketcan', action='store_true', help='Enable Linux socketcan driver') + gr.add_option('--with-driver-usart', default=None, metavar='DRIVER', help='Build USART driver. [windows, linux, None]') + + # OS + gr.add_option('--with-os', metavar='OS', default='posix', help='Set operating system. Must be either \'posix\', \'macosx\', \'windows\' or \'freertos\'') + gr.add_option('--enable-init-shutdown', action='store_true', help='Use init system commands for shutdown/reboot') + + # Options + gr.add_option('--with-rdp-max-window', metavar='SIZE', type=int, default=20, help='Set maximum window size for RDP') + gr.add_option('--with-max-bind-port', metavar='PORT', type=int, default=31, help='Set maximum bindable port') + gr.add_option('--with-max-connections', metavar='COUNT', type=int, default=10, help='Set maximum number of concurrent connections') + gr.add_option('--with-conn-queue-length', metavar='SIZE', type=int, default=100, help='Set maximum number of packets in queue for a connection') + gr.add_option('--with-router-queue-length', metavar='SIZE', type=int, default=10, help='Set maximum number of packets to be queued at the input of the router') + gr.add_option('--with-padding', metavar='BYTES', type=int, default=8, help='Set padding bytes before packet length field') + gr.add_option('--with-loglevel', metavar='LEVEL', default='debug', help='Set minimum compile time log level. Must be one of \'error\', \'warn\', \'info\' or \'debug\'') + gr.add_option('--with-rtable', metavar='TABLE', default='static', help='Set routing table type') + gr.add_option('--with-connection-so', metavar='CSP_SO', type=int, default='0x0000', help='Set outgoing connection socket options, see csp.h for valid values') + gr.add_option('--with-bufalign', metavar='BYTES', type=int, help='Set buffer alignment') + +def configure(ctx): + # Validate OS + if not ctx.options.with_os in ('posix', 'windows', 'freertos', 'macosx'): + ctx.fatal('--with-os must be either \'posix\', \'windows\', \'macosx\' or \'freertos\'') + + # Validate USART drivers + if not ctx.options.with_driver_usart in (None, 'windows', 'linux'): + ctx.fatal('--with-driver-usart must be either \'windows\' or \'linux\'') + + if not ctx.options.with_loglevel in ('error', 'warn', 'info', 'debug'): + ctx.fatal('--with-loglevel must be either \'error\', \'warn\', \'info\' or \'debug\'') + + # Setup and validate toolchain + if (len(ctx.stack_path) <= 1) and ctx.options.toolchain: + ctx.env.CC = ctx.options.toolchain + 'gcc' + ctx.env.AR = ctx.options.toolchain + 'ar' + + ctx.load('gcc') + + # Set git revision define + git_rev = os.popen('git describe --always 2> /dev/null || echo unknown').read().strip() + + # Setup DEFINES + ctx.define('GIT_REV', git_rev) + + # Set build output format + ctx.env.FEATURES = ['c'] + if not ctx.options.disable_stlib: + ctx.env.FEATURES += ['cstlib'] + + # Setup CFLAGS + if (len(ctx.stack_path) <= 1) and (len(ctx.env.CFLAGS) == 0): + ctx.env.prepend_value('CFLAGS', ["-std=gnu99", "-g", "-Os", "-Wall", "-Wextra", "-Wshadow", "-Wcast-align", "-Wwrite-strings", "-Wno-unused-parameter"]) + + # Setup extra includes + ctx.env.append_unique('INCLUDES_CSP', ['include'] + ctx.options.includes.split(',')) + + # Add default files + ctx.env.append_unique('FILES_CSP', ['src/*.c','src/interfaces/csp_if_lo.c','src/transport/csp_udp.c','src/arch/{0}/**/*.c'.format(ctx.options.with_os)]) + + # Store OS as env variable + ctx.env.append_unique('OS', ctx.options.with_os) + + # Libs + if 'posix' in ctx.env.OS: + ctx.env.append_unique('LIBS', ['rt', 'pthread', 'util']) + elif 'macosx' in ctx.env.OS: + ctx.env.append_unique('LIBS', ['pthread']) + + # Check for recursion + if ctx.path == ctx.srcnode: + ctx.options.install_csp = True + + # Windows build flags + if ctx.options.with_os == 'windows': + ctx.env.append_unique('CFLAGS', ['-D_WIN32_WINNT=0x0600']) + + ctx.define_cond('CSP_FREERTOS', ctx.options.with_os == 'freertos') + ctx.define_cond('CSP_POSIX', ctx.options.with_os == 'posix') + ctx.define_cond('CSP_WINDOWS', ctx.options.with_os == 'windows') + ctx.define_cond('CSP_MACOSX', ctx.options.with_os == 'macosx') + + # Add CAN driver + if ctx.options.enable_can_socketcan: + ctx.env.append_unique('FILES_CSP', 'src/drivers/can/can_socketcan.c') + + # Add USART driver + if ctx.options.with_driver_usart != None: + ctx.env.append_unique('FILES_CSP', 'src/drivers/usart/usart_{0}.c'.format(ctx.options.with_driver_usart)) + + # Interfaces + if ctx.options.enable_if_can: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_can.c') + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_can_pbuf.c') + if ctx.options.enable_if_i2c: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_i2c.c') + if ctx.options.enable_if_kiss: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_kiss.c') + if ctx.options.enable_if_zmqhub: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_zmqhub.c') + ctx.check_cfg(package='libzmq', args='--cflags --libs') + ctx.env.append_unique('LIBS', ctx.env.LIB_LIBZMQ) + + # Store configuration options + ctx.env.ENABLE_BINDINGS = ctx.options.enable_bindings + ctx.env.ENABLE_EXAMPLES = ctx.options.enable_examples + + # Check for python development + if ctx.options.enable_bindings: + ctx.env.LIBCSP_PYTHON2 = ctx.check_cfg(package='python2', args='--cflags --libs', atleast_version='2.7', mandatory=False) + if ctx.options.enable_python3_bindings: + ctx.env.LIBCSP_PYTHON3 = ctx.check_cfg(package='python3', args='--cflags --libs', atleast_version='3.5', mandatory=False) + + # Create config file + if not ctx.options.disable_output: + ctx.env.append_unique('FILES_CSP', 'src/csp_debug.c') + else: + ctx.env.append_unique('EXCL_CSP', 'src/csp_debug.c') + + if ctx.options.enable_rdp: + ctx.env.append_unique('FILES_CSP', 'src/transport/csp_rdp.c') + + if ctx.options.enable_crc32: + ctx.env.append_unique('FILES_CSP', 'src/csp_crc32.c') + else: + ctx.env.append_unique('EXCL_CSP', 'src/csp_crc32.c') + + if not ctx.options.enable_dedup: + ctx.env.append_unique('EXCL_CSP', 'src/csp_dedup.c') + + if ctx.options.enable_hmac: + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_hmac.c') + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_sha1.c') + + if ctx.options.enable_xtea: + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_xtea.c') + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_sha1.c') + + ctx.env.append_unique('FILES_CSP', 'src/rtable/csp_rtable_' + ctx.options.with_rtable + '.c') + + ctx.define_cond('CSP_DEBUG', not ctx.options.disable_output) + ctx.define_cond('CSP_USE_RDP', ctx.options.enable_rdp) + ctx.define_cond('CSP_USE_CRC32', ctx.options.enable_crc32) + ctx.define_cond('CSP_USE_HMAC', ctx.options.enable_hmac) + ctx.define_cond('CSP_USE_XTEA', ctx.options.enable_xtea) + ctx.define_cond('CSP_USE_PROMISC', ctx.options.enable_promisc) + ctx.define_cond('CSP_USE_QOS', ctx.options.enable_qos) + ctx.define_cond('CSP_USE_DEDUP', ctx.options.enable_dedup) + ctx.define_cond('CSP_USE_INIT_SHUTDOWN', ctx.options.enable_init_shutdown) + ctx.define_cond('CSP_USE_CAN', ctx.options.enable_if_can) + ctx.define_cond('CSP_USE_I2C', ctx.options.enable_if_i2c) + ctx.define_cond('CSP_USE_KISS', ctx.options.enable_if_kiss) + ctx.define_cond('CSP_USE_ZMQHUB', ctx.options.enable_if_zmqhub) + ctx.define('CSP_CONN_MAX', ctx.options.with_max_connections) + ctx.define('CSP_CONN_QUEUE_LENGTH', ctx.options.with_conn_queue_length) + ctx.define('CSP_FIFO_INPUT', ctx.options.with_router_queue_length) + ctx.define('CSP_MAX_BIND_PORT', ctx.options.with_max_bind_port) + ctx.define('CSP_RDP_MAX_WINDOW', ctx.options.with_rdp_max_window) + ctx.define('CSP_PADDING_BYTES', ctx.options.with_padding) + ctx.define('CSP_CONNECTION_SO', ctx.options.with_connection_so) + + if ctx.options.with_bufalign != None: + ctx.define('CSP_BUFFER_ALIGN', ctx.options.with_bufalign) + + # Set logging level + ctx.define_cond('CSP_LOG_LEVEL_DEBUG', ctx.options.with_loglevel in ('debug')) + ctx.define_cond('CSP_LOG_LEVEL_INFO', ctx.options.with_loglevel in ('debug', 'info')) + ctx.define_cond('CSP_LOG_LEVEL_WARN', ctx.options.with_loglevel in ('debug', 'info', 'warn')) + ctx.define_cond('CSP_LOG_LEVEL_ERROR', ctx.options.with_loglevel in ('debug', 'info', 'warn', 'error')) + + # Check compiler endianness + endianness = ctx.check_endianness() + ctx.define_cond('CSP_LITTLE_ENDIAN', endianness == 'little') + ctx.define_cond('CSP_BIG_ENDIAN', endianness == 'big') + + # Check for stdbool.h + ctx.check_cc(header_name='stdbool.h', mandatory=False, define_name='CSP_HAVE_STDBOOL_H', type='cstlib') + + # Check for libsocketcan.h + if ctx.options.enable_if_can and ctx.options.enable_can_socketcan: + have_socketcan = ctx.check_cc(lib='socketcan', mandatory=False, define_name='CSP_HAVE_LIBSOCKETCAN') + if have_socketcan: + ctx.env.append_unique('LIBS', ['socketcan']) + + ctx.define('LIBCSP_VERSION', VERSION) + + ctx.write_config_header('include/csp/csp_autoconfig.h') + +def build(ctx): + + # Set install path for header files + install_path = False + if ctx.options.install_csp: + install_path = '${PREFIX}/lib' + ctx.install_files('${PREFIX}/include/csp', ctx.path.ant_glob('include/csp/*.h')) + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_lo.h') + + if 'src/interfaces/csp_if_can.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_can.h') + if 'src/interfaces/csp_if_i2c.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_i2c.h') + if 'src/interfaces/csp_if_kiss.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_kiss.h') + if 'src/interfaces/csp_if_zmqhub.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_zmqhub.h') + if 'src/drivers/usart/usart_{0}.c'.format(ctx.options.with_driver_usart) in ctx.env.FILES_CSP: + ctx.install_as('${PREFIX}/include/csp/drivers/usart.h', 'include/csp/drivers/usart.h') + if 'src/drivers/can/can_socketcan.c' in ctx.env.FILES_CSP: + ctx.install_as('${PREFIX}/include/csp/drivers/can_socketcan.h', 'include/csp/drivers/can_socketcan.h') + + ctx.install_files('${PREFIX}/include/csp', 'include/csp/csp_autoconfig.h', cwd=ctx.bldnode) + + ctx(export_includes='include', name='csp_h') + + ctx(features=ctx.env.FEATURES, + source=ctx.path.ant_glob(ctx.env.FILES_CSP, excl=ctx.env.EXCL_CSP), + target = 'csp', + includes= ctx.env.INCLUDES_CSP, + export_includes = ctx.env.INCLUDES_CSP, + use = 'include freertos_h', + install_path = install_path, + ) + + # Build shared library for Python bindings + if ctx.env.ENABLE_BINDINGS: + ctx.shlib(source = ctx.path.ant_glob(ctx.env.FILES_CSP, excl=ctx.env.EXCL_CSP), + name = 'csp_shlib', + target = 'csp', + includes = ctx.env.INCLUDES_CSP, + export_includes = 'include', + use = ['include'], + lib = ctx.env.LIBS) + + # python3 bindings + if ctx.env.LIBCSP_PYTHON3: + ctx.shlib(source = ['src/bindings/python/pycsp.c'], + target = 'csp_py3', + includes = ctx.env.INCLUDES_CSP + ctx.env.INCLUDES_PYTHON3, + export_includes = 'include', + use = ['csp_shlib', 'include'], + lib = ctx.env.LIBS) + + # python2 bindings + if ctx.env.LIBCSP_PYTHON2: + ctx.shlib(source = ['src/bindings/python/pycsp.c'], + target = 'csp_py2', + includes = ctx.env.INCLUDES_CSP + ctx.env.INCLUDES_PYTHON2, + export_includes = 'include', + use = ['csp_shlib', 'include'], + lib = ctx.env.LIBS) + + if ctx.env.ENABLE_EXAMPLES: + ctx.program(source = ctx.path.ant_glob('examples/simple.c'), + target = 'simple', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if ctx.options.enable_if_kiss: + ctx.program(source = 'examples/kiss.c', + target = 'kiss', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if ctx.options.enable_if_zmqhub: + ctx.program(source = 'examples/zmqproxy.c', + target = 'zmqproxy', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if 'posix' in ctx.env.OS: + ctx.program(source = 'examples/csp_if_fifo.c', + target = 'fifo', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if 'windows' in ctx.env.OS: + ctx.program(source = ctx.path.ant_glob('examples/csp_if_fifo_windows.c'), + target = 'csp_if_fifo', + includes = ctx.env.INCLUDES_CSP, + use = 'csp') + +def dist(ctx): + ctx.excl = 'build/* **/.* **/*.pyc **/*.o **/*~ *.tar.gz' diff --git a/gomspace/libgscsp/src/bindings/python/pygscsp.c b/gomspace/libgscsp/src/bindings/python/pygscsp.c new file mode 100644 index 00000000..c69d346b --- /dev/null +++ b/gomspace/libgscsp/src/bindings/python/pygscsp.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +#include + +#include + +#if PY_MAJOR_VERSION == 3 +#define IS_PY3 +#endif + +/* gs_error_t gs_csp_i2c_init(uint8_t device, uint8_t csp_addr); */ +static PyObject* pygscsp_csp_i2c_init(PyObject *self, PyObject *args) { + uint8_t device; + uint8_t csp_addr; + + if (!PyArg_ParseTuple(args, "BB", &device, &csp_addr)) { + Py_RETURN_NONE; + } + + return Py_BuildValue("i", gs_csp_i2c_init(device, csp_addr)); +} + + +static PyMethodDef methods[] = { + + {"i2c_init", pygscsp_csp_i2c_init, METH_VARARGS, ""}, + + /* sentinel */ + {NULL, NULL, 0, NULL} +}; + +#ifdef IS_PY3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "libgscsp_py3", + NULL, + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#ifdef IS_PY3 +PyMODINIT_FUNC PyInit_libgscsp_py3(void) { +#else +PyMODINIT_FUNC initlibgscsp_py2(void) { +#endif + +#ifdef IS_PY3 + PyObject* m = PyModule_Create(&moduledef); +#else + Py_InitModule("libgscsp_py2", methods); +#endif + +#ifdef IS_PY3 + return m; +#endif + } + diff --git a/gomspace/libgscsp/src/clock.c b/gomspace/libgscsp/src/clock.c new file mode 100644 index 00000000..9e9a7d53 --- /dev/null +++ b/gomspace/libgscsp/src/clock.c @@ -0,0 +1,23 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + 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); +*/ + +#include +#include + +void clock_get_time(csp_timestamp_t * time) +{ + gs_clock_get_time((gs_timestamp_t*)time); +} + +void clock_set_time(csp_timestamp_t * time) +{ + gs_clock_set_time((gs_timestamp_t*)time); +} diff --git a/gomspace/libgscsp/src/commands.c b/gomspace/libgscsp/src/commands.c new file mode 100644 index 00000000..6abd3019 --- /dev/null +++ b/gomspace/libgscsp/src/commands.c @@ -0,0 +1,652 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static gs_error_t parse_node_timeout(gs_command_context_t *ctx, int node_index, uint8_t * node, int timeout_index, uint32_t * timeout) +{ + gs_error_t error = GS_OK; + if (node) { + *node = csp_get_address(); + + if (ctx->argc > node_index) { + error = gs_string_to_uint8(ctx->argv[node_index], node); + } + } + + if (timeout && (error == GS_OK)) { + *timeout = 1000; + + if (ctx->argc > timeout_index) { + error = gs_string_to_uint32(ctx->argv[timeout_index], timeout); + } + } + + return error; +} + +static int cmd_ping(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + uint32_t size = 1; + if ((ctx->argc > 3) && (gs_string_to_uint32(ctx->argv[3], &size) != GS_OK)) { + return GS_ERROR_ARG; + } + + uint32_t options = CSP_O_NONE; + if (ctx->argc > 4) { + const char * features = ctx->argv[4]; + if (strchr(features, 'r')) + options |= CSP_O_RDP; + if (strchr(features, 'x')) + options |= CSP_O_XTEA; + if (strchr(features, 'h')) + options |= CSP_O_HMAC; + if (strchr(features, 'c')) + options |= CSP_O_CRC32; + } + + printf("Ping node %u, timeout %" PRIu32 ", size %" PRIu32 ": options: 0x%" PRIx32 " ... ", node, timeout, size, options); + + const uint64_t start = gs_clock_get_nsec(); + const int time = csp_ping(node, timeout, size, options); + const uint64_t stop = gs_clock_get_nsec(); + const float elapsed = (((float)(stop - start)) / 1E6); + if (time < 0) { + printf("timeout after %.03f ms\r\n", elapsed); + return GS_ERROR_TIMEOUT; + } + + printf("reply in %.03f ms\r\n", elapsed); + + return GS_OK; +} + +static int cmd_ps(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_ps(node, timeout); + + return GS_OK; +} + +static int cmd_memfree(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_memfree(node, timeout); + + return GS_OK; +} + +static int cmd_reboot(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + uint8_t node; + int res = parse_node_timeout(ctx, 1, &node, 0, NULL); + if (res) { + return res; + } + + csp_reboot(node); + + return GS_OK; +} + +static int cmd_shutdown(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + uint8_t node; + int res = parse_node_timeout(ctx, 1, &node, 0, NULL); + if (res) { + return res; + } + + csp_shutdown(node); + + return GS_OK; +} + +static int cmd_buf_free(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_buf_free(node, timeout); + + return GS_OK; +} + +static int cmd_uptime(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_uptime(node, timeout); + + return GS_OK; +} + +#ifdef CSP_DEBUG + +static int cmd_csp_route_print_table(gs_command_context_t *ctx) +{ + csp_route_print_table(); + return GS_OK; +} + +static int cmd_csp_route_print_interfaces(gs_command_context_t *ctx) +{ + csp_route_print_interfaces(); + return GS_OK; +} + +static int cmd_csp_conn_print_table(gs_command_context_t *ctx) +{ + csp_conn_print_table(); + return GS_OK; +} + +#endif + +#if CSP_USE_RDP +static int cmd_csp_rdp_set_opt(gs_command_context_t *ctx) +{ + if (ctx->argc < 7) { + return GS_ERROR_ARG; + } + int res; + uint32_t window_size; + if ((res = gs_string_to_uint32(ctx->argv[1], &window_size))) { + return res; + } + uint32_t conn_timeout; + if ((res = gs_string_to_uint32(ctx->argv[2], &conn_timeout))) { + return res; + } + uint32_t packet_timeout; + if ((res = gs_string_to_uint32(ctx->argv[3], &packet_timeout))) { + return res; + } + uint32_t delayed_acks; + if ((res = gs_string_to_uint32(ctx->argv[4], &delayed_acks))) { + return res; + } + uint32_t ack_timeout; + if ((res = gs_string_to_uint32(ctx->argv[5], &ack_timeout))) { + return res; + } + uint32_t ack_delay_count; + if ((res = gs_string_to_uint32(ctx->argv[6], &ack_delay_count))) { + return res; + } + + printf("Setting arguments to: window size %" PRIu32 ", conn timeout %" PRIu32 ", packet timeout %" PRIu32 ", delayed acks %" PRIu32 ", ack timeout %" PRIu32 ", ack delay count %" PRIu32 "\r\n", + window_size, conn_timeout, packet_timeout, delayed_acks, ack_timeout, ack_delay_count); + + csp_rdp_set_opt(window_size, conn_timeout, packet_timeout, delayed_acks, ack_timeout, ack_delay_count); + + return GS_OK; +} +#endif + +static int cmd_cmp_ident(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int ret = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (ret) { + return ret; + } + + struct csp_cmp_message msg; + + ret = csp_cmp_ident(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret);; + } + + printf("Hostname: %s\r\n", msg.ident.hostname); + printf("Model: %s\r\n", msg.ident.model); + printf("Revision: %s\r\n", msg.ident.revision); + printf("Date: %s\r\n", msg.ident.date); + printf("Time: %s\r\n", msg.ident.time); + + return GS_OK; +} + +static int cmd_cmp_route_set(gs_command_context_t *ctx) +{ + if (ctx->argc != 6) + return GS_ERROR_ARG; + + uint8_t node = atoi(ctx->argv[1]); + uint32_t timeout = atoi(ctx->argv[2]); + printf("Sending route_set to node %"PRIu8" timeout %"PRIu32"\r\n", node, timeout); + + struct csp_cmp_message msg; + msg.route_set.dest_node = atoi(ctx->argv[3]); + msg.route_set.next_hop_mac = atoi(ctx->argv[4]); + strncpy(msg.route_set.interface, ctx->argv[5], 10); + printf("Dest_node: %u, next_hop_mac: %u, interface %s\r\n", msg.route_set.dest_node, msg.route_set.next_hop_mac, msg.route_set.interface); + + int ret = csp_cmp_route_set(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + return GS_OK; +} + +static int cmd_cmp_ifc(gs_command_context_t *ctx) { + + uint8_t node; + uint32_t timeout; + char * interface; + + if (ctx->argc > 4 || ctx->argc < 3) + return GS_ERROR_ARG; + + node = atoi(ctx->argv[1]); + interface = ctx->argv[2]; + + if (ctx->argc < 4) + timeout = 1000; + else + timeout = atoi(ctx->argv[3]); + + struct csp_cmp_message msg; + strncpy(msg.if_stats.interface, interface, CSP_CMP_ROUTE_IFACE_LEN); + + printf("Requesting interface stats for interface %s\r\n", interface); + + int ret = csp_cmp_if_stats(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + msg.if_stats.tx = csp_ntoh32(msg.if_stats.tx); + msg.if_stats.rx = csp_ntoh32(msg.if_stats.rx); + msg.if_stats.tx_error = csp_ntoh32(msg.if_stats.tx_error); + msg.if_stats.rx_error = csp_ntoh32(msg.if_stats.rx_error); + msg.if_stats.drop = csp_ntoh32(msg.if_stats.drop); + msg.if_stats.autherr = csp_ntoh32(msg.if_stats.autherr); + msg.if_stats.frame = csp_ntoh32(msg.if_stats.frame); + msg.if_stats.txbytes = csp_ntoh32(msg.if_stats.txbytes); + msg.if_stats.rxbytes = csp_ntoh32(msg.if_stats.rxbytes); + msg.if_stats.irq = csp_ntoh32(msg.if_stats.irq); + + printf("%-5s tx: %05"PRIu32" rx: %05"PRIu32" txe: %05"PRIu32" rxe: %05"PRIu32"\r\n" + " drop: %05"PRIu32" autherr: %05"PRIu32 " frame: %05"PRIu32"\r\n" + " txb: %"PRIu32" rxb: %"PRIu32"\r\n\r\n", + msg.if_stats.interface, msg.if_stats.tx, msg.if_stats.rx, msg.if_stats.tx_error, msg.if_stats.rx_error, msg.if_stats.drop, + msg.if_stats.autherr, msg.if_stats.frame, msg.if_stats.txbytes, msg.if_stats.rxbytes); + + return GS_OK; +} + +static int cmd_cmp_peek(gs_command_context_t *ctx) +{ + if ((ctx->argc != 4) && (ctx->argc != 5)) + return GS_ERROR_ARG; + + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 4, &timeout); + if (res) { + return res; + } + + uint32_t addr; + if (gs_string_to_uint32(ctx->argv[2], &addr)) { + return GS_ERROR_ARG; + } + + uint32_t len; + if (gs_string_to_uint32(ctx->argv[3], &len)) { + return GS_ERROR_ARG; + } + if (len > CSP_CMP_PEEK_MAX_LEN) { + return GS_ERROR_RANGE; + } + + printf("Dumping mem from node %u addr 0x%"PRIx32" len %"PRIx32" timeout %"PRIu32"\r\n", node, addr, len, timeout); + + struct csp_cmp_message msg; + msg.peek.addr = csp_hton32(addr); + msg.peek.len = len; + + int ret = csp_cmp_peek(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + gs_hexdump_addr(msg.peek.data, len, GS_TYPES_UINT2PTR(addr)); + + return GS_OK; +} + +static int cmd_cmp_poke(gs_command_context_t *ctx) +{ + if ((ctx->argc != 4) && (ctx->argc != 5)) + return GS_ERROR_ARG; + + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 4, &timeout); + if (res) { + return res; + } + + uint32_t addr; + if (gs_string_to_uint32(ctx->argv[2], &addr)) { + return GS_ERROR_ARG; + } + + unsigned char data[CSP_CMP_POKE_MAX_LEN]; + uint32_t len = base16_decode(ctx->argv[3], data); + if (len > CSP_CMP_PEEK_MAX_LEN) { + printf("Max length is: %u\r\n", CSP_CMP_PEEK_MAX_LEN); + return GS_ERROR_RANGE; + } + + printf("Writing to mem at node %u addr 0x%"PRIx32" len %"PRIx32" timeout %"PRIu32"\r\n", node, addr, len, timeout); + gs_hexdump_addr(data, len, GS_TYPES_UINT2PTR(addr)); + + struct csp_cmp_message msg; + msg.poke.addr = csp_hton32(addr); + msg.poke.len = len; + memcpy(msg.poke.data, data, CSP_CMP_POKE_MAX_LEN); + + int ret = csp_cmp_poke(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + return GS_OK; +} + +static int cmd_cmp_clock(gs_command_context_t *ctx, uint32_t node, uint32_t timeout, const gs_timestamp_t * set) +{ + char tbuf[GS_CLOCK_ISO8601_BUFFER_LENGTH]; + struct csp_cmp_message msg; + memset(&msg, 0, sizeof(msg)); + + if (set) { + gs_clock_to_iso8601_string(set, tbuf, sizeof(tbuf)); + printf("Set time: %s (%" PRIu32 ".%09" PRIu32 " sec)\r\n", tbuf, set->tv_sec, set->tv_nsec); + msg.clock.tv_sec = csp_hton32(set->tv_sec); + msg.clock.tv_nsec = csp_hton32(set->tv_nsec); + } + + gs_timestamp_t t1, t2; + gs_clock_get_time(&t1); + int ret = csp_cmp_clock(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + return gs_csp_error(ret); + } + gs_clock_get_time(&t2); + + /* Calculate round-trip time */ + const int64_t rtt = ((uint64_t)t2.tv_sec * 1000000000 + t2.tv_nsec) - ((uint64_t)t1.tv_sec * 1000000000 + t1.tv_nsec); + + gs_timestamp_t timestamp; + timestamp.tv_sec = csp_ntoh32(msg.clock.tv_sec); + timestamp.tv_nsec = csp_ntoh32(msg.clock.tv_nsec); + + gs_clock_to_iso8601_string(×tamp, tbuf, sizeof(tbuf)); + printf("Get time: %s (%" PRIu32 ".%09" PRIu32 " sec)\r\n", tbuf, timestamp.tv_sec, timestamp.tv_nsec); + + /* Calculate time difference to local clock. This takes the round-trip + * into account, but assumes a symmetrical link */ + const int64_t remote = (uint64_t)timestamp.tv_sec * 1000000000 + timestamp.tv_nsec; + const int64_t local = (uint64_t)t1.tv_sec * 1000000000 + t1.tv_nsec + rtt / 2; + + const double diff = (remote - local) / 1000000.0; + printf("Remote is %f ms %s local time\r\n", fabs(diff), diff > 0 ? "ahead of" : "behind"); + + return GS_OK; +} + +static int cmd_cmp_clock_get(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + + uint32_t node; + if (gs_string_to_uint32(ctx->argv[1], &node) != GS_OK) { + return GS_ERROR_ARG; + } + + uint32_t timeout = 1000; + if (ctx->argc > 2) { + if (gs_string_to_uint32(ctx->argv[2], &timeout) != GS_OK) { + return GS_ERROR_ARG; + } + } + + return cmd_cmp_clock(ctx, node, timeout, NULL); +} + +static int cmd_cmp_clock_set(gs_command_context_t *ctx) +{ + if (ctx->argc < 3) { + return GS_ERROR_ARG; + } + + uint32_t node; + if (gs_string_to_uint32(ctx->argv[1], &node) != GS_OK) { + return GS_ERROR_ARG; + } + + gs_timestamp_t ts; + if (gs_clock_from_string(ctx->argv[2], &ts) != GS_OK) { + return GS_ERROR_ARG; + } + + uint32_t timeout = 1000; + if (ctx->argc > 3) { + if (gs_string_to_uint32(ctx->argv[3], &timeout) != GS_OK) { + return GS_ERROR_ARG; + } + } + + return cmd_cmp_clock(ctx, node, timeout, &ts); +} + +static int cmd_cmp_clock_sync(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + + uint32_t node; + if (gs_string_to_uint32(ctx->argv[1], &node) != GS_OK) { + return GS_ERROR_ARG; + } + + uint32_t timeout = 1000; + if (ctx->argc > 2) { + if (gs_string_to_uint32(ctx->argv[2], &timeout) != GS_OK) { + return GS_ERROR_ARG; + } + } + + gs_timestamp_t ts; + gs_clock_get_time(&ts); + + return cmd_cmp_clock(ctx, node, timeout, &ts); +} + +static const gs_command_t GS_COMMAND_SUB cmp_clock_commands[] = { + { + .name = "get", + .help = "Get clock on ", + .usage = " [timeout]", + .handler = cmd_cmp_clock_get, + }, + { + .name = "set", + .help = "Set time of ", + .usage = " [timeout]", + .handler = cmd_cmp_clock_set, + }, + { + .name = "sync", + .help = "Sync/set time of to time of this node", + .usage = " [timeout]", + .handler = cmd_cmp_clock_sync, + } +}; + +static const gs_command_t GS_COMMAND_SUB cmp_commands[] = { + { + .name = "ident", + .help = "Node id", + .usage = "[node] [timeout]", + .handler = cmd_cmp_ident, + },{ + .name = "route_set", + .help = "Update table", + .usage = " ", + .handler = cmd_cmp_route_set, + },{ + .name = "ifc", + .help = "Remote IFC", + .usage = " [timeout]", + .handler = cmd_cmp_ifc, + },{ + .name = "peek", + .help = "Show remote memory", + .usage = " [timeout]", + .handler = cmd_cmp_peek, + },{ + .name = "poke", + .help = "Modify remote memory", + .usage = " [timeout]", + .handler = cmd_cmp_poke, + },{ + .name = "clock", + .help = "Get/set clock", + .chain = GS_COMMAND_INIT_CHAIN(cmp_clock_commands), + } +}; + +static const gs_command_t GS_COMMAND_ROOT csp_commands[] = { + { + .name = "ping", + .help = "csp: Ping", + .usage = "[node] [timeout] [size] [opt: r|x|h|c]", + .handler = cmd_ping, + },{ + .name = "rps", + .help = "csp: Remote ps", + .usage = "[node] [timeout]", + .handler = cmd_ps, + },{ + .name = "memfree", + .help = "csp: Memory free", + .usage = "[node] [timeout]", + .handler = cmd_memfree, + },{ + .name = "buffree", + .help = "csp: Buffer free", + .usage = "[node] [timeout]", + .handler = cmd_buf_free, + },{ + .name = "reboot", + .help = "csp: Reboot", + .usage = "", + .handler = cmd_reboot, + },{ + .name = "shutdown", + .help = "csp: Shutdown", + .usage = "", + .handler = cmd_shutdown, + },{ + .name = "uptime", + .help = "csp: Uptime", + .usage = "[node] [timeout]", + .handler = cmd_uptime, + },{ + .name = "cmp", + .help = "csp: Management", + .chain = GS_COMMAND_INIT_CHAIN(cmp_commands), + }, +#ifdef CSP_DEBUG + { + .name = "route", + .help = "csp: Show routing table", + .handler = cmd_csp_route_print_table, + },{ + .name = "ifc", + .help = "csp: Show interfaces", + .handler = cmd_csp_route_print_interfaces, + },{ + .name = "conn", + .help = "csp: Show connection table", + .handler = cmd_csp_conn_print_table, + }, +#endif +#if CSP_USE_RDP + { + .name = "rdpopt", + .help = "csp: Set RDP options", + .handler = cmd_csp_rdp_set_opt, + .usage = " " + }, +#endif +}; + +gs_error_t gs_csp_register_commands(void) +{ + return GS_COMMAND_REGISTER(csp_commands); +} diff --git a/gomspace/libgscsp/src/conn.c b/gomspace/libgscsp/src/conn.c new file mode 100644 index 00000000..05e6459e --- /dev/null +++ b/gomspace/libgscsp/src/conn.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include <../lib/libcsp/src/csp_conn.h> // internal libcsp header + +size_t gs_csp_conn_get_open(void) +{ + size_t open = 0; + size_t max_connections; + const csp_conn_t * connections = csp_conn_get_array(&max_connections); + if (connections) { + for (unsigned int i = 0; i < max_connections; ++i) { + if (connections[i].state != CONN_CLOSED) { + ++open; + } + } + } + + // csp_conn_print_table(); + + return open; +} diff --git a/gomspace/libgscsp/src/csp.c b/gomspace/libgscsp/src/csp.c new file mode 100644 index 00000000..2367ec6a --- /dev/null +++ b/gomspace/libgscsp/src/csp.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include "local.h" + +void gs_csp_conf_get_defaults_embedded(gs_csp_conf_t * conf) +{ + static const gs_csp_conf_t defaults = { + .use_gs_log = true, + .use_command_line_options = true, + .csp_buffer_size = 256, // typical MTU size is 256 + .csp_buffers = 10, // in case of RDP connections, must be > RDP Windows size + .address = 1, + .hostname = "hostname", + .model = "model", + .revision = "revision", + }; + + *conf = defaults; + +#if GS_CSP_COMMAND_LINE_SUPPORT + conf->address = gs_csp_command_line_get_address(); +#endif +} + +void gs_csp_conf_get_defaults_server(gs_csp_conf_t * conf) +{ + gs_csp_conf_get_defaults_embedded(conf); + conf->csp_buffer_size = 512; + conf->csp_buffers = 400; +} + +gs_error_t gs_csp_init(const gs_csp_conf_t * conf) +{ + GS_CHECK_ARG(conf != NULL); + + if (conf->use_gs_log) { + gs_csp_log_init(); + } + + int res = csp_buffer_init(conf->csp_buffers, conf->csp_buffer_size); + if (res != CSP_ERR_NONE) { + log_error("%s: csp_buffer_init(buffers: %u, size: %u) failed, CSP error: %d, error: %d", + __FUNCTION__, (unsigned int) conf->csp_buffers, (unsigned int) conf->csp_buffer_size, res, gs_csp_error(res)); + return gs_csp_error(res); + } + + csp_set_hostname(conf->hostname); + csp_set_model(conf->model); + csp_set_revision(conf->revision); + + uint8_t csp_address = conf->address; +#if GS_CSP_COMMAND_LINE_SUPPORT + if (gs_csp_command_line_is_address_set()) { + csp_address = gs_csp_command_line_get_address(); + } +#endif + + res = csp_init(csp_address); + if (res != CSP_ERR_NONE) { + log_error("%s: csp_init(address: %u) failed, CSP error: %d, error: %d", + __FUNCTION__, conf->address, res, gs_csp_error(res)); + return gs_csp_error(res); + } + +#if GS_CSP_COMMAND_LINE_SUPPORT + if (conf->use_command_line_options) { + gs_error_t error = gs_csp_command_line_configure_interfaces(); + if (error) { + log_error("%s: gs_csp_command_line_configure_interfaces() failed, error: %d", + __FUNCTION__, error); + return error; + } + } +#endif + + return GS_OK; +} + +bool gs_csp_is_address_valid(uint8_t address) +{ + if (address < 1) { + return false; + } + if (address >= 33) { + return false; + } + return true; +} diff --git a/gomspace/libgscsp/src/drivers/can/can.c b/gomspace/libgscsp/src/drivers/can/can.c new file mode 100644 index 00000000..dc19d1a7 --- /dev/null +++ b/gomspace/libgscsp/src/drivers/can/can.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +#include +#include +#include +#include +#include +#include + +#define NO_OF_CAN_CHANNELS 2 +#define MAX_NAME_LENGTH 10 // It says in csp_types.h, that it should be below 10 bytes + +// change default log group +#define LOG_DEFAULT gs_can_log + +typedef struct { + // self reference device handle + uint8_t can_ch; + // CSP interface name + char interface_name[MAX_NAME_LENGTH]; + // CSP interface + csp_iface_t interface; +} gs_csp_can_interface_t; + +static gs_csp_can_interface_t csp_can_interfaces[NO_OF_CAN_CHANNELS]; + +static void gs_csp_can_rxdata_callback_isr(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) +{ + csp_can_rx(&csp_can_interfaces[hdl].interface, canMsgId, data, data_size, &cswitch->task_woken); +} + +// Required by libcsp +int csp_can_tx_frame(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc) +{ + return gs_can_send_extended(((gs_csp_can_interface_t *)interface->driver)->can_ch, id, data, dlc, 1000); +} + +gs_error_t gs_csp_can_init2(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, bool set_default_route, csp_iface_t ** csp_if) +{ + GS_CHECK_HANDLE(device < NO_OF_CAN_CHANNELS); + gs_csp_can_interface_t * interface = &csp_can_interfaces[device]; + + // Register/subscribe to CAN frames for CSP + const uint32_t can_id = CFP_MAKE_DST(csp_get_address()); + const uint32_t can_mask = CFP_MAKE_DST((1 << CFP_HOST_SIZE) - 1); + + log_debug("%s(%u): id=0x%" PRIx32 ", mask=0x%" PRIx32, __FUNCTION__, device, can_id, can_mask); + + if (gs_string_empty(name)) { + name = GS_CSP_CAN_DEFAULT_IF_NAME; + } + if (strlen(name) >= MAX_NAME_LENGTH) { + return GS_ERROR_ARG; + } + + if (csp_iflist_get_by_name(name)) { + log_error("%s(%u): name: [%s] - already exists", __FUNCTION__, device, name); + return GS_ERROR_EXIST; + } + + // hook CAN into CSP + GS_STRNCPY(interface->interface_name, name); + interface->interface.name = interface->interface_name; + interface->interface.nexthop = csp_can_tx; + interface->interface.mtu = mtu; + interface->interface.driver = interface; + + csp_iflist_add(&interface->interface); + + if (csp_if) { + *csp_if = &interface->interface; + } + + gs_error_t error = gs_can_set_extended_filter_mask(0, can_id, can_mask, gs_csp_can_rxdata_callback_isr, NULL); + if (error) { + log_error("%s: gs_can_set_extended_filter_mask() failed, error: %s", __FUNCTION__, gs_error_string(error)); + return error; + } + + error = gs_can_start(device); + if (error) { + log_error("%s: gs_can_start() failed, error: %s", __FUNCTION__, gs_error_string(error)); + return error; + } + + if (set_default_route) { + // Route all to CAN + csp_rtable_set(0, 0, &interface->interface, CSP_NODE_MAC); + } + + return GS_OK; +} + +gs_error_t gs_csp_can_init(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, csp_iface_t ** csp_if) +{ + return gs_csp_can_init2(device, csp_addr, mtu, name, true, csp_if); +} diff --git a/gomspace/libgscsp/src/drivers/i2c/i2c.c b/gomspace/libgscsp/src/drivers/i2c/i2c.c new file mode 100644 index 00000000..c40cd9c8 --- /dev/null +++ b/gomspace/libgscsp/src/drivers/i2c/i2c.c @@ -0,0 +1,77 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#if !defined(__linux__) +#include +#endif + +#define E_FAIL -19 // The CSP I2C driver evaluates any other value than -1 as fail + +#define I2C_FRAME_OVERHEAD (sizeof(i2c_frame_t) - sizeof(((i2c_frame_t *)0)->data)) + +static void gs_csp_i2c_rxdata_callback_isr(uint8_t handle, const uint8_t * rx, size_t rx_length, gs_context_switch_t * cswitch) +{ + i2c_frame_t * frame = (i2c_frame_t *) (rx - I2C_FRAME_OVERHEAD); + frame->len = rx_length; +#if (__linux__) + csp_i2c_rx(frame, NULL); +#else + csp_i2c_rx(frame, &cswitch->task_woken); +#endif +} + +static void * gs_csp_i2c_get_buffer(uint8_t handle) +{ + void * buff = csp_buffer_get_isr(I2C_MTU); + if (buff != NULL) { + buff = ((uint8_t *)buff) + I2C_FRAME_OVERHEAD; + } + return buff; +} + +/** + CSP send function, required by libcsp + */ +int i2c_send(int handle, i2c_frame_t * frame, uint16_t timeout) +{ + int res_tx = gs_i2c_master_transaction(handle, frame->dest, frame->data, frame->len, 0, 0, timeout); + if (res_tx == GS_OK) { + csp_buffer_free(frame); + return E_NO_ERR; + } else { + return E_FAIL; + } +} + +/** + CSP init function, required by libcsp + */ +int i2c_init(int handle, int mode, uint8_t addr, uint16_t speed, int queue_len_tx, int queue_len_rx, + i2c_callback_t callback) +{ + if (gs_i2c_slave_set_rx(handle, gs_csp_i2c_rxdata_callback_isr) != GS_OK) { + return E_FAIL; + } + if (gs_i2c_slave_set_get_rx_buf(handle, gs_csp_i2c_get_buffer, I2C_MTU) != GS_OK) { + return E_FAIL; + } + if (gs_i2c_slave_start(handle) != GS_OK) { + return E_FAIL; + } + return E_NO_ERR; +} + +gs_error_t gs_csp_i2c_init(uint8_t device, uint8_t csp_addr) +{ + int dummy_speed = 0; // Speed not used + + /* Calls CSP I2C init, which has the I2C interface instance + From here the above "i2c_init" is called */ + return gs_csp_error(csp_i2c_init(csp_addr, device, dummy_speed)); +} diff --git a/gomspace/libgscsp/src/drivers/kiss/kiss.c b/gomspace/libgscsp/src/drivers/kiss/kiss.c new file mode 100644 index 00000000..c3357f7d --- /dev/null +++ b/gomspace/libgscsp/src/drivers/kiss/kiss.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +static csp_iface_t csp_if_kiss; +static uint8_t uart_csp_device; + +static void usart_rx_callback(void * user_data, const uint8_t * data, size_t data_size, gs_context_switch_t * cswitch) +{ + csp_kiss_rx(&csp_if_kiss, (uint8_t *)data, data_size, cswitch); +} + +static void csp_kiss_putc(char c) +{ + gs_uart_write(uart_csp_device, -1, c); +} + +static void csp_kiss_discard(char c, void *pxTaskWoken) +{ + // Do nothing with discarded characters +} + +gs_error_t gs_csp_kiss_init(uint8_t device) +{ + static csp_kiss_handle_t csp_kiss_driver; + static const char * kiss_name = "KISS"; + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_kiss, CSP_NODE_MAC); + + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, csp_kiss_putc, csp_kiss_discard, kiss_name); + + uart_csp_device = device; + + return gs_uart_set_rx_callback(device, usart_rx_callback, NULL); +} diff --git a/gomspace/libgscsp/src/error.c b/gomspace/libgscsp/src/error.c new file mode 100644 index 00000000..45777e31 --- /dev/null +++ b/gomspace/libgscsp/src/error.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +gs_error_t gs_csp_error(int csp_error) +{ + switch (csp_error) { + case CSP_ERR_NONE: /* No error */ + return GS_OK; + + case CSP_ERR_NOMEM: /* Not enough memory */ + return GS_ERROR_ALLOC; + + case CSP_ERR_INVAL: /* Invalid argument */ + return GS_ERROR_ARG; + + case CSP_ERR_TIMEDOUT: /* Operation timed out */ + return GS_ERROR_TIMEOUT; + + case CSP_ERR_USED: /* Resource already in use */ + return GS_ERROR_IN_USE; + + case CSP_ERR_NOTSUP: /* Operation not supported */ + return GS_ERROR_NOT_SUPPORTED; + + case CSP_ERR_BUSY: /* Device or resource busy */ + return GS_ERROR_BUSY; + + case CSP_ERR_ALREADY: /* Connection already in progress */ + return GS_ERROR_ALREADY_IN_PROGRESS; + + case CSP_ERR_RESET: /* Connection reset */ + return GS_ERROR_CONNECTION_RESET; + + case CSP_ERR_NOBUFS: /* No more buffer space available */ + return GS_ERROR_NO_BUFFERS; + + case CSP_ERR_TX: /* Transmission failed */ + case CSP_ERR_DRIVER: /* Error in driver layer */ + return GS_ERROR_IO; + + case CSP_ERR_AGAIN: + return GS_ERROR_AGAIN; + + case CSP_ERR_HMAC: /* HMAC failed */ + case CSP_ERR_XTEA: /* XTEA failed */ + case CSP_ERR_CRC32: /* CRC32 failed */ + return GS_ERROR_DATA; + + default: + break; + } + return csp_error; +} diff --git a/gomspace/libgscsp/src/freertos/cpu.c b/gomspace/libgscsp/src/freertos/cpu.c new file mode 100644 index 00000000..a17fd62b --- /dev/null +++ b/gomspace/libgscsp/src/freertos/cpu.c @@ -0,0 +1,8 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +void cpu_reset(void) +{ + gs_sys_reset(GS_SYS_RESET_CSP); +} diff --git a/gomspace/libgscsp/src/linux/command_line.c b/gomspace/libgscsp/src/linux/command_line.c new file mode 100644 index 00000000..7c2b9bec --- /dev/null +++ b/gomspace/libgscsp/src/linux/command_line.c @@ -0,0 +1,265 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include "../local.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IF_NAME "if" + +#define DEFAULT_CAN_DEVICE "can0" + +#define DEFAULT_KISS_IF_NAME "KISS" +#define DEFAULT_KISS_DEVICE "/dev/ttyUSB0" +#define KISS_SPEED "speed" +#define DEFAULT_KISS_SPEED 500000 + +#define DEFAULT_ZMQ_SERVER "localhost" + +#define DEFAULT_I2C_DEVICE "0" + +#define CSP_ADDRESS_NOT_SET 255 +#define DEFAULT_CSP_ADDRESS 8 + +static uint8_t csp_address = CSP_ADDRESS_NOT_SET; +static const char * csp_can_device = NULL; +static const char * csp_kiss_device = NULL; +static const char * csp_i2c_device = NULL; +static const char * csp_zmq_server = NULL; +static const char * csp_rtable = NULL; + +static int parser(int key, char *arg, struct argp_state *state) +{ +switch (key) { + case 'a': + return gs_string_to_uint8(arg, &csp_address); + + case 'c': + if (csp_can_device) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_can_device = arg; + } else { + csp_can_device = DEFAULT_CAN_DEVICE; + } + break; + + case 'k': + if (csp_kiss_device) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_kiss_device = arg; + } else { + csp_kiss_device = DEFAULT_KISS_DEVICE; + } + break; + + case 'i': + if (csp_i2c_device) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_i2c_device = arg; + } else { + csp_i2c_device = DEFAULT_I2C_DEVICE; + } + break; + + case 'z': + if (csp_zmq_server) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_zmq_server = arg; + } else { + csp_zmq_server = DEFAULT_ZMQ_SERVER; + } + break; + + case 'R': + csp_rtable = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static const struct argp_option options[] = { + { + .name = "csp-address", + .key = 'a', + .arg = "ADDR", + .flags = 0, + .doc = "Set address, default: " GS_DEF2STRING(DEFAULT_CSP_ADDRESS) + }, + { + .name = "csp-rtable", + .key = 'R', + .arg = "RTABLE", + .flags = 0, + .doc = "Set routing table\nRTABLE=
/ [mac]\nExample: \"0/0 ZMQHUB 24, 24/2 ZMQHUB\"" + }, +#if (CSP_USE_CAN) + { + .name = "csp-can", + .key = 'c', + .arg = "DEVICE", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add CAN interface\nDEVICE=" DEFAULT_CAN_DEVICE + }, +#endif +#if (CSP_USE_KISS) + { + .name = "csp-kiss", + .key = 'k', + .arg = "DEVICE", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add KISS over UART interface\nDEVICE=" DEFAULT_KISS_DEVICE "," IF_NAME "=" DEFAULT_KISS_IF_NAME","KISS_SPEED"=" GS_DEF2STRING(DEFAULT_KISS_SPEED) + }, +#endif +#if (CSP_USE_I2C) + { + .name = "csp-i2c", + .key = 'i', + .arg = "DEVICE", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add I2C interface\nDEVICE=0,"GS_I2C_COMMAND_LINE_SPEED"=" GS_DEF2STRING(GS_I2C_DEFAULT_BPS) "," GS_I2C_COMMAND_LINE_ADDRESS "=1," GS_I2C_COMMAND_LINE_DEVICE "=" GS_DEF2STRING(GS_I2C_ALL_DEVICES) + }, +#endif +#if (CSP_USE_ZMQHUB) + { + .name = "csp-zmq", + .key = 'z', + .arg = "SERVER", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add ZMQ interface\nSERVER=" DEFAULT_ZMQ_SERVER + }, +#endif + { + .flags = OPTION_DOC, + .name = "Examples:" +#if (CSP_USE_CAN) + "\n CAN: configure address 10 and CAN interface can0:" + "\n $ -a10 -ccan0" +#endif +#if (CSP_USE_KISS) + "\n KISS: configure address 10 and uart on /dev/ttyUSB0 at baudrate 500000:" + "\n $ -a10 -k/dev/ttyUSB0,speed=500000" +#endif +#if (CSP_USE_I2C) + "\n I2C: configure address 10 and I2C Aardvark dongle with id 2238384015, speed 400K:" + "\n $ -a10 -i2238384015,speed=400000" +#endif +#if (CSP_USE_ZMQHUB) + "\n ZMQ: configure address 10 and ZMQ proxy on localhost:" + "\n $ -a10 -zlocalhost" +#endif + }, + {0} +}; + +static const struct argp argp = {.options = options, .parser = parser}; + +const struct argp_child gs_csp_command_line_options = {.argp = &argp, .header = "CSP"}; + +gs_error_t gs_csp_command_line_configure_interfaces(void) +{ +#if (CSP_USE_KISS) + // KISS - only here, because the embedded init functions are stubbed in libemul + if (csp_kiss_device) { + static char device[50]; + static char ifname[50]; + uint32_t speed; + int res = gs_string_get_suboption_string(csp_kiss_device, NULL, DEFAULT_KISS_DEVICE, device, sizeof(device)); + res |= gs_string_get_suboption_string(csp_kiss_device, IF_NAME, DEFAULT_KISS_IF_NAME, ifname, sizeof(ifname)); + res |= gs_string_get_suboption_uint32(csp_kiss_device, KISS_SPEED, DEFAULT_KISS_SPEED, &speed); + if (res == GS_OK) { + static csp_iface_t csp_if_kiss; + static csp_kiss_handle_t csp_kiss_driver; + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, usart_putc, usart_insert, ifname); + struct usart_conf conf = {.device = device, .baudrate = speed}; + usart_init(&conf); + void my_usart_rx(uint8_t * buf, int len, void * pxTaskWoken) { + csp_kiss_rx(&csp_if_kiss, buf, len, pxTaskWoken); + } + usart_set_callback(my_usart_rx); + } + } +#endif + +#if (CSP_USE_CAN) + // CAN - only here, because the embedded init functions are stubbed in libemul + if (csp_can_device) { + char device[50]; + int res = gs_string_get_suboption_string(csp_can_device, NULL, DEFAULT_CAN_DEVICE, device, sizeof(device)); + if (res == GS_OK) { + csp_can_socketcan_init(device, 0, 0); + } + } +#endif + +#if (CSP_USE_ZMQHUB) + // ZMQ - currently ZMQ is only supported on Linux, and therefor handled here + if (csp_zmq_server) { + char server[50]; + int res = gs_string_get_suboption_string(csp_zmq_server, NULL, DEFAULT_ZMQ_SERVER, server, sizeof(server)); + if (res == GS_OK) { + csp_zmqhub_init(csp_get_address(), server); + } + } +#endif + +#if (CSP_USE_I2C) + // I2C + if (csp_i2c_device) { + uint8_t device = 0; + gs_string_get_suboption_uint8(csp_i2c_device, GS_I2C_COMMAND_LINE_DEVICE, GS_I2C_ALL_DEVICES, &device); + if (device == GS_I2C_ALL_DEVICES) { + device = 0; + } + + char modified_options[300]; + snprintf(modified_options, sizeof(modified_options), "%s,%s=%u", csp_i2c_device, GS_I2C_COMMAND_LINE_ADDRESS, csp_get_address()); + gs_error_t error = gs_function_invoke("i2c", modified_options); + if (error) { + log_error("Failed to initialize I2C adapter, error: %s", gs_error_string(error)); + } else { + error = gs_csp_i2c_init(device, csp_get_address()); + if (error) { + log_error("gs_csp_i2c_init(%u, %u) failed, error: %s", device, csp_get_address(), gs_error_string(error)); + } + } + } +#endif + + return GS_OK; +} + +bool gs_csp_command_line_is_address_set(void) +{ + return (csp_address != CSP_ADDRESS_NOT_SET); +} + +uint8_t gs_csp_command_line_get_address(void) +{ + if (gs_csp_command_line_is_address_set()) { + return csp_address; + } + return DEFAULT_CSP_ADDRESS; +} + +const char * gs_csp_command_line_get_rtable(void) +{ + return csp_rtable; +} diff --git a/gomspace/libgscsp/src/local.h b/gomspace/libgscsp/src/local.h new file mode 100644 index 00000000..5c033c14 --- /dev/null +++ b/gomspace/libgscsp/src/local.h @@ -0,0 +1,21 @@ +#ifndef GS_CSP_SRC_LOCAL_H +#define GS_CSP_SRC_LOCAL_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#if (__linux__) +#define GS_CSP_COMMAND_LINE_SUPPORT 1 +#include +#endif + +GS_LOG_GROUP_EXTERN(gs_csp_log); +#define LOG_DEFAULT gs_csp_log + +// local command line APIs +bool gs_csp_command_line_is_address_set(void); +uint8_t gs_csp_command_line_get_address(void); +const char * gs_csp_command_line_get_rtable(void); +gs_error_t gs_csp_command_line_configure_interfaces(void); + +#endif diff --git a/gomspace/libgscsp/src/log.c b/gomspace/libgscsp/src/log.c new file mode 100644 index 00000000..932e5394 --- /dev/null +++ b/gomspace/libgscsp/src/log.c @@ -0,0 +1,64 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include "local.h" + +GS_LOG_GROUP(gs_csp_log, "csp", GS_LOG_CAT_CSP, LOG_DEFAULT_MASK); + +static void gs_log_csp_debug_hook(csp_debug_level_t level, const char *format, va_list args) +{ + gs_log_level_t mapped_level; + + switch (level) { + /* Regular log levels */ + default: + case CSP_ERROR: + mapped_level = LOG_ERROR; + break; + case CSP_WARN: + mapped_level = LOG_WARNING; + break; + case CSP_INFO: + mapped_level = LOG_INFO; + break; + /* Extended log levels */ + case CSP_BUFFER: + mapped_level = LOG_TRACE; + break; + case CSP_PACKET: + mapped_level = LOG_INFO; + break; + case CSP_PROTOCOL: + mapped_level = LOG_DEBUG; + break; + case CSP_LOCK: + mapped_level = LOG_TRACE; + break; + } + + const int do_log = ((LOG_DEFAULT->mask & (1 << mapped_level)) > 0); + + if (do_log) { + /* forward to log system */ + gs_log_va(mapped_level, LOG_DEFAULT, format, args); + } +} + +gs_error_t gs_csp_log_init(void) +{ + gs_log_group_register(LOG_DEFAULT); + + csp_debug_set_level(CSP_ERROR, true); + csp_debug_set_level(CSP_WARN, true); + csp_debug_set_level(CSP_INFO, true); + csp_debug_set_level(CSP_BUFFER, true); + csp_debug_set_level(CSP_PACKET, true); + csp_debug_set_level(CSP_PROTOCOL, true); + csp_debug_set_level(CSP_LOCK, true); + + csp_debug_hook_set(gs_log_csp_debug_hook); + + return GS_OK; +} diff --git a/gomspace/libgscsp/src/router.c b/gomspace/libgscsp/src/router.c new file mode 100644 index 00000000..95f013eb --- /dev/null +++ b/gomspace/libgscsp/src/router.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include "../lib/libcsp/src/csp_qfifo.h" // internal libcsp header -> FIFO_TIMEOUT, csp_qfifo_wake_up() +#include <../lib/libcsp/src/csp_conn.h> // internal libcsp header +#include "local.h" + +typedef struct { + bool run; + gs_thread_t thread; +} gs_csp_router_t; + +static gs_csp_router_t gs_csp_router; + +static void * gs_csp_router_task(void *param) +{ + /* Here there be routing */ + while (gs_csp_router.run) { + csp_route_work(FIFO_TIMEOUT); + } + log_info("CSP router task terminating"); + gs_thread_exit(0); +} + +gs_error_t gs_csp_router_task_stop(void) +{ + GS_CHECK_HANDLE(gs_csp_router.run && (gs_csp_router.thread != 0)); + + // Close connections in state "RDP closing" - instead of waiting for timeout +#ifdef CSP_USE_RDP + { + size_t max_connections; + const csp_conn_t * conn = csp_conn_get_array(&max_connections); + if (conn && max_connections) { + for (unsigned int i = 0; i < max_connections; ++i, ++conn) { + if ((conn->state == CONN_OPEN) && (conn->rdp.state == RDP_CLOSE_WAIT)) { + log_info("Force close RDP %p in state closing", conn); + csp_close((csp_conn_t *) conn); + } + } + } + } +#endif + + // wait for RDP connections to close + unsigned int open = gs_csp_conn_get_open(); + if (open) { + const unsigned int MAX_TIMEOUT_MS = 30000; + log_info("Waiting up to %u mS for %u connection(s) to timeout/close ...", MAX_TIMEOUT_MS, open); + const uint32_t start_ms = gs_time_rel_ms(); + while (gs_csp_conn_get_open() && (gs_time_diff_ms(start_ms, gs_time_rel_ms()) < MAX_TIMEOUT_MS)) { + gs_time_sleep_ms(200); + } + } + + log_info("Waiting for CSP router task to stop ..."); + gs_csp_router.run = false; + csp_qfifo_wake_up(); + gs_error_t error = gs_thread_join(gs_csp_router.thread, NULL); + memset(&gs_csp_router, 0, sizeof(gs_csp_router)); + log_info("CSP router task stopped"); + return error; +} + +gs_error_t gs_csp_router_task_start(size_t stack_size, gs_thread_priority_t priority) +{ + if (gs_csp_router.run) { + return GS_ERROR_IN_USE; + } + + gs_csp_router.run = true; + gs_error_t error = gs_thread_create("RTE", gs_csp_router_task, NULL, stack_size, priority, + GS_THREAD_CREATE_JOINABLE, &gs_csp_router.thread); + if (error) { + memset(&gs_csp_router, 0, sizeof(gs_csp_router)); + } + return error; +} diff --git a/gomspace/libgscsp/src/rtable.c b/gomspace/libgscsp/src/rtable.c new file mode 100644 index 00000000..f836e3d0 --- /dev/null +++ b/gomspace/libgscsp/src/rtable.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include "local.h" + +// Return interface, if only one configured +static csp_iface_t * get_single_if(unsigned int * return_count) +{ + unsigned int count = 0; + csp_iface_t * found = NULL; + + for (csp_iface_t * ifc = csp_iflist_get(); ifc; ifc = ifc->next) { + if (strcasecmp(ifc->name, "LOOP") == 0) { + // ignore loopback + } else { + ++count; + found = ifc; + } + } + *return_count = count; + return found; +} + +gs_error_t gs_csp_rtable_load(const char * rtable, bool set_default_route, bool use_command_line_option) +{ + //csp_rtable_clear(); + +#if GS_CSP_COMMAND_LINE_SUPPORT + if (use_command_line_option && gs_string_empty(rtable)) { + // try rtable from command line (if set) + rtable = gs_csp_command_line_get_rtable(); + } +#endif + + if (gs_string_empty(rtable) == false) { + + if (csp_rtable_check(rtable) > 0) { + csp_rtable_load(rtable); + log_info("%s: loaded routing table [%s]", __FUNCTION__, rtable); + return GS_OK; + } + + log_warning("%s: ignoring route table: [%s] due to error(s)", __FUNCTION__, rtable); + } + + if (set_default_route) { + unsigned int count = 0; + csp_iface_t * ifc = get_single_if(&count); + if (count == 0) { + log_warning("%s: no interfaces configured", __FUNCTION__); + return GS_ERROR_NOT_FOUND; + } + + if (count > 1) { + log_warning("%s: %u interfaces configured - will not set default routes", __FUNCTION__, count); + return GS_ERROR_AMBIGUOUS; + } + + // set default route + int res = csp_route_set(CSP_DEFAULT_ROUTE, ifc, CSP_NODE_MAC); + if (res != CSP_ERR_NONE) { + log_warning("%s: failed to set default route on interface: [%s], CSP error: %d", __FUNCTION__, ifc->name, res); + return gs_csp_error(res); + } + } + + return GS_OK; +} diff --git a/gomspace/libgscsp/src/service_dispatcher.c b/gomspace/libgscsp/src/service_dispatcher.c new file mode 100644 index 00000000..507549af --- /dev/null +++ b/gomspace/libgscsp/src/service_dispatcher.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include "../lib/libcsp/src/csp_conn.h" // internal libcsp header +#include "local.h" + +static GS_LOG_GROUP(gs_cspdispatcher_log, "cspdispatcher", GS_LOG_CAT_CSP, LOG_DEFAULT_MASK); +#undef LOG_DEFAULT +#define LOG_DEFAULT gs_cspdispatcher_log + +#define WD_TIMEOUT_SECOUNDS 30 +#define DEFAULT_TIMEOUT_MS (((WD_TIMEOUT_SECOUNDS * 1000) / 3) * 2) + +struct gs_csp_service_dispatcher { + // Configuration + const gs_csp_service_dispatcher_conf_t * conf; + // Run or stop/exit. + bool run; + // Server socket + csp_socket_t * socket; + // Software watchdog + gs_swwd_hdl_t * wd; + // Thread handle. + gs_thread_t thread; +}; + +static void * service_dispatcher_task(void * parameter) +{ + gs_csp_service_dispatcher_t handle = parameter; + + unsigned int timeout_ms = (handle->conf->callback) ? 0 : DEFAULT_TIMEOUT_MS; + + log_debug("[%s] entering connection loop, timeout: %u mS", handle->conf->name, timeout_ms); + + while (handle->run) { + if (handle->wd) { + gs_swwd_touch(handle->wd); + } + + /* Wait for incoming connection, or timeout */ + csp_conn_t * conn = csp_accept(handle->socket, timeout_ms); + + if (conn) { + unsigned int in_port = csp_conn_dport(conn); + + log_debug("[%s] new connection %p on port: %u, source: %d:%d, flags: 0x%x", + handle->conf->name, conn, in_port, csp_conn_src(conn), csp_conn_sport(conn), csp_conn_flags(conn)); + + gs_csp_service_handler_t handler; + if (in_port < handle->conf->handler_array_size) { + handler = handle->conf->handler_array[in_port]; + } else { + handler = NULL; + } + + if (handler) { + gs_error_t error = (handler)(conn); + log_debug("[%s] connection on port: %u processed by %p, error: %s", + handle->conf->name, in_port, handler, gs_error_string(error)); + } else { + log_warning("[%s] no handler on port: %u - closing connection: source: %d:%d, flags: 0x%x", + handle->conf->name, in_port, csp_conn_src(conn), csp_conn_sport(conn), csp_conn_flags(conn)); + csp_close(conn); + } + } + + if (handle->conf->callback) { + timeout_ms = handle->conf->callback(); + if (timeout_ms > DEFAULT_TIMEOUT_MS) { + timeout_ms = DEFAULT_TIMEOUT_MS; + } + } + } + + log_debug("[%s] terminating ...", handle->conf->name); + gs_thread_exit(NULL); +} + +static gs_error_t service_dispatcher_create(const gs_csp_service_dispatcher_conf_t * conf, + size_t stack_size, + gs_thread_priority_t priority, + gs_csp_service_dispatcher_t * return_handle) +{ + gs_csp_service_dispatcher_t handle = calloc(1, sizeof(*handle)); + if (handle == 0) { + return GS_ERROR_ALLOC; + } + + *return_handle = handle; + + handle->conf = conf; + + // Create watchdog + if (conf->disable_watchdog == false) { + gs_error_t error = gs_swwd_register(&handle->wd, WD_TIMEOUT_SECOUNDS, NULL, NULL, conf->name); + if (error) { + log_error("[%s] gs_swwd_register(%s, %u) failed, error: %d", + conf->name, conf->name, WD_TIMEOUT_SECOUNDS, error); + handle->wd = NULL; + return error; + } + } + + // Open "server" socket + handle->socket = csp_socket(conf->socket_options); + if (handle->socket == NULL) { + log_error("[%s] csp_socket(0) failed", + conf->name); + return GS_ERROR_ALLOC; + } + + // Bind to port(s) to socket + for (unsigned int i = 0; i < conf->handler_array_size; ++i) { + if (conf->handler_array[i]) { + int res = csp_bind(handle->socket, i); + if (res) { + log_error("[%s] csp_bind(socket: %p, port: %u) failed, result: %d", + conf->name, handle->socket, i, res); + return GS_ERROR_IN_USE; + } + } + } + + // Bind on "any" port? + if (conf->bind_any) { + int res = csp_bind(handle->socket, CSP_ANY); + if (res) { + log_error("[%s] csp_bind(socket: %p, port: %u) failed, result: %d", + conf->name, handle->socket, CSP_ANY, res); + return GS_ERROR_IN_USE; + } + } + + // Create listen backlog + { + size_t backlog = conf->listen_backlog ? conf->listen_backlog : 10; + int res = csp_listen(handle->socket, backlog); + if (res) { + log_error("[%s] csp_listen(%p, %zu) failed, result: %d", + conf->name, handle->socket, backlog, res); + return GS_ERROR_UNKNOWN; + } + } + + // Launch thread + handle->run = true; + gs_error_t error = gs_thread_create(handle->conf->name, service_dispatcher_task, handle, stack_size, priority, + GS_THREAD_CREATE_JOINABLE, &handle->thread); + if (error) { + handle->thread = 0; + } + + return error; +} + +gs_error_t gs_csp_service_dispatcher_create(const gs_csp_service_dispatcher_conf_t * conf, + size_t stack_size, + gs_thread_priority_t priority, + gs_csp_service_dispatcher_t * return_handle) +{ + GS_CHECK_ARG(conf != NULL); + + gs_log_group_register(gs_cspdispatcher_log); + + gs_csp_service_dispatcher_t handle; + gs_error_t error = service_dispatcher_create(conf, stack_size, priority, &handle); + if (error) { + //gs_csp_service_dispatcher_destroy(handle); + handle = NULL; + } + + if (return_handle) { + *return_handle = handle; + } + + return error; +} + +gs_error_t gs_csp_service_dispatcher_wake_up(gs_csp_service_dispatcher_t handle) +{ + GS_CHECK_HANDLE(handle && handle->socket && handle->socket->socket); + csp_packet_t * packet = NULL; + int res = csp_queue_enqueue(handle->socket->socket, &packet, 0); + return (res == CSP_QUEUE_OK) ? GS_OK : GS_ERROR_FULL; +} + +gs_error_t gs_csp_service_dispatcher_destroy(gs_csp_service_dispatcher_t handle) +{ + GS_CHECK_HANDLE(handle && handle->conf); + + log_debug("[%s] stopping dispatcher ...", handle->conf->name); + + handle->run = false; + if (handle->thread) { + gs_csp_service_dispatcher_wake_up(handle); + gs_thread_join(handle->thread, NULL); + } + + csp_close(handle->socket); + + if (handle->wd) { + gs_swwd_deregister(&handle->wd); + } + + memset(handle, 0, sizeof(*handle)); + free(handle); + + return GS_OK; +} diff --git a/gomspace/libgscsp/src/service_handler.c b/gomspace/libgscsp/src/service_handler.c new file mode 100644 index 00000000..b602847d --- /dev/null +++ b/gomspace/libgscsp/src/service_handler.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include + +// callback for processing packets on a connection. +typedef void (*csp_service_packet_handler_t)(csp_conn_t * conn, csp_packet_t * packet); + +// Process all packets on the connectio and close it when done. +static gs_error_t call_csp_packet_handler(csp_conn_t * conn, csp_service_packet_handler_t handler) +{ + csp_packet_t *packet; + while ((packet = csp_read(conn, 0))) { + (handler)(conn, packet); + } + csp_close(conn); + return GS_OK; +} + +gs_error_t gs_csp_cmp_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +gs_error_t gs_csp_ping_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +gs_error_t gs_csp_ps_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +static void memfree(csp_conn_t * conn, csp_packet_t * packet) +{ + uint32_t mem_free = 0; + + gs_mem_ram_type_t ram_type = gs_mem_get_ram_default(); + gs_mem_ram_stat_t ram_stat; + if(gs_mem_get_ram_stat(ram_type, &ram_stat) == GS_OK) { + mem_free = ram_stat.available; + } + + mem_free = util_hton32(mem_free); + memcpy(packet->data, &mem_free, sizeof(mem_free)); + packet->length = sizeof(mem_free); + + if (!csp_send(conn, packet, 0)) { + csp_buffer_free(packet); + } +} + +gs_error_t gs_csp_mem_free_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, memfree); +} + +gs_error_t gs_csp_reboot_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +gs_error_t gs_csp_buf_free_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +static void uptime(csp_conn_t * conn, csp_packet_t * packet) +{ + uint32_t time = gs_time_uptime(); + time = util_hton32(time); + memcpy(packet->data, &time, sizeof(time)); + packet->length = sizeof(time); + + if (!csp_send(conn, packet, 0)) { + csp_buffer_free(packet); + } +} + +gs_error_t gs_csp_uptime_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, uptime); +} diff --git a/gomspace/libgscsp/src/transaction.c b/gomspace/libgscsp/src/transaction.c new file mode 100644 index 00000000..96ba262a --- /dev/null +++ b/gomspace/libgscsp/src/transaction.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include + +gs_error_t gs_csp_transaction_persistent(csp_conn_t * conn, uint32_t timeout, const void * tx_buf, + size_t tx_len, void * rx_buf, size_t rx_max_len, size_t * rx_len) +{ + GS_CHECK_HANDLE(conn != NULL); + + size_t size = (rx_max_len > tx_len) ? rx_max_len : tx_len; + csp_packet_t * packet = csp_buffer_get(size); + if (packet == NULL) { + return GS_ERROR_ALLOC; + } + + /* Copy the request */ + if (tx_len > 0 && tx_buf != NULL) { + memcpy(packet->data, tx_buf, tx_len); + } + + packet->length = tx_len; + + if (!csp_send(conn, packet, timeout)) { + csp_buffer_free(packet); + return GS_ERROR_IO; + } + + /* If no reply is expected, return now */ + if (rx_max_len == 0) { + return GS_OK; + } + + packet = csp_read(conn, timeout); + if (packet == NULL) { + return GS_ERROR_IO; + } + + gs_error_t return_val; + if (rx_max_len >= packet->length) { + size = packet->length; + return_val = GS_OK; + } else { + csp_log_error("Reply length %u, buffer only %u", packet->length, rx_max_len); + size = rx_max_len; + return_val = GS_ERROR_OVERFLOW; + } + memcpy(rx_buf, packet->data, size); + *rx_len = packet->length; + csp_buffer_free(packet); + return return_val; +} + +gs_error_t gs_csp_transaction2(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, const void * tx_buf, + size_t tx_len, void * rx_buf, size_t rx_max_len, size_t * rx_len, uint32_t opts) +{ + csp_conn_t * conn = csp_connect(prio, dest, port, 0, opts); + if (conn == NULL) { + return GS_ERROR_HANDLE; + } + + gs_error_t res = gs_csp_transaction_persistent(conn, timeout, tx_buf, tx_len, rx_buf, rx_max_len, rx_len); + + csp_close(conn); + + return res; +} diff --git a/gomspace/libgscsp/wscript b/gomspace/libgscsp/wscript new file mode 100644 index 00000000..a78d7f56 --- /dev/null +++ b/gomspace/libgscsp/wscript @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. + +import gs_gcc +import gs_doc +from waflib.Build import BuildContext + +APPNAME = 'gscsp' + + +def libcsp_with_os(ctx): + if ctx.gs_is_linux(): + return 'posix' + if ctx.gs_is_freertos(): + return 'freertos' + return None + + +def libcsp_with_driver_usart(ctx): + if ctx.gs_is_linux(): + return 'linux' + return None + + +def options(ctx): + ctx.load('gs_gcc gs_doc') + gs_gcc.gs_recurse(ctx) + + +def configure(ctx): + ctx.load('gs_gcc gs_doc') + + ctx.env.append_unique('FILES_GSCSP', 'src/*.c') + ctx.env.append_unique('USE_GSCSP', ['csp', 'csp_h', 'util']) + + if ctx.options.enable_if_i2c: + ctx.env.append_unique('FILES_GSCSP', 'src/drivers/i2c/*.c') + + if ctx.gs_is_freertos(): + ctx.env.append_unique('FILES_GSCSP', 'src/drivers/can/*.c') + ctx.env.append_unique('FILES_GSCSP', 'src/drivers/kiss/*.c') + ctx.env.append_unique('FILES_GSCSP', 'src/freertos/*.c') + ctx.env.append_unique('USE_GSCSP', ['embed']) + + if ctx.gs_is_linux(): + ctx.env.append_unique('FILES_GSCSP', 'src/linux/*.c') + + # libcsp options + ctx.options.with_os = libcsp_with_os(ctx) + ctx.options.with_driver_usart = libcsp_with_driver_usart(ctx) + bindings = True if (ctx.gs_is_linux() and not ctx.gs_is_build_disabled(['shlib', 'csp_shlib'])) else False + ctx.options.enable_bindings = bindings + ctx.options.enable_python3_bindings = bindings + ctx.options.disable_stlib = True + ctx.options.enable_crc32 = True + ctx.options.with_connection_so = ctx.options.with_connection_so | 0x0040 # always CRC32, disable CSP_O_NOCRC32 + + if ctx.options.enable_if_can and ctx.options.enable_can_socketcan: + ctx.check_cc(lib='socketcan', mandatory=True) + + ctx.gs_add_doxygen(input=['include', 'lib/libcsp/include']) + + gs_gcc.gs_recurse(ctx) + + +def build(ctx): + gs_gcc.gs_recurse(ctx) + + public_include = ctx.gs_include(name=APPNAME, + includes=['include']) + + ctx.gs_objects(source=ctx.path.ant_glob(ctx.env.FILES_GSCSP), + target=APPNAME, + use=ctx.env.USE_GSCSP + [public_include]) + + ctx.gs_shlib(source=ctx.path.ant_glob(ctx.env.FILES_GSCSP), + target=APPNAME, + gs_prefix='', # make library libgscsp + gs_use_shlib=ctx.env.USE_GSCSP + [public_include]) + + ctx.gs_python_bindings(source=ctx.path.ant_glob('src/bindings/python/pygscsp.c'), + target=APPNAME, + gs_prefix='', # make library libgscsp + gs_use_shlib=ctx.env.USE_GSCSP + [APPNAME, public_include], + package='libgscsp') + + +def doc(ctx): + gs_doc.add_task_library_doc(ctx, keyvalues={ + 'gs_prod_name': 'lib'+APPNAME, + 'gs_prod_desc': 'GomSpace CSP extension', + }) + + +class Doc(BuildContext): + cmd = fun = 'doc' + + +def gs_dist(ctx): + gs_gcc.gs_recurse(ctx) + ctx.add_default_files(source_module=True) + ctx.add_files(ctx.path.ant_glob(['lib/libcsp/**/*'])) + ctx.add_license_file("CSP", "lib/libcsp/COPYING") 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/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/conf_util.h b/gomspace/libutil/include/gs/util/conf_util.h new file mode 100644 index 00000000..55831fb5 --- /dev/null +++ b/gomspace/libutil/include/gs/util/conf_util.h @@ -0,0 +1,10 @@ +#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/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..8e931d8a --- /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 + +#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/mission/core/InitMission.cpp b/mission/core/InitMission.cpp index be16750d..62b50418 100644 --- a/mission/core/InitMission.cpp +++ b/mission/core/InitMission.cpp @@ -132,13 +132,21 @@ void InitMission::initTasks(){ #if ADD_TEST_CODE == 1 - FixedTimeslotTaskIF* TestTimeslotTask = TaskFactory::instance()-> - createFixedTimeslotTask("PST_TEST_TASK", 10, - PeriodicTaskIF::MINIMUM_STACK_SIZE, 1.0, nullptr); - result = pst::pollingSequenceTestFunction(TestTimeslotTask); - if(result != HasReturnvaluesIF::RETURN_OK) { - sif::error << "InitMission::createTasks: Test PST initialization " - << "failed!" << std::endl; +// FixedTimeslotTaskIF* TestTimeslotTask = TaskFactory::instance()-> +// createFixedTimeslotTask("PST_TEST_TASK", 10, +// PeriodicTaskIF::MINIMUM_STACK_SIZE, 1.0, nullptr); +// result = pst::pollingSequenceTestFunction(TestTimeslotTask); +// if(result != HasReturnvaluesIF::RETURN_OK) { +// sif::error << "InitMission::createTasks: Test PST initialization " +// << "failed!" << std::endl; +// } + + PeriodicTaskIF* P60DockTestTask = TaskFactory::instance()-> + createPeriodicTask("P60 Dock", 50 , 4096, + 1, nullptr); + result = PusLowPrio->addComponent(objects::P60_DOCK_TEST_TASK); + if(result!=HasReturnvaluesIF::RETURN_OK){ + sif::error << "Object add component failed" << std::endl; } #endif @@ -154,7 +162,9 @@ void InitMission::initTasks(){ PusMedPrio->startTask(); PusLowPrio->startTask(); #if ADD_TEST_CODE == 1 - TestTimeslotTask->startTask(); +// TestTimeslotTask->startTask(); + P60DockTestTask->startTask(); + #endif sif::info << "Tasks started.." << std::endl; } diff --git a/mission/core/ObjectFactory.cpp b/mission/core/ObjectFactory.cpp index 898a2b51..d6bc80c4 100644 --- a/mission/core/ObjectFactory.cpp +++ b/mission/core/ObjectFactory.cpp @@ -37,8 +37,9 @@ #if ADD_TEST_CODE == 1 //#include //#include -#include +//#include //#include +#include #endif void Factory::setStaticFrameworkObjectIds(){ @@ -130,5 +131,6 @@ void ObjectFactory::produce(){ // new TestDevice(objects::TEST_DEVICE_HANDLER, objects::TEST_ECHO_COM_IF, // testCookie, true); new ArduinoComIF(objects::ARDUINO_COM_IF, true, nullptr); + new P60DockTestTask(objects::P60DOCK_TEST_TASK); #endif } diff --git a/mission/devices/P60DockHandler.cpp b/mission/devices/P60DockHandler.cpp new file mode 100644 index 00000000..500355d7 --- /dev/null +++ b/mission/devices/P60DockHandler.cpp @@ -0,0 +1,23 @@ +/* + * P60DockHandler.cpp + * + * Created on: 18.11.2020 + * Author: jakob + */ + +#include +#include +#include "P60DockHandler.h" + +P60DockHandler::P60DockHandler() { + +} + + +P60DockHandler::~P60DockHandler() { +} + + +P60DockHandler::performOperation(uint8_t operationCode) { + +} diff --git a/mission/devices/P60DockHandler.h b/mission/devices/P60DockHandler.h new file mode 100644 index 00000000..cecd5e75 --- /dev/null +++ b/mission/devices/P60DockHandler.h @@ -0,0 +1,18 @@ +/* + * P60DockHandler.h + * + * Created on: 18.11.2020 + * Author: jakob + */ + +#ifndef MISSION_DEVICES_P60DOCKHANDLER_H_ +#define MISSION_DEVICES_P60DOCKHANDLER_H_ + +class P60DockHandler: public DeviceHandlerBase { +public: + P60DockHandler(); + virtual ~P60DockHandler(); + virtual ReturnValue_t performOperation(uint8_t operationCode = 0); +}; + +#endif /* MISSION_DEVICES_P60DOCKHANDLER_H_ */ diff --git a/test/testtasks/P60DockTestTask.cpp b/test/testtasks/P60DockTestTask.cpp new file mode 100644 index 00000000..962521cb --- /dev/null +++ b/test/testtasks/P60DockTestTask.cpp @@ -0,0 +1,88 @@ +/* + * P60DockTestTask.cpp + * + * Created on: 18.11.2020 + * Author: jakob + */ + +#include +#include "P60DockTestTask.h" + +P60DockTestTask::P60DockTestTask(object_id_t objectId_): +SystemObject(objectId_){ + /* Init buffer system with 10 packets of maximum 320 bytes each */ +// csp_buffer_init(10, 320); + + uint8_t device = 0; + uint8_t csp_addr = 4; /* Current address of p60 dock */ + uint8_t mtu = 320; /* Packets larger than the set mtu will be discarded */ + char name[5] = "can0"; + + /* Init the CAN interface */ + gs_error_t result = gs_csp_can_init(device, csp_addr, mtu, name, csp_if); + if(result != GS_OK){ + sif::error << "gs_csp_can_init failed with error code: " << result + << std::endl; + } +} + + +ReturnValue_t P60DockTestTask::performOperation(uint8_t operationCode) { + + char data[5] = "test"; + int timeout_ms = 1000; + /* Send a csp packet to the can interface */ + g_error_t result = csp_can_tx_frame(csp_if, canExtMsgId, (uint8*) data, sizeof(data), + timeout_ms); + if(result != GS_OK){ + sif::error << "csp_can_tx_frame failed with error code " << result + << std::endl; + } +} + + +ReturnValue_t P60DockTestTask::sendPacket(void){ + + /* Get packet buffer for data */ + csp_packet_t *packet = csp_buffer_get(data_size); + if (packet == NULL) { + /* Could not get buffer element */ + sif::error("Failed to get buffer element\\n"); + return HasReturnvaluesIF::RETURN_FAILED; + } + + /* Connect to host HOST, port PORT with regular UDP-like protocol and + * 1000 ms timeout */ + csp_conn_t *conn = csp_connect(CSP_PRIO_NORM, HOST, PORT, 1000, CSP_O_NONE); + + if (conn == NULL) { + /* Connect failed */ + sif::error("Connection failed\\n"); + /* Remember to free packet buffer */ + csp_buffer_free(packet); + return HasReturnvaluesIF::RETURN_FAILED; + } + + /* Copy message to packet */ + char *msg = "HELLO"; + strcpy(packet->data, msg); + /* Set packet length */ + packet->length = strlen(msg); + + /* Send packet */ + if (!csp_send(conn, packet, 1000)) { + /* Send failed */ + sif::error("Send failed\\n"); + csp_buffer_free(packet); + } + /* Close connection */ + csp_close(conn); + + return HasReturnvaluesIF::RETURN_OK; +} + + +P60DockTestTask::~P60DockTestTask() { + // TODO Auto-generated destructor stub +} + diff --git a/test/testtasks/P60DockTestTask.h b/test/testtasks/P60DockTestTask.h new file mode 100644 index 00000000..860a3937 --- /dev/null +++ b/test/testtasks/P60DockTestTask.h @@ -0,0 +1,35 @@ +/* + * P60DockTestTask.h + * + * Created on: 18.11.2020 + * Author: jakob + */ + +#ifndef TEST_TESTTASKS_P60DOCKTESTTASK_H_ +#define TEST_TESTTASKS_P60DOCKTESTTASK_H_ + +extern "C" { +#include +#include +} + + +class P60DockTestTask: public ExecutableObjectIF { +public: + P60DockTestTask(); + virtual ~P60DockTestTask(); + + virtual ReturnValue_t performOperation(uint8_t operationCode = 0); + +private: + /* Interface struct for csp protocol stack */ + csp_iface_t csp_if; + uint32_t canExtMsgId = 4; + /* CAN configuration struct for SocketCAN interface "can0" */ + struct csp_can_config can_conf = {.ifc = "can0"}; + + ReturnValue_t sendPacket(void); + +}; + +#endif /* TEST_TESTTASKS_P60DOCKTESTTASK_H_ */