diff options
| author | root <root@dc13.lemmi> | 2020-07-21 10:00:46 +0200 |
|---|---|---|
| committer | root <root@dc13.lemmi> | 2020-07-21 10:00:46 +0200 |
| commit | 1192c4575ea7afdf38e3fa0df4587a08620af11e (patch) | |
| tree | 6910a62487c282d0840d2e668aa7eb40b7c8a3fd /project/dclib/lib-bmg.c | |
| parent | 76de8a89f7c1d1b4522eb66e8f89d248e8c1781f (diff) | |
SZS v2.18a
Diffstat (limited to 'project/dclib/lib-bmg.c')
| -rwxr-xr-x | project/dclib/lib-bmg.c | 6346 |
1 files changed, 6346 insertions, 0 deletions
diff --git a/project/dclib/lib-bmg.c b/project/dclib/lib-bmg.c new file mode 100755 index 0000000..4e42c9c --- /dev/null +++ b/project/dclib/lib-bmg.c @@ -0,0 +1,6346 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * 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 <errno.h> + +#include "lib-bmg.h" +#include "dclib-utf8.h" +#include "dclib-xdump.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// compiler options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if 1 + #define ENABLE_BMG_REGEX 1 +#else + #define ENABLE_BMG_REGEX 0 +#endif + +#if ENABLE_BMG_REGEX + #include "dclib-regex.h" +#endif + +//----------------------------------------------------------------------------- + +#if defined(TEST) || defined(DEBUG) || HAVE_WIIMM_EXT + #define COMPARE_CREATE 1 // 0=off, 1=on +#else + #define COMPARE_CREATE 0 +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global bmg options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint opt_bmg_force_count = 0; // >0: force some operations +uint opt_bmg_align = 0x20; // alignment of raw-bg sections +bool opt_bmg_export = 0; // true: optimize for exports +bool opt_bmg_no_attrib = 0; // true: suppress attributes +bool opt_bmg_x_escapes = 0; // true: use x-scapes insetad of z-escapes +bool opt_bmg_old_escapes = 0; // true: use only old escapes +int opt_bmg_single_line = 0; // >0: single line, >1: suppress atrributes +bool opt_bmg_support_mkw = 0; // true: support MKW specific extensions +bool opt_bmg_support_ctcode = 0; // true: support MKW/CT-CODE specific extensions +bool opt_bmg_support_lecode = 0; // true: support MKW/LE-CODE specific extensions +uint opt_bmg_rcup_fill_limit = 0; // >0: limit racing cups to reduce BMG size +uint opt_bmg_track_fill_limit= 0; // >0: limit tracks to reduce BMG size +bool opt_bmg_inline_attrib = true; // true: print attrinbutes inline +int opt_bmg_colors = 1; // use c-escapss: 0:off, 1:auto, 2:on +ColNameLevelBMG opt_bmg_color_name = BMG_CNL_ALL; +uint opt_bmg_max_recurse = 10; // max recurse depth +bool opt_bmg_allow_print = 0; // true: allow '$' to print a log message +bool opt_bmg_use_slots = 0; // true: use predifined slots +bool opt_bmg_use_raw_sections= 0; // true: use raw sections + +//----------------------------------------------------------------------------- + +int opt_bmg_encoding = BMG_ENC__UNDEFINED; +uint opt_bmg_inf_size = 0; +OffOn_t opt_bmg_mid = OFFON_AUTO; +bool opt_bmg_force_attrib = false; +bool opt_bmg_def_attrib = false; + +u8 bmg_force_attrib[BMG_ATTRIB_SIZE]; +u8 bmg_def_attrib[BMG_ATTRIB_SIZE]; + +bmg_t *bmg_macros = 0; + +//----------------------------------------------------------------------------- + +const KeywordTab_t PatchKeysBMG[] = +{ + // 'opt' + // 0: no param + // 1: file param + // 2: string param + + { BMG_PM_PRINT, "PRINT", 0, 1 }, + { BMG_PM_FORMAT, "FORMAT", 0, 2 }, + #if ENABLE_BMG_REGEX + { BMG_PM_REGEX, "REGEXP", 0, 2 }, + { BMG_PM_RM_REGEX, "RM-REGEXP", "RMREGEXP", 2 }, + #endif + { BMG_PM_ID, "ID", 0, 0 }, + { BMG_PM_ID_ALL, "ID-ALL", "IDALL", 0 }, + { BMG_PM_UNICODE, "UNICODE", 0, 0 }, + { BMG_PM_RM_ESCAPES, "RM-ESCAPES", "RMESCAPES", 0 }, + + { BMG_PM_REPLACE, "REPLACE", 0, 1 }, + { BMG_PM_INSERT, "INSERT", 0, 1 }, + { BMG_PM_OVERWRITE, "OVERWRITE", 0, 1 }, + { BMG_PM_DELETE, "DELETE", 0, 1 }, + { BMG_PM_MASK, "MASK", 0, 1 }, + { BMG_PM_EQUAL, "EQUAL", 0, 1 }, + { BMG_PM_NOT_EQUAL, "NOTEQUAL", "NEQUAL", 1 }, + + { BMG_PM_GENERIC, "GENERIC", 0, 0 }, + { BMG_PM_RM_CUPS, "RM-CUPS", "RMCUPS", 0 }, + + { BMG_PM_CT_COPY, "CT-COPY", "CTCOPY", 0 }, + { BMG_PM_CT_FORCE_COPY, "CT-FORCE-COPY","CTFORCECOPY", 0 }, + { BMG_PM_CT_FILL, "CT-FILL", "CTFILL", 0 }, + + { BMG_PM_LE_COPY, "LE-COPY", "LECOPY", 0 }, + { BMG_PM_LE_FORCE_COPY, "LE-FORCE-COPY","LEFORCECOPY", 0 }, + { BMG_PM_LE_FILL, "LE-FILL", "LEFILL", 0 }, + + { BMG_PM_X_COPY, "X-COPY", "XCOPY", 0 }, + { BMG_PM_X_FORCE_COPY, "X-FORCE-COPY", "XFORCECOPY", 0 }, + { BMG_PM_X_FILL, "X-FILL", "XFILL", 0 }, + + { BMG_PM_RM_FILLED, "RM-FILLED", "XFILLED", 0 }, + + { 0,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print & scan string16 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t bmg_color[] = +{ + { 0x0000, "OFF", "NONE", BMG_CNL_BASICS }, + { 0x0000, "GRAY", "GREY", BMG_CNL_BASICS }, + { 0x0001, "RED4", 0, BMG_CNL_BASICS }, + { 0x0002, "WHITE", 0, BMG_CNL_BASICS }, + + { 0x0008, "TRANSP", "TRANSPARENT", BMG_CNL_BASICS }, + { 0x0008, "CLEAR", 0, BMG_CNL_BASICS }, + + { 0x0010, "YOR0", "YR0", BMG_CNL_YOR }, + { 0x0011, "YOR1", "YR1", BMG_CNL_YOR }, + { 0x0012, "YOR2", "YR2", BMG_CNL_YOR }, + { 0x0013, "YOR3", "YR3", BMG_CNL_YOR }, + { 0x0014, "YOR4", "YR4", BMG_CNL_YOR }, + { 0x0015, "YOR5", "YR5", BMG_CNL_YOR }, + { 0x0016, "YOR6", "YR6", BMG_CNL_YOR }, + { 0x0017, "YOR7", "YR7", BMG_CNL_YOR }, + + { 0x0020, "RED2", 0, BMG_CNL_BASICS }, + { 0x0021, "BLUE1", "BLUE", BMG_CNL_BASICS }, + + { 0x0030, "YELLOW", 0, BMG_CNL_BASICS }, + { 0x0031, "BLUE2", 0, BMG_CNL_BASICS }, + { 0x0032, "RED3", 0, BMG_CNL_BASICS }, + { 0x0033, "GREEN", 0, BMG_CNL_BASICS }, + + { 0x0040, "RED1", "RED", BMG_CNL_BASICS }, + + {0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +uint GetWordLength16BMG ( const u16 *source ) +{ + DASSERT(source); + u16 code = ntohs(*source); + if ( code != 0x1a ) + return 1; + const uint len = ( ((u8*)source)[2] + 1 & 0xfe ) / 2; + return len ? len : 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetLength16BMG +( + const u16 *source, // source + uint max_len // max possible length +) +{ + DASSERT(source); + + const u16 *src = source, *src_end = src + max_len; + + while ( src < src_end ) + { + u16 code = ntohs(*src++); + if (!code) + return src - source - 1; + + if ( code == 0x1a ) + { + // nintendos special escape sequence! + const uint len = *(u8*)src+1 & 0xfe; + if ( len > 0 ) + src += len - 1; + } + } + + const uint len = src - source; + return len < max_len ? len : max_len; +} + +/////////////////////////////////////////////////////////////////////////////// +#undef TEST_MODE // 0:off, 1:compare, 2:replace +#if HAVE_WIIMM_EXT + #define TEST_MODE 1 +#else + #define TEST_MODE 0 +#endif +//-------------------------------------------------- + +uint PrintString16BMG +( + char *buf, // destination buffer + uint buf_size, // size of 'buf' + const u16 *src, // source + int src_len, // length of source in u16 words, -1:NULL term + u16 utf8_max, // max code printed as UTF-8 + uint quote, // 0:no quotes, 1:escape ", 2: print in quotes + int use_color // 0:no, 1:use, 2:use all +) +{ + DASSERT(buf); + DASSERT( buf_size >= 100 ); + DASSERT(src); + + //-------------------------------------------------- + #if TEST_MODE > 1 + //-------------------------------------------------- + + if ( buf_size < 2 ) + { + *buf = 0; + return 0; + } + + struct { FastBuf_t b; char space[1000]; } fb; + InitializeFastBuf(&fb,sizeof(fb)); + PrintFastBuf16BMG(&fb.b,src,src_len,utf8_max,quote,use_color); + mem_t mem = GetFastBufMem(&fb.b); + + if ( mem.len >= buf_size ) + mem.len = buf_size - 1; + memcpy(buf,mem.ptr,mem.len); + buf[mem.len] = 0; + ResetFastBuf(&fb.b); + return mem.len; + + //-------------------------------------------------- + #else // TEST_MODE <= 1 + //-------------------------------------------------- + + #if TEST_MODE == 1 + struct { FastBuf_t b; char space[1000]; } fb; + InitializeFastBuf(&fb,sizeof(fb)); + PrintFastBuf16BMG(&fb.b,src,src_len,utf8_max,quote,use_color); + #endif + + if ( buf_size < 30 ) + { + #if TEST_MODE == 1 + ResetFastBuf(&fb.b); + #endif + *buf = 0; + return 0; + } + + #undef PRINT_UTF8_MAX + //#define PRINT_UTF8_MAX 0xdb7f + + if ( utf8_max < 0x7f ) + utf8_max = 0x7f; + #ifdef PRINT_UTF8_MAX + else if ( utf8_max > PRINT_UTF8_MAX ) + utf8_max = PRINT_UTF8_MAX; + #endif + + char *dest = buf, *last_u = 0; + char *dest_end = dest + buf_size - 25; + const u16 * src_end = src + ( src_len < 0 ? buf_size : src_len ); + + if (quote>1) + *dest++ = '"'; + + while ( dest < dest_end && src < src_end ) + { + u16 code = ntohs(*src++); + if ( !code && src_len < 0 ) + break; + + if ( code == 0x1a ) + { + // nintendos special escape sequence! + uint total_words = *(u8*)src+1 >> 1; + if ( total_words >= 3 && src + total_words - 1 <= src_end ) + { + //HexDump(stdout,1,0,2,2*total_words,src-1,2*total_words); + const u16 code2 = ntohs(*src++); + if ( code2 == 0x800 && use_color > 0 && be16(src) == 1 ) + { + const u16 color = be16(src+1); + const KeywordTab_t *kt; + for ( kt = bmg_color; kt->name1 && kt->id != color; kt++ ) + ; + if ( kt->name1 && opt_bmg_color_name >= kt->opt ) + { + *dest++ = '\\'; + *dest++ = 'c'; + *dest++ = '{'; + ccp name = kt->name1; + while (*name) + *dest++ = tolower((int)*name++); + *dest++ = '}'; + } + else + dest += sprintf(dest,"\\c{%x}",color); + src += 2; + continue; + } + + if ( code2 == 0x801 ) + { + if ( opt_bmg_old_escapes || last_u != dest ) + dest = snprintfE(dest,dest_end,"\\u{%x}",be32(src)); + else + dest = snprintfE(dest-1,dest_end,",%x}",be32(src)); + last_u = dest; + src += 2; + continue; + } + + if ( ( total_words > 6 || opt_bmg_x_escapes ) && opt_bmg_old_escapes ) + { + src -= 2; + while ( total_words-- > 0 ) + dest = snprintfE(dest,dest_end,"\\x{%x}",ntohs(*src++)); + continue; + } + else if ( opt_bmg_x_escapes ) + { + if ( dest < dest_end ) + { + dest = StringCopyE(dest,dest_end,"\\x{1a"); + src--; + while ( total_words-- > 1 ) + dest = snprintfE(dest,dest_end,",%x",ntohs(*src++)); + } + *dest++ = '}'; + continue; + } + + u64 xcode = 0; + switch( total_words & 3 ) + { + case 3 & 3: xcode = be16(src); src += 1; break; + case 4 & 3: xcode = be32(src); src += 2; break; + case 5 & 3: xcode = be48(src); src += 3; break; + case 6 & 3: xcode = be64(src); src += 4; break; + } + + dest = snprintfE(dest,dest_end,"\\z{%x,%llx",code2,xcode); + while ( total_words > 6 ) + { + dest = snprintfE(dest,dest_end,",%llx",be64(src)); + src += 4; + total_words -= 4; + } + *dest++ = '}'; + continue; + } + } + + if ( code < ' ' || code == 0x7f || code == 0xff ) + { + *dest++ = '\\'; + switch(code) + { + case '\\': *dest++ = '\\'; break; + case '\a': *dest++ = 'a'; break; + case '\b': *dest++ = 'b'; break; + case '\f': *dest++ = 'f'; break; + case '\n': *dest++ = 'n'; break; + case '\r': *dest++ = 'r'; break; + case '\t': *dest++ = 't'; break; + case '\v': *dest++ = 'v'; break; + + default: + *dest++ = ( code >> 6 & 3 ) | '0'; + *dest++ = ( code >> 3 & 7 ) | '0'; + *dest++ = ( code & 7 ) | '0'; + } + } + #ifdef PRINT_UTF8_MAX + else if ( code > utf8_max ) + dest += sprintf(dest,"\\x{%x}",code); + #else + else if ( quote && code == '"' ) + { + *dest++ = '\\'; + *dest++ = '"'; + } + else if ( code > utf8_max + || code >= 0xdb80 && code <= 0xf8ff // surrogates & private use + || ( code & 0xffff ) >= 0xfffe // invalid + ) + { + dest = snprintfE(dest,dest_end,"\\x{%x}",code); + } + #endif + else + dest = PrintUTF8Char(dest,code); + } + + if ( dest > buf && dest[-1] == ' ' ) + { + dest[-1] = '\\'; + *dest++ = '0'; + *dest++ = '4'; + *dest++ = '0'; + } + + if (quote>1) + *dest++ = '"'; + + *dest = 0; + + #if TEST_MODE == 1 + const mem_t mem = GetFastBufMem(&fb.b); + if ( mem.len < buf_size ) + { + + XDump_t xd; + InitializeXDump(&xd); + xd.f = stderr; + xd.indent = 2; + xd.assumed_size = mem.len; + XDiff( &xd, buf,dest-buf,true, mem.ptr,mem.len,true, 5,false ); + } + ResetFastBuf(&fb.b); + #endif + + return dest - buf; + + //-------------------------------------------------- + #endif // TEST_MODE <= 1 + //-------------------------------------------------- +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintFastBuf16BMG +( + // return the number of appended bytes + + FastBuf_t *fb, // destination buffer (data appended) + const u16 *src, // source + int src_len, // length of source in u16 words, -1:NULL term + u16 utf8_max, // max code printed as UTF-8 + uint quote, // 0:no quotes, 1:escape ", 2: print in quotes + int use_color // >0: use \c{...} +) +{ + DASSERT(fb); + DASSERT(src); + + #undef PRINT_UTF8_MAX + //#define PRINT_UTF8_MAX 0xdb7f + + if ( utf8_max < 0x7f ) + utf8_max = 0x7f; + #ifdef PRINT_UTF8_MAX + else if ( utf8_max > PRINT_UTF8_MAX ) + utf8_max = PRINT_UTF8_MAX; + #endif + + const uint start_len = GetFastBufLen(fb); + + char buf[1300]; + char *dest = buf; + char *dest_term = dest + sizeof(buf) - 1; + char *dest_end = dest_term - 270; // escape sequence with 256 bytes possible + char *last_u = 0; + + if ( src_len < 0 ) + src_len = GetLength16BMG(src,1000000); + const u16 * src_end = src + src_len; + + if (quote>1) + *dest++ = '"'; + + while ( src < src_end ) + { + if ( dest >= dest_end ) + { + AppendFastBuf(fb,buf,dest-buf); + dest = buf; + last_u = 0; + } + + u16 code = ntohs(*src++); + if ( !code && src_len < 0 ) + break; + + if ( code == 0x1a ) + { + // nintendos special escape sequence! + uint total_words = *(u8*)src+1 >> 1; + if ( total_words >= 3 && src + total_words - 1 <= src_end ) + { + //HexDump(stdout,1,0,2,2*total_words,src-1,2*total_words); + const u16 code2 = ntohs(*src++); + if ( code2 == 0x800 && use_color > 0 && be16(src) == 1 ) + { + const u16 color = be16(src+1); + const KeywordTab_t *kt; + for ( kt = bmg_color; kt->name1 && kt->id != color; kt++ ) + ; + if ( kt->name1 && opt_bmg_color_name >= kt->opt ) + { + *dest++ = '\\'; + *dest++ = 'c'; + *dest++ = '{'; + ccp name = kt->name1; + while (*name) + *dest++ = tolower((int)*name++); + *dest++ = '}'; + } + else + dest += sprintf(dest,"\\c{%x}",color); + src += 2; + continue; + } + + if ( code2 == 0x801 ) + { + if ( opt_bmg_old_escapes || last_u != dest ) + dest = snprintfE(dest,dest_term,"\\u{%x}",be32(src)); + else + dest = snprintfE(dest-1,dest_term,",%x}",be32(src)); + last_u = dest; + src += 2; + continue; + } + + if ( ( total_words > 6 || opt_bmg_x_escapes ) && opt_bmg_old_escapes ) + { + src -= 2; + while ( total_words-- > 0 ) + dest = snprintfE(dest,dest_term,"\\x{%x}",ntohs(*src++)); + continue; + } + else if ( opt_bmg_x_escapes ) + { + if ( dest < dest_term ) + { + dest = StringCopyE(dest,dest_term,"\\x{1a"); + src--; + while ( total_words-- > 1 ) + dest = snprintfE(dest,dest_term,",%x",ntohs(*src++)); + } + *dest++ = '}'; + continue; + } + + u64 xcode = 0; + switch( total_words & 3 ) + { + case 3 & 3: xcode = be16(src); src += 1; break; + case 4 & 3: xcode = be32(src); src += 2; break; + case 5 & 3: xcode = be48(src); src += 3; break; + case 6 & 3: xcode = be64(src); src += 4; break; + } + + dest = snprintfE(dest,dest_term,"\\z{%x,%llx",code2,xcode); + while ( total_words > 6 ) + { + dest = snprintfE(dest,dest_term,",%llx",be64(src)); + src += 4; + total_words -= 4; + } + *dest++ = '}'; + continue; + } + } + + if ( code < ' ' || code == 0x7f || code == 0xff ) + { + *dest++ = '\\'; + switch(code) + { + case '\\': *dest++ = '\\'; break; + case '\a': *dest++ = 'a'; break; + case '\b': *dest++ = 'b'; break; + case '\f': *dest++ = 'f'; break; + case '\n': *dest++ = 'n'; break; + case '\r': *dest++ = 'r'; break; + case '\t': *dest++ = 't'; break; + case '\v': *dest++ = 'v'; break; + + default: + *dest++ = ( code >> 6 & 3 ) | '0'; + *dest++ = ( code >> 3 & 7 ) | '0'; + *dest++ = ( code & 7 ) | '0'; + } + } + #ifdef PRINT_UTF8_MAX + else if ( code > utf8_max ) + dest += sprintf(dest,"\\x{%x}",code); + #else + else if ( quote && code == '"' ) + { + *dest++ = '\\'; + *dest++ = '"'; + } + else if ( code > utf8_max + || code >= 0xdb80 && code <= 0xf8ff // surrogates & private use + || ( code & 0xffff ) >= 0xfffe // invalid + ) + { + dest = snprintfE(dest,dest_term,"\\x{%x}",code); + } + #endif + else + dest = PrintUTF8Char(dest,code); + } + + if ( dest > buf && dest[-1] == ' ' ) + { + dest[-1] = '\\'; + *dest++ = '0'; + *dest++ = '4'; + *dest++ = '0'; + } + + if (quote>1) + *dest++ = '"'; + + AppendFastBuf(fb,buf,dest-buf); + return GetFastBufLen(fb) - start_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +#if 0 +uint ScanFastBuf16BMG +( + + FastBuf_t *fb, // destination buffer + ccp src, // UTF-8 source + int src_len, // length of source, -1: determine with strlen() + const bmg_t *bmg // NULL or support for \m{...} +); +#endif + +uint ScanString16BMG +( + u16 * buf, // destination buffer + uint buf_size, // size of 'buf' in u16 words + ccp src, // UTF-8 source + int src_len, // length of source, -1: determine with strlen() + const bmg_t *bmg // NULL or support for \m{...} +) +{ + DASSERT(buf); + DASSERT(src); + + u16 * dest = buf; + u16 * dest_end = dest + buf_size; + ccp src_end = src + ( src_len < 0 ? strlen(src) : src_len ); + + while ( dest < dest_end && src < src_end ) + { + if ( *src == '\\' && src+1 < src_end ) + { + src++; + switch(*src++) + { + case '\\': *dest++ = htons('\\'); break; + case 'a': *dest++ = htons('\a'); break; + case 'b': *dest++ = htons('\b'); break; + case 'f': *dest++ = htons('\f'); break; + case 'n': *dest++ = htons('\n'); break; + case 'r': *dest++ = htons('\r'); break; + case 't': *dest++ = htons('\t'); break; + case 'v': *dest++ = htons('\v'); break; + + case 'm': + case 'M': + if ( src < src_end && *src == '{' ) + { + const char mode = src[-1]; + src++; + bmg_scan_mid_t scan; + for(;;) + { + while ( src < src_end && *src == ',' ) + src++; + if ( ScanBMGMID(&scan,0,src,src_end) < 1 ) + break; + src = scan.scan_end; + + PRINT("\\M{%x} : bmg=%d\n",scan.mid[0],bmg!=0); + + const bmg_item_t *dptr = 0; + if ( bmg && mode == 'm' ) + dptr = FindItemBMG(bmg,scan.mid[0]); + if ( !dptr && bmg_macros ) + dptr = FindItemBMG(bmg_macros,scan.mid[0]); + + if (!dptr) + { + char buf[16]; + snprintf(buf,sizeof(buf),"\\%c{%x}",mode,scan.mid[0]); + ccp src = buf; + while ( *src && dest < dest_end ) + *dest++ = htons(*src++); + } + else if ( dptr->text ) + { + const u16 *copy = dptr->text; + const u16 *copy_end = copy + dptr->len; + while ( copy < copy_end && dest < dest_end ) + *dest++ = *copy++; + } + if ( *src != ',' ) + break; + } + if ( *src == '}' ) + src++; + } + break; + + case 'x': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + for(;;) + { + char *end; + u16 code = strtoul(src,&end,16); + src = end; + if ( dest < dest_end ) + *dest++ = htons(code); + if ( !have_brace || *src != ',' ) + break; + src++; + } + if ( have_brace && *src == '}' ) + src++; + } + break; + + case 'z': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + char * end; + const u16 code1 = strtoul(src,&end,16); + src = end; + uint total_words = code1+0x100 >> 9; + if ( dest + total_words <= dest_end ) + { + *dest++ = htons(0x1a); + *dest++ = htons(code1); + u64 xcode = 0; + if ( *src == ',' ) + { + xcode = strtoull(src+1,&end,16); + src = end; + } + switch( total_words & 3 ) + { + case 3 & 3: write_be16(dest,xcode); dest += 1; break; + case 4 & 3: write_be32(dest,xcode); dest += 2; break; + case 5 & 3: write_be48(dest,xcode); dest += 3; break; + case 6 & 3: write_be64(dest,xcode); dest += 4; break; + } + while ( total_words > 6 ) + { + total_words -= 4; + if ( *src == ',' ) + { + xcode = strtoull(src+1,&end,16); + src = end; + } + else + xcode = 0; + write_be64(dest,xcode); + dest += 4; + } + while ( *src == ',' ) + { + strtoull(src+1,&end,16); + src = end; + } + } + if ( have_brace && *src == '}' ) + src++; + } + break; + + case 'u': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + for(;;) // >>>>> comma list support since v1.44 <<<<< + { + char * end; + u32 code = strtoul(src,&end,16); + src = end; + if ( dest + 4 <= dest_end ) + { + *dest++ = htons(0x001a); + *dest++ = htons(0x0801); + *dest++ = htons(code>>16); + *dest++ = htons(code); + } + if ( !have_brace || *src != ',' ) + break; + src++; + } + if ( have_brace && *src == '}' ) + src++; + } + break; + + case 'c': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + + u16 code = 0; + if ( *src >= '0' && *src <= '9' ) + { + char *end; + code = strtoul(src,&end,16); + src = end; + } + else + { + char keyname[50], *kp = keyname; + while (isalnum((int)*src)) + { + if ( kp < keyname + sizeof(keyname) - 1 ) + *kp++ = *src; + src++; + } + *kp = 0; + + const KeywordTab_t *key = ScanKeyword(0,keyname,bmg_color); + if (key) + code = key->id; + else + code = strtoul(keyname,0,16); + } + if ( have_brace && *src == '}' ) + src++; + + if ( dest + 4 <= dest_end ) + { + *dest++ = htons(0x001a); + *dest++ = htons(0x0800); + *dest++ = htons(0x0001); + *dest++ = htons(code); + } + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + src--; + u16 code = 0; + int idx; + for ( idx = 0; idx < 3 && *src >= '0' && *src <= '7'; idx++ ) + code = code << 3 | *src++ - '0'; + *dest++ = htons(code); + } + break; + + default: + *dest++ = htons('\\'); + src--; + break; + } + } + else + *dest++ = htons(ScanUTF8AnsiChar(&src)); + } + + return dest - buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static unsigned long int strtoul16 +( + const u16 * ptr16, + u16 ** end16, + int base +) +{ + DASSERT(ptr16); + + char buf[100]; + char * dest = buf; + const u16 * p16 = ptr16; + while ( dest < buf + sizeof(buf)-1 ) + { + int ch = be16(p16++); + if ( ch >= '0' && ch <= '9' + || ch >= 'a' && ch <= 'f' + || ch >= 'A' && ch <= 'F' + || ch == 'x' + || ch == 'X' + || isspace(ch) + ) + { + *dest++ = ch; + } + else + break; + } + DASSERT( dest < buf + sizeof(buf) ); + *dest = 0; + + char *end; + unsigned long num = strtoul(buf,&end,base); + if (end16) + *end16 = (u16*)ptr16 + (end-buf); + return num; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u16 * skip_ctrl16 ( const u16 * ptr ) +{ + DASSERT(ptr); + for(;;) + { + const u16 ch = be16(ptr); + if ( !ch || ch > ' ' ) + return (u16*)ptr; + ptr++; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static u16 * ScanRange16U32 +( + const u16 * arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..2) + u32 * p_n1, // first return value (valid pointer) + u32 * p_n2, // second return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +) +{ + DASSERT(arg); + DASSERT(p_n1); + DASSERT(p_n2); + TRACE("ScanRange16U32()\n"); + + int stat = 0; + + u32 n1 = ~(u32)0, n2 = 0; + arg = skip_ctrl16(arg); + + if ( be16(arg) == '-' ) + n1 = min; + else + { + u16 * end; + u32 num = strtoul16(arg,&end,0); + if ( arg == end ) + goto abort; + + stat = 1; + arg = skip_ctrl16(end); + n1 = num; + } + + if ( be16(arg) != '-' ) + { + stat = 1; + n2 = n1; + goto abort; + } + + arg = skip_ctrl16(arg+1); + u16 * end; + n2 = strtoul16(arg,&end,0); + if ( end == arg ) + n2 = max; + stat = 2; + arg = end; + + abort: + + if ( stat > 0 ) + { + if ( n1 < min ) + n1 = min; + if ( n2 > max ) + n2 = max; + } + + if ( !stat || n1 > n2 ) + { + stat = 0; + n1 = ~(u32)0; + n2 = 0; + } + + if (p_stat) + *p_stat = stat; + *p_n1 = n1; + *p_n2 = n2; + + arg = skip_ctrl16(arg); + + TRACE("END ScanRange16U32() stat=%u, n=%u..%u\n",stat,n1,n2); + return (u16*)arg; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanSectionsBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bmg_sect_list_t * ScanSectionsBMG ( cvp data, uint size ) +{ + //--- initialize + + if ( !data || size < sizeof(bmg_header_t) || memcmp(data,BMG_MAGIC,8) ) + return 0; + + bmg_sect_list_t sl; + memset(&sl,0,sizeof(sl)); + sl.header = (bmg_header_t*)data; + sl.source_size = ntohl(sl.header->size); + sl.n_sections = ntohl(sl.header->n_sections); + + FastBuf_t fb; + InitializeFastBufAlloc(&fb,10*sizeof(bmg_sect_info_t)); + + bmg_sect_info_t si; + + + //--- scan sections + + u8 *data_end = (u8*)data + size; + bmg_section_t *sect = (bmg_section_t*)(sl.header+1); + for(;;) + { + if ( (u8*)sect + sizeof(*sect) > data_end ) + break; + + memset(&si,0,sizeof(si)); + si.offset = (u8*)sect - (u8*)data; + si.sect_size = si.real_size = ntohl(sect->size); + if ( (u8*)sect + si.real_size > data_end ) + si.real_size = data_end - (u8*)sect; + + si.sect = sect; + si.info = "?"; + + if (!memcmp(sect->magic,BMG_INF_MAGIC,sizeof(sect->magic))) + { + sl.pinf = (bmg_inf_t*)sect; + si.head_size = sizeof(bmg_inf_t); + si.elem_size = ntohs(sl.pinf->inf_size); + si.n_elem_head = ntohs(sl.pinf->n_msg); + si.known = true; + si.supported = true; + si.info = "offset & attributes"; + } + else if (!memcmp(sect->magic,BMG_DAT_MAGIC,sizeof(sect->magic))) + { + sl.pdat = (bmg_dat_t*)sect; + si.head_size = sizeof(bmg_dat_t); + si.known = true; + si.supported = true; + si.info = "string pool"; + } + else if (!memcmp(sect->magic,BMG_STR_MAGIC,sizeof(sect->magic))) + { + sl.pstr = (bmg_str_t*)sect; + si.head_size = sizeof(bmg_str_t); + si.known = true; + si.supported = false; + si.info = "secondary string pool"; + } + else if (!memcmp(sect->magic,BMG_MID_MAGIC,sizeof(sect->magic))) + { + sl.pmid = (bmg_mid_t*)sect; + si.head_size = sizeof(bmg_mid_t); + si.elem_size = sizeof(u32); + si.n_elem_head = ntohs(sl.pinf->n_msg); + si.known = true; + si.supported = true; + si.info = "message ids"; + } + else if (!memcmp(sect->magic,BMG_FLW_MAGIC,sizeof(sect->magic))) + { + sl.pflw = (bmg_flw_t*)sect; + si.known = true; + } + else if (!memcmp(sect->magic,BMG_FLI_MAGIC,sizeof(sect->magic))) + { + sl.pfli = (bmg_fli_t*)sect; + si.known = true; + } + + if (si.elem_size) + { + si.n_elem = ( si.real_size - si.head_size ) / si.elem_size; + if ( si.n_elem_head && si.n_elem > si.n_elem_head ) + si.n_elem = si.n_elem_head; + } + + if ( si.head_size && si.head_size < si.real_size ) + { + si.is_empty = true; + const u8 *ptr = (u8*)sect + si.head_size; + const u8 *end = (u8*)sect + si.real_size; + while ( ptr < end ) + if ( *ptr++ ) + { + si.is_empty = false; + break; + } + } + + sect = (bmg_section_t*)( (u8*)sect + si.real_size ); + AppendFastBuf(&fb,&si,sizeof(si)); + sl.n_info++; + } + + + //--- finalize + + memset(&si,0,sizeof(si)); + AppendFastBuf(&fb,&si,sizeof(si)); + + mem_t mem = GetFastBufMem(&fb); + sl.this_size = sizeof(sl) + mem.len; + bmg_sect_list_t *dest = MALLOC(sl.this_size); + memcpy(dest,&sl,sizeof(sl)); + memcpy(dest->info,mem.ptr,mem.len); + ResetFastBuf(&fb); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +const bmg_sect_info_t * SearchSectionBMG +( + const bmg_sect_list_t *sl, // valid, created by ScanSectionsBMG() + cvp search, // magic to search + bool abbrev, // if not found: allow abbreviations _> strcmp() + bool icase // if not found: ignore case and try again +) +{ + DASSERT(sl); + + // make sure, that magic is null terminated + char magic[5]; + memcpy(magic,search,4); + magic[4] = 0; + + + //--- search exact + + const bmg_sect_info_t *si; + for ( si = sl->info; si->sect; si++ ) + if (!memcmp(magic,si->sect->magic,4)) + return si; + + + //--- search abbrev + + const uint mlen = strlen(magic); + if (!mlen) + abbrev = false; + + if (abbrev) + { + for ( si = sl->info; si->sect; si++ ) + if (!memcmp(magic,si->sect->magic,mlen)) + return si; + } + + + //--- search icase + + if (icase) + { + uint i; + for ( i = 0; i < 4; i++ ) + magic[i] = tolower((int)magic[i]); + + const bmg_sect_info_t *abbrev_found = 0; + char buf[5]; + + for ( si = sl->info; si->sect; si++ ) + { + for ( i = 0; i < 4; i++ ) + buf[i] = tolower((int)si->sect->magic[i]); + + if (!memcmp(magic,buf,4)) + return si; + if ( abbrev && !memcmp(magic,buf,mlen) ) + abbrev_found = si; + } + return abbrev_found; + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_raw_section_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bmg_raw_section_t * GetRawSectionBMG ( bmg_t *bmg, cvp magic ) +{ + DASSERT(bmg); + DASSERT(magic); + + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + if (!memcmp(raw->magic,magic,sizeof(raw->magic))) + { + PRINT("GetRawSectionBMG(%s), found\n",PrintID(magic,4,0)); + return raw; + } + + PRINT("GetRawSectionBMG(%s), not found\n",PrintID(magic,4,0)); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_raw_section_t * CreateRawSectionBMG ( bmg_t *bmg, cvp magic ) +{ + bmg_raw_section_t **raw; + for ( raw = &bmg->first_raw; *raw; raw = &(*raw)->next ) + if (!memcmp((*raw)->magic,magic,sizeof((*raw)->magic))) + { + PRINT("CreateRawSectionBMG(%s), found\n",PrintID(magic,4,0)); + return (*raw); + } + + DASSERT(raw); + PRINT("CreateRawSectionBMG(%s), create\n",PrintID(magic,4,0)); + bmg_raw_section_t *p = CALLOC(1,sizeof(*p)); + memcpy(p->magic,magic,sizeof(p->magic)); + InitializeFastBuf(&p->data,sizeof(p->data)); + return *raw = p; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// attribute helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetAttribBMG +( + const bmg_t * bmg, // pointer to bmg + bmg_item_t * item // dest item +) +{ + DASSERT(bmg); + DASSERT(item); + + item->slot = BMG_NO_PREDEF_SLOT; + item->attrib_used = bmg->attrib_used; + memcpy(item->attrib,bmg->attrib,sizeof(item->attrib)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyAttribBMG +( + const bmg_t * dest, // pointer to destination bmg + bmg_item_t * dptr, // dest item + const bmg_item_t * sptr // source item +) +{ + DASSERT(dest); + DASSERT(dptr); + DASSERT(sptr); + + DASSERT( dest->attrib_used <= BMG_ATTRIB_SIZE ); + DASSERT( sptr->attrib_used <= BMG_ATTRIB_SIZE ); + + if ( sptr->slot != BMG_NO_PREDEF_SLOT ) + dptr->slot = sptr->slot; + + if ( opt_bmg_force_attrib ) + { + //dptr->attrib_used = dest->attrib_used; + memcpy(dptr->attrib,bmg_force_attrib,dptr->attrib_used); + } + else if ( sptr->attrib_used ) + { + if ( dest->attrib_used <= sptr->attrib_used ) + dptr->attrib_used = dest->attrib_used; + else + { + dptr->attrib_used = sptr->attrib_used; + memset( dptr->attrib+dptr->attrib_used, 0, BMG_ATTRIB_SIZE - sptr->attrib_used ); + } + memcpy(dptr->attrib,sptr->attrib,dptr->attrib_used); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static char * scan_attrib ( u8 *dest, uint size, ccp src ) +{ + DASSERT(dest); + DASSERT( size <= BMG_ATTRIB_SIZE ); + memset(dest,0,BMG_ATTRIB_SIZE); + + while ( *src == ' ' || *src == '\t' ) + src++; + const bool have_brackets = *src == '['; + if ( have_brackets ) + src++; + + int idx = 0, add_idx = 0; + for(;;) + { + while ( *src == ' ' || *src == '\t' ) + src++; + + if ( *src == ',' ) + { + src++; + idx++; + add_idx = 0; + } + else if ( *src == '/' ) + { + src++; + idx = ALIGN32(idx+1,4); + add_idx = 0; + } + else if ( *src == '%' ) + { + src++; + char *end; + u32 num = str2ul(src,&end,16); + if ( src == end ) + break; + + idx = ALIGN32(idx+add_idx,4); + noPRINT("U32[0x%02x/%02x]: %8x\n",idx,size,num); + + char buf[4]; + write_be32(buf,num); + const int max_copy = (int)size - (int)idx; + if ( max_copy > 0 ) + memcpy(dest+idx,buf, max_copy < 4 ? max_copy : 4 ); + idx += 3; + add_idx = 1; + src = end; + } + else + { + char *end; + u8 num = str2ul(src,&end,16); + if ( src == end ) + break; + + idx += add_idx; + noPRINT("U8[0x%02x/%02x]: %8x\n",idx,size,num); + if ( idx < size ) + dest[idx] = num; + add_idx = 1; + src = end; + } + } + //HexDump16(stderr,0,0,dest,size); + + if ( have_brackets && *src == ']' ) + src++; + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void copy_param +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + DASSERT(dest); + DASSERT(src); + + if ( !dest->param_defined && src->param_defined ) + { + memcpy(dest->attrib,src->attrib,sizeof(dest->attrib)); + dest->inf_size = src->inf_size; + dest->have_mid = src->have_mid; + dest->attrib_used = src->attrib_used; + dest->use_color_names = src->use_color_names; + dest->use_mkw_messages = src->use_mkw_messages; + dest->param_defined = true; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_item_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// special values for bmg_item_t::text +u16 bmg_null_entry[] = {0}; + +////////////////////////////////////////////////////////////////////////////// + +void FreeItemBMG( bmg_item_t * bi ) +{ + DASSERT(bi); + if (bi->text) + { + if ( bi->alloced_size && !isSpecialEntryBMG(bi->text) ) + FREE( bi->text); + bi->text = bmg_null_entry; + } + bi->len = bi->alloced_size = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignItemText16BMG +( + bmg_item_t * bi, // valid item + const u16 * text16, // text to store + int len // length of 'text16'. + // if -1: detect length by finding + // first NULL value. +) +{ + DASSERT(bi); + if ( !text16 || isSpecialEntryBMG(text16) ) + { + FreeItemBMG(bi); + bi->text = (u16*)text16; + return; + } + + if ( len < 0 ) + { + const u16 * ptr = text16; + while (*ptr) + ptr++; + len = ptr - text16; + } + + if ( bi->text && bi->alloced_size ) + { + if ( len+1 <= bi->alloced_size ) + { + memcpy(bi->text,text16,len*sizeof(*bi->text)); + bi->text[len] = 0; + bi->len = len; + return; + } + FREE(bi->text); + } + + bi->alloced_size = len + 1; + bi->len = len; + bi->text = CALLOC(bi->alloced_size,sizeof(*bi->text)); + memcpy(bi->text,text16,len*sizeof(*bi->text)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignItemScanTextBMG +( + bmg_item_t * bi, // valid item + ccp utf8, // UTF-8 text to scan + int len // length of 'utf8'. + // if -1: detect length with strlen() +) +{ + DASSERT(bi); + + if (!utf8) + AssignItemText16BMG(bi,0,0); + else + { + const uint bufsize = 10000; + u16 buf[bufsize]; + len = ScanString16BMG(buf,bufsize,utf8,len,0); + AssignItemText16BMG(bi,buf,len); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignItemTextBMG +( + bmg_item_t * bi, // valid item + ccp utf8, // UTF-8 text to store + int len // length of 'utf8'. + // if -1: detect length with strlen() +) +{ + DASSERT(bi); + + if (!utf8) + AssignItemText16BMG(bi,0,0); + else + { + u16 buf[10000]; + u16 *ptr = buf; + u16 *end = buf + sizeof(buf)/sizeof(*buf); + + if ( len < 0 ) + { + while ( ptr < end ) + { + ulong code = ScanUTF8AnsiChar(&utf8); + if (!code) + break; + *ptr++ = htons(code); + } + } + else + { + ccp utf8_end = utf8 + len; + while ( utf8 < utf8_end && ptr < end ) + *ptr++ = htons(ScanUTF8AnsiCharE(&utf8,utf8_end)); + } + AssignItemText16BMG(bi,buf,ptr-buf); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsItemEqualBMG +( + const bmg_item_t * bi1, // NULL or valid item + const bmg_item_t * bi2 // NULL or valid item +) +{ + if ( bi1 == bi2 ) + return true; + if ( !bi1 || !bi2 ) + return false; + + return bi1->len == bi2->len + && bi1->attrib_used == bi2->attrib_used + && !memcmp(bi1->text,bi2->text,bi1->len*sizeof(*bi2->text)) + && !memcmp(bi1->attrib,bi2->attrib,bi1->attrib_used); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint find_item_helper ( const bmg_t * bmg, u32 mid, bool * found ) +{ + DASSERT(bmg); + DASSERT(found); + + int beg = 0; + int end = bmg->item_used - 1; + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + const bmg_item_t * item = bmg->item + idx; + if ( mid < item->mid ) + end = idx - 1 ; + else if ( mid > item->mid ) + beg = idx + 1; + else + { + *found = true; + return idx; + } + } + + *found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_item_t * FindItemBMG ( const bmg_t * bmg, u32 mid ) +{ + DASSERT(bmg); + + bool found; + uint idx = find_item_helper(bmg,mid,&found); + DASSERT( idx < bmg->item_used || idx == bmg->item_used && !found ); + + return found ? bmg->item + idx : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_item_t * FindAnyItemBMG ( const bmg_t * bmg, u32 mid ) +{ + DASSERT(bmg); + + bool found; + uint idx = find_item_helper(bmg,mid,&found); + DASSERT( idx < bmg->item_used || idx == bmg->item_used && !found ); + + bmg_item_t * item = bmg->item + idx; + if (!found) + { + bmg_item_t * item_end = bmg->item + bmg->item_used; + while ( item < item_end && item->mid < mid ) + item++; + } + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_item_t * InsertItemBMG + ( bmg_t *bmg, u32 mid, u8 *attrib, uint attrib_used, bool *old_item ) +{ + DASSERT(bmg); + DASSERT( attrib_used <= BMG_ATTRIB_SIZE ); + + if (opt_bmg_force_attrib) + { + attrib_used = bmg->attrib_used; + attrib = bmg_force_attrib; + } + else if ( attrib_used > bmg->attrib_used ) + attrib_used = bmg->attrib_used; + + bmg->param_defined = true; // don't accept modifications on patch + + //if ( attrib && attrib_used ) HexDump16(stderr,0,0x100,attrib,attrib_used); + + bool found; + uint idx = find_item_helper(bmg,mid,&found); + DASSERT( idx < bmg->item_used || idx == bmg->item_used && !found ); + + if (old_item) + *old_item = found; + + if (found) + { + bmg_item_t * item = bmg->item + idx; + if ( attrib && attrib_used ) + { + item->attrib_used = bmg->attrib_used; + memcpy(item->attrib,bmg->attrib,sizeof(item->attrib)); + memcpy(item->attrib,attrib,attrib_used); + } + return item; + } + + DASSERT( bmg->item_used <= bmg->item_size ); + if ( bmg->item_used == bmg->item_size ) + { + bmg->item_size += 1000; + bmg->item = REALLOC(bmg->item,bmg->item_size*sizeof(*bmg->item)); + } + + bmg_item_t * item = bmg->item + idx; + memmove( item+1, item, (bmg->item_used-idx) * sizeof(*bmg->item) ); + bmg->item_used++; + DASSERT( idx < bmg->item_used ); + memset(item,0,sizeof(*item)); + item->mid = mid; + item->slot = BMG_NO_PREDEF_SLOT; + + memcpy(item->attrib,bmg->attrib,sizeof(item->attrib)); + if ( attrib && attrib_used ) + { + item->attrib_used = bmg->attrib_used; + memcpy(item->attrib,attrib,attrib_used); + } + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DeleteItemBMG ( bmg_t *bmg, u32 mid ) +{ + DASSERT(bmg); + + bmg_item_t *dptr = FindItemBMG(bmg,mid); + if (dptr) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(bmg,dptr); + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool HavePredifinedSlots ( bmg_t *bmg ) +{ + DASSERT(bmg); + + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + for ( bi = bmg->item; bi < bi_end; bi++ ) + if ( bi->text && bi->slot != BMG_NO_PREDEF_SLOT ) + return bmg->have_predef_slots = true; + return bmg->have_predef_slots = false; +} + +/////////////////////////////////////////////////////////////////////////////// + +u16 * ModifiyItem +( + // return a fixed 'ptr' + bmg_item_t * bi, // valid item, prepare text modification + const u16 * ptr // NULL or a pointer within 'bi->text' +) +{ + DASSERT(bi); + + if (!bi->alloced_size) + { + const uint size = (bi->len+1) * sizeof(*bi->text); + u16 *text = MALLOC(size); + memcpy(text,bi->text,size); + if (ptr) + ptr = text + ( ptr - bi->text ); + bi->text = text; + bi->alloced_size = bi->len; + } + return (u16*)ptr; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MKW hooks /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int GetTrackIndexBMG0 ( uint idx, int result_on_invalid ) +{ + static const u8 track_pos[BMG_N_TRACK] = + { + 8, 1, 2, 4, 0, 5, 6, 7, 9,15,11, 3, 14,10,12,13, + 16,20,25,26, 27,31,23,18, 21,30,29,17, 24,22,19,28 + }; + + return idx < BMG_N_TRACK + ? track_pos[idx] + : idx < BMG_N_CT_TRACK + ? idx + : result_on_invalid; +} + +int (*GetTrackIndexBMG) ( uint idx, int result_on_invalid ) = GetTrackIndexBMG0; + +/////////////////////////////////////////////////////////////////////////////// + +static int GetArenaIndexBMG0 ( uint idx, int result_on_invalid ) +{ + static const u8 arena_pos[BMG_N_ARENA] = { 1,0,3,2,4, 7,8,9,5,6 }; + + return idx < BMG_N_ARENA + ? arena_pos[idx] + : result_on_invalid; +} + +int (*GetArenaIndexBMG) ( uint idx, int result_on_invalid ) = GetArenaIndexBMG0; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// bmg encoding support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t TabEncodingBMG[] = +{ + // if opt==1: not implemented yet + { BMG_ENC_CP1252, "CP-1252", "CP1252", 0 }, + { BMG_ENC_CP1252, "1", 0, 0 }, + + { BMG_ENC_UTF16BE, "UTF-16BE", "UTF16BE", 0 }, + { BMG_ENC_UTF16BE, "2", 0, 0 }, + + { BMG_ENC_SHIFT_JIS, "SHIFT-JIS", "SHIFTJIS", 0 }, + { BMG_ENC_SHIFT_JIS, "3", "SJIS", 0 }, + + { BMG_ENC_UTF8, "UTF-8", "UTF8", 0 }, + { BMG_ENC_UTF8, "4", 0, 0 }, + + { BMG_ENC__UNDEFINED, "AUTO", "UNDEFINED", 0 }, + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +ccp GetEncodingNameBMG ( int encoding, ccp return_if_invalid ) +{ + switch (encoding) + { + case BMG_ENC_CP1252: return "CP1252"; + case BMG_ENC_UTF16BE: return "UTF-16/be"; + case BMG_ENC_SHIFT_JIS: return "Shift-JIS"; + case BMG_ENC_UTF8: return "UTF-8"; + } + return return_if_invalid; +} + +//----------------------------------------------------------------------------- + +int CheckEncodingBMG ( int encoding, int return_if_invalid ) +{ + return encoding >= BMG_ENC__MIN && encoding <= BMG_ENC__MAX + ? encoding : return_if_invalid; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup/reset bmg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AssignEncodingBMG ( bmg_t * bmg, int encoding ) +{ + if ( encoding >= BMG_ENC__MIN && encoding <= BMG_ENC__MAX ) + bmg->encoding = encoding; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignInfSizeBMG ( bmg_t * bmg, uint inf_size ) +{ + bmg->param_defined = true; + + if ( inf_size < 4 || inf_size > BMG_INF_LIMIT ) + { + ERROR0(ERR_WARNING, + "INF data size is outside allowed range 4..%u => ignored: %s\n", + BMG_INF_LIMIT, bmg->fname ); + return; + } + + if ( inf_size > BMG_INF_MAX_SIZE ) + ERROR0(ERR_WARNING, + "INF data size is %u bytes, but only %u bytes are supported." + " The additional bytes are always assumed to be NULL: %s", + inf_size, BMG_INF_MAX_SIZE, bmg->fname ); + + bmg->inf_size = opt_bmg_inf_size ? opt_bmg_inf_size : inf_size; + bmg->attrib_used = bmg->inf_size - 4; + if ( bmg->attrib_used > sizeof(bmg->attrib) ) + bmg->attrib_used = sizeof(bmg->attrib); + if (opt_bmg_def_attrib) + memcpy(bmg->attrib,bmg_def_attrib,bmg->attrib_used); + + if ( inf_size != 8 ) + { + // not MKW => change defaults + if ( bmg->use_color_names == 2 ) + bmg->use_color_names = 1; + if ( bmg->use_mkw_messages == 2 ) + bmg->use_mkw_messages = 1; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeBMG ( bmg_t * bmg ) +{ + ASSERT( MID_VERSUS_POINTS >= MID_PARAM_BEG + && MID_VERSUS_POINTS < MID_PARAM_END ); + + DASSERT(bmg); + memset(bmg,0,sizeof(*bmg)); + bmg->have_mid = 1; + bmg->encoding = BMG_ENC__DEFAULT; + bmg->use_slots = opt_bmg_use_slots; + bmg->use_raw_sections = opt_bmg_use_raw_sections; + + bmg->unknown_inf_0c = BMG_INF_DEFAULT_0C; + bmg->unknown_mid_0a = BMG_MID_DEFAULT_0A; + bmg->unknown_mid_0c = BMG_MID_DEFAULT_0C; + + //-- setup for MKW + + if (opt_bmg_support_mkw) + { + bmg->use_color_names = 1; + bmg->use_mkw_messages = 1; + AssignInfSizeBMG(bmg,BMG_INF_STD_SIZE); + + if ( !opt_bmg_def_attrib && bmg->attrib_used == 4 ) + write_be32(bmg->attrib,BMG_INF_STD_ATTRIB); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetBMG ( bmg_t * bmg ) +{ + DASSERT(bmg); + + bmg_item_t * bi = bmg->item; + bmg_item_t * bi_end = bi + bmg->item_used; + while ( bi < bi_end ) + FreeItemBMG(bi++); + + FREE(bmg->item); + + if (bmg->data_alloced) + FREE(bmg->data); + FREE(bmg->raw_data); + FreeString(bmg->fname); + + bmg_raw_section_t *raw = bmg->first_raw; + while (raw) + { + ResetFastBuf(&raw->data); + bmg_raw_section_t *del = raw; + raw = raw->next; + FREE(del); + } + + memset(bmg,0,sizeof(*bmg)); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int CompareU32 ( const u32 *a, const u32 *b ) +{ + return *a < *b ? -1 : *a > *b; +} + +//----------------------------------------------------------------------------- + +static u32 FindMajority32 ( cvp first, uint n_elem, uint delta ) +{ + DASSERT( first || !n_elem ); + + if ( n_elem < 3 ) + return n_elem ? *(u32*)first : 0; + + u32 *buf = MALLOC(n_elem*sizeof(*buf)); + + uint i; + for ( i = 0; i < n_elem; i++ ) + { + buf[i] = *(u32*)first; + first = (u32*)( (u8*)first + delta ); + } + //HexDump16(stderr,0,0,buf,64); + qsort(buf,n_elem,sizeof(*buf),(qsort_func)CompareU32); + + uint max_val = 0, max_count = 0; + const u32 *ptr = buf; + const u32 *end = ptr + n_elem; + while ( ptr < end ) + { + const u32 *beg = ptr; + const u32 val = *ptr++; + while ( ptr < end && *ptr == val ) + ptr++; + if ( ptr - beg > max_count ) + { + max_count = ptr - beg; + max_val = val; + } + } + + FREE(buf); + return max_val; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void scan_raw_cp1252 + ( bmg_item_t *bi, const u8 *start, const u8 *end, FastBuf_t *fb ) +{ + u8 buf[4]; + const u8 *ptr; + for ( ptr = start; ptr < end && *ptr; ) + { + u16 code = *ptr++; + if ( code >= 0x80 && code < 0xa0 ) + code = TableCP1252_80[code-0x80]; + if ( code == 0x1a ) + { + const uint len = *ptr++; + if ( len > 2 ) + { + buf[0] = 0; + buf[1] = 0x1a; + buf[2] = len+1; + AppendFastBuf(fb,buf,3); + AppendFastBuf(fb,ptr,len-2); + if (!(len&1)) + AppendFastBuf(fb,buf,1); + ptr += len - 2; + } + } + else + { + code = htons(code); + AppendFastBuf(fb,&code,2); + } + } + + const mem_t mem = GetFastBufMem(fb); + AssignItemText16BMG(bi,(u16*)mem.ptr,mem.len/2); +} + +//----------------------------------------------------------------------------- + +static void scan_raw_utf16 ( bmg_item_t *bi, const u8 *start, const u8 *end ) +{ + const u8 *ptr; + for ( ptr = start; ptr < end; ptr += 2 ) + { + if (!ptr[0]) + { + if (!ptr[1]) + break; + if ( ptr[1] == 0x1a ) + { + ptr += 2; + const uint skip = *ptr + 1 & 0x1e; + if ( skip > 4 ) + ptr += skip - 4; + } + } + } + + AssignItemText16BMG( bi, (u16*)start, ( ptr - start )/2 ); +} + +//----------------------------------------------------------------------------- + +static void scan_raw_shift_jis + ( bmg_item_t *bi, const u8 *start, const u8 *end, FastBuf_t *fb ) +{ + u8 buf[4]; + const u8 *ptr; + for ( ptr = start; ptr < end; ) + { + int code = ScanShiftJISChar(&ptr); + if (!code) + break; + if ( code == 0x1a ) + { + const uint len = *ptr++; + if ( len > 2 ) + { + buf[0] = 0; + buf[1] = 0x1a; + buf[2] = len+1; + AppendFastBuf(fb,buf,3); + AppendFastBuf(fb,ptr,len-2); + if (!(len&1)) + AppendFastBuf(fb,buf,1); + ptr += len - 2; + } + } + else if ( code >= 0 ) + { + code = htons(code); + AppendFastBuf(fb,&code,2); + } + } + + const mem_t mem = GetFastBufMem(fb); + AssignItemText16BMG(bi,(u16*)mem.ptr,mem.len/2); +} + +//----------------------------------------------------------------------------- + +static void scan_raw_utf8 + ( bmg_item_t *bi, const u8 *start, const u8 *end, FastBuf_t *fb ) +{ + u8 buf[4]; + const u8 *ptr; + for ( ptr = start; ptr < end && *ptr; ) + { + u32 code = ScanUTF8AnsiCharE((ccp*)&ptr,(ccp)end); + if ( code >= 0x80 && code < 0xa0 ) + code = TableCP1252_80[code -0x80]; + if ( code == 0x1a ) + { + const uint len = *ptr++; + if ( len > 2 ) + { + buf[0] = 0; + buf[1] = 0x1a; + buf[2] = len+1; + AppendFastBuf(fb,buf,3); + AppendFastBuf(fb,ptr,len-2); + if (!(len&1)) + AppendFastBuf(fb,buf,1); + ptr += len - 2; + } + } + else + { + code = htons(code); + AppendFastBuf(fb,&code,2); + } + } + + const mem_t mem = GetFastBufMem(fb); + AssignItemText16BMG(bi,(u16*)mem.ptr,mem.len/2); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawBMG ( bmg_t * bmg ) +{ + DASSERT(bmg); + DASSERT(bmg->data); + + const bmg_header_t * bh = (bmg_header_t*)bmg->data; + DASSERT(!memcmp(bh->magic,BMG_MAGIC,sizeof(bh->magic))); + + u32 data_size = ntohl(bh->size); + if ( data_size > bmg->data_size ) + { + ERROR0(ERR_WARNING, + "BMG is %u bytes shorter than announced: %s", + data_size - bmg->data_size, bmg->fname ); + data_size = bmg->data_size; + } + + const bmg_sect_list_t *sl = ScanSectionsBMG(bmg->data,data_size); + if ( !sl || !sl->header ) + return ERROR0(ERR_INVALID_DATA,"Invalid BMG data: %s",bmg->fname); + + + //--- check encoding + + bmg->encoding = bh->encoding; + switch (bmg->encoding) + { + case BMG_ENC_CP1252: + case BMG_ENC_UTF16BE: + case BMG_ENC_SHIFT_JIS: + case BMG_ENC_UTF8: + //>>> ok! <<< + break; + + default: + if (!opt_bmg_force_count) + return ERROR0(ERR_ERROR, + "Can't read BMG with encoding #%u (%s): %s\n", + bmg->encoding, GetEncodingNameBMG(bmg->encoding,"?"), + bmg->fname ); + ERROR0(ERR_WARNING, + "Try to read BMG with unsupported encoding #%u (%s): %s\n", + bmg->encoding, GetEncodingNameBMG(bmg->encoding,"?"), + bmg->fname ); + } + + + //--- iterate sections + + const bmg_sect_info_t *si; + for ( si = sl->info; si->sect; si++ ) + { + if ( si->real_size < si->sect_size ) + ERROR0(ERR_WARNING, + "Size of BMG section %s set to %u bytes (%u lesser than announced): %s\n", + PrintID(si->sect->magic,4,0), + si->real_size, si->sect_size - si->real_size, bmg->fname ); + + if ( !si->supported ) + { + bmg_raw_section_t *raw = CreateRawSectionBMG(bmg,si->sect->magic); + DASSERT(raw); + AssignFastBuf( &raw->data, si->sect->data, + si->real_size - sizeof(bmg_section_t) ); + raw->total_size = si->real_size; + PRINT("SCAN/RAW SECTION »%s« data: %u = 0x%x, total: %u = 0x%x\n", + PrintID(raw->magic,4,0), + GetFastBufLen(&raw->data), GetFastBufLen(&raw->data), + raw->total_size, raw->total_size ); + } + } + + PRINT("%p %p %p\n",sl->pinf,sl->pdat,sl->pmid); + const bmg_inf_t *pinf = bmg->inf = sl->pinf; + const bmg_dat_t *pdat = bmg->dat = sl->pdat; + const bmg_mid_t *pmid = bmg->mid = sl->pmid; + + if ( !sl->pinf || !sl->pdat ) + return ERROR0(ERR_INVALID_DATA,"Corrupted BMG file: %s\n",bmg->fname); + + bmg->unknown_inf_0c = ntohl(pinf->unknown_0c); + + uint max_item; + bmg->have_mid = pmid != 0; + if (bmg->have_mid) + { + bmg->unknown_mid_0a = ntohs(pmid->unknown_0a); + bmg->unknown_mid_0c = ntohl(pmid->unknown_0c); + + max_item = ( ntohl(pmid->size) - sizeof(*pmid) ) / sizeof(*pmid->mid); + if ( max_item > ntohs(pmid->n_msg) ) + max_item = ntohs(pmid->n_msg); + PRINT("BMG with MID1: N=%u [inf=%u,mid=%u], %s\n", + max_item, ntohs(pinf->n_msg), ntohs(pmid->n_msg), bmg->fname ); + } + else + { + max_item = ntohs(pinf->n_msg); + PRINT("BMG without MID1: N=%u, %s\n",max_item,bmg->fname); + } + + const uint max_offset = ( ntohl(pdat->size) - sizeof(*pdat) ) + / sizeof(*pdat->text_pool); + const u8 *text_end = pdat->text_pool + max_offset; + + const u8 *raw_inf = (u8*)pinf->list; + const uint src_inf_size = ntohs(pinf->inf_size); + AssignInfSizeBMG(bmg,src_inf_size); + + + //--- find default attributes + + if (!opt_bmg_def_attrib) + { + uint idx; + for ( idx = 0; idx < bmg->attrib_used; idx += 4 ) + { + const u32 major = FindMajority32(raw_inf+idx+4,max_item,src_inf_size); + noPRINT("-> IDX: %2u -> %8x\n",idx,ntohl(major)); + memcpy(bmg->attrib+idx,&major,4); + } + //HexDump16(stderr,8,0x10,bmg->attrib,bmg->attrib_used); + } + + + //--- decode raw files + + struct { FastBuf_t b; char space[500]; } fb_mgr; + InitializeFastBuf(&fb_mgr,sizeof(fb_mgr)); + #if HAVE_PRINT + uint ins_count = 0, empty_count = 0; + #endif + + uint prev_mid = 0; + uint slot, unsort_count = 0, ffff_count = 0; + for ( slot = 0; slot < max_item; slot++, raw_inf += src_inf_size ) + { + bmg_inf_item_t *item = (bmg_inf_item_t*)raw_inf; + const u32 offset = ntohl(item->offset); + + u32 mid = pmid ? ntohl(pmid->mid[slot]) : slot; + if ( mid == 0xffff && !offset ) + ffff_count++; + if ( mid < prev_mid ) + unsort_count++; + prev_mid = mid; + + #ifdef TEST + if ( offset >= max_offset ) + return ERROR0(ERR_INVALID_DATA, + "Invalid pointer at file offset 0x%zx (INF item #%u): %s\n", + (u8*)&pinf->list[slot].offset - bmg->data, slot, bmg->fname); + #else + if ( offset >= max_offset ) + return ERROR0(ERR_INVALID_DATA,"Corrupted BMG file: %s\n",bmg->fname); + #endif + + #if HAVE_PRINT0 + { + bmg_item_t *bi = FindItemBMG(bmg,mid); + if (bi) + PRINT("MID EXISTS: %x, slot:%x\n",mid,slot); + } + #endif + + bmg_item_t *bi = InsertItemBMG(bmg,mid,item->attrib,bmg->attrib_used,0); + DASSERT(bi); + bi->slot = slot < BMG_NO_PREDEF_SLOT ? slot : BMG_NO_PREDEF_SLOT; + + if (!offset) + { + #if HAVE_PRINT + empty_count++; + #endif + AssignItemText16BMG(bi,bmg_null_entry,0); + continue; + } + + switch (bmg->encoding) + { + case BMG_ENC_CP1252: + ClearFastBuf(&fb_mgr.b); + scan_raw_cp1252( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + + case BMG_ENC_UTF16BE: + scan_raw_utf16( bi, pdat->text_pool + offset, text_end ); + break; + + case BMG_ENC_SHIFT_JIS: + ClearFastBuf(&fb_mgr.b); + scan_raw_shift_jis( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + + case BMG_ENC_UTF8: + ClearFastBuf(&fb_mgr.b); + scan_raw_utf8( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + + default: + // fallback for cast opt_bmg_force_count>0 + DASSERT(opt_bmg_force_count); + ClearFastBuf(&fb_mgr.b); + scan_raw_cp1252( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + } + + #if HAVE_PRINT + ins_count++; + #endif + } + + #if HAVE_PRINT + PRINT("BMG: %u+%u=%u/%u messages inserted => %u | n(unsort)=%d, n(ffff)=%d\n", + empty_count, ins_count, empty_count+ins_count, + max_item, bmg->item_used, + unsort_count, ffff_count ); + #endif + ResetFastBuf(&fb_mgr.b); + + bmg->have_predef_slots = pmid && max_item > 1 + && ( unsort_count > 1 || ffff_count > 1 ); + + if (bmg->have_predef_slots) + { + bmg_item_t *bi = FindItemBMG(bmg,0xffff); + if ( bi && isSpecialEntryBMG(bi->text) ) + AssignItemText16BMG(bi,0,0); + } + else + { + // clear slots settings + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + for ( bi = bmg->item; bi < bi_end; bi++ ) + bi->slot = BMG_NO_PREDEF_SLOT; + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int (*AtHookBMG) ( bmg_t * bmg, ccp line, ccp line_end ) = 0; + +/////////////////////////////////////////////////////////////////////////////// + +static char * GetNext ( ccp ptr, ccp end ) +{ + DASSERT(ptr); + DASSERT(end); + + while ( ptr < end && ( *ptr == ' ' || *ptr == '\t' || *ptr == '\r' ) ) + ptr++; + return (char*)ptr; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xbuf]] GetText ( FastBuf_t *fb ccp ptr, ccp end, uint *len ) + +static char * GetText ( char *buf, uint bufsize, ccp ptr, ccp end, uint *len ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + + char *dest = buf; + char *dest_end = buf + bufsize - 1; + + ptr = GetNext(ptr,end); + + if ( ptr < end && ( *ptr == '\'' || *ptr == '\"' ) ) + { + const char quote = *ptr++; + while ( ptr < end && *ptr != quote ) + { + if ( dest < dest_end ) + *dest++ = *ptr; + ptr++; + } + if ( *ptr == quote ) + ptr++; + } + else + { + while ( ptr < end && (uchar)*ptr > ' ' ) + { + if ( dest < dest_end ) + *dest++ = *ptr; + ptr++; + } + } + + *dest = 0; + if (len) + *len = dest - buf; + return GetNext(ptr,end); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextBMG ( bmg_t * bmg ) +{ + DASSERT(bmg); + TRACE("SCAN BMG/TEXT: %s\n",bmg->fname); + bmg->is_text_src = true; + + ccp ptr = (ccp)bmg->data; + ccp end = ptr + bmg->data_size; + ptr += GetTextBOMLen(ptr,bmg->data_size); + + ccp bmg_name = 0; +// [[xbuf]] + char xbuf[2*BMG_MSG_BUF_SIZE]; + enumError err_stat = ERR_OK; + u32 cond = 0; + + ParamField_t assign; + InitializeParamField(&assign); + assign.free_data = true; + + for(;;) + { + while ( ptr < end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr >= end ) + break; + + ccp start = ptr = GetNext(ptr,end); + while ( ptr < end && *ptr != '\r' && *ptr != '\n' ) + ptr++; + + if ( *start == '@' ) + { + if (AtHookBMG) + { + const int stat = AtHookBMG(bmg,start,ptr); + if ( stat < -1 ) + continue; + if ( stat >= 0 ) + { + err_stat = stat; + break; + } + } + + start = GetNext(start+1,ptr); + const bool silent = start < ptr && *start == '!'; + if (silent) + start = GetNext(start+1,ptr); + + if ( *start == '?' ) + { + // SYNTAX/IF: '@?' MID_COND + + u32 mid; + ScanMidBMG(bmg,&mid,0,0,start+1,end,(char**)&start); + if ( ptr >= end ) + break; + cond = mid; + continue; + } + + if ( *start == '=' ) + { + // SYNTAX/MATCH: '@=' PATTERN [':'] LINE + + uint len; +// [[xbuf]] + start = GetText(xbuf,sizeof(xbuf),start+1,ptr,&len); + if (len) + { +// [[xbuf]] + PRINT("MATCH |%s|\n",xbuf); + if (!bmg_name) + { + bmg_name = GetNameBMG(bmg,0); + PRINT("NAME |%s|\n",bmg_name); + } + +// [[xbuf?]] + if (MatchPattern(xbuf,bmg_name,'/')) + { + ptr = GetNext(start,ptr); + if ( *ptr == ':' ) + ptr++; + } + } + continue; + } + + if ( *start == '$' ) + { + // SYNTAX/ECHO: '@$' TEXT_TO_EOL + + if (opt_bmg_allow_print) + { + start++; + printf("#%.*s\n",(int)(ptr-start),start); + } + continue; + } + + const bool close_current = *start == '>'; + if ( close_current || *start == '<' ) + { + // SYNTAX/GOTO: '@' ['!'] '>' FILE + // SYNTAX/INCLUDE: '@' ['!'] '<' FILE + + uint len; +// [[xbuf]] + start = GetText(xbuf,sizeof(xbuf),start+1,ptr,&len); + if ( len && bmg->recurse_depth < opt_bmg_max_recurse ) + { + PRINT("BMG/%s: %s\n",close_current?"GOTO":"INCLUDE",xbuf); + bmg_t bmg2; +// [[xbuf?]] + const enumError err + = LoadBMG(&bmg2,true,bmg->fname,xbuf,silent?FM_SILENT:0); + if ( err == ERR_OK ) + PatchOverwriteBMG(bmg,&bmg2,true); + ResetBMG(&bmg2); + if ( close_current && err != ERR_CANT_OPEN ) + break; + } + continue; + } + + char namebuf[100], *nptr = namebuf; + while ( start < ptr && *start > ' ' && *start < 'z' && *start != '=' ) + { + if ( nptr < namebuf + sizeof(namebuf) - 1 ) + *nptr++ = toupper((int)*start); + start++; + } + *nptr = 0; + start = GetNext(start,ptr); + + + //--- settings without mandatory '=' + + const bool have_equal = start < ptr && *start == '='; + if (have_equal) + start = GetNext(start+1,ptr); + + if (!strcmp(namebuf,"SECTION")) + { + bmg->current_raw = 0; + uint len = ScanEscapedString(namebuf,sizeof(namebuf), + start, ptr-start, false, -1, 0 ); + if (!len) + continue; + if ( len > 4 ) + { + ERROR0(ERR_WARNING, + "@SECTION name exceeds 4 [%u] characters: %s\n", + len, namebuf ); + continue; + } + while ( len < 4 ) + namebuf[len++] = 0; + PRINT("@SECTION »%s« %02x %02x %02x %02x\n", + PrintID(namebuf,4,0), + namebuf[0], namebuf[1], namebuf[2], namebuf[3] ); + bmg->current_raw = CreateRawSectionBMG(bmg,namebuf); + DASSERT(bmg->current_raw); + ClearFastBuf(&bmg->current_raw->data); + continue; + } + + if (!strcmp(namebuf,"X")) + { + if (!bmg->current_raw) + continue; + + // skip addr + + ccp p = start; + while (isalnum((int)*p)) + p++; + if ( p > start && *p == ':' ) + start = p+1; + + for (;;) + { + start = GetNext(start,ptr); + if ( start >= ptr || *start == ':' ) + break; + char *end; + uint num = str2ul(start,&end,16); + if ( end == start ) + break; + start = end; + AppendCharFastBuf(&bmg->current_raw->data,num); + } + continue; + } + + + //--- settings with mandatory '=' + + if ( !have_equal || start >= ptr ) + continue; + + noPRINT("PARAM: %s = |%.*s|\n",namebuf,(int)(ptr-start),start); + if (!strcmp(namebuf,"ENCODING")) + AssignEncodingBMG(bmg,str2l(start,0,10)); + else if (!strcmp(namebuf,"BMG-MID")) + bmg->have_mid = str2ul(start,0,10) > 0; + else if (!strcmp(namebuf,"INF-SIZE")) + AssignInfSizeBMG(bmg,str2ul(start,0,10)); + else if (!strcmp(namebuf,"DEFAULT-ATTRIBS")) + { + noPRINT("--- SCAN ATTRIB: %.*s\n",(int)(ptr-start),start); + scan_attrib(bmg->attrib,bmg->attrib_used,start); + //HEXDUMP16(0,0,bmg->attrib,bmg->attrib_used); + } + else if (!strcmp(namebuf,"UNKNOWN-INF32-0C")) + bmg->unknown_inf_0c = str2ul(start,0,10); + else if (!strcmp(namebuf,"UNKNOWN-MID16-0A")) + bmg->unknown_mid_0a = str2ul(start,0,10); + else if (!strcmp(namebuf,"UNKNOWN-MID32-0C")) + bmg->unknown_mid_0c = str2ul(start,0,10); + continue; + } + + + //--- scan message ids + + u32 mid1 = 0, mid2 = 0, mid3 = 0; + const int mid_stat + = ScanMidBMG(bmg,&mid1,&mid2,&mid3,start,end,(char**)&start); + + if ( mid_stat <= 0 ) // not a valid message line + continue; + + //--- scan predifined slot + + bmg_slot_t slot = BMG_NO_PREDEF_SLOT; + if ( *start == '@' ) + { + char *end; + uint num = str2l(++start,&end,16); + PRINT0("SLOT[%#x] = %#x\n",mid1,num); + if ( end > start && num < BMG_NO_PREDEF_SLOT ) + slot = num; + start = GetNext(end,ptr); + } + + + //--- scan attributes + + u8 attrib_buf[BMG_ATTRIB_SIZE]; + memcpy(attrib_buf,bmg->attrib,sizeof(attrib_buf)); + uint attrib_used = 0; + + if ( *start == '[' ) + { + ccp aptr = scan_attrib(attrib_buf,bmg->attrib_used,start+1); + aptr = GetNext(aptr,ptr); + if ( *aptr == ']' ) + { + attrib_used = bmg->attrib_used; + start = GetNext(aptr+1,ptr); + } + } + + if ( *start == '/' || *start == '~' ) + { + if ( *start == '~' ) + { + attrib_used = bmg->attrib_used; + start = GetNext(start+1,ptr); + + if ( *start == '[' ) + scan_attrib(attrib_buf,attrib_used,start); + else + { + memset(attrib_buf,0,sizeof(attrib_buf)); + const u32 attr = str2ul(start,0,10); + write_be32(attrib_buf,attr); + } + } + + bmg_item_t * bi = InsertItemBMG(bmg,mid1,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->slot = slot; + AssignItemText16BMG(bi,bmg_null_entry,0); + if (mid2) + { + bi = InsertItemBMG(bmg,mid2,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->slot = slot; + AssignItemText16BMG(bi,bmg_null_entry,0); + } + if (mid3) + { + bi = InsertItemBMG(bmg,mid3,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->slot = slot; + AssignItemText16BMG(bi,bmg_null_entry,0); + } + continue; + } + + if ( *start == ':' ) + { + start = GetNext(start+1,end); + ccp ptr = start; + while ( ptr < end && isalnum((int)*ptr) ) + ptr++; + + if ( ptr > start ) + { + u32 *tab[] = { &mid1, &mid2, &mid3, 0 }, **tptr; + for ( tptr = tab; *tptr; tptr++ ) + { + const u32 mid = **tptr; + if (mid) + { + DeleteItemBMG(bmg,mid); + + char midbuf[20]; + snprintf(midbuf,sizeof(midbuf),"%x",mid); + noPRINT("BMG ASSIGN: %5s = |%.*s|\n", + midbuf,(int)(ptr-start),start); + InsertParamField(&assign,midbuf,false,mid,MEMDUP(start,ptr-start)); + } + } + } + continue; + } + + if ( *start != '=' ) // not a valid line + continue; + +// [[xbuf]] + // assume that xbuf is large enough + // => make only simple and silent buffer overrun tests + +// [[xbuf?]] + char * in_beg = xbuf + sizeof(xbuf)/2; + char * in_end = xbuf + sizeof(xbuf) - 0x10; + DASSERT( in_beg < in_end ); + char * in_ptr = in_beg; + + for(;;) + { + if ( ptr[-1] == '\r' ) + ptr--; + start++; + if ( *start == ' ' ) + start++; + if ( start > ptr ) + start = ptr; + + int len = ptr - start; + if ( len > in_end - in_ptr ) + len = in_end - in_ptr; + memcpy(in_ptr,start,len); + in_ptr += len; + + while ( ptr < end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr == end || *ptr != '+' ) + break; + + start = ptr; + while ( ptr < end && *ptr != '\n' ) + ptr++; + } + *in_ptr = 0; + + const int in_len = in_ptr - in_beg; + noPRINT("%x,%x,%x |%.*s|\n",mid1,mid2,mid3,in_len,in_beg); +// [[xbuf]] ScanFastBuf16BMG(); + u16 * dest_buf = (u16*)xbuf; + const uint len16 + = ScanString16BMG( dest_buf, sizeof(xbuf)/sizeof(u16), in_beg, in_len, bmg ); + + bmg_item_t * bi = InsertItemBMG(bmg,mid1,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->cond = cond; + bi->slot = slot; + AssignItemText16BMG(bi,dest_buf,len16); + if (mid2) + { + bi = InsertItemBMG(bmg,mid2,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->cond = cond; + bi->slot = slot; + AssignItemText16BMG(bi,dest_buf,len16); + } + if (mid3) + { + bi = InsertItemBMG(bmg,mid3,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->cond = cond; + bi->slot = slot; + AssignItemText16BMG(bi,dest_buf,len16); + } + } + + + //--- manage assignments + + bool dirty = assign.used > 0; + while (dirty) + { + dirty = false; + ParamFieldItem_t *pfi = assign.field, *end; + for ( end = pfi + assign.used; pfi < end; pfi++ ) + { + if (!pfi->num) + continue; + + bmg_item_t *bi = FindItemBMG(bmg,pfi->num); + noPRINT("## %5x = %5s = |%s| -> found=%d, len=%d\n", + pfi->num, pfi->key, (ccp)pfi->data, bi!=0, bi ? bi->len : -1 ); + pfi->key = 0; + if ( bi && bi->len ) + { + pfi->num = 0; + continue; + } + + bmg_scan_mid_t scan; + ScanBMGMID(&scan,bmg,(ccp)pfi->data,0); + if ( scan.status < 1 ) + { + pfi->num = 0; + continue; + } + + uint i; + for ( i = 0; i < scan.n_mid; i++ ) + { + bmg_item_t *src = FindItemBMG(bmg,scan.mid[i]); + if ( src && src->text ) + { + bmg_item_t *dest + = InsertItemBMG(bmg,pfi->num,src->attrib,src->attrib_used,0); + DASSERT(dest); + + // src is invalid now => search again + src = FindItemBMG(bmg,scan.mid[i]); + if (src) + { + AssignItemText16BMG(dest,src->text,src->len); + dest->attrib_used = src->attrib_used; + memcpy(dest->attrib,src->attrib,dest->attrib_used); + dirty = true; + pfi->num = 0; + } + break; + } + } + } + } + ResetParamField(&assign); + + + //--- terminate + + if (bmg->data_alloced) + FREE(bmg->data); + bmg->data = 0; + bmg->data_size = 0; + + return err_stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanBMG(), LoadBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp fname, // NULL or filename for error messages + const u8 * data, // data / if NULL: use internal data + size_t data_size // size of 'data' if data not NULL +) +{ + if (initialize) + InitializeBMG(bmg); + else if (data) + ResetBMG(bmg); + + if (data) + { + bmg->data = (u8*)data; + bmg->data_size = data_size; + } + + if (fname) + bmg->fname = STRDUP(fname); + + const bmg_header_t * bh = (bmg_header_t*)bmg->data; + if ( !memcmp(bh->magic,BMG_MAGIC,sizeof(bh->magic)) + && ntohl(bh->size) <= bmg->data_size ) + return ScanRawBMG(bmg); + + ccp ptr = (ccp)bmg->data + GetTextBOMLen(bmg->data,bmg->data_size); + if ( !memcmp(ptr,BMG_TEXT_MAGIC,4) ) + return ScanTextBMG(bmg); + + return ERROR0(ERR_INVALID_DATA,"No BMG file: %s\n",bmg->fname); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp parent_fname, // NULL or filename of parent for dir-extract + ccp fname, // filename of source + FileMode_t file_mode // open modes +) +{ + DASSERT(bmg); + DASSERT(fname); + PRINT("LoadBMG(%p,init=%d) fname=%s\n", bmg, initialize, fname ); + + if (initialize) + InitializeBMG(bmg); + else + ResetBMG(bmg); + + if (!strcmp(fname,"0")) + { + bmg->is_text_src = false; + bmg->fname = STRDUP(fname); + return ERR_OK; + } + + char path[PATH_MAX]; + if ( *fname != '/' && parent_fname ) + { + ccp slash = strrchr(parent_fname,'/'); + char *dest; + if (slash) + dest = StringCopySM(path,sizeof(path)-2,parent_fname,slash-parent_fname); + else + dest = StringCopyS(path,sizeof(path)-2,parent_fname); + *dest++ = '/'; + StringCopyE(dest,path+sizeof(path),fname); + fname = path; + } + noPRINT(">>> fname: |%s|d\n",fname); + + u8 *data; + uint size; + enumError err = OpenReadFile( fname, 0, file_mode, 0,0, + &data, &size, &bmg->fname, &bmg->fatt ); + if ( err == ERR_OK ) + { + bmg->data = data; + bmg->data_size = size; + bmg->data_alloced = true; + return ScanBMG(bmg,false,0,0,0); + } + + FREE(data); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_create_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void grow_bi_list ( bmg_create_t *bc, uint slot ) +{ + if ( bc->bi_size <= slot ) + { + const uint esize = sizeof(*bc->bi_list); + const uint old_size = bc->bi_size; + const uint byte_size = ( slot + slot/8 + 10 ) * esize; + bc->bi_size = GetGoodAllocSize(byte_size) / esize; + bc->bi_list = REALLOC( bc->bi_list, bc->bi_size * esize ); + memset( bc->bi_list + old_size, 0, ( bc->bi_size - old_size ) * esize ); + PRINT("GROW BI-LIST: %u/%u/%u\n",slot,bc->bi_used,bc->bi_size); + } +} + +//----------------------------------------------------------------------------- + +void InitializeCreateBMG ( bmg_create_t *bc, bmg_t *bmg ) +{ + DASSERT(bc); + DASSERT(bmg); + + memset(bc,0,sizeof(*bc)); + bc->bmg = bmg; + bc->have_mid = opt_bmg_mid == OFFON_AUTO + ? bmg->have_mid : opt_bmg_mid >= OFFON_ON; + + uint n_predef = 0, other_count = 0; + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + for ( bi = bmg->item; bi < bi_end; bi++ ) + if (bi->text) + { + if ( bi->slot == BMG_NO_PREDEF_SLOT ) + other_count++; + else if ( n_predef <= bi->slot ) + n_predef = bi->slot + 1; + } + + bmg->have_predef_slots = n_predef >= 0; + if (n_predef) + { + grow_bi_list( bc, n_predef + other_count ); + + uint next = n_predef; + for ( bi = bc->bmg->item; bi < bi_end; bi++ ) + if (bi->text) + { + uint slot = bi->slot; + if ( slot >= n_predef || bc->bi_list[slot] ) + { + slot = next++; + grow_bi_list(bc,slot); + } + PRINT0(">>> ASSIGN %4x = %4x\n",slot,bi->mid); + bc->bi_list[slot] = bi; + } + + bc->bi_used = next; + PRINT("BI-LIST: %u/%u/%u\n",next,bc->bi_used,bc->bi_size); + } + + uint n_item = bc->bi_used; + if ( n_item < bmg->item_used ) + n_item = bmg->item_used; + + InitializeFastBufAlloc( &bc->inf, n_item * bmg->inf_size ); + InitializeFastBufAlloc( &bc->dat, n_item * 50 ); + InitializeFastBufAlloc( &bc->mid, n_item * sizeof(u32) ); + + if (bc->have_mid) + { + uint i; + for ( i = 0; i < bc->bi_used; i++ ) + AppendBE32FastBuf(&bc->mid,0xffff); + } +} + +//----------------------------------------------------------------------------- + +void ResetCreateBMG ( bmg_create_t *bc ) +{ + DASSERT(bc); + + FREE(bc->bi_list); + ResetFastBuf(&bc->inf); + ResetFastBuf(&bc->dat); + ResetFastBuf(&bc->mid); + memset(bc,0,sizeof(0)); +} + +//----------------------------------------------------------------------------- + +bmg_item_t * GetFirstBI ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + + bc->bi = bc->bmg->item; + bc->bi_end = bc->bi + bc->bmg->item_used; + bc->slot = -1; + return GetNextBI(bc); +} + +//----------------------------------------------------------------------------- + +bmg_item_t * GetNextBI ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + + bmg_t *bmg = bc->bmg; + + bmg_item_t *bi = 0; + int slot = bc->slot + 1; + if (bc->bi_list) + { + while ( slot < bc->bi_used && !bc->bi_list[slot] ) + slot++; + if ( slot < bc->bi_used ) + { + bi = bc->bi_list[slot]; + PRINT0(">>> BI/LIST: %4x: %04x\n",slot,bi->mid); + } + } + else if ( bc->bi < bc->bi_end ) + { + bi = bc->bi++; + PRINT0(">>> BI/DIRECT: %4x: %04x\n",slot,bi->mid); + } + + if (!bi) + { + bc->slot = -1; + return 0; + } + + //--- found: assign data + + bc->slot = slot; + + u32 *pmid = (u32*)WriteFastBuf( &bc->mid, bc->slot*sizeof(u32), + 0, sizeof(u32) ); + DASSERT(pmid); + *pmid = htonl(bi->mid); + + const uint inf_size = bmg->inf_size; + bmg_inf_item_t *ii = (bmg_inf_item_t*)WriteFastBuf + ( &bc->inf, bc->slot * inf_size, 0, inf_size ); + DASSERT(ii); + if ( !isSpecialEntryBMG(bi->text) ) + ii->offset = htonl(GetFastBufLen(&bc->dat)); + memcpy(ii->attrib,bi->attrib,bmg->attrib_used); + + if ( bc->n_msg <= bc->slot ) + bc->n_msg = bc->slot + 1; + return bi; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void create_raw_cp1252 ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendCharFastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + const u16 *ptr = bi->text; + const u16 *end = ptr + bi->len; + while ( ptr < end ) + { + u16 code = ntohs(*ptr++); + if ( code == 0x1a ) + { + AppendCharFastBuf(&bc->dat,code); + uint len = *(u8*)ptr; + AppendCharFastBuf(&bc->dat,len-1); + AppendFastBuf(&bc->dat,(u8*)ptr+1,len-3); + ptr += (len-1)/2; + } + else if (code) + { + if ( code >= 0x100 ) + { + uint i; + for ( i = 0; i < 0x20; i++ ) + if ( TableCP1252_80[i] == code ) + { + code = i + 0x80; + break; + } + } + if ( code < 0x100 ) + AppendCharFastBuf(&bc->dat,code); + } + } + AppendCharFastBuf(&bc->dat,0); + } + } +} + +//----------------------------------------------------------------------------- + +static void create_raw_utf16 ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendBE16FastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + AppendFastBuf( &bc->dat, bi->text, bi->len*2 ); + AppendBE16FastBuf(&bc->dat,0); + } + } +} + +//----------------------------------------------------------------------------- + +static void create_raw_shift_jis ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendCharFastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + const u16 *ptr = bi->text; + const u16 *end = ptr + bi->len; + while ( ptr < end ) + { + const u16 code = ntohs(*ptr++); + if ( code == 0x1a ) + { + AppendCharFastBuf(&bc->dat,code); + uint len = *(u8*)ptr; + AppendCharFastBuf(&bc->dat,len-1); + AppendFastBuf(&bc->dat,(u8*)ptr+1,len-3); + ptr += (len-1)/2; + } + else + { + const int val = GetShiftJISChar(code); + if ( val >= 0x100 ) + AppendBE16FastBuf(&bc->dat,val); + else if ( val > 0 ) + AppendCharFastBuf(&bc->dat,val); + } + } + AppendCharFastBuf(&bc->dat,0); + } + } +} + +//----------------------------------------------------------------------------- + +static void create_raw_utf8 ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendCharFastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + const u16 *ptr = bi->text; + const u16 *end = ptr + bi->len; + while ( ptr < end ) + { + u16 code = ntohs(*ptr++); + if ( code == 0x1a ) + { + AppendCharFastBuf(&bc->dat,code); + uint len = *(u8*)ptr; + AppendCharFastBuf(&bc->dat,len-1); + AppendFastBuf(&bc->dat,(u8*)ptr+1,len-3); + ptr += (len-1)/2; + } + else if (code) + AppendUTF8CharFastBuf(&bc->dat,code); + } + AppendCharFastBuf(&bc->dat,0); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawBMG +( + bmg_t * bmg // pointer to valid BMG +) +{ + DASSERT(bmg); + TRACE("CreateRawBMG()\n"); + + + //--- setup bmg_create_t + + bmg_create_t bc; + InitializeCreateBMG(&bc,bmg); + PRINT("fb:inf: %s\n", GetFastBufStatus(&bc.inf) ); + PRINT("fb:dat: %s\n", GetFastBufStatus(&bc.dat) ); + PRINT("fb:mid: %s\n", GetFastBufStatus(&bc.mid) ); + + + //--- check encoding + + const int encoding = CheckEncodingBMG(opt_bmg_encoding,bmg->encoding); + switch (encoding) + { + case BMG_ENC_CP1252: + create_raw_cp1252(&bc); + break; + + case BMG_ENC_UTF16BE: + create_raw_utf16(&bc); + break; + + case BMG_ENC_SHIFT_JIS: + SetupGetShiftJISCache(); + create_raw_shift_jis(&bc); + break; + + case BMG_ENC_UTF8: + create_raw_utf8(&bc); + break; + + default: + return ERROR0(ERR_ERROR, + "Can't create BMG with encoding #%u (%s): %s\n", + encoding, GetEncodingNameBMG(encoding,"?"), bmg->fname ); + } + + + //--- count messages + +#if 0 + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + u32 n_msg = 0; + + for ( bi = bmg->item; bi < bi_end; bi++ ) + { + if (bi->text) + n_msg++; + } +#endif + + //--- count size of raw sections + + uint total_raw_size = 0, raw_count = 0; + if (bmg->use_raw_sections) + { + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + { + const uint good_size = sizeof(bmg_section_t) + GetFastBufLen(&raw->data); + if ( raw->total_size < good_size ) + raw->total_size = ALIGN32(good_size, raw->next ? opt_bmg_align : 4 ); + total_raw_size += raw->total_size; + raw_count++; + } + } + + + //--- calculate sizes + + const mem_t inf = GetFastBufMem(&bc.inf); + const mem_t mid = GetFastBufMem(&bc.mid); + const mem_t dat = GetFastBufMem(&bc.dat); + + const u32 inf_size = ALIGN32( sizeof(bmg_inf_t) + inf.len, opt_bmg_align ); + const u32 dat_size = ALIGN32( sizeof(bmg_dat_t) + dat.len, opt_bmg_align ); + const u32 mid_size = bc.have_mid + ? ALIGN32( sizeof(bmg_mid_t) + mid.len, opt_bmg_align ) : 0; + + const u32 total_size = sizeof(bmg_header_t) + + inf_size + dat_size + mid_size + + total_raw_size; + + PRINT("SIZE(%u msg): %zx + %x[/%x] + %x + %x = %x\n", + bc.n_msg, sizeof(bmg_header_t), + inf_size, bmg->inf_size, dat_size, mid_size, total_size ); + + + //--- alloc data + + FREE(bmg->raw_data); + bmg->raw_data = CALLOC(total_size,1); + bmg->raw_data_size = total_size; + + + //---- setup bmg header + + bmg_header_t *bh = (bmg_header_t*)bmg->raw_data; + bh->size = htonl(total_size); + bh->n_sections = htonl( ( bc.have_mid ? 3 : 2 ) + raw_count ); + bh->encoding = encoding; + memcpy(bh->magic,BMG_MAGIC,sizeof(bh->magic)); + + u8 *dest = (u8*)(bh+1); + DASSERT( dest <= bmg->raw_data + total_size ); + + + //---- setup inf section + + bmg_inf_t *pinf = (bmg_inf_t*)dest; + pinf->size = htonl(inf_size); + pinf->n_msg = htons(bc.n_msg); + pinf->inf_size = htons(bmg->inf_size); + pinf->unknown_0c = htonl(bmg->unknown_inf_0c); + memcpy(pinf->magic,BMG_INF_MAGIC,sizeof(pinf->magic)); + memcpy(pinf->list,inf.ptr,inf.len); + + dest += inf_size; + DASSERT( dest <= bmg->raw_data + total_size ); + + + //---- setup dat section + + bmg_dat_t * pdat = (bmg_dat_t*)dest; + pdat->size = htonl(dat_size); + memcpy(pdat->magic,BMG_DAT_MAGIC,sizeof(pdat->magic)); + memcpy(pdat->text_pool,dat.ptr,dat.len); + + dest += dat_size; + DASSERT( dest <= bmg->raw_data + total_size ); + + + //---- setup mid section + + if (bc.have_mid) + { + bmg_mid_t *pmid = (bmg_mid_t*)dest; + pmid->size = htonl(mid_size); + pmid->n_msg = htons(bc.n_msg); + pmid->unknown_0a = htons(bmg->unknown_mid_0a); + pmid->unknown_0c = htonl(bmg->unknown_mid_0c); + memcpy(pmid->magic,BMG_MID_MAGIC,sizeof(pmid->magic)); + memcpy(pmid->mid,mid.ptr,mid.len); + + dest += mid_size; + DASSERT( dest <= bmg->raw_data + total_size ); + } + + + //---- copy raw sections + + if (total_raw_size) + { + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + { + PRINT("SAVE/RAW SECTION »%s« data: %u = 0x%x, total: %u = 0x%x\n", + PrintID(raw->magic,4,0), + GetFastBufLen(&raw->data), GetFastBufLen(&raw->data), + raw->total_size, raw->total_size ); + + bmg_section_t *sect = (bmg_section_t*)dest; + memcpy(sect->magic,raw->magic,sizeof(sect->magic)); + sect->size = htonl(raw->total_size); + const mem_t mem = GetFastBufMem(&raw->data); + memcpy(sect->data,mem.ptr,mem.len); + + dest += raw->total_size; + DASSERT( dest <= bmg->raw_data + total_size ); + } + } + + + //--- clean + + DASSERT( dest == bmg->raw_data + total_size ); + + PRINT("fb:inf: %s\n", GetFastBufStatus(&bc.inf) ); + PRINT("fb:mid: %s\n", GetFastBufStatus(&bc.mid) ); + PRINT("fb:dat: %s\n", GetFastBufStatus(&bc.dat) ); + ResetCreateBMG(&bc); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + FileMode_t fmode, // create-file mode + bool set_time // true: set time stamps +) +{ + DASSERT(bmg); + DASSERT(fname); + PRINT("SaveRawBMG(%s,%d)\n",fname,set_time); + + + //--- create raw data + + enumError err = CreateRawBMG(bmg); + if (err) + return err; + DASSERT(bmg->raw_data); + DASSERT(bmg->raw_data_size); + + + //--- write to file + + File_t F; + err = CreateFile(&F,true,fname,fmode); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&bmg->fatt,0); + + if ( fwrite(bmg->raw_data,1,bmg->raw_data_size,F.f) != bmg->raw_data_size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintAttribBMG +( + char *buf, // destination buffer + uint buf_size, // size of 'buf', BMG_ATTRIB_BUF_SIZE is good + const u8 *attrib, // NULL or attrib to print + uint attrib_used, // used length of attrib + const u8 *def_attrib, // NULL or default attrib + bool force // true: always print '[]' +) +{ + DASSERT(buf); + DASSERT( buf_size >= 10 ); + DASSERT( attrib_used <= BMG_ATTRIB_SIZE); + + static u8 all0[BMG_ATTRIB_SIZE] = {0}; + + if ( !attrib + || !attrib_used +// [[BMG_ATTRIB_SIZE]] + || def_attrib && !memcmp(attrib,def_attrib,attrib_used) ) + { + if (force) + { + *buf++ = '['; + *buf++ = ']'; + } + *buf = 0; + return buf; + } + + char *buf_end = buf + buf_size - 2; + *buf++ = '['; + +// [[BMG_ATTRIB_SIZE]] + if (!memcmp(attrib,all0,attrib_used)) + *buf++ = '0'; + else + { + uint idx; + for ( idx = 0; idx < attrib_used; idx += 4 ) + { + if ( idx && buf < buf_end ) + *buf++ = '/'; + + u32 attrib_val = be32(attrib+idx); + ccp sep = ""; + while (attrib_val) + { + const uint val = (attrib_val >> 24) & 0xff; + if (val) + buf = snprintfE(buf,buf_end,"%s%x",sep,val); + else if ( *sep && buf < buf_end ) + *buf++ = *sep; + sep = ","; + attrib_val <<= 8; + } + } + while ( buf[-1] == '/' ) + buf--; + } + *buf++ = ']'; + *buf = 0; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_message + ( File_t *F, bmg_t *bmg, const bmg_item_t *bi, ccp mid_name ) +{ + DASSERT(bmg); + DASSERT(bi); + + if ( bmg->active_cond != bi->cond ) + { + bmg->active_cond = bi->cond; + fprintf(F->f,"@? %x\n",bmg->active_cond); + } + + ccp slot; + char slot_buf[10]; + if (!bmg->have_predef_slots) + slot = EmptyString; + else if ( bi->slot == BMG_NO_PREDEF_SLOT ) + slot = " "; + else + { + snprintf(slot_buf,sizeof(slot_buf)," @%04x",bi->slot); + slot = slot_buf; + } + + char attrib[100+BMG_ATTRIB_BUF_SIZE]; +// [[xbuf]] + char xbuf[BMG_MSG_BUF_SIZE]; + + const bool print_attrib + = !opt_bmg_no_attrib + && opt_bmg_single_line < 2 + && bi->attrib_used + && memcmp(bi->attrib,bmg->attrib,bmg->attrib_used); + + if ( print_attrib && !opt_bmg_inline_attrib ) + { + if ( bmg->attrib_used == 4 ) + { + // classic, compatible with all versions + fprintf(F->f,"%s\t~%s 0x%08x\r\n",mid_name,slot,be32(bi->attrib)); + } + else + { + // use new attrib vector + PrintAttribBMG( attrib, sizeof(attrib), + bi->attrib, bi->attrib_used, bmg->attrib, true ); + fprintf(F->f,"%s\t~%s %s\r\n",mid_name,slot,attrib); + } + } + + if ( print_attrib && opt_bmg_inline_attrib ) + { + char *dest = attrib + snprintfS(attrib,sizeof(attrib),"%s%s ",mid_name,slot); + dest = PrintAttribBMG( dest, attrib+sizeof(attrib)-dest, + bi->attrib, bi->attrib_used, bmg->attrib, false ); + *dest++ = ' '; + *dest = 0; + DASSERT( dest < attrib + sizeof(attrib) - 1 ); + } + else + if (*slot) + snprintf(attrib,sizeof(attrib),"%s%s ",mid_name,slot); + else + snprintf(attrib,sizeof(attrib),"%s\t",mid_name); + + if ( bi->text == bmg_null_entry ) + fprintf(F->f,"%s/\r\n",attrib); + else if ( bi->text ) + { + fputs(attrib,F->f); + const int use_colors = opt_bmg_colors != 1 + ? opt_bmg_colors + : bmg->use_color_names > 0; +// [[xbuf?]] + uint len = PrintString16BMG( xbuf, sizeof(xbuf), bi->text, bi->len, + BMG_UTF8_MAX, 0, use_colors ); +// [[xbuf?]] + ccp ptr = xbuf; + ccp end = ptr + len; + ccp sep = "="; + do + { + ccp start = ptr; + ccp temp_end = ptr + 72; + if ( temp_end > end ) + temp_end = end; + ccp good_break = 0; + if ( opt_bmg_single_line || opt_bmg_export ) + ptr = end; + + bool first_loop = true; + while ( ptr < temp_end && !good_break ) + { + for ( ; ptr <= temp_end; ptr++ ) + { + if ( ptr == end ) + { + good_break = ptr; + break; + } + + if ( *ptr == '\\' ) + { + if ( ++ptr >= temp_end ) + break; + if ( *ptr == 'n' ) + { + good_break = ++ptr; + break; + } + if ( *ptr != '\\' && ptr-start > 40 ) + { + good_break = ptr-1; + if (!first_loop) + break; + } + } + else if ( *ptr == ' ' && ptr-start > 40 ) + { + good_break = ptr; + if (!first_loop) + break; + } + } + temp_end = end; + first_loop = false; + } + if ( good_break && good_break < end - 8 ) + ptr = good_break; + + fprintf(F->f,"%s %.*s\r\n", sep, (int)(ptr-start), start ); + sep = "\t+"; + + } while ( ptr < end ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + FileMode_t fmode, // create-file mode + bool set_time, // true: set time stamps + bool force_numeric, // true: force numric MIDs + uint brief_count // >0: suppress syntax info + // >1: suppress all comments + // >2: suppress '#BMG' magic +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(bmg); + DASSERT(fname); + PRINT("SaveTextBMG(%s,%d)\n",fname,set_time); + bmg->active_cond = 0; + + enum { BRIEF_ALL, BRIEF_NO_SYNTAX, BRIEF_NO_COMMENT, BRIEF_NO_MAGIC }; + + bool already_printed[BMG__VIP_N]; + memset(already_printed,0,sizeof(already_printed)); + char mid_name[10], abuf[BMG_ATTRIB_BUF_SIZE]; + + + static ccp param_announce = + "# All parameters begin with '@'. Unknown parameters are ignored on scanning.\r\n"; + + static const char text_head[] = + { + "#BMG <<< The first 4 characters '#BMG' are the magic for a BMG text file.\r\n" + "# <<< Don't remove them!\r\n" + "#\r\n" + "# Details about BMG text files are available in the Web:\r\n" + "# * Syntax and Semantics: https://szs.wiimm.de/doc/bmg/text\r\n" + "# * The BMG file format: https://szs.wiimm.de/r/wiki/BMG\r\n" + "#\r\n" + "#\f\r\n" + "#------------------------------------------------------------------------------\r\n" + }; + + static const char text_param[] = + { + "%s" + "\r\n" + "# Define encoding of BMG: 1=CP1252, 2=UTF-16/be, 3=Shift-JIS, 4=UTF-8\r\n" + "@ENCODING = %u\r\n" + "\r\n" + "# Create »MID1« section: 0:off, 1=on\r\n" + "@BMG-MID = %u\r\n" + "\r\n" + "# Size of each element of section 'INF1' (MKW=8).\r\n" + "# This setting has impact to attributes and MKW features.\r\n" + "@INF-SIZE = 0x%02x\r\n" + "\r\n" + "# Default attribute values for this BMG (MKW=[1])\r\n" + "@DEFAULT-ATTRIBS = %s\r\n" + "\r\n" + "# Use MKW specific color names: 0=off, 1=auto, 2=on\r\n" + "@COLOR-NAMES = %u\r\n" + "\r\n" + "# Use MKW messages and track names: 0=off, 1=auto, 2=on\r\n" + "@MKW-MESSAGES = %u\r\n" + "\r\n" + "#\f\r\n" + "#------------------------------------------------------------------------------\r\n" + }; + + static const char unknown_param[] = + { + "%s" + "\r\n" + "# This part define values for unknown parameters of section headers.\r\n" + "@UNKNOWN-INF32-0C = %#10x # 32 bit value of section INF1 offset 0x0c\r\n" + "@UNKNOWN-MID16-0A = %#10x # 16 bit value of section MID1 offset 0x0a\r\n" + "@UNKNOWN-MID32-0C = %#10x # 32 bit value of section MID1 offset 0x0c\r\n" + "\r\n" + "#\f\r\n" + "#------------------------------------------------------------------------------\r\n" + }; + + + //--- open file + + File_t F; + enumError err = CreateFile(&F,true,fname,fmode); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&bmg->fatt,0); + + const bool print_full = brief_count == BRIEF_ALL && !opt_bmg_export; + if (print_full) + fwrite(text_head,1,sizeof(text_head)-1,F.f); + else if ( brief_count < BRIEF_NO_MAGIC ) + fprintf(F.f,"%s\r\n\r\n",BMG_TEXT_MAGIC); + + HavePredifinedSlots(bmg); + const int encoding = CheckEncodingBMG(opt_bmg_encoding,bmg->encoding); + + const bool print_settings // print only, if unusual + = encoding != BMG_ENC__DEFAULT + || bmg->attrib_used != 4 + || be32(bmg->attrib) != BMG_INF_STD_ATTRIB + || !bmg->have_mid + || !bmg->use_color_names + || !bmg->use_mkw_messages + ; + + if (print_settings) + { + PrintAttribBMG(abuf,sizeof(abuf),bmg->attrib,bmg->attrib_used,0,true); + if (print_full) + { + fprintf(F.f,text_param, + param_announce, + encoding, bmg->have_mid, bmg->inf_size, abuf, + bmg->use_color_names, bmg->use_mkw_messages ); + param_announce = ""; + } + else if ( brief_count < BRIEF_NO_MAGIC ) + fprintf(F.f,"@ENCODING = %u\r\n" + "@BMG-MID = %u\r\n" + "@INF-SIZE = 0x%02x\r\n" + "@DEFAULT-ATTRIBS = %s\r\n" + "@COLOR-NAMES = %u\r\n" + "@MKW-MESSAGES = %u\r\n" + "\r\n", + encoding, bmg->have_mid, bmg->inf_size, abuf, + bmg->use_color_names, bmg->use_mkw_messages ); + } + + if ( print_settings + || bmg->unknown_inf_0c != BMG_INF_DEFAULT_0C + || bmg->unknown_mid_0a != BMG_MID_DEFAULT_0A + || bmg->unknown_mid_0c != BMG_MID_DEFAULT_0C + ) + { + if (print_full) + fprintf(F.f,unknown_param, + param_announce, + bmg->unknown_inf_0c, + bmg->unknown_mid_0a, bmg->unknown_mid_0c ); + else if ( brief_count < BRIEF_NO_MAGIC ) + fprintf(F.f,"@UNKNOWN-INF32-0C = %#x\r\n" + "@UNKNOWN-MID16-0A = %#x\r\n" + "@UNKNOWN-MID32-0C = %#x\r\n" + "\r\n", + bmg->unknown_inf_0c, + bmg->unknown_mid_0a, bmg->unknown_mid_0c ); + } + + + //--- messages + + ccp intro = "\r\n"; + static const char intro_other[] = "\r\n#--- other messages\r\n\r\n"; + + const bool support_mkw + = opt_bmg_support_mkw + && !force_numeric + && !opt_bmg_export + && bmg->attrib_used == 4 + && be32(bmg->attrib) == BMG_INF_STD_ATTRIB + && bmg->have_mid + && !bmg->have_predef_slots; + + if (support_mkw) + { + //--- find track names + + int idx; + uint last_cup = 0; + for ( idx = 0; idx < BMG_N_TRACK; idx++ ) + { + const int tidx = GetTrackIndexBMG(idx,-1); + if ( tidx < 0 ) + continue; + + const bmg_item_t * bi1 = FindItemBMG(bmg,MID_TRACK1_BEG+tidx); + if (bi1) + { + const bmg_item_t * bi2 = FindItemBMG(bmg,MID_TRACK2_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + { + const uint cup = idx / 4 + '1'; + if ( brief_count < BRIEF_NO_COMMENT && last_cup != cup ) + { + if (!last_cup) + { + intro = intro_other; + fprintf(F.f,"\r\n#--- standard track names\r\n\r\n"); + } + else + fputs("\r\n",F.f); + last_cup = cup; + } + snprintf(mid_name,sizeof(mid_name), " T%c%c", cup, idx % 4 + '1' ); + print_message(&F,bmg,bi1,mid_name); + + already_printed[ bi1->mid - MID__VIP_BEG ] = true; + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + + if (opt_bmg_support_lecode) + { + bi2 = FindItemBMG(bmg,MID_LE_TRACK_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + else if (opt_bmg_support_ctcode) + { + bi2 = FindItemBMG(bmg,MID_CT_TRACK_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + } + } + } + + + //--- find battle names + + last_cup = 0; + for ( idx = 0; idx < BMG_N_ARENA; idx++ ) + { + const int tidx = GetArenaIndexBMG(idx,-1); + if ( tidx < 0 ) + continue; + + const bmg_item_t * bi1 = FindItemBMG(bmg,MID_ARENA1_BEG+tidx); + if (bi1) + { + const bmg_item_t * bi2 = FindItemBMG(bmg,MID_ARENA2_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + { + const uint cup = idx / 5 + '1'; + if ( brief_count < BRIEF_NO_COMMENT && last_cup != cup ) + { + if (!last_cup) + { + intro = intro_other; + fprintf(F.f,"\r\n#--- battle track names\r\n\r\n"); + } + else + fputs("\r\n",F.f); + last_cup = cup; + } + snprintf(mid_name,sizeof(mid_name), " U%c%c", cup, idx % 5 + '1' ); + print_message(&F,bmg,bi1,mid_name); + + already_printed[ bi1->mid - MID__VIP_BEG ] = true; + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + + if (opt_bmg_support_lecode) + { + bi2 = FindItemBMG(bmg,MID_LE_ARENA_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + else if (opt_bmg_support_ctcode) + { + bi2 = FindItemBMG(bmg,MID_CT_ARENA_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + } + } + } + + + //--- find chat strings + + const bmg_item_t * bi1 = FindAnyItemBMG(bmg,MID_CHAT_BEG); + const bmg_item_t * bi1end = FindAnyItemBMG(bmg,MID_CHAT_END); + if ( bi1 < bi1end ) + { + if ( brief_count < BRIEF_NO_COMMENT ) + { + intro = intro_other; + fprintf(F.f,"\r\n#--- online chat\r\n\r\n"); + } + + for ( ; bi1 < bi1end; bi1++ ) + { + DASSERT( bi1->mid >= MID_CHAT_BEG && bi1->mid <= MID_CHAT_END ); + snprintf(mid_name,sizeof(mid_name)," M%02u",bi1->mid-MID_CHAT_BEG+1); + print_message(&F,bmg,bi1,mid_name); + already_printed[bi1->mid-MID__VIP_BEG] = true; + } + } + } + + + //--- print messages not already printed + + struct sep_t { u32 mid; ccp info; }; + + static const struct sep_t sep_std_tab[] = + { + {0,0} + }; + + static const struct sep_t sep_mkw_tab[] = + { + { MID_ENGINE_BEG, "engines" }, + { MID_ENGINE_END, 0 }, + { MID_CHAT_BEG, "online chat" }, + { MID_CHAT_END, 0 }, + + { MID_RCUP_BEG, "racing cup names" }, + { MID_RCUP_END, 0 }, + { MID_TRACK1_BEG, "track names, 1. copy" }, + { MID_TRACK1_END, 0 }, + { MID_BCUP_BEG, "battle cup names" }, + { MID_BCUP_END, 0 }, + { MID_TRACK2_BEG, "track names, 2. copy" }, + { MID_TRACK2_END, 0 }, + + { MID_ARENA1_BEG, "arena names, 1. copy" }, + { MID_ARENA1_END, 0 }, + { MID_ARENA2_BEG, "arena names, 2. copy" }, + { MID_ARENA2_END, 0 }, + + { MID_PARAM_IDENTIFY, "identification for third party tools" }, + { MID_PARAM_IDENTIFY, 0 }, + + { MID_PARAM_BEG, "parameters for mkw-ana" }, + { MID_PARAM_END, 0 }, + + { MID_CT_TRACK_BEG, "CT-CODE: track names" }, + { MID_CT_TRACK_END, 0 }, + { MID_CT_CUP_BEG, "CT-CODE: cup names" }, + { MID_CT_CUP_END, 0 }, + { MID_CT_CUP_REF_BEG, "CT-CODE: track-to-cup reference" }, + { MID_CT_CUP_REF_END, 0 }, + + { MID_X_MESSAGE_BEG, "new messages for CT/LE-CODE" }, + { MID_X_MESSAGE_END, 0 }, + + { MID_LE_CUP_BEG, "LE-CODE: cup names" }, + { MID_LE_CUP_END, 0 }, + { MID_LE_TRACK_BEG, "LE-CODE: track names" }, + { MID_LE_TRACK_END, 0 }, + { MID_LE_CUP_REF_BEG, "LE-CODE: track-to-cup reference" }, + { MID_LE_CUP_REF_END, 0 }, + + {0,0} + }; + + #ifdef TEST + { + fflush(stdout); + const struct sep_t *sep; + for ( sep = sep_std_tab+1; sep->mid; sep++ ) + if ( sep[-1].mid > sep->mid ) + fprintf(stderr, + "Wrong order in sep_std_tab[%zu]:\n\t%04x %s\n\t%04x %s\n", + sep-sep_std_tab, sep[-1].mid, sep[-1].info, sep->mid, sep->info ); + for ( sep = sep_mkw_tab+1; sep->mid; sep++ ) + if ( sep[-1].mid > sep->mid ) + fprintf(stderr, + "Wrong order in sep_mkw_tab[%zu]:\n\t%04x %s\n\t%04x %s\n", + sep-sep_mkw_tab, sep[-1].mid, sep[-1].info, sep->mid, sep->info ); + fflush(stderr); + } + #endif + + const struct sep_t *sep_tab = support_mkw ? sep_mkw_tab : sep_std_tab; + const struct sep_t *sep = brief_count < BRIEF_NO_COMMENT ? sep_tab : 0; + + bmg_item_t * bi = bmg->item; + bmg_item_t * bi_end = bi + bmg->item_used; + for ( ; bi < bi_end; bi++ ) + { + const u32 mid = bi->mid; + if ( mid < MID__VIP_BEG + || mid >= MID__VIP_END + || !already_printed[mid-MID__VIP_BEG] + ) + { + if ( sep && sep->mid && mid >= sep->mid ) + { + sep++; + while ( sep->mid && mid >= sep->mid ) + sep++; + if ( sep > sep_tab+1 && sep[-2].mid == mid && sep[-2].info ) + fprintf(F.f,"\r\n#--- [%x] %s\r\n", mid, sep[-2].info ); + else if ( sep[-1].info ) + fprintf(F.f,"\r\n#--- [%x:%x] %s\r\n", + sep[-1].mid, sep->mid-1, sep[-1].info ); + else + fputs("\r\n",F.f); + intro = 0; + } + + if ( brief_count < BRIEF_NO_COMMENT && intro ) + { + fputs(intro,F.f); + intro = 0; + } + snprintf(mid_name,sizeof(mid_name),"%6x",bi->mid); + print_message(&F,bmg,bi,mid_name); + } + } + + + //--- raw sections + + if ( bmg->use_raw_sections && bmg->first_raw ) + { + XDump_t xd; + InitializeXDump(&xd); + SetupXDump(&xd,XDUMPC_DUMP); + xd.f = F.f; + xd.indent = 1; + xd.prefix = "@X"; + xd.eol = "\r\n"; + xd.format = XDUMPF_INT_1; + xd.print_format = false; + xd.print_summary= false; + + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + { + PRINT("HEXDUMP SECTION »%s« data: %u = 0x%x, total: %u = 0x%x\n", + PrintID(raw->magic,4,0), + GetFastBufLen(&raw->data), GetFastBufLen(&raw->data), + raw->total_size, raw->total_size ); + + char name[20]; + fprintf(F.f,"\r\n#\f\r\n#--------------------------------------" + "----------------------------------------\r\n\r\n" + "@SECTION \"%s\"\r\n\r\n", + PrintEscapedString( name, sizeof(name), + raw->magic, sizeof(raw->magic), + CHMD_ESC, '"', 0) ); + + const mem_t data = GetFastBufMem(&raw->data); + xd.assumed_size = data.len; + XDump(&xd,data.ptr,data.len,true); + } + } + + + //--- terminate + + if ( brief_count < 2 ) + fputs("\r\n",F.f); + ResetFile(&F,set_time); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanBMGMID() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanBMGMID +( + // returns 'scan->status' + + bmg_scan_mid_t *scan, // result, never NULL + bmg_t *bmg, // NULL or bmg to update parameters + ccp src, // source pointer + ccp src_end // NULL or end of source +) +{ + DASSERT(scan); + + //---- setup + + memset(scan,0,sizeof(*scan)); + + ccp ptr = src; + if (!src) + return 0; + if (!src_end) + src_end = src + strlen(src); + + + //--- skip blanks and controls + + while ( ptr < src_end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr == src_end ) + goto abort; + + + //--- find keyword + + ccp key = ptr; + while ( ptr < src_end && isalnum((int)*ptr) ) + ptr++; + const uint keylen = ptr - key; + + if ( keylen == 3 ) + { + if ( ( *key == 'T' || *key == 't' ) + && key[1] >= '1' && key[1] <= '8' + && key[2] >= '1' && key[2] <= '4' ) + { + int idx = 4 * ( key[1] - '1' ) + ( key[2] - '1' ); + DASSERT(GetTrackIndexBMG); + idx = GetTrackIndexBMG(idx,-1); + if ( idx >= 0 ) + { + scan->mid[0] = MID_TRACK1_BEG + idx; + scan->mid[1] = MID_TRACK2_BEG + idx; + scan->n_mid = 2; + if (opt_bmg_support_lecode) + { + scan->mid[2] = MID_LE_TRACK_BEG + idx; + scan->n_mid++; + } + else if (opt_bmg_support_ctcode) + { + scan->mid[2] = MID_CT_TRACK_BEG + idx; + scan->n_mid++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( ( *key == 'U' || *key == 'u' ) + && key[1] >= '1' && key[1] <= '2' + && key[2] >= '1' && key[2] <= '5' ) + { + int idx = 5 * ( key[1] - '1' ) + ( key[2] - '1' ); + DASSERT(GetArenaIndexBMG); + idx = GetArenaIndexBMG(idx,-1); + if ( idx >= 0 ) + { + scan->mid[0] = MID_ARENA1_BEG + idx; + scan->mid[1] = MID_ARENA2_BEG + idx; + scan->n_mid = 2; + if (opt_bmg_support_lecode) + { + scan->mid[2] = MID_LE_ARENA_BEG + idx; + scan->n_mid++; + } + else if (opt_bmg_support_ctcode) + { + scan->mid[2] = MID_CT_ARENA_BEG + idx; + scan->n_mid++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( *key == 'M' || *key == 'm' ) + { + char *end; + u32 num = strtoul(key+1,&end,10); + if ( num > 0 && num <= BMG_N_CHAT && end == ptr ) + { + scan->mid[0] = MID_CHAT_BEG + num - 1; + scan->n_mid = 1; + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + } + + if ( !scan->n_mid && keylen ) + { + char *end; + uint num = strtoul(key,&end,16); + if ( end == ptr ) + { + scan->mid[0] = num; + scan->n_mid = 1; + } + else + scan->status = -1; + } + + if ( scan->n_mid > 0 ) + while ( ptr < src_end && ( *ptr == ' ' || *ptr == '\t' ) ) + ptr++; + + abort: + if (!scan->status) + scan->status = scan->n_mid; + scan->scan_end = scan->status > 0 ? (char*)ptr : (char*)src; + return scan->status; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanMidBMG +( + // returns: + // -1: invalid MID + // 0: empty line + // 1: single mid found + // 2: double mid found (track or arena) + // 3: triple mid found (track or arena, CTCODE) + + bmg_t *bmg, // NULL or bmg to update parameters + u32 *ret_mid1, // return value: pointer to first MID + u32 *ret_mid2, // return value: pointer to second MID + u32 *ret_mid3, // return value: pointer to third MID + ccp src, // source pointer + ccp src_end, // NULL or end of source + char **scan_end // return value: NULL or end of scanned 'source' +) +{ +// [[2do]] + #if defined(TEST) || defined(DEBUG) || 1 + + bmg_scan_mid_t scan; + ScanBMGMID(&scan,bmg,src,src_end); + + if (ret_mid1) + *ret_mid1 = scan.n_mid > 0 ? scan.mid[0] : 0; + if (ret_mid2) + *ret_mid2 = scan.n_mid > 1 ? scan.mid[1] : 0; + if (ret_mid3) + *ret_mid3 = scan.n_mid > 2 ? scan.mid[2] : 0; + if (scan_end) + *scan_end = scan.scan_end; + return scan.status; + + #else + //---- setup + + u32 mid1 = 0, mid2 = 0, mid3 = 0; + int stat = 0; + ccp ptr = src; + + if (!src) + goto abort; + if (!src_end) + src_end = src + strlen(src); + + + //--- skip blanks and controls + + while ( ptr < src_end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr == src_end ) + goto abort; + + + //--- find keyword + + ccp key = ptr; + while ( ptr < src_end && isalnum((int)*ptr) ) + ptr++; + const uint keylen = ptr - key; + + if ( keylen == 3 ) + { + if ( ( *key == 'T' || *key == 't' ) + && key[1] >= '1' && key[1] <= '8' + && key[2] >= '1' && key[2] <= '4' ) + { + int idx = 4 * ( key[1] - '1' )+ ( key[2] - '1' ); + DASSERT(GetTrackIndexBMG); + idx = GetTrackIndexBMG(idx,-1); + if ( idx >= 0 ) + { + mid1 = MID_TRACK1_BEG + idx; + mid2 = MID_TRACK2_BEG + idx; + stat = 2; + if (opt_bmg_support_lecode) + { + mid3 = MID_LE_TRACK_BEG + idx; + stat++; + } + else if (opt_bmg_support_ctcode) + { + mid3 = MID_CT_TRACK_BEG + idx; + stat++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( ( *key == 'U' || *key == 'u' ) + && key[1] >= '1' && key[1] <= '2' + && key[2] >= '1' && key[2] <= '5' ) + { + int idx = 5 * ( key[1] - '1' ) + ( key[2] - '1' ); + DASSERT(GetArenaIndexBMG); + idx = GetArenaIndexBMG(idx,-1); + if ( idx >= 0 ) + { + mid1 = MID_ARENA1_BEG + idx; + mid2 = MID_ARENA2_BEG + idx; + stat = 2; + if (opt_bmg_support_lecode) + { + mid3 = MID_LE_ARENA_BEG + idx; + stat++; + } + else if (opt_bmg_support_ctcode) + { + mid3 = MID_CT_ARENA_BEG + idx; + stat++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( *key == 'M' || *key == 'm' ) + { + char *end; + u32 num = strtoul(key+1,&end,10); + if ( num > 0 && num <= BMG_N_CHAT && end == ptr ) + { + mid1 = MID_CHAT_BEG + num - 1; + stat = 1; + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + } + + if ( !stat && keylen ) + { + char *end; + uint num = strtoul(key,&end,16); + if ( end == ptr ) + { + mid1 = num; + stat = 1; + } + else + stat = -1; + } + + if ( stat > 0 ) + while ( ptr < src_end && ( *ptr == ' ' || *ptr == '\t' ) ) + ptr++; + + abort: + if (ret_mid1) + *ret_mid1 = mid1; + if (ret_mid2) + *ret_mid2 = mid2; + if (ret_mid3) + *ret_mid3 = mid3; + if (scan_end) + *scan_end = stat > 0 ? (char*)ptr : (char*)src; + return stat; + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch bmg: helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp (*GetContainerNameBMG) ( const bmg_t *bmg ) = 0; + +//----------------------------------------------------------------------------- + +ccp GetNameBMG ( const bmg_t *bmg, uint *name_len ) +{ + DASSERT(bmg); + + ccp name = 0; + if (GetContainerNameBMG) + name = GetContainerNameBMG(bmg); + + if ( !name && bmg->fname ) + { + name = bmg->src_is_arch ? 0 : strstr(bmg->fname,".d/"); + if (name) + { + while ( name > bmg->fname && name[-1] != '/' ) + name--; + } + else + { + name = strrchr(bmg->fname,'/'); + name = name ? name + 1 : bmg->fname; + } + } + + if (!name) + name = ""; + else + while ( *name == '.' ) + name++; + + if (name_len) + { + ccp end = strchr(name,'.'); + *name_len = end ? end-name : strlen(name); + } + + return name; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct format_message_t +{ + bmg_t *dest; // valid destination bmg + uint mod_count; // incremented for each modification + + ccp full_name; // NULL or full name of source file + uint full_len; // length of 'full_name' + ccp good_name; // NULL or good name of source file + uint good_len; // length of 'good_name' +} +format_message_t; + +//----------------------------------------------------------------------------- + +static bool format_message +( + format_message_t *fm, // valid object + bmg_item_t *dptr, // valid message + const u16 *format, // format string + uint format_len // length of 'format' +) +{ + DASSERT(fm); + DASSERT(fm->dest); + DASSERT(dptr); + DASSERT(format); + + + //--- line support + + typedef struct line_t + { + const u16 *beg; + const u16 *end; + } + line_t; + + const uint MAX_LINE = 100; + line_t line[100]; + uint n_line = 0; + + + //--- setup dest and source + + u16 buf[10000]; + u16 *dest = buf; + u16 *dest_end = buf + sizeof(buf)/sizeof(*buf); + u16* marker = buf; + + const u16 *source = format; + const u16 *source_end = format + format_len; + + + //--- main loop + + while ( dest < dest_end && source < source_end ) + { + if ( be16(source) != '%' ) + { + *dest++ = *source++; + continue; + } + + if ( source[0] == source[1] ) + { + source++; + *dest++ = *source++; + continue; + } + + PRINT("%04x = %c\n",be16(source), be16(source) & 0xff ); + const u16 *start = source++; + + u32 p1, p2, stat; + source = ScanRange16U32(source,&stat,&p1,&p2,0,~(u32)0); + if ( stat == 1 ) + p1 = 0; + else if ( stat < 1 ) + { + p1 = 0; + p2 = M1(p2); + } + + char buf8[50]; + uint copy_len = 0; + const u8 * copy_src8 = 0; + const u16 * copy_src16 = 0; + + const u16 conv_char = be16(source++); + switch (conv_char) + { + case 's': + case 'S': + copy_len = dptr->len; + copy_src16 = dptr->text; + break; + + case 'i': + copy_len = snprintf(buf8,sizeof(buf8),"%x",dptr->mid); + copy_src8 = (u8*)buf8; + break; + + case 'I': + copy_len = snprintf(buf8,sizeof(buf8),"%04x",dptr->mid); + copy_src8 = (u8*)buf8; + break; + + case 'n': + case 'N': + if (!fm->full_name) + { + fm->full_name = GetNameBMG(fm->dest,&fm->full_len); + if ( fm->full_len > 4 && !strncasecmp(fm->full_name,"Menu",4) ) + { + fm->good_name = fm->full_name + 4; + fm->good_len = fm->full_len - 4; + } + else + { + fm->good_name = fm->full_name; + fm->good_len = fm->full_len; + } + } + + if ( conv_char == 'N' ) + { + copy_len = fm->good_len; + copy_src8 = (u8*)fm->good_name; + } + else + { + copy_len = fm->full_len; + copy_src8 = (u8*)fm->full_name; + } + break; + + case 'm': + marker = dest; + break; + + case 'l': + case 'L': + case 'M': + if (!n_line) + { + const u16 *src = dptr->text; + const u16 *end = src + dptr->len; + while ( n_line < MAX_LINE ) + { + line[n_line].beg = src; + while ( src < end ) + { + if ( ntohs(*src) == '\n' ) + break; + + src += GetWordLength16BMG(src); + } + line[n_line].end = src; + n_line++; + if ( src >= end ) + break; + src++; + } + PRINT("L: %u lines found\n",n_line); + } + + if ( stat == 1 ) + p1 = p2; + if ( p2 > n_line-1 ) + p2 = n_line-1; + PRINT("L: select %u..%u/%u\n",p1,p2,n_line); + + if ( p1 <= p2 ) + { + copy_src16 = line[p1].beg; + copy_len = line[p2].end - copy_src16; + } + + if ( conv_char == 'M' ) + { + if ( !copy_len && marker ) + dest = marker; + //marker = 0; + copy_len = 0; + } + else + { + if ( !copy_len && conv_char == 'L' && marker ) + dest = marker; + //if ( conv_char == 'L' ) + // marker = 0; + } + p1 = 0; + p2 = M1(p2); + break; + + default: + copy_len = source - start; + copy_src16 = start; + break; + } + + if (copy_len) + { + // correct p1 and p2 + if ( p1 > copy_len ) + p1 = copy_len; + if ( p2 > copy_len ) + p2 = copy_len; + + u32 count = p2 - p1; + if (copy_src16) + { + copy_src16 += p1; + while ( count-- > 0 && dest < dest_end ) + *dest++ = *copy_src16++; + } + else if (copy_src8) + { + copy_src8 += p1; + while ( count-- > 0 && dest < dest_end ) + *dest++ = htons(*copy_src8++); + } + } + } + + const uint new_len = dest - buf; + if ( new_len != dptr->len || memcmp(buf,dptr->text,new_len*sizeof(*dest)) ) + { + AssignItemText16BMG(dptr,buf,dest-buf); + fm->mod_count++; + return true; + } + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch bmg: functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchPrintBMG +( + bmg_t * bmg_dest, // pointer to destination bmg + const bmg_t * bmg_src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source - mix mix // + //--------------------------------------// + + DASSERT(bmg_dest); + DASSERT(bmg_src); + copy_param(bmg_dest,bmg_src); + + format_message_t fm; + memset(&fm,0,sizeof(fm)); + fm.dest = bmg_dest; + + bmg_item_t * dptr = bmg_dest->item; + bmg_item_t * dend = dptr + bmg_dest->item_used; + + const bmg_item_t * sptr = bmg_src->item; + const bmg_item_t * send = sptr + bmg_src->item_used; + + for ( ; dptr < dend; dptr++ ) + { + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + if ( sptr->mid == mid ) + format_message(&fm,dptr,sptr->text,sptr->len); + } + return fm.mod_count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchFormatBMG +( + bmg_t * bmg_dest, // pointer to destination bmg + const bmg_t * bmg_src, // pointer to source bmg + ccp format // format parameter +) +{ + DASSERT(bmg_dest); + DASSERT(bmg_src); + copy_param(bmg_dest,bmg_src); + + const uint form_size = 2000; + u16 form_buf[form_size]; + uint form_len = ScanString16BMG(form_buf,form_size,format,-1,bmg_dest); + + format_message_t fm; + memset(&fm,0,sizeof(fm)); + fm.dest = bmg_dest; + + bmg_item_t * dptr = bmg_dest->item; + bmg_item_t * dend = dptr + bmg_dest->item_used; + for ( ; dptr < dend; dptr++ ) + format_message(&fm,dptr,form_buf,form_len); + + return fm.mod_count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRegexBMG +( + bmg_t * bmg, // pointer to destination bmg + ccp regex // format parameter +) +{ + #if ENABLE_BMG_REGEX + DASSERT(bmg); + DASSERT(regex); + + PRINT("REGEX=%s\n",regex); + + Regex_t re; + enumError err = ScanRegex(&re,true,regex); + if ( err || !re.valid ) + { + ResetRegex(&re); + return false; + } + + char buf1[1000]; + struct { FastBuf_t b; char space[500]; } res; + InitializeFastBuf(&res,sizeof(res)); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + uint len1 = PrintString16BMG( buf1, sizeof(buf1), iptr->text, iptr->len, + BMG_UTF8_MAX, 0, true ); +// printf("-> [%d] %s\n",len1,buf1); + + const int stat = ReplaceRegex(&re,&res.b,buf1,len1); + if ( stat >= 0 ) + { +// printf("{%d] %.*s\n",stat,stat,GetFastBufString(&res.b)); + AssignItemScanTextBMG(iptr,GetFastBufString(&res.b),stat); + dirty = true; + } + } + + ResetFastBuf(&res.b); + ResetRegex(&re); + return dirty; + + #else + return false; + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmRegexBMG +( + bmg_t * bmg, // pointer to destination bmg + ccp regex // format parameter +) +{ + #if ENABLE_BMG_REGEX + DASSERT(bmg); + DASSERT(regex); + + PRINT("REGEX=%s\n",regex); + + Regex_t re; + enumError err = ScanRegex(&re,true,regex); + if ( err || !re.valid ) + { + ResetRegex(&re); + return false; + } + + char buf1[1000]; + struct { FastBuf_t b; char space[500]; } res; + InitializeFastBuf(&res,sizeof(res)); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + uint len1 = PrintString16BMG( buf1, sizeof(buf1), iptr->text, iptr->len, + BMG_UTF8_MAX, 0, true ); + //printf("-> [%d] %s\n",len1,buf1); + + int stat = ReplaceRegex(&re,&res.b,buf1,len1); + if ( stat >= 0 ) + { + if (stat) + AssignItemScanTextBMG(iptr,GetFastBufString(&res.b),stat); + else + { + FreeItemBMG(iptr); + iptr->text = 0; + ResetAttribBMG(bmg,iptr); + } + dirty = true; + } + } + + ResetFastBuf(&res.b); + ResetRegex(&re); + return dirty; + + #else + return false; + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchReplaceBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source - patch both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + if (!dptr->text) + continue; + + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + + if ( sptr->mid == mid + && !IsItemEqualBMG(dptr,sptr) + && ( !sptr->cond || FindItemBMG(dest,sptr->cond) ) ) + { + FreeItemBMG(dptr); + DASSERT( dptr->mid == sptr->mid ); + + CopyAttribBMG(dest,dptr,sptr); + dptr->len = sptr->len; + dptr->text = sptr->text; + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchInsertBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source patch source both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; sptr < send; sptr++ ) + { + if ( sptr->cond && !FindItemBMG(dest,sptr->cond) ) + continue; + + bool old_item; + bmg_item_t * dptr = InsertItemBMG(dest,sptr->mid,0,0,&old_item); + if (!old_item) + { + FreeItemBMG(dptr); + DASSERT( dptr->mid == sptr->mid ); + + CopyAttribBMG(dest,dptr,sptr); + dptr->len = sptr->len; + dptr->text = sptr->text; + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchOverwriteBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src, // pointer to source bmg + bool dup_strings // true: allocate mem for copied strings +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source patch patch both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; sptr < send; sptr++ ) + { + if ( sptr->cond && !FindItemBMG(dest,sptr->cond) ) + continue; + + bool old_item; + bmg_item_t * dptr = InsertItemBMG(dest,sptr->mid,0,0,&old_item); + if ( !old_item || !IsItemEqualBMG(dptr,sptr) ) + { + if (dup_strings) + AssignItemText16BMG(dptr,sptr->text,sptr->len); + else + { + FreeItemBMG(dptr); + dptr->len = sptr->len; + dptr->text = sptr->text; + } + DASSERT( dptr->mid == sptr->mid ); + + CopyAttribBMG(dest,dptr,sptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchDeleteBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source - - - // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; sptr < send; sptr++ ) + { + bmg_item_t * dptr = FindItemBMG(dest,sptr->mid); + if (dptr) + { + FreeItemBMG(dptr); + DASSERT( dptr->mid == sptr->mid ); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchMaskBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // - - source both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + bmg_item_t * sptr = FindItemBMG(src,dptr->mid); + if (!sptr) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchEqualBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // - - - both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + if ( sptr == send || sptr->mid != mid || !IsItemEqualBMG(dptr,sptr) ) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchNotEqualBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // - - source - // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + if ( sptr != send && sptr->mid == mid && IsItemEqualBMG(dptr,sptr) ) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool generic_helper +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src, // pointer to source bmg + uint dest_id, // index fo message to create + uint src_id, // relevant source message + uint src_beg, // source message for begining tests + uint src_end // source message for end tests +) +{ + DASSERT(dest); + DASSERT(src); + + //--- find source messages + + const bmg_item_t *s0 = FindItemBMG(src,src_id); + if ( !s0 || !s0->text || !s0->len ) + return false; + + const bmg_item_t *s1 = FindItemBMG(src,src_beg); + if ( !s1 || !s1->text || !s1->len ) + return false; + + const bmg_item_t *s2 = FindItemBMG(src,src_end); + if ( !s2 || !s2->text || !s2->len ) + return false; + + + //--- compare beginning of text + + const u16 *b0 = s0->text; + const u16 *b1 = s1->text; + const uint maxlen = s0->len < s1->len ? s0->len : s1->len; + const u16 *b0end = b0 + maxlen; + + while ( b0 < b0end && *b0 == *b1 ) + b0++, b1++; + if ( b0 == b0end ) + return false; + + while ( b0 > s0->text && isalnum((int)be16(b0-1)) ) + b0--, b1--; + + PRINT("GENERIC: |%c%c%c| |%c%c%c|\n", + be16(b0+0) & 0xff, be16(b0+1) & 0xff, be16(b0+2) & 0xff, + be16(b1+0) & 0xff, be16(b1+1) & 0xff, be16(b1+2) & 0xff ); + + + //--- compare end of text + + const u16 *e0 = s0->text + s0->len - 1; + const u16 *e2 = s2->text + s2->len - 1; + while ( e0 > b0 && *e0 == *e2 ) + e0--, e2--; + e0++, e2++; + + const u16 *e0end = s0->text + s0->len; + while ( b0 < e0end && isalnum((int)be16(b0-1)) ) + e0++, e2++; + + if ( b0 < e0 ) + { + const uint len = e0 - b0; + bmg_item_t *dptr = InsertItemBMG(dest,dest_id,0,0,0); + DASSERT(dptr); + if ( !dptr->text + || dptr->len != len + || memcmp(dptr->text,b0,len*sizeof(*b0)) ) + { + AssignItemText16BMG(dptr,b0,len); + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool PatchGenericBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + DASSERT(dest); + DASSERT(src); + bool dirty = false; + + dirty |= generic_helper(dest,src, MID_G_SMALL, 0x1f44, 0x1f46, 0x1f46 ); + dirty |= generic_helper(dest,src, MID_G_MEDIUM, 0x1f45, 0x1f46, 0x1f46 ); + dirty |= generic_helper(dest,src, MID_G_LARGE, 0x1f46, 0x1f44, 0x1f44 ); + + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchIdBMG +( + bmg_t * bmg_dest, // pointer to destination bmg + bool prefix_all // true: prefix all messages +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // prefix n.a. n.a. n.a. // + //--------------------------------------// + + + uint name_len; + ccp name = GetNameBMG(bmg_dest,&name_len); + noPRINT("\e[31;1m#### NAME=%s\e[0m\n",name); + if ( name_len > 4 && !strncasecmp(name,"menu",4) ) + name += 4, name_len -= 4; + + u16 buf[10000]; + u16 *bufend = buf + sizeof(buf)/sizeof(*buf); + + bool dirty = false; + bmg_item_t * dptr = bmg_dest->item; + bmg_item_t * dend = dptr + bmg_dest->item_used; + + for ( ; dptr < dend; dptr++ ) + { + if ( dptr->text && ( prefix_all || dptr->text != bmg_null_entry ) ) + { + u16 *dest = buf; + if (name_len) + *dest++ = htons(*name); + char num[20]; + snprintf(num,sizeof(num),"%x",dptr->mid); + ccp src = num; + while (*src) + *dest++ = htons(*src++); + + if ( dptr->text != bmg_null_entry ) + { + *dest++ = htons(':'); + + uint count = bufend - dest; + if ( count > dptr->len ) + count = dptr->len; + memcpy(dest,dptr->text,count*sizeof(*dest)); + dest += count; + } + AssignItemText16BMG(dptr,buf,dest-buf); + dirty = true; + } + } + + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmEscapesBMG +( + bmg_t * bmg, // pointer to destination bmg + bool unicode, // replace '\u{16bit}' by unicode chars + bool rm_escapes // remove 1A escapes +) +{ + DASSERT(bmg); + PRINT("patch_bmg_rm_escapes(unicode=%d,rm_esc=%d)\n",unicode,rm_escapes); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + // fast find first escape + u16 *sp = iptr->text, *end = sp + iptr->len; + if (!sp) + continue; + while ( sp < end && ntohs(*sp) != 0x1a ) + sp++; + if ( sp == end ) + continue; + + sp = ModifiyItem(iptr,sp); + end = iptr->text + iptr->len; + DASSERT( iptr->alloced_size > 0 ); + + u16 *dp = sp; + while ( sp < end ) + { + if ( ntohs(*sp) != 0x1a ) + { + *dp++ = *sp++; + continue; + } + + if ( unicode && ntohs(sp[1]) == 0x0801 && !ntohs(sp[2]) ) + { + const u16 code = ntohs(sp[3]); + if ( code >= ' ' ) + { + *dp++ = sp[3]; + sp += 4; + HexDump16(stdout,0,0x100,iptr->text,(dp-iptr->text)*2); + HexDump16(stdout,0,0x200,sp,(iptr->text+iptr->len-sp)*2); + continue; + } + } + + const uint esc_len = ((u8*)sp)[2] & 0xfe; + if (!rm_escapes) + { + memmove(dp,sp,esc_len); + dp += esc_len/2; + } + sp += esc_len/2; + } + iptr->len = dp - iptr->text; + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmCupsBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + DASSERT(bmg); + bool dirty = false; + uint mid; + for ( mid = MID_RCUP_BEG; mid < MID_RCUP_END; mid++ ) + { + bmg_item_t * ptr = FindItemBMG(bmg,mid); + if (ptr) + { + FreeItemBMG(ptr); + DASSERT( ptr->mid == mid ); + ptr->text = 0; + ResetAttribBMG(bmg,ptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint bmg_copy_helper +( + bmg_t * bmg, // pointer to bmg + bool force, // true: force replace + uint mid_src_1, // first source message id + uint mid_src_2, // NULL or second source message id + uint mid_src_3, // NULL or third source message id + uint mid_dest, // destination message id + uint n_mid // numbers of messages to procced +) +{ + DASSERT(bmg); + + uint i, count = 0; + for ( i = 0; i < n_mid; i++ ) + { + bmg_item_t * sptr = FindItemBMG(bmg,mid_src_1+i); + if ( !sptr && mid_src_2 ) + { + sptr = FindItemBMG(bmg,mid_src_2+i); + if ( !sptr && mid_src_3 ) + sptr = FindItemBMG(bmg,mid_src_3+i); + } + + if (sptr) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid_dest+i,0,0,&old_item); + if ( force || !old_item || !dptr->len ) + { + AssignItemText16BMG(dptr,sptr->text,sptr->len); + CopyAttribBMG(bmg,dptr,sptr); + count++; + } + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchCTCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, false, MID_TRACK1_BEG, MID_TRACK2_BEG, 0, + MID_CT_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, false, MID_ARENA1_BEG, MID_ARENA2_BEG, 0, + MID_CT_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, false, MID_RCUP_BEG, 0, 0, + MID_CT_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, false, MID_BCUP_BEG, 0, 0, + MID_CT_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchLECopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, false, MID_ARENA1_BEG, MID_ARENA2_BEG, MID_CT_ARENA_BEG, + MID_LE_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, false, MID_TRACK1_BEG, MID_TRACK2_BEG, MID_CT_TRACK_BEG, + MID_LE_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, false, MID_RCUP_BEG, MID_CT_CUP_BEG, 0, + MID_LE_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, false, MID_BCUP_BEG, MID_CT_BCUP_BEG, 0, + MID_LE_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchXCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + if (opt_bmg_support_lecode) + return PatchLECopyBMG(bmg); + if (opt_bmg_support_ctcode) + return PatchCTCopyBMG(bmg); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchCTForceCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, true, MID_TRACK1_BEG, MID_TRACK2_BEG, 0, + MID_CT_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, true, MID_ARENA1_BEG, MID_ARENA2_BEG, 0, + MID_CT_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, true, MID_RCUP_BEG, 0, 0, + MID_CT_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, true, MID_BCUP_BEG, 0, 0, + MID_CT_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchLEForceCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, true, MID_TRACK1_BEG, MID_TRACK2_BEG, MID_CT_TRACK_BEG, + MID_LE_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, true, MID_ARENA1_BEG, MID_ARENA2_BEG, MID_CT_ARENA_BEG, + MID_LE_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, true, MID_RCUP_BEG, MID_CT_CUP_BEG, 0, + MID_LE_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, true, MID_BCUP_BEG, MID_CT_BCUP_BEG, 0, + MID_LE_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchXForceCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + if (opt_bmg_support_lecode) + return PatchLEForceCopyBMG(bmg); + if (opt_bmg_support_ctcode) + return PatchCTForceCopyBMG(bmg); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchIdentificationBMG ( bmg_t *bmg, ct_bmg_t *ctb ) +{ + DASSERT(bmg); + DASSERT(ctb); + + bmg_item_t *bi = InsertItemBMG(bmg,MID_PARAM_IDENTIFY,0,0,0); + DASSERT(bi); + AssignItemTextBMG(bi,GetCtBMGIdentification(ctb,true),-1); + return true; +} + +//----------------------------------------------------------------------------- + +bool PatchFillBMG + ( bmg_t *bmg, ct_mode_t ct_mode, uint rcup_limit, uint track_limit ) +{ + ct_bmg_t ctb; + ct_mode = SetupCtBMG(&ctb,ct_mode,ct_mode); + if (!ctb.is_ct_code) + return false; + + + //--- check existence of at least one relevvant message + + bool found = false; + bmg_item_t *dptr = bmg->item; + bmg_item_t *dend = dptr + bmg->item_used; + for ( ; dptr < dend; dptr++ ) + { + const u32 m = dptr->mid; + if ( m >= MID_TRACK1_BEG && m < MID_TRACK1_END + || m >= MID_TRACK2_BEG && m < MID_TRACK2_END + || m >= MID_ARENA1_BEG && m < MID_ARENA1_END + || m >= MID_ARENA2_BEG && m < MID_ARENA2_END + || m >= MID_RCUP_BEG && m < MID_RCUP_END + || m >= MID_BCUP_BEG && m < MID_BCUP_END + || m >= ctb.track_name1.beg && m < ctb.track_name1.end + || m >= ctb.track_name2.beg && m < ctb.track_name2.end + || m >= ctb.arena_name1.beg && m < ctb.arena_name1.end + || m >= ctb.arena_name2.beg && m < ctb.arena_name2.end + || m >= ctb.rcup_name.beg && m < ctb.rcup_name.end + || m >= ctb.bcup_name.beg && m < ctb.bcup_name.end + || m == ctb.random.beg + ) + { + if ( dptr->text && dptr->text != bmg_null_entry ) + { + found = true; + break; + } + } + } + + if (!found) + return false; + + + //--- random + + char buf[100]; + bool dirty = false; + + if (ctb.random.beg) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,ctb.random.beg,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + AssignItemTextBMG(dptr,"???",3); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill arena names + + uint mid; + for ( mid = ctb.arena_name1.beg; mid < ctb.arena_name1.end; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_A%u_",mid-ctb.arena_name1.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill track names + + if (!track_limit) + track_limit = opt_bmg_track_fill_limit; + uint max_track; + if (track_limit) + { + max_track = ctb.track_name1.beg + track_limit; + if ( max_track > ctb.track_name1.end ) + max_track = ctb.track_name1.end; + } + else + max_track = ctb.track_name1.end; + + for ( mid = ctb.track_name1.beg; mid < max_track; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_T%03X_",mid-ctb.track_name1.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill battle cup names + + for ( mid = ctb.bcup_name.beg; mid < ctb.bcup_name.end; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_B%u_",mid-ctb.bcup_name.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill racing cup names + + if (!rcup_limit) + rcup_limit = opt_bmg_rcup_fill_limit; + uint max_cup; + if (rcup_limit) + { + max_cup = ctb.rcup_name.beg + rcup_limit; + if ( max_cup > ctb.rcup_name.end ) + max_cup = ctb.rcup_name.end; + } + else + max_cup = ctb.rcup_name.end; + + for ( mid = ctb.rcup_name.beg; mid < max_cup; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_R%u_",mid-ctb.rcup_name.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + //--- identification + + if (dirty) + PatchIdentificationBMG(bmg,&ctb); + + return dirty; +} + +//----------------------------------------------------------------------------- + +bool PatchXFillBMG ( bmg_t *bmg, uint rcup_limit, uint track_limit ) +{ + if (opt_bmg_support_lecode) + return PatchLEFillBMG(bmg,rcup_limit,track_limit); + + if (opt_bmg_support_ctcode) + return PatchCTFillBMG(bmg,rcup_limit,track_limit); + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmFilledBMG ( bmg_t *bmg ) +{ + DASSERT(bmg); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + if ( iptr->mid >= 0x4000 + && iptr->len > 3 + && ntohs(iptr->text[0]) == '_' + && ntohs(iptr->text[iptr->len-1]) == '_' ) + { + u16 ch = ntohs(iptr->text[1]); + if ( ch == 'A' || ch == 'T' || ch == 'B' || ch == 'R' ) + { + FreeItemBMG(iptr); + iptr->text = 0; + ResetAttribBMG(bmg,iptr); + dirty = true; + } + } + } + + return dirty; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchRemove*BMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRemoveBMG ( bmg_t * bmg, int mid1, int mid2 ) +{ + DASSERT(bmg); + + bmg_item_t * ptr = bmg->item; + bmg_item_t * end = ptr + bmg->item_used; + + bool dirty = false; + for ( ; ptr < end && ptr->mid < mid2; ptr++ ) + { + if ( ptr->mid >= mid1 ) + { + FreeItemBMG(ptr); + ptr->text = 0; + ResetAttribBMG(bmg,ptr); + dirty = true; + } + } + return dirty; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError PatchBMG +( + bmg_t *dest, // pointer to destination bmg + const bmg_t *src, // pointer to source bmg + PatchModeBMG_t patch_mode, // patch mode + ccp string, // NULL or string parameter + bool dup_strings // true: alloc mem for copied strings (OVERWRITE) +) +{ + PRINT("PatchBMG(%p,%p,%u,%s,%d)\n", + dest, src, patch_mode, string, dup_strings ); + DASSERT(dest); + + bool dirty = false; + switch(patch_mode) + { + case BMG_PM_PRINT: dirty = PatchPrintBMG(dest,src); break; + case BMG_PM_FORMAT: dirty = PatchFormatBMG(dest,src,string); break; + case BMG_PM_REGEX: dirty = PatchRegexBMG(dest,string); break; + case BMG_PM_RM_REGEX: dirty = PatchRmRegexBMG(dest,string); break; + case BMG_PM_ID: dirty = PatchIdBMG(dest,false); break; + case BMG_PM_ID_ALL: dirty = PatchIdBMG(dest,true); break; + case BMG_PM_UNICODE: dirty = PatchRmEscapesBMG(dest,true,false); break; + case BMG_PM_RM_ESCAPES: dirty = PatchRmEscapesBMG(dest,false,true); break; + + case BMG_PM_REPLACE: dirty = PatchReplaceBMG(dest,src); break; + case BMG_PM_INSERT: dirty = PatchInsertBMG(dest,src); break; + case BMG_PM_OVERWRITE: dirty = PatchOverwriteBMG(dest,src,dup_strings); break; + case BMG_PM_DELETE: dirty = PatchDeleteBMG(dest,src); break; + case BMG_PM_MASK: dirty = PatchMaskBMG(dest,src); break; + case BMG_PM_EQUAL: dirty = PatchEqualBMG(dest,src); break; + case BMG_PM_NOT_EQUAL: dirty = PatchNotEqualBMG(dest,src); break; + + case BMG_PM_GENERIC: dirty = PatchGenericBMG(dest,dest); break; + case BMG_PM_RM_CUPS: dirty = PatchRmCupsBMG(dest); break; + + case BMG_PM_CT_COPY: dirty = PatchCTCopyBMG(dest); break; + case BMG_PM_CT_FORCE_COPY: dirty = PatchCTForceCopyBMG(dest); break; + case BMG_PM_CT_FILL: dirty = PatchCTFillBMG(dest,0,0); break; + + case BMG_PM_LE_COPY: dirty = PatchLECopyBMG(dest); break; + case BMG_PM_LE_FORCE_COPY: dirty = PatchLEForceCopyBMG(dest); break; + case BMG_PM_LE_FILL: dirty = PatchLEFillBMG(dest,0,0); break; + + case BMG_PM_X_COPY: dirty = PatchXCopyBMG(dest); break; + case BMG_PM_X_FORCE_COPY: dirty = PatchXForceCopyBMG(dest); break; + case BMG_PM_X_FILL: dirty = PatchXFillBMG(dest,0,0); break; + + case BMG_PM_RM_FILLED: dirty = PatchRmFilledBMG(dest); break; + + default: return ERROR0(ERR_NOT_IMPLEMENTED,0); + } + + if (dirty) + TouchFileAttrib(&dest->fatt); + + return dirty ? ERR_DIFFER : ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CT/LE support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ct_mode_t NormalizeCtModeBMG ( ct_mode_t mode, ct_mode_t fallback ) +{ + for(;;) + { + switch(mode) + { + case CTM_NINTENDO: + case CTM_CTCODE: + case CTM_LECODE2: + return mode; + + case CTM_CTCODE_BASE: + return CTM_CTCODE; + + case CTM_LECODE1: + case CTM_LECODE_BASE: + return CTM_LECODE_DEF; + + case CTM_OPTIONS: + if (opt_bmg_support_lecode) + return CTM_LECODE2; + if (opt_bmg_support_ctcode) + return CTM_CTCODE; + return CTM_NINTENDO; + + case CTM_NO_INIT: + case CTM_AUTO: + break; + } + + mode = fallback; + fallback = CTM_OPTIONS; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ct_mode_t SetupCtModeBMG ( ct_mode_t mode ) +{ + mode = NormalizeCtModeBMG(mode,CTM_NINTENDO); + switch (mode) + { + case CTM_NINTENDO: + opt_bmg_support_mkw = true; + opt_bmg_support_ctcode = false; + opt_bmg_support_lecode = false; + break; + + case CTM_CTCODE: + opt_bmg_support_mkw = true; + opt_bmg_support_ctcode = true; + opt_bmg_support_lecode = false; + break; + + case CTM_LECODE2: + opt_bmg_support_mkw = true; + opt_bmg_support_ctcode = false; + opt_bmg_support_lecode = true; + break; + + default: + opt_bmg_support_mkw = false; + opt_bmg_support_ctcode = false; + opt_bmg_support_lecode = false; + break; + } + return mode; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetCtModeNameBMG ( ct_mode_t mode, bool full ) +{ + switch(mode) + { + case CTM_NINTENDO: return "Nintendo"; + case CTM_CTCODE: return "CT-CODE"; + case CTM_LECODE1: return full ? "LE-CODE v1" : "LE-CODE"; + case CTM_LECODE2: return full ? "LE-CODE v2" : "LE-CODE"; + default: return "?"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ct_mode_t SetupCtBMG ( ct_bmg_t *ctb, ct_mode_t mode, ct_mode_t fallback ) +{ + mode = NormalizeCtModeBMG(mode,fallback); + if (ctb) + { + ctb->ct_mode = mode; + ctb->identify = MID_PARAM_IDENTIFY; + ctb->param.beg = MID_PARAM_BEG; + ctb->param.end = MID_PARAM_END; + ctb->param.n = ctb->param.end - ctb->param.beg; + + switch(mode) + { + case CTM_CTCODE: + ctb->version = 0; + ctb->is_ct_code = 1; + ctb->le_phase = 0; + + ctb->rcup_name.beg = MID_CT_RCUP_BEG; + ctb->rcup_name.end = MID_CT_RCUP_END; + ctb->bcup_name.beg = MID_CT_BCUP_BEG; + ctb->bcup_name.end = MID_CT_BCUP_END; + + ctb->track_name1.beg = MID_CT_TRACK_BEG; + ctb->track_name1.end = MID_CT_TRACK_END; + ctb->track_name2.beg = 0; + ctb->track_name2.end = 0; + + ctb->arena_name1.beg = MID_CT_ARENA_BEG; + ctb->arena_name1.end = MID_CT_ARENA_END; + ctb->arena_name2.beg = 0; + ctb->arena_name2.end = 0; + + ctb->cup_ref.beg = MID_CT_CUP_REF_BEG; + ctb->cup_ref.end = MID_CT_CUP_REF_END; + + ctb->random.beg = MID_CT_RANDOM; + ctb->random.end = MID_CT_RANDOM+1; + break; + + case CTM_LECODE1: + case CTM_LECODE2: + ctb->version = mode == CTM_LECODE2 ? 2 : 1; + ctb->le_phase = ctb->version; + ctb->is_ct_code = 2; + + ctb->rcup_name.beg = MID_LE_RCUP_BEG; + ctb->rcup_name.end = MID_LE_RCUP_END; + ctb->bcup_name.beg = MID_LE_BCUP_BEG; + ctb->bcup_name.end = MID_LE_BCUP_END; + + ctb->track_name1.beg = MID_LE_TRACK_BEG; + ctb->track_name1.end = MID_LE_TRACK_END; + ctb->track_name2.beg = 0; + ctb->track_name2.end = 0; + + ctb->arena_name1.beg = MID_LE_ARENA_BEG; + ctb->arena_name1.end = MID_LE_ARENA_END; + ctb->arena_name2.beg = 0; + ctb->arena_name2.end = 0; + + ctb->cup_ref.beg = MID_LE_CUP_REF_BEG; + ctb->cup_ref.end = MID_LE_CUP_REF_END; + + ctb->random.beg = 0; + ctb->random.end = 0; + break; + + default: + ctb->version = 0; + ctb->is_ct_code = 0; + ctb->le_phase = 0; + + ctb->rcup_name.beg = MID_RCUP_BEG; + ctb->rcup_name.end = MID_RCUP_END; + ctb->bcup_name.beg = MID_BCUP_BEG; + ctb->bcup_name.end = MID_BCUP_END; + + ctb->track_name1.beg = MID_TRACK1_BEG; + ctb->track_name1.end = MID_TRACK1_END; + ctb->track_name2.beg = MID_TRACK2_BEG; + ctb->track_name2.end = MID_TRACK2_END; + + ctb->arena_name1.beg = MID_ARENA1_BEG; + ctb->arena_name1.end = MID_ARENA1_END; + ctb->arena_name2.beg = MID_ARENA2_BEG; + ctb->arena_name2.end = MID_ARENA2_END; + + ctb->cup_ref.beg = 0; + ctb->cup_ref.end = 0; + + ctb->random.beg = MID_RANDOM; + ctb->random.end = MID_RANDOM+1; + break; + } + + u32 min = M1(u32), max = 0; + bmg_range_t *ptr; + for ( ptr = &ctb->rcup_name; ptr <= &ctb->random; ptr++ ) + { + ptr->n = ptr->end - ptr->beg; + if (ptr->n) + { + if ( min > ptr->beg ) min = ptr->beg; + if ( max < ptr->end ) max = ptr->end; + } + } + ctb->range.beg = min; + ctb->range.end = max; + ctb->range.n = max - min; + } + return mode; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef TAB +#define TAB(nam) ctb->nam.beg, ctb->nam.n + +ccp GetCtBMGIdentification ( ct_bmg_t *ctb, bool full ) +{ + DASSERT(ctb); + + ccp name = GetCtModeNameBMG(ctb->ct_mode,false); + char buf[200]; + int len; + if (full) + len = snprintf(buf,sizeof(buf), + "%x, %s, v=%x, ct=%x, le=%x, par=%x+%x," + " rcup=%x+%x, bcup=%x+%x, t1=%x+%x, t2=%x+%x," + " a1=%x+%x, a2=%x+%x, ref=%x+%x", + ctb->ct_mode, name, ctb->version, + ctb->is_ct_code==1, ctb->le_phase, + TAB(param), + TAB(rcup_name), TAB(bcup_name), + TAB(track_name1), TAB(track_name2), + TAB(arena_name1), TAB(arena_name2), + TAB(cup_ref) ); + else + len = snprintf(buf,sizeof(buf),"%x, %s v%d",ctb->ct_mode,name,ctb->version); + return len > 0 ? CopyCircBuf(buf,len+1) : EmptyString; +} + +#undef TAB + +/////////////////////////////////////////////////////////////////////////////// + +static void DumpCtMidHelper1 ( FILE *f, int indent, u32 id, ccp info ) +{ + DASSERT(f); + if (id) + fprintf(f,"%*s%-20s %04x\n", indent, "", info, id ); +} + +//----------------------------------------------------------------------------- + +static void DumpCtMidHelper2 ( FILE *f, int indent, bmg_range_t *id, ccp info ) +{ + DASSERT(f); + DASSERT(id); + if (id->beg) + fprintf(f,"%*s%-20s %04x .. %04x, %5u = 0x%04x\n", + indent, "", info, id->beg, id->end, id->n, id->n ); +} + +//----------------------------------------------------------------------------- + +void DumpCtBMG ( FILE *f, int indent, ct_bmg_t *ctb ) +{ + DASSERT(f); + DASSERT(ctb); + indent = NormalizeIndent(indent); + + #if defined(TEST) || defined(DEBUG) + fprintf(f,"%*s%s\n",indent,"",GetCtBMGIdentification(ctb,true)); + #else + fprintf(f,"%*s%s\n",indent,"",GetCtBMGIdentification(ctb,false)); + #endif + + indent += 2; + DumpCtMidHelper1( f,indent, ctb->identify, "Identification:" ); + DumpCtMidHelper2( f,indent, &ctb->param, "Parameters:" ); + DumpCtMidHelper2( f,indent, &ctb->range, "Data range:" ); + + DumpCtMidHelper2( f,indent, &ctb->rcup_name, " Racing cup names:" ); + DumpCtMidHelper2( f,indent, &ctb->bcup_name, " Battle cup names:" ); + DumpCtMidHelper2( f,indent, &ctb->track_name1, " Track names 1:" ); + DumpCtMidHelper2( f,indent, &ctb->track_name2, " Track names 2:" ); + DumpCtMidHelper2( f,indent, &ctb->arena_name1, " Battle names 1:" ); + DumpCtMidHelper2( f,indent, &ctb->arena_name2, " Battle names 2:" ); + DumpCtMidHelper2( f,indent, &ctb->cup_ref, " Cup reference:" ); + DumpCtMidHelper1( f,indent, ctb->random.beg, " Random text:" ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + |
