// SPDX-License-Identifier: LGPL-3.0-only
/* credits to IDA for the flirt tech */
/* original cpp code from Rheax <rheaxmascot@gmail.com> */
/* thanks LemonBoy for the improved research on rheax original work */
/* more information on flirt https://www.hex-rays.com/products/ida/tech/flirt/in_depth.shtml */

/*
   Flirt file format
   =================
   High level layout:
   After the v5 header, there might be two more header fields depending of the version.
   If version == 6 or version == 7, there is one more header field.
   If version == 8 or version == 9, there is two more header field.
   See idasig_v* structs for their description.
   Next there is the non null terminated library name of library_name_len length.
   Next see Parsing below.

   Endianness:
   All multi bytes values are stored in little endian form in the headers.
   For the rest of the file they are stored in big endian form.

   Parsing:
   - described headers
   - library name, not null terminated, length of library_name_len.

   parse_tree (cf. parse_tree):
   - read number of initial root nodes: 1 byte if strictly inferior to 127 otherwise 2 bytes,
   stored in big endian mode, and the most significant bit isn't used. cf. read_multiple_bytes().
   if 0, this is a leaf, goto leaf (cf. parse_leaf). else continue parsing (cf. parse_tree).

   - for number of root node do:
    - read node length, one unsigned byte (the pattern size in this node) (cf. read_node_length)
    - read node variant mask (bit array) (cf. read_node_variant_mask):
      if node length < 0x10 read up to two bytes. cf. read_max_2_bytes
      if node length < 0x20 read up to five bytes. cf. read_multiple_bytes
    - read non-variant bytes (cf. read_node_bytes)
    - goto parse_tree

   leaf (cf. parse_leaf):
   - read crc length, 1 byte
   - read crc value, 2 bytes
   module:
    - read total module length:
      if version >= 9 read up to five bytes, cf. read_multiple_bytes
      else read up to two bytes, cf. read_max_2_bytes
    - read module public functions (cf. read_module_public_functions):
    same crc:
      public function name:
        - read function offset:
          if version >= 9 read up to five bytes, cf. read_multiple_bytes
          else read up to two bytes, cf. read_max_2_bytes
        - if current byte < 0x20, read it : this is a function flag, see IDASIG_FUNCTION* defines
        - read function name until current byte < 0x20
        - read parsing flag, 1 byte
        - if flag & IDASIG__PARSE__MORE_PUBLIC_NAMES: goto public function name
        - if flag & IDASIG__PARSE__READ_TAIL_BYTES, read tail bytes, cf. read_module_tail_bytes:
          - if version >= 8: read number of tail bytes, else suppose one
          - for number of tail bytes do:
            - read tail byte offset:
              if version >= 9 read up to five bytes, cf. read_multiple_bytes
              else read up to two bytes, cf. read_max_2_bytes
            - read tail byte value, one byte

        - if flag & IDASIG__PARSE__READ_REFERENCED_FUNCTIONS, read referenced functions, cf. read_module_referenced_functions:
          - if version >= 8: read number of referenced functions, else suppose one
          - for number of referenced functions do:
            - read referenced function offset:
              if version >= 9 read up to five bytes, cf. read_multiple_bytes
              else read up to two bytes, cf. read_max_2_bytes
            - read referenced function name length, one byte:
              - if name length == 0, read length up to five bytes, cf. read_multiple_bytes
            - for name length, read name chars:
              - if name is null terminated, it means the offset is negative

        - if flag & IDASIG__PARSE__MORE_MODULES_WITH_SAME_CRC, goto same crc, read function with same crc
        - if flag & IDASIG__PARSE__MORE_MODULES, goto module, to read another module


   More Information
   -----------------
   Function flags:
   - local functions ((l) with dumpsig) which are static ones.
   - collision functions ((!) with dumpsig) are the result of an unresolved collision.

   Tail bytes:
   When two modules have the same pattern, and same crc, flirt tries to identify
   a byte which is different in all the same modules.
   Their offset is from the first byte after the crc.
   They appear as "(XXXX: XX)" in dumpsig output

   Referenced functions:
   When two modules have the same pattern, and same crc, and are identical in
   non-variant bytes, they only differ by the functions they call. These functions are
   "referenced functions". They need to be identified first before the module can be
   identified.
   The offset is from the start of the function to the referenced function name.
   They appear as "(REF XXXX: NAME)" in dumpsig output
 */

#include <rz_types.h>
#include <rz_lib.h>
#include <rz_sign.h>
#include <signal.h>

#define DEBUG 0

/*arch flags*/
#define IDASIG__ARCH__386       0 // Intel 80x86
#define IDASIG__ARCH__Z80       1 // 8085, Z80
#define IDASIG__ARCH__I860      2 // Intel 860
#define IDASIG__ARCH__8051      3 // 8051
#define IDASIG__ARCH__TMS       4 // Texas Instruments TMS320C5x
#define IDASIG__ARCH__6502      5 // 6502
#define IDASIG__ARCH__PDP       6 // PDP11
#define IDASIG__ARCH__68K       7 // Motoroal 680x0
#define IDASIG__ARCH__JAVA      8 // Java
#define IDASIG__ARCH__6800      9 // Motorola 68xx
#define IDASIG__ARCH__ST7       10 // SGS-Thomson ST7
#define IDASIG__ARCH__MC6812    11 // Motorola 68HC12
#define IDASIG__ARCH__MIPS      12 // MIPS
#define IDASIG__ARCH__ARM       13 // Advanced RISC Machines
#define IDASIG__ARCH__TMSC6     14 // Texas Instruments TMS320C6x
#define IDASIG__ARCH__PPC       15 // PowerPC
#define IDASIG__ARCH__80196     16 // Intel 80196
#define IDASIG__ARCH__Z8        17 // Z8
#define IDASIG__ARCH__SH        18 // Renesas (formerly Hitachi) SuperH
#define IDASIG__ARCH__NET       19 // Microsoft Visual Studio.Net
#define IDASIG__ARCH__AVR       20 // Atmel 8-bit RISC processor(s)
#define IDASIG__ARCH__H8        21 // Hitachi H8/300, H8/2000
#define IDASIG__ARCH__PIC       22 // Microchip's PIC
#define IDASIG__ARCH__SPARC     23 // SPARC
#define IDASIG__ARCH__ALPHA     24 // DEC Alpha
#define IDASIG__ARCH__HPPA      25 // Hewlett-Packard PA-RISC
#define IDASIG__ARCH__H8500     26 // Hitachi H8/500
#define IDASIG__ARCH__TRICORE   27 // Tasking Tricore
#define IDASIG__ARCH__DSP56K    28 // Motorola DSP5600x
#define IDASIG__ARCH__C166      29 // Siemens C166 family
#define IDASIG__ARCH__ST20      30 // SGS-Thomson ST20
#define IDASIG__ARCH__IA64      31 // Intel Itanium IA64
#define IDASIG__ARCH__I960      32 // Intel 960
#define IDASIG__ARCH__F2MC      33 // Fujistu F2MC-16
#define IDASIG__ARCH__TMS320C54 34 // Texas Instruments TMS320C54xx
#define IDASIG__ARCH__TMS320C55 35 // Texas Instruments TMS320C55xx
#define IDASIG__ARCH__TRIMEDIA  36 // Trimedia
#define IDASIG__ARCH__M32R      37 // Mitsubishi 32bit RISC
#define IDASIG__ARCH__NEC_78K0  38 // NEC 78K0
#define IDASIG__ARCH__NEC_78K0S 39 // NEC 78K0S
#define IDASIG__ARCH__M740      40 // Mitsubishi 8bit
#define IDASIG__ARCH__M7700     41 // Mitsubishi 16bit
#define IDASIG__ARCH__ST9       42 // ST9+
#define IDASIG__ARCH__FR        43 // Fujitsu FR Family
#define IDASIG__ARCH__MC6816    44 // Motorola 68HC16
#define IDASIG__ARCH__M7900     45 // Mitsubishi 7900
#define IDASIG__ARCH__TMS320C3  46 // Texas Instruments TMS320C3
#define IDASIG__ARCH__KR1878    47 // Angstrem KR1878
#define IDASIG__ARCH__AD218X    48 // Analog Devices ADSP 218X
#define IDASIG__ARCH__OAKDSP    49 // Atmel OAK DSP
#define IDASIG__ARCH__TLCS900   50 // Toshiba TLCS-900
#define IDASIG__ARCH__C39       51 // Rockwell C39
#define IDASIG__ARCH__CR16      52 // NSC CR16
#define IDASIG__ARCH__MN102L00  53 // Panasonic MN10200
#define IDASIG__ARCH__TMS320C1X 54 // Texas Instruments TMS320C1x
#define IDASIG__ARCH__NEC_V850X 55 // NEC V850 and V850ES/E1/E2
#define IDASIG__ARCH__SCR_ADPT  56 // Processor module adapter for processor modules written in scripting languages
#define IDASIG__ARCH__EBC       57 // EFI Bytecode
#define IDASIG__ARCH__MSP430    58 // Texas Instruments MSP430
#define IDASIG__ARCH__SPU       59 // Cell Broadband Engine Synergistic Processor Unit
#define IDASIG__ARCH__DALVIK    60 // Android Dalvik Virtual Machine

/*file_types flags*/
#define IDASIG__FILE__DOS_EXE_OLD 0x00000001
#define IDASIG__FILE__DOS_COM_OLD 0x00000002
#define IDASIG__FILE__BIN         0x00000004
#define IDASIG__FILE__DOSDRV      0x00000008
#define IDASIG__FILE__NE          0x00000010
#define IDASIG__FILE__INTELHEX    0x00000020
#define IDASIG__FILE__MOSHEX      0x00000040
#define IDASIG__FILE__LX          0x00000080
#define IDASIG__FILE__LE          0x00000100
#define IDASIG__FILE__NLM         0x00000200
#define IDASIG__FILE__COFF        0x00000400
#define IDASIG__FILE__PE          0x00000800
#define IDASIG__FILE__OMF         0x00001000
#define IDASIG__FILE__SREC        0x00002000
#define IDASIG__FILE__ZIP         0x00004000
#define IDASIG__FILE__OMFLIB      0x00008000
#define IDASIG__FILE__AR          0x00010000
#define IDASIG__FILE__LOADER      0x00020000
#define IDASIG__FILE__ELF         0x00040000
#define IDASIG__FILE__W32RUN      0x00080000
#define IDASIG__FILE__AOUT        0x00100000
#define IDASIG__FILE__PILOT       0x00200000
#define IDASIG__FILE__DOS_EXE     0x00400000
#define IDASIG__FILE__DOS_COM     0x00800000
#define IDASIG__FILE__AIXAR       0x01000000

/*os_types flags*/
#define IDASIG__OS__MSDOS   0x01
#define IDASIG__OS__WIN     0x02
#define IDASIG__OS__OS2     0x04
#define IDASIG__OS__NETWARE 0x08
#define IDASIG__OS__UNIX    0x10
#define IDASIG__OS__OTHER   0x20

/*app types flags*/
#define IDASIG__APP__CONSOLE         0x0001
#define IDASIG__APP__GRAPHICS        0x0002
#define IDASIG__APP__EXE             0x0004
#define IDASIG__APP__DLL             0x0008
#define IDASIG__APP__DRV             0x0010
#define IDASIG__APP__SINGLE_THREADED 0x0020
#define IDASIG__APP__MULTI_THREADED  0x0040
#define IDASIG__APP__16_BIT          0x0080
#define IDASIG__APP__32_BIT          0x0100
#define IDASIG__APP__64_BIT          0x0200

/*feature flags*/
#define IDASIG__FEATURE__STARTUP       0x01
#define IDASIG__FEATURE__CTYPE_CRC     0x02
#define IDASIG__FEATURE__2BYTE_CTYPE   0x04
#define IDASIG__FEATURE__ALT_CTYPE_CRC 0x08
#define IDASIG__FEATURE__COMPRESSED    0x10

/*parsing flags*/
#define IDASIG__PARSE__MORE_PUBLIC_NAMES          0x01
#define IDASIG__PARSE__READ_TAIL_BYTES            0x02
#define IDASIG__PARSE__READ_REFERENCED_FUNCTIONS  0x04
#define IDASIG__PARSE__MORE_MODULES_WITH_SAME_CRC 0x08
#define IDASIG__PARSE__MORE_MODULES               0x10

/*functions flags*/
#define IDASIG__FUNCTION__LOCAL                0x02 // describes a static function
#define IDASIG__FUNCTION__UNRESOLVED_COLLISION 0x08 // describes a collision that wasn't resolved

typedef struct idasig_v5_t {
	/* newer header only add fields, that's why we'll always read a v5 header first */
	ut8 magic[6]; /* should be set to IDASGN */
	ut8 version; /*from 5 to 9*/
	ut8 arch;
	ut32 file_types;
	ut16 os_types;
	ut16 app_types;
	ut16 features;
	ut16 old_n_functions;
	ut16 crc16;
	ut8 ctype[12]; // XXX: how to use it
	ut8 library_name_len;
	ut16 ctypes_crc16;
} idasig_v5_t;

typedef struct idasig_v6_v7_t {
	ut32 n_functions;
} idasig_v6_v7_t;

typedef struct idasig_v8_v9_t {
	ut16 pattern_size;
} idasig_v8_v9_t;

typedef struct idasig_v10_t {
	ut16 unknown;
} idasig_v10_t;

#if DEBUG
static int header_size = 0;
#endif

/* newer header only add fields, that's why we'll always read a v5 header first */
/*
   arch             : target architecture
   file_types       : files where we expect to find the functions (exe, coff, ...)
   os_types         : os where we expect to find the functions
   app_types        : applications in which we expect to find the functions
   features         : signature file features
   old_n_functions  : number of functions
   crc16            : certainly crc16 of the tree
   ctype[12]        : unknown field
   library_name_len : length of the library name, which is right after the header
   ctypes_crc16     : unknown field
   n_functions      : number of functions
   pattern_size     : number of the leading pattern bytes
 */

// XXX need more infos on compression of version 5 sigs
// rz_inflate doesn't work with them

#define RZ_FLIRT_NAME_MAX 1024

typedef struct RzFlirtTailByte {
	ut16 offset; // from pattern_size + crc_length
	ut8 value;
} RzFlirtTailByte;

typedef struct RzFlirtFunction {
	char name[RZ_FLIRT_NAME_MAX];
	ut16 offset; // function offset from the module start
	ut8 negative_offset; // true if offset is negative, for referenced functions
	ut8 is_local; // true if function is static
	ut8 is_collision; // true if was an unresolved collision
} RzFlirtFunction;

typedef struct RzFlirtModule {
	ut32 crc_length;
	ut32 crc16; // crc16 of the module after the pattern bytes
	// until but not including the first variant byte
	// this is a custom crc16
	ut16 length; // total length of the module, should < 0x8000
	RzList *public_functions;
	RzList *tail_bytes;
	RzList *referenced_functions;
} RzFlirtModule;

typedef struct RzFlirtNode {
	RzList *child_list;
	RzList *module_list;
	ut32 length; // length of the pattern
	ut64 variant_mask; // this is the mask that will define variant bytes in ut8 *pattern_bytes
	ut8 *pattern_bytes; // holds the pattern bytes of the signature
	ut8 *variant_bool_array; // bool array, if true, byte in pattern_bytes is a variant byte
} RzFlirtNode;

static ut8 version; // version of the sig file being parsed
// used in some cases to parse the right way

// This is from flair tools flair/crc16.cpp
#define POLY 0x8408
ut16 crc16(const unsigned char *data_p, size_t length) {
	ut8 i;
	ut32 data;
	ut32 crc = 0xFFFF;

	if (length == 0) {
		return 0;
	}
	do {
		data = *data_p++;
		for (i = 0; i < 8; i++) {
			if ((crc ^ data) & 1) {
				crc = (crc >> 1) ^ POLY;
			} else {
				crc >>= 1;
			}
			data >>= 1;
		}
	} while (--length > 0);

	crc = ~crc;
	data = crc;
	crc = (crc << 8) | ((data >> 8) & 0xff);
	return (ut16)(crc);
}

// this is ugly, but we can't afford to change the return size of read_byte
static bool buf_eof;
static bool buf_err;

static ut8 read_byte(RzBuffer *b) {
	ut8 r = 0;
	int length;

	if (buf_eof || buf_err) {
		return 0;
	}
	if ((length = rz_buf_read(b, &r, 1)) != 1) {
		if (length == -1) {
			buf_err = true;
		}
		if (length == 0) {
			buf_eof = true;
		}
		return 0;
	}
	return r;
}

static ut16 read_short(RzBuffer *b) {
	ut16 r = (read_byte(b) << 8);
	r += read_byte(b);
	return r;
}

static ut32 read_word(RzBuffer *b) {
	ut32 r = (read_short(b) << 16);
	r += read_short(b);
	return r;
}

static ut16 read_max_2_bytes(RzBuffer *b) {
	ut16 r = read_byte(b);
	return (r & 0x80)
		? ((r & 0x7f) << 8) + read_byte(b)
		: r;
}

static ut32 read_multiple_bytes(RzBuffer *b) {
	ut32 r = read_byte(b);
	if ((r & 0x80) != 0x80) {
		return r;
	}
	if ((r & 0xc0) != 0xc0) {
		return ((r & 0x7f) << 8) + read_byte(b);
	}
	if ((r & 0xe0) != 0xe0) {
		r = ((r & 0x3f) << 24) + (read_byte(b) << 16);
		r += read_short(b);
		return r;
	}
	return read_word(b);
}

static void module_free(RzFlirtModule *module) {
	if (!module) {
		return;
	}
	if (module->public_functions) {
		module->public_functions->free = (RzListFree)free;
		rz_list_free(module->public_functions);
	}
	if (module->tail_bytes) {
		module->tail_bytes->free = (RzListFree)free;
		rz_list_free(module->tail_bytes);
	}
	if (module->referenced_functions) {
		module->referenced_functions->free = (RzListFree)free;
		rz_list_free(module->referenced_functions);
	}
	free(module);
}

static void node_free(RzFlirtNode *node) {
	if (!node) {
		return;
	}
	free(node->variant_bool_array);
	free(node->pattern_bytes);
	if (node->module_list) {
		node->module_list->free = (RzListFree)module_free;
		rz_list_free(node->module_list);
	}
	if (node->child_list) {
		node->child_list->free = (RzListFree)node_free;
		rz_list_free(node->child_list);
	}
	free(node);
}

static void print_module(const RzAnalysis *analysis, const RzFlirtModule *module) {
	RzListIter *pub_func_it, *ref_func_it, *tail_byte_it;
	RzFlirtFunction *func, *ref_func;
	RzFlirtTailByte *tail_byte;

	analysis->cb_printf("%02X %04X %04X ", module->crc_length, module->crc16, module->length);
	rz_list_foreach (module->public_functions, pub_func_it, func) {
		if (func->is_local || func->is_collision) {
			analysis->cb_printf("(");
			if (func->is_local) {
				analysis->cb_printf("l");
			}
			if (func->is_collision) {
				analysis->cb_printf("!");
			}
			analysis->cb_printf(")");
		}
		analysis->cb_printf("%04X:%s", func->offset, func->name);
		if (pub_func_it->n) {
			analysis->cb_printf(" ");
		}
	}
	if (module->tail_bytes) {
		rz_list_foreach (module->tail_bytes, tail_byte_it, tail_byte) {
			analysis->cb_printf(" (%04X: %02X)", tail_byte->offset, tail_byte->value);
		}
	}
	if (module->referenced_functions) {
		analysis->cb_printf(" (REF ");
		rz_list_foreach (module->referenced_functions, ref_func_it, ref_func) {
			analysis->cb_printf("%04X: %s", ref_func->offset, ref_func->name);
			if (ref_func_it->n) {
				analysis->cb_printf(" ");
			}
		}
		analysis->cb_printf(")");
	}
	analysis->cb_printf("\n");
}

static void print_node_pattern(const RzAnalysis *analysis, const RzFlirtNode *node) {
	int i;
	for (i = 0; i < node->length; i++) {
		if (node->variant_bool_array[i]) {
			analysis->cb_printf("..");
		} else {
			analysis->cb_printf("%02X", node->pattern_bytes[i]);
		}
	}
	analysis->cb_printf(":\n");
}

static void print_indentation(const RzAnalysis *analysis, int indent) {
	analysis->cb_printf("%s", rz_str_pad(' ', indent));
}

static void print_node(const RzAnalysis *analysis, const RzFlirtNode *node, int indent) {
	/*Prints a signature node. The output is similar to dumpsig*/
	int i;
	RzListIter *child_it, *module_it;
	RzFlirtNode *child;
	RzFlirtModule *module;

	if (node->pattern_bytes) { // avoid printing the root node
		print_indentation(analysis, indent);
		print_node_pattern(analysis, node);
	}
	if (node->child_list) {
		rz_list_foreach (node->child_list, child_it, child) {
			print_node(analysis, child, indent + 1);
		}
	} else if (node->module_list) {
		i = 0;
		rz_list_foreach (node->module_list, module_it, module) {
			print_indentation(analysis, indent + 1);
			analysis->cb_printf("%d. ", i);
			print_module(analysis, module);
			i++;
		}
	}
}

static int module_match_buffer(RzAnalysis *analysis, const RzFlirtModule *module,
	ut8 *b, ut64 address, ut32 buf_size) {
	/* Returns true if module matches b, according to the signatures infos.
	* Return false otherwise.
	* The buffer starts from the first byte after the pattern */
	RzFlirtFunction *flirt_func;
	RzAnalysisFunction *next_module_function;
	RzListIter *tail_byte_it, *flirt_func_it;
	RzFlirtTailByte *tail_byte;

	if (32 + module->crc_length < buf_size &&
		module->crc16 != crc16(b + 32, module->crc_length)) {
		return false;
	}
	if (module->tail_bytes) {
		rz_list_foreach (module->tail_bytes, tail_byte_it, tail_byte) {
			if (32 + module->crc_length + tail_byte->offset < buf_size &&
				b[32 + module->crc_length + tail_byte->offset] != tail_byte->value) {
				return false;
			}
		}
	}

	// TODO referenced functions

	rz_list_foreach (module->public_functions, flirt_func_it, flirt_func) {
		// Once the first module function is found, we need to go through the module->public_functions
		// list to identify the others. See flirt doc for more information

		next_module_function = rz_analysis_get_function_at((RzAnalysis *)analysis, address + flirt_func->offset);
		if (next_module_function) {
			char *name;
			int name_offs = 0;
			ut32 next_module_function_size;

			// get function size from flirt signature
			ut64 flirt_fcn_size = module->length - flirt_func->offset;
			RzFlirtFunction *next_flirt_func;
			RzListIter *next_flirt_func_it = flirt_func_it->n;
			while (next_flirt_func_it) {
				next_flirt_func = next_flirt_func_it->data;
				if (!next_flirt_func->is_local && !next_flirt_func->negative_offset) {
					flirt_fcn_size = next_flirt_func->offset - flirt_func->offset;
					break;
				}
				next_flirt_func_it = next_flirt_func_it->n;
			}
			// resize function if needed
			next_module_function_size = rz_analysis_function_linear_size(next_module_function);
			if (next_module_function_size < flirt_fcn_size) {
				RzListIter *iter;
				RzListIter *iter_tmp;
				RzAnalysisFunction *fcn;
				rz_list_foreach_safe (analysis->fcns, iter, iter_tmp, fcn) {
					if (fcn != next_module_function &&
						fcn->addr >= next_module_function->addr + next_module_function_size &&
						fcn->addr < next_module_function->addr + flirt_fcn_size) {
						RzListIter *iter_bb;
						RzAnalysisBlock *block;
						rz_list_foreach (fcn->bbs, iter_bb, block) {
							rz_analysis_function_add_block(next_module_function, block);
						}
						next_module_function->ninstr += fcn->ninstr;
						rz_analysis_function_delete(fcn);
					}
				}
				rz_analysis_function_resize(next_module_function, flirt_fcn_size);
				next_module_function_size = rz_analysis_function_linear_size(next_module_function);
				rz_analysis_trim_jmprefs((RzAnalysis *)analysis, next_module_function);
			}

			while (flirt_func->name[name_offs] == '?') { // skip '?' chars
				name_offs++;
			}
			if (!flirt_func->name[name_offs]) {
				continue;
			}
			name = rz_name_filter2(flirt_func->name + name_offs);
			free(next_module_function->name);
			next_module_function->name = rz_str_newf("flirt.%s", name);
			analysis->flb.set(analysis->flb.f, next_module_function->name,
				next_module_function->addr, next_module_function_size);
			analysis->cb_printf("Found %s\n", next_module_function->name);
			free(name);
		}
	}
	return true;
}

/* Returns true if b matches the pattern in node. */
/* Returns false otherwise. */
static int node_pattern_match(const RzFlirtNode *node, ut8 *b, int buf_size) {
	int i;
	if (buf_size < node->length) {
		return false;
	}
	for (i = 0; i < node->length; i++) {
		if (!node->variant_bool_array[i]) {
			if (i < node->length && node->pattern_bytes[i] != b[i]) {
				return false;
			}
		}
	}
	return true;
}

static int node_match_buffer(RzAnalysis *analysis, const RzFlirtNode *node, ut8 *b, ut64 address, ut32 buf_size, ut32 buf_idx) {
	RzListIter *node_child_it, *module_it;
	RzFlirtNode *child;
	RzFlirtModule *module;

	if (node_pattern_match(node, b + buf_idx, buf_size - buf_idx)) {
		if (node->child_list) {
			rz_list_foreach (node->child_list, node_child_it, child) {
				if (node_match_buffer(analysis, child, b, address, buf_size, buf_idx + node->length)) {
					return true;
				}
			}
		} else if (node->module_list) {
			rz_list_foreach (node->module_list, module_it, module) {
				if (module_match_buffer(analysis, module, b, address, buf_size)) {
					return true;
				}
			}
		}
	}

	return false;
}

static int node_match_functions(RzAnalysis *analysis, const RzFlirtNode *root_node) {
	/* Tries to find matching functions between the signature infos in root_node
	* and the analyzed functions in anal
	* Returns false on error. */

	if (rz_list_length(analysis->fcns) == 0) {
		analysis->cb_printf("There are no analyzed functions. Have you run 'aa'?\n");
		return true;
	}

	analysis->flb.push_fs(analysis->flb.f, "flirt");
	RzListIter *it_func;
	RzAnalysisFunction *func;
	rz_list_foreach (analysis->fcns, it_func, func) {
		if (func->type != RZ_ANALYSIS_FCN_TYPE_FCN && func->type != RZ_ANALYSIS_FCN_TYPE_LOC) { // scan only for unknown functions
			continue;
		}

		ut64 func_size = rz_analysis_function_linear_size(func);
		ut8 *func_buf = malloc(func_size);
		if (!func_buf) {
			continue;
		}
		if (!analysis->iob.read_at(analysis->iob.io, func->addr, func_buf, (int)func_size)) {
			eprintf("Couldn't read function %s at 0x%" PFMT64x "\n", func->name, func->addr);
			free(func_buf);
			continue;
		}
		RzListIter *node_child_it;
		RzFlirtNode *child;
		rz_list_foreach (root_node->child_list, node_child_it, child) {
			if (node_match_buffer(analysis, child, func_buf, func->addr, func_size, 0)) {
				break;
			}
		}
		free(func_buf);
	}
	analysis->flb.pop_fs(analysis->flb.f);

	return true;
}

static ut8 read_module_tail_bytes(RzFlirtModule *module, RzBuffer *b) {
	/*parses a module tail bytes*/
	/*returns false on parsing error*/
	int i;
	ut8 number_of_tail_bytes;
	RzFlirtTailByte *tail_byte = NULL;
	if (!(module->tail_bytes = rz_list_newf((RzListFree)free))) {
		goto err_exit;
	}

	if (version >= 8) { // this counter was introduced in version 8
		number_of_tail_bytes = read_byte(b); // XXX are we sure it's not read_multiple_bytes?
		if (buf_eof || buf_err) {
			goto err_exit;
		}
	} else { // suppose there's only one
		number_of_tail_bytes = 1;
	}
	for (i = 0; i < number_of_tail_bytes; i++) {
		tail_byte = RZ_NEW0(RzFlirtTailByte);
		if (!tail_byte) {
			return false;
		}
		if (version >= 9) {
			/*/!\ XXX don't trust ./zipsig output because it will write a version 9 header, but keep the old version offsets*/
			tail_byte->offset = read_multiple_bytes(b);
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		} else {
			tail_byte->offset = read_max_2_bytes(b);
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		}
		tail_byte->value = read_byte(b);
		if (buf_eof || buf_err) {
			goto err_exit;
		}
		rz_list_append(module->tail_bytes, tail_byte);
#if DEBUG
		eprintf("READ TAIL BYTE: %04X: %02X\n", tail_byte->offset, tail_byte->value);
#endif
	}

	return true;

err_exit:
	free(tail_byte);
	rz_list_free(module->tail_bytes);
	return false;
}

static ut8 read_module_referenced_functions(RzFlirtModule *module, RzBuffer *b) {
	/*parses a module referenced functions*/
	/*returns false on parsing error*/
	int i, j;
	ut8 number_of_referenced_functions;
	ut32 ref_function_name_length;
	RzFlirtFunction *ref_function = NULL;

	module->referenced_functions = rz_list_new();

	if (version >= 8) { // this counter was introduced in version 8
		number_of_referenced_functions = read_byte(b); // XXX are we sure it's not read_multiple_bytes?
		if (buf_eof || buf_err) {
			goto err_exit;
		}
	} else { // suppose there's only one
		number_of_referenced_functions = 1;
	}

	for (i = 0; i < number_of_referenced_functions; i++) {
		ref_function = RZ_NEW0(RzFlirtFunction);
		if (!ref_function) {
			goto err_exit;
		}
		if (version >= 9) {
			ref_function->offset = read_multiple_bytes(b);
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		} else {
			ref_function->offset = read_max_2_bytes(b);
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		}
		ref_function_name_length = read_byte(b);
		if (buf_eof || buf_err) {
			goto err_exit;
		}
		if (!ref_function_name_length) {
			// not sure why it's not read_multiple_bytes() in the first place
			ref_function_name_length = read_multiple_bytes(b); // XXX might be read_max_2_bytes, need more data
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		}
		if ((int)ref_function_name_length < 0 || ref_function_name_length >= RZ_FLIRT_NAME_MAX) {
			goto err_exit;
		}
		for (j = 0; j < ref_function_name_length; j++) {
			ref_function->name[j] = read_byte(b);
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		}
		if (!ref_function->name[ref_function_name_length]) {
			// if the last byte of the name is 0, it means the offset is negative
			ref_function->negative_offset = true;
		} else {
			ref_function->name[ref_function_name_length] = '\0';
		}
		rz_list_append(module->referenced_functions, ref_function);
#if DEBUG
		eprintf("(REF: %04X: %s)\n", ref_function->offset, ref_function->name);
#endif
	}

	return true;

err_exit:
	free(ref_function);
	return false;
}

static ut8 read_module_public_functions(RzFlirtModule *module, RzBuffer *b, ut8 *flags) {
	/* Reads and set the public functions names and offsets associated within a module */
	/*returns false on parsing error*/
	int i;
	ut16 offset = 0;
	ut8 current_byte;
	RzFlirtFunction *function = NULL;

	module->public_functions = rz_list_new();

	do {
		function = RZ_NEW0(RzFlirtFunction);
		if (version >= 9) { // seems like version 9 introduced some larger offsets
			offset += read_multiple_bytes(b); // offsets are dependent of the previous ones
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		} else {
			offset += read_max_2_bytes(b); // offsets are dependent of the previous ones
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		}
		function->offset = offset;

		current_byte = read_byte(b);
		if (buf_eof || buf_err) {
			goto err_exit;
		}
		if (current_byte < 0x20) {
			if (current_byte & IDASIG__FUNCTION__LOCAL) { // static function
				function->is_local = true;
			}
			if (current_byte & IDASIG__FUNCTION__UNRESOLVED_COLLISION) {
				// unresolved collision (happens in *.exc while creating .sig from .pat)
				function->is_collision = true;
			}
			if (current_byte & 0x01 || current_byte & 0x04) { // appears as 'd' or '?' in dumpsig
#if DEBUG
				// XXX investigate
				eprintf("INVESTIGATE PUBLIC NAME FLAG: %02X @ %04X\n", current_byte,
					rz_buf_tell(b) + header_size);
#endif
			}
			current_byte = read_byte(b);
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		}

		for (i = 0; current_byte >= 0x20 && i < RZ_FLIRT_NAME_MAX; i++) {
			function->name[i] = current_byte;
			current_byte = read_byte(b);
			if (buf_eof || buf_err) {
				goto err_exit;
			}
		}

		if (i == RZ_FLIRT_NAME_MAX) {
			eprintf("Function name too long\n");
			function->name[RZ_FLIRT_NAME_MAX - 1] = '\0';
		} else {
			function->name[i] = '\0';
		}

#if DEBUG
		eprintf("%04X:%s ", function->offset, function->name);
#endif
		*flags = current_byte;
		rz_list_append(module->public_functions, function);
	} while (*flags & IDASIG__PARSE__MORE_PUBLIC_NAMES);
#if DEBUG
	eprintf("\n");
#endif

	return true;

err_exit:
	free(function);
	return false;
}

static ut8 parse_leaf(const RzAnalysis *analysis, RzBuffer *b, RzFlirtNode *node) {
	/*parses a signature leaf: modules with same leading pattern*/
	/*returns false on parsing error*/
	ut8 flags, crc_length;
	ut16 crc16;
	RzFlirtModule *module = NULL;

	node->module_list = rz_list_new();
	do { // loop for all modules having the same prefix

		crc_length = read_byte(b);
		if (buf_eof || buf_err) {
			goto err_exit;
		}
		crc16 = read_short(b);
		if (buf_eof || buf_err) {
			goto err_exit;
		}
#if DEBUG
		if (crc_length == 0x00 && crc16 != 0x0000) {
			eprintf("WARNING non zero crc of zero length @ %04X\n",
				rz_buf_tell(b) + header_size);
		}
		eprintf("crc_len: %02X crc16: %04X\n", crc_length, crc16);
#endif

		do { // loop for all modules having the same crc
			module = RZ_NEW0(RzFlirtModule);
			if (!module) {
				goto err_exit;
			}

			module->crc_length = crc_length;
			module->crc16 = crc16;

			if (version >= 9) { // seems like version 9 introduced some larger length
				/*/!\ XXX don't trust ./zipsig output because it will write a version 9 header, but keep the old version offsets*/
				module->length = read_multiple_bytes(b); // should be < 0x8000
				if (buf_eof || buf_err) {
					goto err_exit;
				}
			} else {
				module->length = read_max_2_bytes(b); // should be < 0x8000
				if (buf_eof || buf_err) {
					goto err_exit;
				}
			}
#if DEBUG
			eprintf("module_length: %04X\n", module->length);
#endif

			if (!read_module_public_functions(module, b, &flags)) {
				goto err_exit;
			}

			if (flags & IDASIG__PARSE__READ_TAIL_BYTES) { // we need to read some tail bytes because in this leaf we have functions with same crc
				if (!read_module_tail_bytes(module, b)) {
					goto err_exit;
				}
			}
			if (flags & IDASIG__PARSE__READ_REFERENCED_FUNCTIONS) { // we need to read some referenced functions
				if (!read_module_referenced_functions(module, b)) {
					goto err_exit;
				}
			}

			rz_list_append(node->module_list, module);
		} while (flags & IDASIG__PARSE__MORE_MODULES_WITH_SAME_CRC);
	} while (flags & IDASIG__PARSE__MORE_MODULES); // same prefix but different crc

	return true;

err_exit:
	module_free(module);
	return false;
}

static ut8 read_node_length(RzFlirtNode *node, RzBuffer *b) {
	node->length = read_byte(b);
	if (buf_eof || buf_err) {
		return false;
	}
#if DEBUG
	eprintf("node length: %02X\n", node->length);
#endif
	return true;
}

static ut8 read_node_variant_mask(RzFlirtNode *node, RzBuffer *b) {
	/*Reads and sets a node's variant bytes mask. This mask is then used to*/
	/*read the non-variant bytes following.*/
	/*returns false on parsing error*/
	if (node->length < 0x10) {
		node->variant_mask = read_max_2_bytes(b);
		if (buf_eof || buf_err) {
			return false;
		}
	} else if (node->length <= 0x20) {
		node->variant_mask = read_multiple_bytes(b);
		if (buf_eof || buf_err) {
			return false;
		}
	} else if (node->length <= 0x40) { // it shouldn't be more than 64 bytes
		node->variant_mask = ((ut64)read_multiple_bytes(b) << 32) + read_multiple_bytes(b);
		if (buf_eof || buf_err) {
			return false;
		}
	}

	return true;
}

static bool read_node_bytes(RzFlirtNode *node, RzBuffer *b) {
	/*Reads the node bytes, and also sets the variant bytes in variant_bool_array*/
	/*returns false on parsing error*/
	int i;
	ut64 current_mask_bit = 0;
	if ((int)node->length < 0) {
		return false;
	}
	current_mask_bit = 1ULL << (node->length - 1);
	if (!(node->pattern_bytes = malloc(node->length))) {
		return false;
	}
	if (!(node->variant_bool_array = malloc(node->length))) {
		return false;
	}
	for (i = 0; i < node->length; i++, current_mask_bit >>= 1) {
		node->variant_bool_array[i] = (bool)(node->variant_mask & current_mask_bit);
		if (node->variant_mask & current_mask_bit) {
			node->pattern_bytes[i] = 0x00;
		} else {
			node->pattern_bytes[i] = read_byte(b);
			if (buf_eof || buf_err) {
				return false;
			}
		}
	}
	return true;
}

static ut8 parse_tree(const RzAnalysis *analysis, RzBuffer *b, RzFlirtNode *root_node) {
	/*parse a signature pattern tree or sub-tree*/
	/*returns false on parsing error*/
	RzFlirtNode *node = NULL;
	int i, tree_nodes = read_multiple_bytes(b); // confirmed it's not read_byte(), XXX could it be read_max_2_bytes() ???
	if (buf_eof || buf_err) {
		return false;
	}
	if (tree_nodes == 0) { // if there's no tree nodes remaining, that means we are on the leaf
		return parse_leaf(analysis, b, root_node);
	}
	root_node->child_list = rz_list_new();

	for (i = 0; i < tree_nodes; i++) {
		if (!(node = RZ_NEW0(RzFlirtNode))) {
			goto err_exit;
		}
		if (!read_node_length(node, b)) {
			goto err_exit;
		}
		if (!read_node_variant_mask(node, b)) {
			goto err_exit;
		}
		if (!read_node_bytes(node, b)) {
			goto err_exit;
		}
		rz_list_append(root_node->child_list, node);
		if (!parse_tree(analysis, b, node)) {
			goto err_exit; // parse child nodes
		}
	}
	return true;
err_exit:
	node_free(node);
	return false;
}

#if DEBUG
#define PRINT_ARCH(define, str) \
	if (arch == define) { \
		eprintf(" %s", str); \
		return; \
	}
static void print_arch(ut8 arch) {
	PRINT_ARCH(IDASIG__ARCH__386, "386");
	PRINT_ARCH(IDASIG__ARCH__Z80, "Z80");
	PRINT_ARCH(IDASIG__ARCH__I860, "I860");
	PRINT_ARCH(IDASIG__ARCH__8051, "8051");
	PRINT_ARCH(IDASIG__ARCH__TMS, "TMS");
	PRINT_ARCH(IDASIG__ARCH__6502, "6502");
	PRINT_ARCH(IDASIG__ARCH__PDP, "PDP");
	PRINT_ARCH(IDASIG__ARCH__68K, "68K");
	PRINT_ARCH(IDASIG__ARCH__JAVA, "JAVA");
	PRINT_ARCH(IDASIG__ARCH__6800, "6800");
	PRINT_ARCH(IDASIG__ARCH__ST7, "ST7");
	PRINT_ARCH(IDASIG__ARCH__MC6812, "MC6812");
	PRINT_ARCH(IDASIG__ARCH__MIPS, "MIPS");
	PRINT_ARCH(IDASIG__ARCH__ARM, "ARM");
	PRINT_ARCH(IDASIG__ARCH__TMSC6, "TMSC6");
	PRINT_ARCH(IDASIG__ARCH__PPC, "PPC");
	PRINT_ARCH(IDASIG__ARCH__80196, "80196");
	PRINT_ARCH(IDASIG__ARCH__Z8, "Z8");
	PRINT_ARCH(IDASIG__ARCH__SH, "SH");
	PRINT_ARCH(IDASIG__ARCH__NET, "NET");
	PRINT_ARCH(IDASIG__ARCH__AVR, "AVR");
	PRINT_ARCH(IDASIG__ARCH__H8, "H8");
	PRINT_ARCH(IDASIG__ARCH__PIC, "PIC");
	PRINT_ARCH(IDASIG__ARCH__SPARC, "SPARC");
	PRINT_ARCH(IDASIG__ARCH__ALPHA, "ALPHA");
	PRINT_ARCH(IDASIG__ARCH__HPPA, "HPPA");
	PRINT_ARCH(IDASIG__ARCH__H8500, "H8500");
	PRINT_ARCH(IDASIG__ARCH__TRICORE, "TRICORE");
	PRINT_ARCH(IDASIG__ARCH__DSP56K, "DSP56K");
	PRINT_ARCH(IDASIG__ARCH__C166, "C166");
	PRINT_ARCH(IDASIG__ARCH__ST20, "ST20");
	PRINT_ARCH(IDASIG__ARCH__IA64, "IA64");
	PRINT_ARCH(IDASIG__ARCH__I960, "I960");
	PRINT_ARCH(IDASIG__ARCH__F2MC, "F2MC");
	PRINT_ARCH(IDASIG__ARCH__TMS320C54, "TMS320C54");
	PRINT_ARCH(IDASIG__ARCH__TMS320C55, "TMS320C55");
	PRINT_ARCH(IDASIG__ARCH__TRIMEDIA, "TRIMEDIA");
	PRINT_ARCH(IDASIG__ARCH__M32R, "M32R");
	PRINT_ARCH(IDASIG__ARCH__NEC_78K0, "NEC_78K0");
	PRINT_ARCH(IDASIG__ARCH__NEC_78K0S, "NEC_78K0S");
	PRINT_ARCH(IDASIG__ARCH__M740, "M740");
	PRINT_ARCH(IDASIG__ARCH__M7700, "M7700");
	PRINT_ARCH(IDASIG__ARCH__ST9, "ST9");
	PRINT_ARCH(IDASIG__ARCH__FR, "FR");
	PRINT_ARCH(IDASIG__ARCH__MC6816, "MC6816");
	PRINT_ARCH(IDASIG__ARCH__M7900, "M7900");
	PRINT_ARCH(IDASIG__ARCH__TMS320C3, "TMS320C3");
	PRINT_ARCH(IDASIG__ARCH__KR1878, "KR1878");
	PRINT_ARCH(IDASIG__ARCH__AD218X, "AD218X");
	PRINT_ARCH(IDASIG__ARCH__OAKDSP, "OAKDSP");
	PRINT_ARCH(IDASIG__ARCH__TLCS900, "TLCS900");
	PRINT_ARCH(IDASIG__ARCH__C39, "C39");
	PRINT_ARCH(IDASIG__ARCH__CR16, "CR16");
	PRINT_ARCH(IDASIG__ARCH__MN102L00, "MN102L00");
	PRINT_ARCH(IDASIG__ARCH__TMS320C1X, "TMS320C1X");
	PRINT_ARCH(IDASIG__ARCH__NEC_V850X, "NEC_V850X");
	PRINT_ARCH(IDASIG__ARCH__SCR_ADPT, "SCR_ADPT");
	PRINT_ARCH(IDASIG__ARCH__EBC, "EBC");
	PRINT_ARCH(IDASIG__ARCH__MSP430, "MSP430");
	PRINT_ARCH(IDASIG__ARCH__SPU, "SPU");
	PRINT_ARCH(IDASIG__ARCH__DALVIK, "DALVIK");
}

#define PRINT_FLAG(define, str) \
	if (flags & define) { \
		eprintf(" %s", str); \
	}
static void print_file_types(ut32 flags) {
	PRINT_FLAG(IDASIG__FILE__DOS_EXE_OLD, "DOS_EXE_OLD");
	PRINT_FLAG(IDASIG__FILE__DOS_COM_OLD, "DOS_COM_OLD");
	PRINT_FLAG(IDASIG__FILE__BIN, "BIN");
	PRINT_FLAG(IDASIG__FILE__DOSDRV, "DOSDRV");
	PRINT_FLAG(IDASIG__FILE__NE, "NE");
	PRINT_FLAG(IDASIG__FILE__INTELHEX, "INTELHEX");
	PRINT_FLAG(IDASIG__FILE__MOSHEX, "MOSHEX");
	PRINT_FLAG(IDASIG__FILE__LX, "LX");
	PRINT_FLAG(IDASIG__FILE__LE, "LE");
	PRINT_FLAG(IDASIG__FILE__NLM, "NLM");
	PRINT_FLAG(IDASIG__FILE__COFF, "COFF");
	PRINT_FLAG(IDASIG__FILE__PE, "PE");
	PRINT_FLAG(IDASIG__FILE__OMF, "OMF");
	PRINT_FLAG(IDASIG__FILE__SREC, "SREC");
	PRINT_FLAG(IDASIG__FILE__ZIP, "ZIP");
	PRINT_FLAG(IDASIG__FILE__OMFLIB, "OMFLIB");
	PRINT_FLAG(IDASIG__FILE__AR, "AR");
	PRINT_FLAG(IDASIG__FILE__LOADER, "LOADER");
	PRINT_FLAG(IDASIG__FILE__ELF, "ELF");
	PRINT_FLAG(IDASIG__FILE__W32RUN, "W32RUN");
	PRINT_FLAG(IDASIG__FILE__AOUT, "AOUT");
	PRINT_FLAG(IDASIG__FILE__PILOT, "PILOT");
	PRINT_FLAG(IDASIG__FILE__DOS_EXE, "EXE");
	PRINT_FLAG(IDASIG__FILE__AIXAR, "AIXAR");
}

static void print_os_types(ut16 flags) {
	PRINT_FLAG(IDASIG__OS__MSDOS, "MSDOS");
	PRINT_FLAG(IDASIG__OS__WIN, "WIN");
	PRINT_FLAG(IDASIG__OS__OS2, "OS2");
	PRINT_FLAG(IDASIG__OS__NETWARE, "NETWARE");
	PRINT_FLAG(IDASIG__OS__UNIX, "UNIX");
}

static void print_app_types(ut16 flags) {
	PRINT_FLAG(IDASIG__APP__CONSOLE, "CONSOLE");
	PRINT_FLAG(IDASIG__APP__GRAPHICS, "GRAPHICS");
	PRINT_FLAG(IDASIG__APP__EXE, "EXE");
	PRINT_FLAG(IDASIG__APP__DLL, "DLL");
	PRINT_FLAG(IDASIG__APP__DRV, "DRV");
	PRINT_FLAG(IDASIG__APP__SINGLE_THREADED, "SINGLE_THREADED");
	PRINT_FLAG(IDASIG__APP__MULTI_THREADED, "MULTI_THREADED");
	PRINT_FLAG(IDASIG__APP__16_BIT, "16_BIT");
	PRINT_FLAG(IDASIG__APP__32_BIT, "32_BIT");
	PRINT_FLAG(IDASIG__APP__64_BIT, "64_BIT");
}

static void print_features(ut16 flags) {
	PRINT_FLAG(IDASIG__FEATURE__STARTUP, "STARTUP");
	PRINT_FLAG(IDASIG__FEATURE__CTYPE_CRC, "CTYPE_CRC");
	PRINT_FLAG(IDASIG__FEATURE__2BYTE_CTYPE, "2BYTE_CTYPE");
	PRINT_FLAG(IDASIG__FEATURE__ALT_CTYPE_CRC, "ALT_CTYPE_CRC");
	PRINT_FLAG(IDASIG__FEATURE__COMPRESSED, "COMPRESSED");
}

static void print_header(idasig_v5_t *header) {
	/*eprintf("magic: %s\n", header->magic);*/
	eprintf("version: %d\n", header->version);
	eprintf("arch:");
	print_arch(header->arch);
	eprintf("\n");
	eprintf("file_types:");
	print_file_types(header->file_types);
	eprintf("\n");
	eprintf("os_types:");
	print_os_types(header->os_types);
	eprintf("\n");
	eprintf("app_types:");
	print_app_types(header->app_types);
	eprintf("\n");
	eprintf("features:");
	print_features(header->features);
	eprintf("\n");
	eprintf("old_n_functions: %04x\n", header->old_n_functions);
	eprintf("crc16: %04x\n", header->crc16);
	eprintf("ctype: %s\n", header->ctype);
	eprintf("library_name_len: %d\n", header->library_name_len);
	eprintf("ctypes_crc16: %04x\n", header->ctypes_crc16);
}
#endif

static int parse_header(RzBuffer *buf, idasig_v5_t *header) {
	rz_buf_seek(buf, 0, RZ_BUF_SET);
	if (rz_buf_read(buf, header->magic, sizeof(header->magic)) != sizeof(header->magic)) {
		return false;
	}
	if (rz_buf_read(buf, &header->version, sizeof(header->version)) != sizeof(header->version)) {
		return false;
	}
	if (rz_buf_read(buf, &header->arch, sizeof(header->arch)) != sizeof(header->arch)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->file_types, sizeof(header->file_types)) != sizeof(header->file_types)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->os_types, sizeof(header->os_types)) != sizeof(header->os_types)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->app_types, sizeof(header->app_types)) != sizeof(header->app_types)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->features, sizeof(header->features)) != sizeof(header->features)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->old_n_functions, sizeof(header->old_n_functions)) != sizeof(header->old_n_functions)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->crc16, sizeof(header->crc16)) != sizeof(header->crc16)) {
		return false;
	}
	if (rz_buf_read(buf, header->ctype, sizeof(header->ctype)) != sizeof(header->ctype)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->library_name_len, sizeof(header->library_name_len)) != sizeof(header->library_name_len)) {
		return false;
	}
	if (rz_buf_read(buf, (unsigned char *)&header->ctypes_crc16, sizeof(header->ctypes_crc16)) != sizeof(header->ctypes_crc16)) {
		return false;
	}

	return true;
}

static int parse_v6_v7_header(RzBuffer *buf, idasig_v6_v7_t *header) {
	if (rz_buf_read(buf, (unsigned char *)&header->n_functions, sizeof(header->n_functions)) != sizeof(header->n_functions)) {
		return false;
	}

	return true;
}

static int parse_v8_v9_header(RzBuffer *buf, idasig_v8_v9_t *header) {
	if (rz_buf_read(buf, (unsigned char *)&header->pattern_size, sizeof(header->pattern_size)) != sizeof(header->pattern_size)) {
		return false;
	}

	return true;
}

static int parse_v10_header(RzBuffer *buf, idasig_v10_t *header) {
	if (rz_buf_read(buf, (unsigned char *)&header->unknown, sizeof(header->unknown)) != sizeof(header->unknown)) {
		return false;
	}

	return true;
}

static RzFlirtNode *flirt_parse(const RzAnalysis *analysis, RzBuffer *flirt_buf) {
	ut8 *name = NULL;
	ut8 *buf = NULL, *decompressed_buf = NULL;
	RzBuffer *rz_buf = NULL;
	int size, decompressed_size;
	RzFlirtNode *node = NULL;
	RzFlirtNode *ret = NULL;
	idasig_v5_t *header = NULL;
	idasig_v6_v7_t *v6_v7 = NULL;
	idasig_v8_v9_t *v8_v9 = NULL;
	idasig_v10_t *v10 = NULL;

	buf_eof = false;
	buf_err = false;

	if (!(version = rz_sign_is_flirt(flirt_buf))) {
		goto exit;
	}

	if (version < 5 || version > 10) {
		eprintf("Unsupported flirt signature version\n");
		goto exit;
	}

	if (!(header = RZ_NEW0(idasig_v5_t))) {
		goto exit;
	}

	parse_header(flirt_buf, header);

	if (version >= 6) {
		if (!(v6_v7 = RZ_NEW0(idasig_v6_v7_t))) {
			goto exit;
		}
		if (!parse_v6_v7_header(flirt_buf, v6_v7)) {
			goto exit;
		}

		if (version >= 8) {
			if (!(v8_v9 = RZ_NEW0(idasig_v8_v9_t))) {
				goto exit;
			}
			if (!parse_v8_v9_header(flirt_buf, v8_v9)) {
				goto exit;
			}

			if (version >= 10) {
				if (!(v10 = RZ_NEW0(idasig_v10_t))) {
					goto exit;
				}
				if (!parse_v10_header(flirt_buf, v10)) {
					goto exit;
				}
			}
		}
	}

	name = malloc(header->library_name_len + 1);
	if (!name) {
		goto exit;
	}

	if (rz_buf_read(flirt_buf, name, header->library_name_len) != header->library_name_len) {
		goto exit;
	}

	name[header->library_name_len] = '\0';

	// analysis->cb_printf  ("Loading: %s\n", name);
#if DEBUG
	print_header(header);
	header_size = rz_buf_tell(flirt_buf);
#endif

	size = rz_buf_size(flirt_buf) - rz_buf_tell(flirt_buf);
	buf = malloc(size);
	if (rz_buf_read(flirt_buf, buf, size) != size) {
		goto exit;
	}

	if (header->features & IDASIG__FEATURE__COMPRESSED) {
		if (version == 5) {
			eprintf("Sorry we do not support the signatures version 5 compression.\n");
			goto exit;
		}
		if (!(decompressed_buf = rz_inflate(buf, size, NULL, &decompressed_size))) {
			eprintf("Decompressing failed.\n");
			goto exit;
		}

		RZ_FREE(buf);
		buf = decompressed_buf;
		size = decompressed_size;
	}

	if (!(node = RZ_NEW0(RzFlirtNode))) {
		goto exit;
	}
	rz_buf = rz_buf_new_with_pointers(buf, size, false);
#if DEBUG
	rz_file_dump("sig_dump", rz_buf->buf, rz_buf_size(rz_buf));
#endif
	if (parse_tree(analysis, rz_buf, node)) {
		ret = node;
	} else {
		free(node);
	}
exit:
	free(buf);
	rz_buf_free(rz_buf);
	free(header);
	free(v6_v7);
	free(v8_v9);
	free(v10);
	free(name);
	return ret;
}

RZ_API int rz_sign_is_flirt(RzBuffer *buf) {
	/*if buf is a flirt signature, returns signature version, otherwise returns false*/
	int ret = false;

	idasig_v5_t *header = RZ_NEW0(idasig_v5_t);
	if (rz_buf_read(buf, header->magic, sizeof(header->magic)) != sizeof(header->magic)) {
		goto exit;
	}

	if (strncmp((const char *)header->magic, "IDASGN", 6)) {
		goto exit;
	}

	if (rz_buf_read(buf, &header->version, sizeof(header->version)) != sizeof(header->version)) {
		goto exit;
	}

	ret = header->version;

exit:
	free(header);

	return ret;
}

RZ_API void rz_sign_flirt_dump(const RzAnalysis *analysis, const char *flirt_file) {
	/*dump a flirt signature content on screen.*/
	RzBuffer *flirt_buf;
	RzFlirtNode *node;

	if (!(flirt_buf = rz_buf_new_slurp(flirt_file))) {
		eprintf("Can't open %s\n", flirt_file);
		return;
	}

	node = flirt_parse(analysis, flirt_buf);
	rz_buf_free(flirt_buf);
	if (node) {
		print_node(analysis, node, -1);
		node_free(node);
		return;
	} else {
		eprintf("We encountered an error while parsing the file. Sorry.\n");
		return;
	}
}

RZ_API void rz_sign_flirt_scan(RzAnalysis *analysis, const char *flirt_file) {
	/*parses a flirt signature file and scan the currently opened file against it.*/
	RzBuffer *flirt_buf;
	RzFlirtNode *node;

	if (!(flirt_buf = rz_buf_new_slurp(flirt_file))) {
		eprintf("Can't open %s\n", flirt_file);
		return;
	}

	node = flirt_parse(analysis, flirt_buf);
	rz_buf_free(flirt_buf);
	if (node) {
		if (!node_match_functions(analysis, node)) {
			eprintf("Error while scanning the file %s\n", flirt_file);
		}
		node_free(node);
		return;
	} else {
		eprintf("We encountered an error while parsing the file %s. Sorry.\n", flirt_file);
		return;
	}
}
