diff options
Diffstat (limited to '')
| -rw-r--r-- | project/dclib/dclib-xdump.c | 2214 |
1 files changed, 2214 insertions, 0 deletions
diff --git a/project/dclib/dclib-xdump.c b/project/dclib/dclib-xdump.c new file mode 100644 index 0000000..c404966 --- /dev/null +++ b/project/dclib/dclib-xdump.c @@ -0,0 +1,2214 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens <wiimm@wiimm.de> * + * * + *************************************************************************** + * * + * 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. * + * * + * 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. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include <stdio.h> +#include <stddef.h> +#include "dclib-xdump.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const ccp XDumpCommandName[XDUMPC__N] = +{ + "DUMP", + "DIFF", + "SCAN", +}; + +//----------------------------------------------------------------------------- + +const ccp XDumpFormatName[XDUMPF__N] = +{ + "AUTO", + "INT_1", + "INT_2", + "INT_3", + "INT_4", + "INT_5", + "INT_6", + "INT_7", + "INT_8", + "FLOAT", + "DOUBLE", +}; + +const char XDumpFormatOption[XDUMPF__N+1] = "A12345678FD"; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// output helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool PrintFormat +( + struct XDump_t *xd, // internal copy of params + int force // >0: ignore 'format_printed' + // >1: print always +) +{ + DASSERT(xd); + if ( !xd->f || !xd->format_name ) + return false; + + if ( force > 1 + || ( force > 0 && xd->print_format ) + || ( xd->print_format && !xd->format_printed ) + ) + { + xd->format_printed = true; + fprintf(xd->f,"%s%*s%sFORMAT: %s", + xd->prefix, xd->indent,"", + xd->mode_c ? "//" : "#", + xd->format_name ); + + if ( xd->format > XDUMPF_INT_1 ) + fprintf(xd->f," %s%s", + xd->endian == DC_BIG_ENDIAN ? "BE" : "LE", + xd->eol ); + else + fputs(xd->eol,xd->f); + + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDumpStandardOutput +( + struct XDump_t *xd, // internal copy of params + ccp prefix, // NULL or additional prefix (used by diff) + ccp dump, // dump to print + ccp text // NULL or text to print +) +{ + DASSERT(xd); + DASSERT(dump); + + if (xd->f) + { + fprintf(xd->f,"%s%*s%s", + xd->prefix, xd->indent,"", prefix ? prefix : "" ); + + if (xd->print_addr) + { + if (xd->mode_c) + fprintf(xd->f,"/*%*llx*/ ",xd->addr_fw,xd->addr); + + else + fprintf(xd->f,"%*llx:%s", + xd->addr_fw, xd->addr, + xd->extra_space >= 0 ? " " : "" ); + } + + if (xd->print_number) + { + if (xd->min_number_fw) + fprintf(xd->f,"%-*s",xd->min_number_fw,dump); + else + fputs(dump,xd->f); + } + + if ( text && xd->print_text ) + { + if (xd->mode_c) + fprintf(xd->f," // %s",text); + else + fprintf(xd->f,"%s :%s:", + xd->extra_space >= 0 ? " " : "", text ); + } + + fputs(xd->eol,xd->f); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_text ( char *buf, const u8 *data, int n ) +{ + while ( n-- > 0 ) + { + const u8 ch = *data++; + *buf++ = ch >= ' ' && ch < 0x7f ? ch : '.'; + } + *buf = 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XParam_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[XParam_t]] + +typedef struct XParam_t +{ + const u8 *d; // pointer to input data + const u8 *end; // end of input data + + uint size; // size of data to dump + bool term; // true: data complete + bool recalc_align; // true: recalculate alignment + bool next_line; // true: continue at next line + const u64 *addr_break; // NULL or pointer to next break + + uint skip; // number of columns to skip + uint line_size; // current line size (source bytes) + uint max_col; // max number of columns to print + int extra_space; // extra space counter + + // buffers + char text_buf[XDUMP_GOOD_TEXT_BUF_SIZE]; + char num_buf[XDUMP_GOOD_NUM_BUF_SIZE]; +} +XParam_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupXParam + ( XParam_t *p, XDump_t *xd, cvp data, uint size, bool term ) +{ + DASSERT(p); + DASSERT(xd); + DASSERT(data); + + if ( size > INT_MAX ) + { + size = INT_MAX; // limit because of return value + term = false; + } + + memset( p, 0, offsetof(XParam_t,num_buf) + 15 & ~7 ); + p->size = size; + p->term = term; + p->d = data; + p->end = p->d + size; + p->recalc_align = xd->mode_align; + p->addr_break = xd->addr_break; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint SetupXParamLine ( XParam_t *p, XDump_t *xd ) +{ + DASSERT(p); + DASSERT(xd); + + if ( p->recalc_align ) + { + p->recalc_align = false; + p->skip = ( xd->addr % xd->bytes_per_line ) / xd->bytes_per_col; + const int delta = p->skip * xd->bytes_per_col; + xd->addr -= delta; + if (xd->print_number) + p->size += delta; + } + + p->next_line = false; + p->line_size = xd->bytes_per_line < p->size + ? xd->bytes_per_line + : p->term ? p->size : 0; + + if ( p->addr_break ) + { + while ( *p->addr_break && xd->addr >= *p->addr_break ) + p->addr_break++; + + if (!*p->addr_break) + p->addr_break = 0; + else + { + const s64 max = *p->addr_break - xd->addr; + if ( p->line_size > max ) + p->line_size = max; + } + } + + p->max_col = p->line_size / xd->bytes_per_col; + p->line_size = p->max_col * xd->bytes_per_col; + p->extra_space = xd->col_space ? xd->col_space+1 : 0; + + if (xd->print_text) + { + if (p->skip) + { + const uint skip_size = p->skip * xd->bytes_per_col; + if ( skip_size < p->line_size ) + { + memset(p->text_buf,' ',skip_size); + print_text(p->text_buf+skip_size,p->d,p->line_size-skip_size); + } + else + *p->text_buf = 0; // should never happen! + } + else + print_text(p->text_buf,p->d,p->line_size); + } + + return p->max_col; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool PrintNullSummary ( XDump_t *xd ) +{ + if ( !xd->null_lines || !xd->out_func ) + return false; + + char buf[100]; + const uint skipped = xd->null_lines * xd->bytes_per_line; + snprintf(buf,sizeof(buf),"%s#NULL: 0x%x bytes", + xd->extra_space >= 0 ? " " : "", skipped ); + + xd->addr -= skipped; + xd->out_func(xd,0,buf,0); + xd->addr += skipped; + xd->null_lines = 0; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool CheckXParamNull ( XParam_t *p, XDump_t *xd ) +{ + DASSERT(p); + DASSERT(xd); + + static u8 nullbuf[XDUMP_MAX_BYTES_PER_LINE] = {}; + + + const bool skip = p->line_size == xd->bytes_per_line + && !memcmp(p->d,nullbuf,xd->bytes_per_line); + if (skip) + { + xd->null_lines++; + xd->addr += p->line_size; + p->d += p->line_size; + p->size -= p->line_size; + + return true; + } + + if ( xd->null_lines && ( !skip || p->size <= xd->bytes_per_line )) + PrintNullSummary(xd); + + return skip; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AbortXParamLine ( XParam_t *p, XDump_t *xd, uint col ) +{ + const uint col1 = col + 1; + if ( col1 < p->max_col ) + { + p->max_col = col1; + p->line_size = col1 * xd->bytes_per_col; + p->text_buf[p->line_size] = 0; + p->recalc_align = xd->mode_align; + p->next_line = true; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// output functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int DumpInt32 +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // current params + const void *p_data, // data to dump + uint p_size, // size of 'p_data' + bool p_term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + if ( !p_data || !xd->out_func ) + return 0; + + XParam_t p; + SetupXParam(&p,xd,p_data,p_size,p_term); + + while(SetupXParamLine(&p,xd)) + { + if ( xd->mode_ignore && CheckXParamNull(&p,xd) ) + continue; + + if (xd->print_number) + { + char *num = p.num_buf; + uint col; + for ( col = 0; col < xd->cols_per_line; col++ ) + { + if (!--p.extra_space) + { + p.extra_space = xd->col_space; + *num++ = ' '; + } + + if ( p.skip > 0 ) + { + p.skip--; + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + else if ( col < p.max_col ) + { + u32 val; + switch(xd->bytes_per_col) + { + case 2: val = xd->endian_func->rd16(p.d); p.d += 2; break; + case 3: val = xd->endian_func->rd24(p.d); p.d += 3; break; + case 4: val = xd->endian_func->rd32(p.d); p.d += 4; break; + default: val = *p.d++; break; + } + num += sprintf(num,xd->num_format1,val); + if ( xd->have_trigger && val == (u32)xd->trigger ) + AbortXParamLine(&p,xd,col); + } + else if ( p.term && !p.next_line && !xd->mode_c ) + { + if ( p.d < p.end ) + num += sprintf(num, "%*c%u", xd->num_format_fw-1, + '>', (int)(p.end-p.d) ); + else if ( xd->print_endmarker ) + num += sprintf(num,"%*c", xd->num_format_fw, '/' ); + else if ( xd->print_text ) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + p.term = false; // print only once + } + else if ( xd->print_text ) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + } + if (!xd->print_text) + while ( num > p.num_buf && num[-1] == ' ' ) + num--; + *num = 0; + ASSERT( num < p.num_buf + sizeof(p.num_buf)); + xd->last_number_fw = num - p.num_buf; + } + else + { + p.d += p.line_size - p.skip * xd->bytes_per_col; + p.skip = 0; + } + + int stat = xd->out_func(xd,0,p.num_buf,p.text_buf); + if ( stat < 0 ) + return stat; + + xd->addr += p.line_size; + p.size -= p.line_size; + } + const uint written = p.d - (u8*)p_data; + xd->written += written; + return written; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int DumpInt64 +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // current params + const void *p_data, // data to dump + uint p_size, // size of 'p_data' + bool p_term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + if ( !p_data || !xd->out_func ) + return 0; + + XParam_t p; + SetupXParam(&p,xd,p_data,p_size,p_term); + + while(SetupXParamLine(&p,xd)) + { + if ( xd->mode_ignore && CheckXParamNull(&p,xd) ) + continue; + + if (xd->print_number) + { + char *num = p.num_buf; + uint col; + for ( col = 0; col < xd->cols_per_line; col++ ) + { + if (!--p.extra_space) + { + p.extra_space = xd->col_space; + *num++ = ' '; + } + + if ( p.skip > 0 ) + { + p.skip--; + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + else if ( col < p.max_col ) + { + u64 val; + switch(xd->bytes_per_col) + { + case 5: val = xd->endian_func->rd40(p.d); p.d += 5; break; + case 6: val = xd->endian_func->rd48(p.d); p.d += 6; break; + case 7: val = xd->endian_func->rd56(p.d); p.d += 7; break; + default: val = xd->endian_func->rd64(p.d); p.d += 8; break; + } + num += sprintf(num,xd->num_format1,val); + if ( xd->have_trigger && val == (u32)xd->trigger ) + AbortXParamLine(&p,xd,col); + } + else if ( p.term && !p.next_line && !xd->mode_c ) + { + if ( p.d < p.end ) + num += sprintf(num, "%*c%u", xd->num_format_fw-1, + '>', (int)(p.end-p.d) ); + else if ( xd->print_endmarker ) + num += sprintf(num,"%*c", xd->num_format_fw, '/' ); + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + p.term = false; // print only once + } + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + } + *num = 0; + ASSERT( num < p.num_buf + sizeof(p.num_buf)); + xd->last_number_fw = num - p.num_buf; + } + else + { + p.d += p.line_size - p.skip * xd->bytes_per_col; + p.skip = 0; + } + + int stat = xd->out_func(xd,0,p.num_buf,p.text_buf); + if ( stat < 0 ) + return stat; + + xd->addr += p.line_size; + p.size -= p.line_size; + } + + const uint written = p.d - (u8*)p_data; + xd->written += written; + return written; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int DumpFloat +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // current params + const void *p_data, // data to dump + uint p_size, // size of 'p_data' + bool p_term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + if ( !p_data || !xd->out_func ) + return 0; + + XParam_t p; + SetupXParam(&p,xd,p_data,p_size,p_term); + + while(SetupXParamLine(&p,xd)) + { + if ( xd->mode_ignore && CheckXParamNull(&p,xd) ) + continue; + + if (xd->print_number) + { + char *num = p.num_buf; + uint col; + for ( col = 0; col < xd->cols_per_line; col++ ) + { + if (!--p.extra_space) + { + p.extra_space = xd->col_space; + *num++ = ' '; + } + + if ( p.skip > 0 ) + { + p.skip--; + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + else if ( col < p.max_col ) + { + double val; + ccp format; + if ( xd->bytes_per_col == 4 ) + { + val = xd->endian_func->rdf4(p.d); + format = fabsf(val) <= 9999.0 && fabsf(val) >= 1e-3 + ? xd->num_format1 : xd->num_format2; + p.d += 4; + } + else + { + val = xd->endian_func->rdf8(p.d); + format = fabs(val) <= 999999999.0 && fabs(val) >= 1e-6 + ? xd->num_format1 : xd->num_format2; + p.d += 8; + } + num += sprintf(num,format,val); + } + else if ( p.term && !p.next_line && !xd->mode_c ) + { + if ( p.d < p.end ) + num += sprintf(num, "%*c%u", xd->num_format_fw-1, + '>', (int)(p.end-p.d) ); + else if ( xd->print_text ) + num += sprintf(num,"%*c", xd->num_format_fw, '/' ); + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + p.term = false; // print only once + } + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + } + *num = 0; + ASSERT( num < p.num_buf + sizeof(p.num_buf)); + xd->last_number_fw = num - p.num_buf; + } + else + { + p.d += p.line_size - p.skip * xd->bytes_per_col; + p.skip = 0; + } + + int stat = xd->out_func(xd,0,p.num_buf,p.text_buf); + if ( stat < 0 ) + return stat; + + xd->addr += p.line_size; + p.size -= p.line_size; + } + + const uint written = p.d - (u8*)p_data; + xd->written += written; + return written; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeXDump ( XDump_t *xd ) +{ + DASSERT(xd); + memset(xd,0,sizeof(*xd)); + + xd->f = stdout; + xd->print_format = + xd->print_addr = + xd->print_number = + xd->print_endmarker = + xd->print_text = + xd->print_diff_sep = + xd->print_summary = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupXDump ( XDump_t *xd, XDumpCommand_t cmd ) +{ + DASSERT(xd); + char buf[100]; + + xd->cmd = (uint)cmd < XDUMPC__N ? cmd : XDUMPC_DIFF; + + if ( xd->endian != DC_SECOND_ENDIAN ) + xd->endian = DC_DEFAULT_ENDIAN; + xd->endian_func = xd->endian == DC_LITTLE_ENDIAN ? &le_func : &be_func; + + + //--- address field width + + xd->addr = xd->start_addr; + xd->addr_fw = xd->min_addr_fw; + if ( !xd->min_addr_fw || xd->addr_fw < xd->min_addr_fw ) + { + uint len = snprintf(buf,sizeof(buf),"%llx",xd->addr); + if ( xd->addr_fw < len ) + xd->addr_fw = len; + + const u64 size = xd->assumed_size ? xd->assumed_size : 0x100; + len = snprintf(buf,sizeof(buf),"%llx", xd->addr + size - 1 ); + if ( xd->addr_fw < len ) + xd->addr_fw = len; + + if (xd->max_addr_fw) + { + if ( xd->addr_fw > xd->max_addr_fw ) + xd->addr_fw = xd->max_addr_fw; + if ( xd->addr_fw < xd->min_addr_fw ) + xd->addr_fw = xd->min_addr_fw; + } + } + + + //--- misc + + xd->indent = NormalizeIndent(xd->indent); + if (!xd->out_func) + xd->out_func = XDumpStandardOutput; + if (!xd->prefix) + xd->prefix = EmptyString; + if (!xd->eol) + xd->eol = "\n"; + + xd->num_format2 = 0; + xd->format_printed = 0; + + + //--- format + + if ( (uint)xd->format >= XDUMPF__N ) + xd->format = XDUMPF_AUTO; + + if ( xd->format == XDUMPF_AUTO && xd->cmd != XDUMPC_SCAN ) + xd->format = XDUMPF_INT_1; + + switch(xd->format) + { + case XDUMPF_INT_2: + xd->bytes_per_col = 2; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC2" : "HEX2"; + xd->max_format_fw = 7; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%5u," : "0x%04x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %05u" : " %04x" ) + : ( xd->mode_dec ? " %5u" : " %4x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + + case XDUMPF_INT_3: + xd->bytes_per_col = 3; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC3" : "HEX3"; + xd->max_format_fw = 9; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%8u," : "0x%06x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %08u" : " %06x" ) + : ( xd->mode_dec ? " %8u" : " %6x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + + case XDUMPF_INT_4: + xd->bytes_per_col = 4; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC4" : "HEX4"; + xd->max_format_fw = 11; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%10u," : "0x%08x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %010u" : " %08x" ) + : ( xd->mode_dec ? " %10u" : " %8x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + + case XDUMPF_INT_5: + xd->bytes_per_col = 5; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC5" : "HEX5"; + xd->max_format_fw = 13; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%12llu," : "0x%010llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %012llu" : " %010llx" ) + : ( xd->mode_dec ? " %12llu" : " %10llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_INT_6: + xd->bytes_per_col = 6; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC6" : "HEX6"; + xd->max_format_fw = 16; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%15llu," : "0x%012llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %015llu" : " %012llx" ) + : ( xd->mode_dec ? " %15llu" : " %12llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_INT_7: + xd->bytes_per_col = 7; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC7" : "HEX7"; + xd->max_format_fw = 18; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%17llu," : "0x%014llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %017llu" : " %014llx" ) + : ( xd->mode_dec ? " %17llu" : " %14llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_INT_8: + xd->bytes_per_col = 8; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC8" : "HEX8"; + xd->max_format_fw = 20; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%19llu," : "0x%016llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %019llu" : " %016llx" ) + : ( xd->mode_dec ? " %19llu" : " %16llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_FLOAT: + xd->bytes_per_col = 4; + xd->dump_func = DumpFloat; + xd->format_name = "FLOAT"; + xd->max_format_fw = 12; + xd->num_format1 = xd->mode_c ? "%11.5g," : " %11.5f"; + xd->num_format2 = xd->mode_c ? "%11.5g," : " %11.5g"; + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1.1); + break; + + case XDUMPF_DOUBLE: + xd->bytes_per_col = 8; + xd->dump_func = DumpFloat; + xd->format_name = "DOUBLE"; + xd->max_format_fw = 21; + xd->num_format1 = xd->mode_c ? "%20.8g," : " %20.8f"; + xd->num_format2 = xd->mode_c ? "%20.8g," : " %20.8g"; + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1.1); + break; + + default: + //case XDUMPF_AUTO: + //case XDUMPF_INT_1: + xd->bytes_per_col = 1; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC1" : "HEX1"; + xd->max_format_fw = 5; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%3u," : "0x%02x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %03u" : " %02x" ) + : ( xd->mode_dec ? " %3u" : " %2x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + } + + + //--- bytes per line + + uint min = xd->min_width ? xd->min_width : 16; + uint max = xd->max_width ? xd->max_width : min + 2*xd->bytes_per_col/3; + if ( max > XDUMP_MAX_BYTES_PER_LINE ) + max = XDUMP_MAX_BYTES_PER_LINE; + if ( min > max || !xd->min_width ) + min = max; + + uint ncols = ( min + xd->bytes_per_col - 1 ) / xd->bytes_per_col; + uint ncols_max = max / xd->bytes_per_col; + if ( ncols > ncols_max ) + ncols = ncols_max; + + xd->cols_per_line = ncols ? ncols : 1; + xd->bytes_per_line = xd->cols_per_line * xd->bytes_per_col; + + + //--- extra space + + if ( xd->extra_space < 0 || xd->cols_per_line <= 3 ) + xd->col_space = 0; + else + { + xd->col_space = xd->extra_space; + if (!xd->col_space) + { + xd->col_space = 4 / xd->bytes_per_col; + if ( xd->col_space < 2 ) + xd->col_space = 2; + } + + if ( xd->col_space >= xd->cols_per_line ) + xd->col_space = 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print xdump params /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintXDumpParam +( + FILE *f, // output file, abort if NULL + int indent, // indention + const ColorSet_t *colset, // NULL or color set + const XDump_t *xd // data to print +) +{ + if ( !f || !xd ) + return; + + indent = NormalizeIndent(indent); + if (!colset) + colset = GetColorSet0(); + + fprintf(f, + "%*s%s" "Command: %s%s%s\n" + "%*s%s" "Format: %s%s, %s%s%s => %s%s\n" + "%*s%s" "Endian: %s%s%s\n", + indent,"", colset->name, colset->value, + (uint)xd->cmd < XDUMPC__N ? XDumpCommandName[xd->cmd] : "?", + colset->reset, + indent,"", colset->name, colset->value, + (uint)xd->format < XDUMPF__N ? XDumpFormatName[xd->format] : "?", + xd->mode_dec ? "decimal" : "hexadecimal", + xd->mode_zero ? " with leading zeros" : "", + xd->mode_c ? ", print in C syntax" : "", + xd->format_name ? xd->format_name : "?", + colset->reset, + indent,"", colset->name, colset->value, + xd->endian == DC_LITTLE_ENDIAN ? "LITTLE" + : xd->endian == DC_BIG_ENDIAN ? "BIG" : "?", + colset->reset ); + + if (xd->num_format2) + fprintf(f, + "%*s%s" "Num format: %s'%s' & '%s' (fw=%u,max=%u)%s\n", + indent,"", colset->name, colset->value, + xd->num_format1 ? xd->num_format1 : "?", + xd->num_format2, xd->num_format_fw, xd->max_format_fw, + colset->reset ); + else + fprintf(f, + "%*s%s" "Num format: %s'%s' (fw=%u,max=%u)%s\n", + indent,"", colset->name, colset->value, + xd->num_format1 ? xd->num_format1 : "?", + xd->num_format_fw, xd->max_format_fw, + colset->reset ); + + if (xd->assumed_size) + fprintf(f, "%*s%s" + "Address: %sstart=0x%llx, current=0x%llx, size=0x%llx%s%s\n", + indent,"", colset->name, colset->value, + xd->start_addr, xd->addr, xd->assumed_size, + xd->mode_align ? ", aligned" : "", + colset->reset ); + else + fprintf(f, "%*s%s" + "Address: %sstart=0x%llx, current=0x%llx%s%s\n", + indent,"", colset->name, colset->value, + xd->start_addr, xd->addr, + xd->mode_align ? ", aligned" : "", + colset->reset ); + + if (xd->have_trigger) + fprintf(f, "%*s%s" + "Line trigger: %s%llu = 0x%llx%s%s\n", + indent,"", colset->name, colset->value, + xd->trigger, xd->trigger, + xd->trigger == '\n' ? " (LF)" : + xd->trigger == '\r' ? " (CR)" : + xd->trigger == '\t' ? " (TAB)" : + xd->trigger == ' ' ? " (SPACE)" : + xd->trigger == 0 ? " (NULL)" : + "", + colset->reset ); + + fprintf(f, + "%*s%s" "Address fw: %smin=%u, max=%u, active=%u%s\n", + indent,"", colset->name, colset->value, + xd->min_addr_fw, xd->max_addr_fw, xd->addr_fw, + colset->reset ); + + fprintf(f, + "%*s%s" "Print: %s%sformat, %saddress, %snumbers, %stext, %ssummary, %sseparator%s\n", + indent,"", colset->name, colset->value, + xd->print_format ? "" : "no ", + xd->print_addr ? "" : "no ", + xd->print_number ? "" : "no ", + xd->print_text ? "" : "no ", + xd->print_summary ? "" : "no ", + xd->print_diff_sep ? "" : "no ", + colset->reset ); + + fprintf(f, + "%*s%s" "Output file: %s%s, indent=%d, prefix=%s, eol=%s%s\n", + indent,"", colset->name, colset->value, + xd->f == stdout ? "STDOUT" + : xd->f == stderr ? "STDERR" + : xd->f ? "FILE" : "NONE", + xd->indent, + !xd->prefix ? "NULL" : !*xd->prefix ? "EMPTY" : "DEFINED", + !xd->eol ? "NULL" + : !*xd->eol ? "EMPTY" + : !strcmp(xd->eol,"\n") ? "LF" + : !strcmp(xd->eol,"\r") ? "CR" + : !strcmp(xd->eol,"\r\n") ? "CR+LF" + : "DEFINED", + colset->reset ); + + fprintf(f, + "%*s%s" "Bytes/line: %smin=%u, max=%u%s\n" + "%*s%s" " =>: %sbytes/column = %u, columns/line = %u, bytes/line = %u%s\n" + "%*s%s" "Extra space: %severy %u column => every %u column%s\n", + indent,"", colset->name, colset->value, + xd->min_width, xd->max_width, + colset->reset, + indent,"", colset->name, colset->value, + xd->bytes_per_col, xd->cols_per_line, xd->bytes_per_line, + colset->reset, + indent,"", colset->name, colset->value, + xd->extra_space, xd->col_space, + colset->reset ); + + if (xd->mode_ignore) + fprintf(f, + "%*s" "%sNULL bytes: %sPrint a summary for lines with NULL bytes only.%s\n", + indent,"", colset->name, colset->value, colset->reset ); + fprintf(f, + "%*s%s" "Functions: %sdump=%s, output=%s, endian=%s%s\n", + indent,"", colset->name, colset->value, + !xd->dump_func ? "-" : "SET", + !xd->out_func ? "-" + : xd->out_func == XDumpStandardOutput + ? "STANDARD" : "SET", + !xd->endian_func ? "-" + : xd->endian_func == &le_func ? "LITTLE" + : xd->endian_func == &be_func ? "BIG" + : "SET", + colset->reset ); + + fprintf(f, + "%*s%s" "User param: %sint=%d, ptr=%s%s\n", + indent,"", colset->name, colset->value, + xd->user_int, xd->user_ptr ? "SET" : "-", + colset->reset ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dump interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void SetupRemainder + ( XDump_t *xd, const XDump_t *src, XDumpCommand_t cmd, uint size ) +{ + DASSERT(xd); + DASSERT(src); + + memcpy(xd,src,sizeof(*xd)); + xd->format = XDUMPF_INT_1; + xd->start_addr = xd->addr; + xd->assumed_size = size; + xd->min_width = size; + xd->max_width = size; + xd->extra_space = 0; + xd->mode_align = false; + xd->mode_ignore = false; + + if (xd->print_text) + xd->min_number_fw = xd->last_number_fw; + + SetupXDump(xd,cmd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int XDumpHelper +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; SetupXDump() already done! + const void *data, // data to dump + uint size, // size of 'data' + bool term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + DASSERT(data||!size); + + PrintFormat(xd,0); + int stat = xd->dump_func(xd,data,size,term); + if ( stat < 0 ) + return stat; + + if (term) + { + PrintNullSummary(xd); + + if ( stat < size ) + { + XDump_t local; + SetupRemainder(&local,xd,XDUMPC_DUMP,size-stat); + PrintFormat(&local,0); + + int stat2 = local.dump_func(&local,data+stat,local.min_width,term); + xd->addr = local.addr; + xd->written = local.written; + + if ( stat2 < 0 ) + return stat2; + stat += stat2; + } + + if ( xd->f && xd->print_summary ) + { + fprintf(xd->f,"%s%*s",xd->prefix,xd->indent,""); + if ( xd->print_addr ) + { + if (xd->mode_c) + fprintf(xd->f,"//%*llx// [%llu Bytes]%s", + xd->addr_fw, xd->addr, + xd->written, xd->eol ); + + else + fprintf(xd->f,"%*llx:%s [%llu Bytes]%s", + xd->addr_fw, xd->addr, + xd->extra_space >= 0 ? " " : "", + xd->written, xd->eol ); + } + else + { + if (xd->mode_c) + fprintf(xd->f,"// [%llu Bytes]%s", + xd->written, xd->eol ); + + else + fprintf(xd->f,"%s [%llu Bytes]%s", + xd->extra_space >= 0 ? " " : "", + xd->written, xd->eol ); + } + } + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDump +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + const void *data, // data to dump + uint size, // size of 'data' + bool term // false: print only whole lines + // true: print complete data +) +{ + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DUMP); + + return XDumpHelper(xd,data,size,term); +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDumpByFile +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FILE *f, // input file + u64 max_size // >0: limit size to print +) +{ + DASSERT(f); + + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DUMP); + + char buf[0x4100]; + uint start = 0; + if (!max_size) + max_size = M1(max_size); + + uint count = 0; + while ( max_size > 0 ) + { + uint max_read = ( sizeof(buf) - start ) / 0x1000 * 0x1000; + if ( max_read > max_size ) + max_read = max_size; + + size_t stat = fread(buf+start,1,max_read,f); + if (!stat) + break; + max_size -= stat; + stat += start; + + int dstat = XDumpHelper(xd,buf,stat,false); + if ( dstat < 0 ) + return dstat; + count += dstat; + + start = stat - dstat; + if (start) + memmove(buf,buf+dstat,start); + } + + int dstat = XDumpHelper(xd,buf,start,true); + return dstat < 0 ? dstat : count + dstat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDump16 // wrapper for a standard hexdump +( + // returns <0 on error, or number of dumped bytes + + FILE *f_out, // output file + int indent, // indention + u64 addr, // start address + const void *data, // data + uint size // size of 'data' +) +{ + XDump_t xd; + InitializeXDump(&xd); + xd.f = f_out; + xd.indent = indent; + xd.start_addr = addr; + xd.assumed_size = size; + + return XDump(&xd,data,size,true); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct XDiff_t +{ + const void *data; // data to compare + uint size; // size of 'data' + bool term; // 'data' reached end + + XDump_t xd; // dump parameters + + //--- buffers to catch output + char num_buf[XDUMP_GOOD_NUM_BUF_SIZE+4]; + char text_buf[XDUMP_GOOD_TEXT_BUF_SIZE]; +} +XDiff_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int XDiffCatchOutput +( + struct XDump_t *xd, // internal copy of params + ccp prefix, // NULL or additional prefix (used by diff) + ccp dump, // dump to print + ccp text // text to print +) +{ + DASSERT(xd); + DASSERT(dump); + DASSERT(text); + + XDiff_t *diff = xd->user_ptr; + DASSERT(diff); + + StringCopyS(diff->num_buf,sizeof(diff->num_buf),dump); + StringCopyS(diff->text_buf,sizeof(diff->text_buf),text); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupXDiff +( + XDiff_t *xdiff, // data to setup + + XDump_t *xd, // NULL or template + + const void *data, // data to compare + uint size, // size of 'data' + bool term // 'data' reached end +) +{ + DASSERT(xdiff); + + xdiff->data = data; + xdiff->size = size; + xdiff->term = term; + *xdiff->num_buf = *xdiff->text_buf = 0; + + if (xd) + memcpy(&xdiff->xd,xd,sizeof(xdiff->xd)); + else + InitializeXDump(&xdiff->xd); + + xd = &xdiff->xd; + SetupXDump(xd,XDUMPC_DIFF); + + xd->user_ptr = xdiff; + xd->out_func = XDiffCatchOutput; + xd->mode_ignore = false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool XDiffText +( + // returns true if differ + + XDump_t *xd, // original params; SetupXDump() already done! + XDiff_t *d1, // first diff file + XDiff_t *d2, // second diff file + bool colorize // true: use red and green on output +) +{ + DASSERT(xd); + DASSERT(d1); + DASSERT(d2); + + char *n1 = d1->num_buf; + char *n2 = d2->num_buf; + + int len1 = strlen(n1); + int len2 = strlen(n2); + if ( len1 < len2 ) + { + memset( n1 + len1, ' ', len2-len1 ); + n1[len2] = 0; + len1 = len2; + } + else if ( len1 > len2 ) + { + memset( n2 + len2, ' ', len1-len2 ); + n2[len1] = 0; + } + + if ( xd->min_number_fw < len1 ) + xd->min_number_fw = len1; + + if (!strcmp(n1,n2)) + return false; + + for(;;) + { + while ( *n1 == ' ' && *n2 == ' ' ) + n1++, n2++; + if ( !*n1 || !*n2 ) + break; + + char *x = n2; + while ( (u8)*n2 > ' ' && *n1 == *n2 && *n2 != '/' && *n2 != '/' ) + n1++, n2++; + if ( n2 > x && (u8)*n2 <= ' ' ) + { + while ( x < n2 ) + *x++ = ' '; + x[-1] = '.'; + } + else + while ( (u8)*n1 > ' ' || (u8)*n2 > ' ' ) + n1++, n2++; + } + + xd->out_func(xd,"< ",d1->num_buf,d1->text_buf); + xd->out_func(xd,"> ",d2->num_buf,d2->text_buf); + if ( xd->f && xd->print_diff_sep ) + fputs(xd->eol,xd->f); + + xd->diff_count++; + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// + +#undef OPTIMIZED_SEARCH +#define OPTIMIZED_SEARCH 0 + +static int XDiffHelper +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // original params; SetupXDump() already done! + XDiff_t *d1, // first diff file + XDiff_t *d2, // second diff file + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + DASSERT(xd); + DASSERT(d1); + DASSERT(d2); + DASSERT( d1->xd.bytes_per_col == d1->xd.bytes_per_col ); + DASSERT( d1->xd.bytes_per_line == d1->xd.bytes_per_line ); + DASSERT( d1->xd.format == d1->xd.format ); + + + //--- size calculations & setup + + uint count = 0; + int size1 = d1->size; + int size2 = d2->size; + if ( !d1->term && !d2->term ) + { + if ( size1 < size2 ) + size2 = size1; + else if ( size1 > size2 ) + size1 = size2; + } + + if (!d1->term) + size1 = size1 / xd->bytes_per_line * xd->bytes_per_line; + + if (!d2->term) + size2 = size2 / xd->bytes_per_line * xd->bytes_per_line; + + const u8 *p1 = d1->data; + const u8 *p2 = d2->data; + + const u64 *save_addr_break = xd->addr_break; + const u64 *addr_break = save_addr_break; + xd->addr_break = 0; + + + //--- main loop + + int done = 0; + while ( size1 > 0 && size2 > 0 + || !done && ( size1 > 0 || size2 > 0 ) ) + { + //--- optimized search + + #if OPTIMIZED_SEARCH + uint same = CountEqual(p1,p2, size1 < size2 ? size1 : size2 ); + if ( same >= xd->bytes_per_line ) + { + same = same / xd->bytes_per_line * xd->bytes_per_line; + p1 += same; + p2 += same; + xd->addr += same; + count += same; + size1 -= same; + size2 -= same; + } + #endif + + //--- + int dumpsize1 = size1 < (int)xd->bytes_per_line ? size1 : xd->bytes_per_line; + int dumpsize2 = size2 < (int)xd->bytes_per_line ? size2 : xd->bytes_per_line; + + bool have_break = false; + if (addr_break) + { + while ( *addr_break && xd->addr >= *addr_break ) + addr_break++; + + if (!*addr_break) + addr_break = 0; + else + { + const s64 max = *addr_break - xd->addr; + if ( dumpsize1 > max || dumpsize2 > max ) + { + have_break = true; + if ( dumpsize1 > max ) dumpsize1 = max; + if ( dumpsize2 > max ) dumpsize2 = max; + } + } + } + + #if !OPTIMIZED_SEARCH + const uint diffsize = dumpsize1 < dumpsize2 ? dumpsize1 : dumpsize2; + #endif + uint donesize = dumpsize1 > dumpsize2 ? dumpsize1 : dumpsize2; + +//DEL fprintf(stderr,">>> %d,%d done=%d, donesize=%d, diffsize=%d\n",size1,size2,done,donesize,diffsize); + + #if !OPTIMIZED_SEARCH + if ( diffsize > 0 + && ( diffsize < xd->bytes_per_line || memcmp(p1,p2,diffsize) )) + #endif + { + if ( dumpsize1 != dumpsize2 && !xd->diff_size ) + { + xd->diff_size = dumpsize1 - dumpsize2; + if (xd->diff_size) + xd->diff_count++; + } + + if ( dumpsize1 >= (int)xd->bytes_per_col ) + { + const int stat = xd->dump_func(&d1->xd,p1,dumpsize1,true); + if ( stat >= 0 && stat < dumpsize1 && donesize > stat ) + donesize = stat; + } + else if ( dumpsize1 > 0 ) + { + SetupRemainder(&d1->xd,&d1->xd,XDUMPC_DIFF,dumpsize1); + d1->xd.dump_func(&d1->xd,p1,dumpsize1,true); + } + else + { + done++; + strcpy(d1->num_buf,"/"); + *d1->text_buf = 0; + } + + if ( dumpsize2 >= (int)xd->bytes_per_col ) + { + const int stat = xd->dump_func(&d2->xd,p2,dumpsize2,true); + if ( stat >= 0 && stat < dumpsize2 && donesize > stat ) + donesize = stat; + } + else if ( dumpsize2 > 0 ) + { + SetupRemainder(&d2->xd,&d2->xd,XDUMPC_DIFF,dumpsize2); + d2->xd.dump_func(&d2->xd,p2,dumpsize2,true); + } + else + { + done++; + strcpy(d2->num_buf,"/"); + *d2->text_buf = 0; + } + + XDiffText(xd,d1,d2,colorize); + if ( limit_lines && !--limit_lines ) + break; + } + + p1 += donesize; + p2 += donesize; + xd->addr += donesize; + count += donesize; + size1 -= donesize; + size2 -= donesize; + + if (have_break) + { + if (!d1->term) + size1 = size1 / xd->bytes_per_line * xd->bytes_per_line; + + if (!d2->term) + size2 = size2 / xd->bytes_per_line * xd->bytes_per_line; + } + } + + xd->addr_break = save_addr_break; + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDiff +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + + const void *data1, // data1 to compare + uint size1, // size of 'data1' + bool term1, // true: 'data1' completed + + const void *data2, // data2 to compare + uint size2, // size of 'data2' + bool term2, // true: 'data2' completed + + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + // fast test for identical data + if ( term1 && term2 && size1 == size2 && ( !size1 || !memcmp(data1,data2,size1) )) + return 0; + + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DIFF); + + XDiff_t diff1, diff2; + SetupXDiff(&diff1,xd,data1,size1,term1); + SetupXDiff(&diff2,xd,data2,size2,term2); + return XDiffHelper(xd,&diff1,&diff2,limit_lines,colorize); +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDiffByFile +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FILE *f1, // first input file + FILE *f2, // second input file + u64 max_size, // >0: limit comparing size + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + DASSERT(f1); + DASSERT(f2); + + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DIFF); + + char buf1[0x4100]; + char buf2[sizeof(buf1)]; + + XDiff_t diff1, diff2; + SetupXDiff(&diff1,xd,buf1,0,false); + SetupXDiff(&diff2,xd,buf2,0,false); + + uint start1 = 0, start2 = 0; + if (!max_size) + max_size = M1(max_size); + + uint count = 0; + while ( max_size > 0 ) + { + uint max_read1 = ( sizeof(buf1) - start1 ) / 0x1000 * 0x1000; + if ( max_read1 > max_size ) + max_read1 = max_size; + size_t stat1 = fread(buf1+start1,1,max_read1,f1); + + uint max_read2 = ( sizeof(buf2) - start2 ) / 0x1000 * 0x1000; + if ( max_read2 > max_size ) + max_read2 = max_size; + size_t stat2 = fread(buf2+start2,1,max_read2,f2); + + uint stat = stat1 < stat2 ? stat1 : stat2; + max_size -= stat; + + diff1.size = stat1 + start1; + diff1.term = !stat1; + diff2.size = stat2 + start2; + diff2.term = !stat2; + + int dstat = XDiffHelper(xd,&diff1,&diff2,limit_lines,colorize); + + if ( dstat <= 0 ) + return dstat; + count += dstat; + if (xd->diff_size) + return count; + + start1 = stat1 + start1 - dstat; + if (start1) + memmove(buf1,buf1+dstat,start1); + + start2 = stat2 + start2 - dstat; + if (start2) + memmove(buf2,buf2+dstat,start2); + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDiff16 +( + // returns <0 on error, or number of dumped bytes + + FILE *f_out, // output file + int indent, // indention + u64 addr, // start address + + const void *data1, // data + uint size1, // size of 'data' + + const void *data2, // data + uint size2, // size of 'data' + + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + XDump_t xd; + InitializeXDump(&xd); + xd.f = f_out; + xd.indent = indent; + xd.start_addr = addr; + xd.assumed_size = size1 > size2 ? size1 : size2; + + return XDiff( &xd, data1,size1,true, + data2,size2,true, limit_lines, colorize ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan interface /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[XScan_t]] + +typedef struct XScan_t +{ + FastBuf_t *dest; // not NULL: append scanned data here & ignore 'fout' + FILE *fout; // valid output file + u64 written; // number of written bytes + + bool auto_format; // true: detect '#FORMAT:' + XDumpFormat_t format; // current scan format + dcEndian_t endian; // current endian + uint num_base; // 8:octal, 10:decimal, 16:hexadecimal + ccp num_allowed; // string with allowed first characters + + ccp prefix; // not NULL: skip this prefix + uint prefix_len; // length of prefix + + const endian_func_t *endian_func; // endian functions +} +XScan_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void UpdateXScan ( XScan_t *scan ) +{ + DASSERT(scan); + + if ( !scan->dest && !scan->fout ) + scan->fout = stdout; + + if (scan->prefix) + { + ccp prefix = scan->prefix; + while ( *prefix > 0 && *prefix <= ' ' ) + prefix++; + scan->prefix = *prefix ? prefix : 0; + scan->prefix_len = strlen(prefix); + } + else + scan->prefix_len = 0; + + scan->endian_func = scan->endian == DC_LITTLE_ENDIAN ? &le_func : &be_func; + + if ( scan->format == XDUMPF_AUTO ) + scan->format = XDUMPF_INT_1; + + if ( scan->num_base < 2 || scan->num_base > 36 ) + scan->num_base = 16; + + scan->num_allowed + = scan->format == XDUMPF_FLOAT || scan->format == XDUMPF_DOUBLE + ? "+-." + : "+-~!"; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupXScan +( + XScan_t *scan, // data to setup + XDump_t *xd, // NULL or template + FastBuf_t *dest // not NULL: append scanned data here +) +{ + DASSERT(scan); + memset(scan,0,sizeof(*scan)); + + if (xd) + { + scan->fout = xd->f; + scan->auto_format = xd->format == XDUMPF_AUTO; + scan->format = xd->format; + scan->endian = xd->endian; + scan->num_base = xd->mode_dec ? 10 : 16; + scan->prefix = xd->prefix; + } + else + { + scan->fout = stdout; + scan->auto_format = true; + scan->format = XDUMPF_INT_1; + scan->endian = DC_DEFAULT_ENDIAN; + scan->num_base = 16; + scan->prefix = 0; + } + + scan->dest = dest; + UpdateXScan(scan); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void XScanFormat +( + // returns <0 on error, or number of dumped bytes + + XScan_t *scan, // valid scan parameters + ccp src, // source pointer + ccp eol // end of line +) +{ + DASSERT(scan); + DASSERT(src); + DASSERT(eol); + DASSERT( src <= eol ); + + noPRINT(stderr,"FORMAT: %.*s\n",(int)(eol-src),src); + + enum { MD_HEX, MD_DEC, MD_OCT, MD_NUM, MD_ENDIAN }; + + static const KeywordTab_t keytab[] = + { + { MD_HEX, "HEX", 0, XDUMPF_AUTO }, + { MD_HEX, "HEX-1", "HEX1", XDUMPF_INT_1 }, + { MD_HEX, "HEX-2", "HEX2", XDUMPF_INT_2 }, + { MD_HEX, "HEX-3", "HEX3", XDUMPF_INT_3 }, + { MD_HEX, "HEX-4", "HEX4", XDUMPF_INT_4 }, + { MD_HEX, "HEX-5", "HEX5", XDUMPF_INT_5 }, + { MD_HEX, "HEX-6", "HEX6", XDUMPF_INT_6 }, + { MD_HEX, "HEX-7", "HEX7", XDUMPF_INT_7 }, + { MD_HEX, "HEX-8", "HEX8", XDUMPF_INT_8 }, + + { MD_DEC, "DEC", 0, XDUMPF_AUTO }, + { MD_DEC, "DEC-1", "DEC1", XDUMPF_INT_1 }, + { MD_DEC, "DEC-2", "DEC2", XDUMPF_INT_2 }, + { MD_DEC, "DEC-3", "DEC3", XDUMPF_INT_3 }, + { MD_DEC, "DEC-4", "DEC4", XDUMPF_INT_4 }, + { MD_DEC, "DEC-5", "DEC5", XDUMPF_INT_5 }, + { MD_DEC, "DEC-6", "DEC6", XDUMPF_INT_6 }, + { MD_DEC, "DEC-7", "DEC7", XDUMPF_INT_7 }, + { MD_DEC, "DEC-8", "DEC8", XDUMPF_INT_8 }, + + { MD_OCT, "OCT", 0, XDUMPF_AUTO }, + { MD_OCT, "OCT-1", "OCT1", XDUMPF_INT_1 }, + { MD_OCT, "OCT-2", "OCT2", XDUMPF_INT_2 }, + { MD_OCT, "OCT-3", "OCT3", XDUMPF_INT_3 }, + { MD_OCT, "OCT-4", "OCT4", XDUMPF_INT_4 }, + { MD_OCT, "OCT-5", "OCT5", XDUMPF_INT_5 }, + { MD_OCT, "OCT-6", "OCT6", XDUMPF_INT_6 }, + { MD_OCT, "OCT-7", "OCT7", XDUMPF_INT_7 }, + { MD_OCT, "OCT-8", "OCT8", XDUMPF_INT_8 }, + + { MD_NUM, "1", 0, XDUMPF_INT_1 }, + { MD_NUM, "2", 0, XDUMPF_INT_2 }, + { MD_NUM, "3", 0, XDUMPF_INT_3 }, + { MD_NUM, "4", 0, XDUMPF_INT_4 }, + { MD_NUM, "5", 0, XDUMPF_INT_5 }, + { MD_NUM, "6", 0, XDUMPF_INT_6 }, + { MD_NUM, "7", 0, XDUMPF_INT_7 }, + { MD_NUM, "8", 0, XDUMPF_INT_8 }, + + { MD_NUM, "FLOAT", 0, XDUMPF_FLOAT }, + { MD_NUM, "DOUBLE", 0, XDUMPF_DOUBLE }, + + { MD_ENDIAN, "LE", "LITTLE", DC_LITTLE_ENDIAN }, + { MD_ENDIAN, "BE", "BIG", DC_BIG_ENDIAN }, + + {0,0,0,0} + }; + + for(;;) + { + while ( src < eol && !isalnum((int)*src) ) + src++; + + char namebuf[20], *name = namebuf; + while ( src < eol && (u8)*src > ' ' ) + { + if ( name < namebuf + sizeof(namebuf) - 1 ) + *name++ = toupper((int)*src); + src++; + } + *name = 0; + + if ( name == namebuf ) + break; + + noPRINT(stderr,"SCAN FORMAT: %s\n",namebuf); + const KeywordTab_t *key = ScanKeyword(0,namebuf,keytab); + if (!key) + continue; + + XDumpFormat_t format = key->opt; + switch(key->id) + { + case MD_HEX: + scan->num_base = 16; + break; + + case MD_DEC: + scan->num_base = 10; + break; + + case MD_OCT: + scan->num_base = 8; + break; + + case MD_NUM: + break; + + case MD_ENDIAN: + scan->endian = key->opt; + format = XDUMPF_AUTO; + break; + } + if ( format != XDUMPF_AUTO ) + scan->format = format; + + noPRINT(stderr,"F: %lld %lld => base=%d, format=%d, endian=%d\n", + key->id, key->opt, + scan->num_base, scan->format, scan->endian ); + } + UpdateXScan(scan); +} + +/////////////////////////////////////////////////////////////////////////////// + +static size_t XScanHelper +( + // returns <0 on error, or number of dumped bytes + + XScan_t *scan, // valid scan parameters + ccp source, // source pointer + ccp end, // end of source + bool term, // true: accept uncomplete last line + u64 max_size // >0: limit output size +) +{ + DASSERT(scan); + DASSERT( scan->dest || scan->fout ); + DASSERT(source); + DASSERT(end); + DASSERT( source <= end ); + + if (!max_size) + max_size = M1(max_size); + max_size -= scan->written; + + ccp src = source; + u8 destbuf[0x4000+10]; + u8 *destmax = destbuf + sizeof(destbuf) - 10; + u8 *dest = destbuf; + + while ( src < end && max_size > 0 ) + { + //--- skip all leading contral characters + + while ( src < end && (u8)*src <= ' ' ) + src++; + + + //--- find end of line + + bool found_eol = term; + ccp eol = src; + while ( eol < end ) + { + const u8 ch = *eol; + if ( !ch || ch == '\n' || ch == '\r' ) + { + found_eol = true; + break; + } + eol++; + } + if (!found_eol) + break; + + PRINT0("LINE: |%.*s|\n",(int)(eol-src),src); + + + //--- skip prefix + + if ( scan->prefix_len + && scan->prefix_len <= eol-src + && !memcmp(scan->prefix,src,scan->prefix_len) ) + { + src += scan->prefix_len; + while ( src < eol && (u8)*src <= ' ' ) + src++; + } + + if ( src == eol ) + continue; + + + //--- check for "#FORMAT:" + + if (!strncasecmp(src,"#FORMAT:",8)) + { + if (scan->auto_format) + XScanFormat(scan,src+8,eol); + src = eol; + continue; + } + + + //--- skip address column + + ccp ptr = src; + while ( ptr < eol && isalnum((int)*ptr) ) + ptr++; + noPRINT(stderr,"ADDR: |%.*s|\n",(int)(ptr-src),src); + if ( ptr > src && ptr < eol && *ptr == ':' ) + src = ptr + 1; + + + //--- scan numbers until unknown character + + noPRINT(stderr,"SCAN: |%.*s| base=%d\n",(int)(eol-src),src,scan->num_base); + + for(;;) + { + while ( src < eol && ( (u8)*src <= ' ' || *src == ',' ) ) + src++; + if ( src == eol + || (u8)TableNumbers[(u8)*src] >= scan->num_base + && !strchr(scan->num_allowed,*src) ) + { + break; + } + + if ( dest >= destmax ) + { + size_t len = dest - destbuf; + if ( len > max_size ) + len = max_size; + if (scan->dest) + AppendFastBuf(scan->dest,destbuf,len); + else + fwrite(destbuf,1,len,scan->fout); + max_size -= len; + scan->written += len; + dest = destbuf; + } + + //u64 num; + //double d; + char *end; + + switch(scan->format) + { + case XDUMPF_INT_2: + scan->endian_func->wr16(dest,str2ll(src,&end,scan->num_base)); + dest += 2; + break; + + case XDUMPF_INT_3: + scan->endian_func->wr24(dest,str2ll(src,&end,scan->num_base)); + dest += 3; + break; + + case XDUMPF_INT_4: + scan->endian_func->wr32(dest,str2ll(src,&end,scan->num_base)); + dest += 4; + break; + + case XDUMPF_INT_5: + scan->endian_func->wr40(dest,str2ll(src,&end,scan->num_base)); + dest += 5; + break; + + case XDUMPF_INT_6: + scan->endian_func->wr48(dest,str2ll(src,&end,scan->num_base)); + dest += 6; + break; + + case XDUMPF_INT_7: + scan->endian_func->wr56(dest,str2ll(src,&end,scan->num_base)); + dest += 7; + break; + + case XDUMPF_INT_8: + scan->endian_func->wr64(dest,str2ll(src,&end,scan->num_base)); + dest += 8; + break; + + case XDUMPF_FLOAT: + scan->endian_func->wrf4(dest,strtod(src,&end)); + dest += 4; + break; + + case XDUMPF_DOUBLE: + scan->endian_func->wrf8(dest,strtod(src,&end)); + dest += 8; + break; + + default: + *dest++ = str2ll(src,&end,scan->num_base); + break; + } + if ( src == end ) + break; + src = end; + } + + //--- prepare next line + + src = eol; + } + + if ( dest > destbuf ) + { + size_t len = dest - destbuf; + if ( len > max_size ) + len = max_size; + if (scan->dest) + AppendFastBuf(scan->dest,destbuf,len); + else + fwrite(destbuf,1,len,scan->fout); + scan->written += len; + } + + return src - source; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XScan +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FastBuf_t *dest, // not NULL: append scanned data here + + const void *data, // data to scan + uint size, // size of 'data' + bool term, // true: accept uncomplete last line + u64 max_size // >0: limit output size +) +{ + if ( !data || !size ) + return 0; + + XScan_t scan; + SetupXScan(&scan,xd,dest); + return XScanHelper(&scan,data,data+size,term,max_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +s64 XScanByFile +( + // returns <0 on error, or number of written bytes + XDump_t *xd, // params; if NULL: use local version + FastBuf_t *dest, // not NULL: append scanned data here + FILE *f, // input file + u64 max_size // >0: limit output size +) +{ + if (!f) + return 0; + + if (!max_size) + max_size = M1(max_size); + + XScan_t scan; + SetupXScan(&scan,xd,dest); + + char buf[0x4100]; + uint start = 0; + + while ( scan.written < max_size ) + { + const uint max_read = ( sizeof(buf) - start ) / 0x1000 * 0x1000; + + size_t stat = fread(buf+start,1,max_read,f); + if (!stat) + break; + stat += start; + + const bool eof = feof(f) != 0; + int scanstat = XScanHelper(&scan,buf,buf+stat,eof,max_size); + if ( scanstat < 0 ) + return scanstat; + + start = stat - scanstat; + if (start) + memmove(buf,buf+scanstat,start); + if (eof) + break; + } + + if (start) + { + int scanstat = XScanHelper(&scan,buf,buf+start,true,max_size); + if ( scanstat < 0 ) + return scanstat; + } + + return scan.written; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// |
