aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-lecode.c
diff options
context:
space:
mode:
Diffstat (limited to 'project/src/lib-lecode.c')
-rw-r--r--project/src/lib-lecode.c4574
1 files changed, 4574 insertions, 0 deletions
diff --git a/project/src/lib-lecode.c b/project/src/lib-lecode.c
new file mode 100644
index 0000000..5bf9667
--- /dev/null
+++ b/project/src/lib-lecode.c
@@ -0,0 +1,4574 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * Wiimms SZS Tools *
+ * https://szs.wiimm.de/ *
+ * *
+ ***************************************************************************
+ * *
+ * This file is part of the SZS project. *
+ * Visit https://szs.wiimm.de/ for project details and sources. *
+ * *
+ * Copyright (c) 2011-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 *
+ * *
+ ***************************************************************************/
+
+#include "dclib-xdump.h"
+#include "lib-lecode.h"
+#include "lib-std.h"
+#include "lib-mkw.h"
+#include "lib-szs.h"
+#include "lpar.inc"
+#include "lex.inc"
+
+#include <stddef.h>
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// vars ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ccp opt_le_define = 0;
+ccp opt_lpar = 0;
+ccp opt_track_dest = 0;
+ParamField_t opt_track_source = {0};
+
+ccp opt_le_alias = 0;
+bool opt_engine_valid = false;
+u8 opt_engine[3] = {0};
+OffOn_t opt_200cc = OFFON_AUTO;
+OffOn_t opt_perfmon = OFFON_AUTO;
+OffOn_t opt_custom_tt = OFFON_AUTO;
+OffOn_t opt_xpflags = OFFON_AUTO;
+
+bool opt_complete = false;
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LEX action log ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+bool LEX_ACTION_LOG ( bool is_patch, const char * format, ... )
+{
+ #if HAVE_PRINT
+ {
+ char buf[200];
+ snprintf(buf,sizeof(buf),">>>[LEX]<<< %s",format);
+ va_list arg;
+ va_start(arg,format);
+ PRINT_ARG_FUNC(buf,arg);
+ va_end(arg);
+ }
+ #endif
+
+// if ( verbose > 2 || KMP_MODE & KMPMD_LOG || is_patch && ~KMP_MODE & KMPMD_SILENT )
+ if ( verbose > 2 || is_patch )
+ {
+ fflush(stdout);
+ fprintf(stdlog," %s>[LEX]%s ",
+ colset->heading,
+ is_patch ? colset->highlight : colset->info );
+ va_list arg;
+ va_start(arg,format);
+ vfprintf(stdlog,format,arg);
+ va_end(arg);
+ fputs(colset->reset,stdlog);
+ fflush(stdlog);
+ return true;
+ }
+
+ return false;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+uint GetNextRacingTrackLE ( uint tid )
+{
+ if ( ++tid == 0xff )
+ tid++;
+
+ if ( tid > MKW_N_TRACKS && tid < LE_FIRST_LOWER_CT_SLOT )
+ tid = LE_FIRST_LOWER_CT_SLOT;
+ return tid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptTrackSource ( ccp arg, int mode )
+{
+ if ( arg && *arg )
+ {
+ if (!opt_track_source.field)
+ InitializeParamField(&opt_track_source);
+ AppendParamField(&opt_track_source,arg,false,mode,0);
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOpt200cc ( ccp arg )
+{
+ const int stat
+ = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --200cc");
+ if ( stat == OFFON_ERROR )
+ return 1;
+
+ opt_200cc = stat;
+ PRINT("200CC=%d\n",opt_200cc);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptPerfMon ( ccp arg )
+{
+ const int stat
+ = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --perfmon");
+ if ( stat == OFFON_ERROR )
+ return 1;
+
+ opt_perfmon = stat;
+ PRINT("PERFMON=%d\n",opt_perfmon);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptCustomTT ( ccp arg )
+{
+ const int stat
+ = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --custom-tt");
+ if ( stat == OFFON_ERROR )
+ return 1;
+
+ opt_custom_tt = stat;
+ PRINT("CUSTOM-TT=%d\n",opt_custom_tt);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptXPFlags ( ccp arg )
+{
+ const int stat
+ = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --xpflags");
+ if ( stat == OFFON_ERROR )
+ return 1;
+
+ opt_xpflags = stat;
+ PRINT("XPFLAGS=%d\n",opt_xpflags);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptAlias ( ccp arg )
+{
+ if ( arg && *arg )
+ {
+ if (opt_le_alias)
+ {
+ const uint len1 = strlen(opt_le_alias);
+ const uint len2 = strlen(arg);
+ char *list = MALLOC(len1+len2+2);
+ memcpy(list,opt_le_alias,len1);
+ list[len1] = ',';
+ memcpy(list+len1+1,arg,len2+1);
+ FreeString(opt_le_alias);
+ opt_le_alias = list;
+ }
+ else
+ opt_le_alias = STRDUP(arg);
+ }
+ PRINT0("OPT ALIAS: %s|\n",opt_le_alias);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptEngine ( ccp arg )
+{
+ if ( !arg || !*arg )
+ {
+ opt_engine_valid = false;
+ return 0;
+ }
+
+ const uint MAX = sizeof(opt_engine)/sizeof(*opt_engine);
+ double val[MAX], sum = 0.0;
+ ccp src = arg;
+ uint n = 0;
+
+ for(;;)
+ {
+ if ( n > MAX )
+ break;
+ char *end;
+ double d = strtod(src,&end);
+ if ( !isnormal(d) || d <= 0.0 )
+ d = 0.0;
+ val[n++] = d;
+ sum += d;
+
+ src = end;
+ while (isspace((int)*src))
+ src++;
+ if ( *src != ',' )
+ break;
+ src++;
+ }
+
+ if ( n != 3 )
+ return ERROR0(ERR_SYNTAX,
+ "Invalid argument for option --engine, »a,b,c« expected: %s",arg);
+
+ if (sum > 0.0 )
+ {
+ sum = 100/sum;
+ uint total = 100;
+ for ( n = 0; n < MAX-1; n++ )
+ {
+ uint num = double2int( val[n] * sum );
+ if ( num > total )
+ num = total;
+ opt_engine[n] = num;
+ total -= num;
+ }
+ opt_engine[MAX-1] += total;
+ }
+ else
+ memset(opt_engine,0,sizeof(opt_engine));
+ opt_engine_valid = true;
+
+ PRINT("ENGINE: %g,%g,%g => %u + %u + %u = %u [200cc=%d])\n",
+ val[0], val[1], val[2],
+ opt_engine[0], opt_engine[1], opt_engine[2],
+ opt_engine[0] + opt_engine[1] + opt_engine[2],
+ opt_200cc );
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// setup ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const VarMap_t * SetupVarsLECODE()
+{
+ static VarMap_t vm = {0};
+ if (!vm.used)
+ DefineMkwVars(&vm);
+ return &vm;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LPAR support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+lpar_mode_t CalcCurrentLparMode ( const le_lpar_t * lp, bool use_limit_param )
+{
+ DASSERT(lp);
+
+ if ( use_limit_param && (uint)lp->limit_mode < LPM_AUTOMATIC )
+ {
+ const lpar_mode_t temp = CalcCurrentLparMode(lp,false);
+ return temp > lp->limit_mode ? temp : lp->limit_mode;
+ }
+
+ if ( lp->enable_perfmon > 1 )
+ return LPM_EXPERIMENTAL;
+
+ return lp->enable_perfmon || !lp->enable_xpflags
+ ? LPM_TESTING : LPM_PRODUCTIVE;
+}
+
+//-----------------------------------------------------------------------------
+
+ccp GetLparModeName ( lpar_mode_t lpmode, bool export )
+{
+ if (export)
+ return PrintCircBuf("%u # %s",lpmode,GetLparModeName(lpmode,false));
+
+ switch (lpmode)
+ {
+ case LPM_PRODUCTIVE: return "LE$PRODUCTIVE";
+ case LPM_TESTING: return "LE$TESTING";
+ case LPM_EXPERIMENTAL: return "LE$EXPERIMENTAL";
+ default: return "LE$AUTOMATIC";
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+bool LimitToLparMode ( le_lpar_t * lp, lpar_mode_t lpmode )
+{
+ DASSERT(lp);
+
+ bool modified = false;
+
+ if ( lpmode == LPM_PRODUCTIVE )
+ {
+ if ( lp->enable_perfmon > 0 )
+ { lp->enable_perfmon = 0; modified = true; }
+ if (!lp->enable_xpflags)
+ { lp->enable_xpflags = 1; modified = true; }
+ }
+ else if ( lpmode == LPM_TESTING )
+ {
+ if ( lp->enable_perfmon > 1 )
+ { lp->enable_perfmon = 1; modified = true; }
+ }
+
+ return modified;
+}
+
+//-----------------------------------------------------------------------------
+
+void ClearLparChat ( le_lpar_t * lp )
+{
+ DASSERT(lp);
+ memset(lp->chat_mode_1,0,sizeof(lp->chat_mode_1));
+ memset(lp->chat_mode_1,0,sizeof(lp->chat_mode_2));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define RACE0 (CHATMD_N_RACE_1-1)
+
+const u16 chat_mode_legacy[BMG_N_CHAT] =
+{
+ 0,0,0,0, 0,0,0,0, // 1 .. 8
+ 0,0,0,0, 0,0,0,0, // 9 .. 16
+ 0,0,0,0, 0,0,0,0, // 17 .. 24
+ 0,0,0,0, 0,0,0,0, // 25 .. 32
+ 0,0,0,0, 0,0,0,0, // 33 .. 40
+ 0,0,0,0, 0,0,0,0, // 41 .. 48
+ 0,0,0,0, 0,0,0,0, // 49 .. 56
+ 0,0,0,0, 0,0,0,0, // 57 .. 64
+
+ CHATMD_ANY_VEHICLE, 0,0,0, // 65 .. 68
+ CHATMD_KARTS_ONLY, // 69
+ CHATMD_BIKES_ONLY, // 70
+ CHATMD_TRACK_BY_HOST, // 71
+ CHATMD_ANY_TRACK, // 72
+
+ RACE0 + 1, // 73
+ RACE0 + 2, // 74
+ RACE0 + 3, // 75
+ RACE0 + 4, // 76
+ RACE0 + 5, // 77
+ RACE0 + 6, // 78
+ RACE0 + 8, // 79
+ RACE0 + 10, // 80
+
+ 0,0,0,0, 0,0,0,0, // 81 .. 88
+ 0,0,0,0, 0,0,0,0, // 89 .. 96
+};
+
+//-----------------------------------------------------------------------------
+
+const u16 chat_mode_mkwfun[BMG_N_CHAT] =
+{
+ 0,0,0,0, 0,0,0,0, // 1 .. 8
+ 0,0,0,0, 0,0,0,0, // 9 .. 16
+ 0,0,0,0, 0,0,0,0, // 17 .. 24
+ 0,0,0,0, 0,0,0,0, // 25 .. 32
+ 0,0,0,0, 0,0,0,0, // 33 .. 40
+ 0,0,0,0, 0,0,0,0, // 41 .. 48
+ 0,0,0,0, 0,0,0,0, // 49 .. 56
+ 0,0,0,0, // 57 .. 60
+
+ CHATMD_RESET_ENGINE, // 61
+ CHATMD_USE_ENGINE_1, // 62
+ CHATMD_USE_ENGINE_2, // 63
+ CHATMD_USE_ENGINE_3, // 64
+
+ CHATMD_ANY_VEHICLE, // 65
+ CHATMD_KARTS_ONLY, // 66
+ CHATMD_BIKES_ONLY, // 67
+ 0, // 68
+
+ CHATMD_ANY_TRACK, // 69
+ CHATMD_TRACK_BY_HOST, // 70
+ 0, // 71
+ 0, // 72
+
+ RACE0 + 1, // 73
+ RACE0 + 2, // 74
+ RACE0 + 3, // 75
+ RACE0 + 4, // 76
+ RACE0 + 5, // 77
+ RACE0 + 6, // 78
+ RACE0 + 8, // 79
+ RACE0 + 10, // 80
+
+ 0,0,0,0, 0,0,0,0, // 81 .. 88
+ 0,0,0,0, 0,0,0,0, // 89 .. 96
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetChatModeName ( u16 mode )
+{
+ char buf[100];
+
+ switch (mode)
+ {
+ case CHATMD_OFF: return "CHAT$OFF";
+
+ case CHATMD_RESET: return "CHAT$RESET";
+
+ case CHATMD_ANY_TRACK: return "CHAT$ANY_TRACK";
+ case CHATMD_TRACK_BY_HOST: return "CHAT$TRACK_BY_HOST";
+
+ case CHATMD_BLOCK_CLEAR: return "CHAT$BLOCK_CLEAR";
+ case CHATMD_BLOCK_DISABLE: return "CHAT$BLOCK_DISABLE";
+ case CHATMD_BLOCK_ENABLE: return "CHAT$BLOCK_ENABLE";
+
+ case CHATMD_ANY_VEHICLE: return "CHAT$ANY_VEHICLE";
+ case CHATMD_KARTS_ONLY: return "CHAT$KARTS_ONLY";
+ case CHATMD_BIKES_ONLY: return "CHAT$BIKES_ONLY";
+
+ case CHATMD_RESET_ENGINE: return "CHAT$RESET_ENGINE";
+ case CHATMD_USE_ENGINE_1: return "CHAT$USE_ENGINE_1";
+ case CHATMD_USE_ENGINE_2: return "CHAT$USE_ENGINE_2";
+ case CHATMD_USE_ENGINE_3: return "CHAT$USE_ENGINE_3";
+ }
+
+
+ //--- vehicles
+
+ if ( mode >= CHATMD_VEHICLE_BEG && mode <= CHATMD_VEHICLE_END )
+ {
+ mode -= CHATMD_VEHICLE_BEG;
+
+ struct mode_t { u8 mode; char name[9]; };
+ static const struct mode_t tab[] =
+ {
+// { CHATVEH_ANY, "ANY" },
+
+ { CHATVEH_ANY_SIZE, "ANY_SIZE" },
+ { CHATVEH_SMALL, "SMALL" },
+ { CHATVEH_MEDIUM, "MEDIUM" },
+ { CHATVEH_LARGE, "LARGE" },
+
+ { CHATVEH_ANY_TYPE, "ANY_TYPE" },
+ { CHATVEH_KART, "KART" },
+ { CHATVEH_BIKE, "BIKE" },
+ { CHATVEH_OUT_BIKE, "OUT_BIKE" },
+ { CHATVEH_IN_BIKE, "IN_BIKE" },
+
+ { 0, "" },
+ };
+
+ char *dest = buf;
+ dest[1] = 0;
+ const struct mode_t *ptr;
+ for ( ptr = tab; ptr->mode && mode; ptr++ )
+ if ( (ptr->mode & mode) == ptr->mode )
+ {
+ mode &= ~ptr->mode;
+ dest = StringCat2E(dest,buf+sizeof(buf),",VEH$",ptr->name);
+ }
+ return PrintCircBuf("chat$vehicles(%s)",buf+1);
+ }
+
+
+ //--- n races
+
+ if ( mode >= CHATMD_N_RACE_1 && mode <= CHATMD_N_RACE_MAX )
+ return PrintCircBuf("chat$n_races(%u)",mode-(CHATMD_N_RACE_1-1));
+
+
+ //--- fallback
+
+ return PrintCircBuf("#%x",mode);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeLPAR ( le_lpar_t *lpar, bool load_lpar )
+{
+ DASSERT(lpar);
+ memset(lpar,0,sizeof(*lpar));
+
+ lpar->limit_mode = LPM__DEFAULT;
+
+ lpar->engine[0] = 10;
+ lpar->engine[1] = 60;
+ lpar->engine[2] = 30;
+ lpar->enable_xpflags= 1;
+ lpar->block_track = 0;
+
+ if ( load_lpar && opt_lpar )
+ LoadLPAR(lpar,false,0,false);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void NormalizeLPAR ( le_lpar_t * lp )
+{
+ DASSERT(lp);
+
+ int sum = lp->engine[0] + lp->engine[1] + lp->engine[2];
+ if ( sum != 0 && sum != 100 )
+ {
+ double factor = 100.0/sum;
+ lp->engine[0] = lp->engine[0] * factor;
+ lp->engine[1] = lp->engine[1] * factor;
+ sum = lp->engine[0] + lp->engine[1];
+ if ( sum > 100 )
+ lp->engine[1] -= sum-100;
+ lp->engine[2] = 100 - lp->engine[0] - lp->engine[1];
+ }
+
+ lp->enable_200cc = lp->enable_200cc > 0;
+ lp->enable_perfmon = lp->enable_perfmon < 2 ? lp->enable_perfmon : 2;
+ lp->enable_custom_tt = lp->enable_custom_tt > 0;
+ lp->enable_xpflags = lp->enable_xpflags > 0;
+ lp->block_track = lp->block_track < LE_MAX_BLOCK_TRACK
+ ? lp->block_track : LE_MAX_BLOCK_TRACK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void CopyLPAR2Data ( le_analyse_t * ana )
+{
+ DASSERT(ana);
+
+ NormalizeLPAR(&ana->lpar);
+ const le_lpar_t *lp = &ana->lpar;
+ le_binpar_v1_t *h = ana->param_v1;
+
+ if ( offsetof(le_binpar_v1_t,engine) < ana->param_size )
+ memcpy(h->engine,lp->engine,sizeof(h->engine));
+
+ if ( offsetof(le_binpar_v1_t,enable_200cc) < ana->param_size )
+ h->enable_200cc = lp->enable_200cc;
+
+ if ( offsetof(le_binpar_v1_t,enable_perfmon) < ana->param_size )
+ h->enable_perfmon = lp->enable_perfmon;
+
+ if ( offsetof(le_binpar_v1_t,enable_custom_tt) < ana->param_size )
+ h->enable_custom_tt = lp->enable_custom_tt;
+
+ if ( offsetof(le_binpar_v1_t,enable_xpflags) < ana->param_size )
+ h->enable_xpflags = lp->enable_xpflags;
+
+ if ( offsetof(le_binpar_v1_t,block_track) < ana->param_size )
+ h->block_track = lp->block_track;
+
+ if ( offsetof(le_binpar_v1_t,chat_mode_1) + sizeof(h->chat_mode_1) <= ana->param_size )
+ write_be16n(h->chat_mode_1,lp->chat_mode_1,BMG_N_CHAT);
+
+ if ( offsetof(le_binpar_v1_t,chat_mode_2) + sizeof(h->chat_mode_2) <= ana->param_size )
+ write_be16n(h->chat_mode_2,lp->chat_mode_2,BMG_N_CHAT);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SaveTextLPAR
+(
+ const le_lpar_t *lpar, // LE-CODE parameters
+ ccp fname, // filename of destination
+ bool set_time // true: set time stamps
+)
+{
+ DASSERT(lpar);
+ DASSERT(fname);
+ PRINT("SaveTextLEX(%s,%d)\n",fname,set_time);
+
+
+ //--- open file
+
+ File_t F;
+ enumError err = CreateFileOpt(&F,true,fname,testmode,fname);
+ if ( err > ERR_WARNING || !F.f )
+ return err;
+
+
+ //--- print header + syntax info
+
+ if ( print_header && !brief_count && !export_count )
+ fprintf(F.f,text_lpar_head_cr);
+ else
+ fprintf(F.f,"%s\r\n",LE_LPAR_MAGIC);
+
+
+ //--- print section
+
+ err = WriteSectionLPAR(F.f,lpar);
+
+
+ //--- print footer
+
+ fputs(section_end,F.f);
+ ResetFile(&F,set_time);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp PrintChatMode ( u16 msg, u16 mode1, u16 mode2, int numeric )
+{
+ msg++;
+ if ( mode1 && mode2 && mode1 != mode2 )
+ {
+ if (numeric)
+ return PrintCircBuf("M%02u = %4u, %4u # %s, %s",
+ msg, mode1, mode2,
+ GetChatModeName(mode1), GetChatModeName(mode2) );
+ return PrintCircBuf("M%02u = %s, %s",
+ msg, GetChatModeName(mode1), GetChatModeName(mode2) );
+ }
+ else if ( mode1 || mode2 )
+ {
+ if (!mode1)
+ mode1 = mode2;
+ if (numeric)
+ return PrintCircBuf("M%02u = %4u # %s",
+ msg, mode1, GetChatModeName(mode1) );
+ return PrintCircBuf("M%02u = %s", msg, GetChatModeName(mode1) );
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError WriteSectionLPAR
+(
+ FILE *f, // output files
+ const le_lpar_t *lpar // LE-CODE parameters
+)
+{
+ // use DOS/Windows line format -> unix can handle it ;)
+
+ DASSERT(f);
+ DASSERT(lpar);
+
+ //--- general parameters
+
+ fprintf(f,"\r\n#\f\r\n%.79s\r\n\r\n[LECODE-PARAMETERS]\r\n",Hash200);
+
+ const bool verbose = print_header && !brief_count && !export_count;
+ if (verbose)
+ fprintf(f,text_lpar_sect_param_cr
+ ,GetLparModeName(CalcCurrentLparMode(lpar,true),export_count>0)
+ ,lpar->engine[0],lpar->engine[1],lpar->engine[2]
+ ,lpar->enable_200cc
+ ,lpar->enable_perfmon
+ ,lpar->enable_custom_tt
+ ,lpar->enable_xpflags
+ ,LE_MAX_BLOCK_TRACK
+ ,lpar->block_track
+ );
+ else
+ fprintf(f,
+ "\r\n"
+ "LIMIT-MODE = %s\r\n\r\n"
+ "ENGINE = %u,%u,%u\r\n"
+ "ENABLE-200CC = %u\r\n"
+ "PERF-MONITOR = %u\r\n"
+ "CUSTOM-TT = %u\r\n"
+ "XPFLAGS = %u\r\n"
+ "BLOCK-TRACK = %u\r\n"
+ ,GetLparModeName(CalcCurrentLparMode(lpar,true),export_count>0)
+ ,lpar->engine[0],lpar->engine[1],lpar->engine[2]
+ ,lpar->enable_200cc
+ ,lpar->enable_perfmon
+ ,lpar->enable_custom_tt
+ ,lpar->enable_xpflags
+ ,lpar->block_track
+ );
+
+
+ //--- chat modes
+
+ fprintf(f,"\r\n#\f\r\n%.79s\r\n\r\n[CHAT-MESSAGE-MODES]\r\n",Hash200);
+
+ if (verbose)
+ {
+ fputs(text_lpar_sect_chat_cr,f);
+
+ int i;
+ for ( i = 0; i < BMG_N_CHAT; i++ )
+ if ( lpar->chat_mode_1[i] || lpar->chat_mode_2[i] )
+ break;
+ if ( i == BMG_N_CHAT )
+ fputs(text_lpar_sect_chat_example_cr,f);
+ }
+
+ int i, last_group = -1;
+ for ( i = 0; i < BMG_N_CHAT; i++ )
+ {
+ const u16 mode1 = lpar->chat_mode_1[i];
+ const u16 mode2 = lpar->chat_mode_2[i];
+ if ( mode1 || mode2 )
+ {
+ if ( last_group != i/4 )
+ {
+ last_group = i/4;
+ fputs("\r\n",f);
+ }
+
+ fprintf(f,"%s\n",PrintChatMode(i,mode1,mode2,export_count));
+ if (export_count)
+ fprintf(f,"M%02u = %4u # %s\n",i+1,mode1,GetChatModeName(mode1));
+ else
+ fprintf(f,"M%02u = %s\n",i+1,GetChatModeName(mode1));
+ }
+ }
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// analyse ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetLEAnalyse ( le_analyse_t *ana )
+{
+ if (ana)
+ {
+ ResetLEAnalyseUsage(ana);
+ memset(ana,0,sizeof(*ana));
+ InitializeLPAR(&ana->lpar,false);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetLEAnalyseUsage ( le_analyse_t *ana )
+{
+ if (ana)
+ {
+ if (ana->usage)
+ {
+ FREE(ana->usage);
+ ana->usage = 0;
+ }
+ ana->usage_size = 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError AnalyseLEBinary
+(
+ le_analyse_t *ana, // NULL or destination of analysis
+ const void *data, // data pointer
+ uint data_size // data size
+)
+{
+ //--- setup analyse
+
+ le_analyse_t ana0 = {0};
+ if (!ana)
+ {
+ ResetLEAnalyse(&ana0);
+ ana = &ana0;
+ }
+ memset(ana,0,sizeof(*ana));
+
+ if ( !data
+ || data_size < sizeof(le_binary_head_t)
+ || memcmp(data,LE_BINARY_MAGIC,4) )
+ {
+ return ERR_INVALID_DATA;
+ }
+
+ le_binary_head_t *head = (le_binary_head_t*)data;
+ const uint head_size = ntohl(head->size);
+ if ( head_size > data_size )
+ return ERR_INVALID_DATA;
+
+ ana->valid = LE_HEAD_FOUND;
+ ana->version = head->phase;
+ ana->data = data;
+ ana->size = data_size;
+ ana->head = head;
+ ana->head_vers = ntohl(head->version);
+ ana->head_size = head_size;
+ ana->end_of_data = (u8*)data + head_size;
+
+ uint off_param = 0;
+ switch(ana->head_vers)
+ {
+ case 4:
+ if ( ana->size > sizeof(le_binary_head_v4_t) )
+ {
+ ana->valid |= LE_HEAD_VALID | LE_HEAD_KNOWN;
+ off_param = ntohl(ana->head_v4->off_param);
+ }
+ break;
+
+ default:
+ // at least v4
+ if ( ana->head_vers > 4 && ana->size > sizeof(le_binary_head_t) )
+ {
+ ana->valid |= LE_HEAD_VALID;
+ off_param = ntohl(head->off_param);
+ }
+ break;
+ }
+
+
+ //--- analyse parameters (prepare)
+
+ typedef struct ptr_ana_t
+ {
+ int off_offset;
+ int off_ptr;
+ }
+ ptr_ana_t;
+
+ #undef DEF_TAB
+ #define DEF_TAB(s,o,p) { offsetof(s,o), offsetof(le_analyse_t,p) },
+
+ const u8 *ptr_list[8]; // for section-size analysis
+ uint n_ptr = 0; // number of used pointers
+
+
+ //--- analyse parameters
+
+ if ( off_param
+ && !(off_param&3)
+ && ana->size >= off_param + sizeof(le_binary_param_t)
+ && !memcmp(data+off_param,LE_PARAM_MAGIC,4) )
+ {
+ le_binary_param_t *param = (le_binary_param_t*)(data+off_param);
+ const uint param_size = ntohl(param->size);
+ if ( off_param + param_size <= data_size
+ && off_param + param_size <= ana->head_size )
+ {
+ ana->valid |= LE_PARAM_FOUND;
+ ana->param = param;
+ ana->param_offset = off_param;
+ ana->param_vers = ntohl(param->version);
+ ana->param_size = param_size;
+
+ const uint max_off = ana->head_size - off_param;
+
+ switch(ana->param_vers)
+ {
+ case 1:
+ if ( param_size >= sizeof(le_binpar_v1_35_t) )
+ {
+ static const ptr_ana_t tab[] =
+ {
+ DEF_TAB( le_binpar_v1_35_t, off_eod, end_of_data )
+ DEF_TAB( le_binpar_v1_35_t, off_cup_par, cup_par )
+ DEF_TAB( le_binpar_v1_35_t, off_cup_track, cup_track )
+ DEF_TAB( le_binpar_v1_35_t, off_cup_arena, cup_arena )
+ DEF_TAB( le_binpar_v1_35_t, off_course_par, course_par )
+ DEF_TAB( le_binpar_v1_35_t, off_property, property )
+ DEF_TAB( le_binpar_v1_35_t, off_music, music )
+ DEF_TAB( le_binpar_v1_35_t, off_flags, flags )
+ {-1,-1}
+ };
+
+ ana->valid |= LE_PARAM_VALID | LE_PARAM_KNOWN;
+ le_binpar_v1_35_t *p = (le_binpar_v1_35_t*)(data+off_param);
+
+ const ptr_ana_t *tptr;
+ u32 min = max_off, max = 0;
+ for ( tptr = tab; tptr->off_offset >= 0; tptr++ )
+ {
+ const u32 offset = be32((u8*)p+tptr->off_offset);
+ if ( offset && offset < max_off )
+ {
+ if ( min > offset ) min = offset;
+ if ( max < offset ) max = offset;
+ u8 *ptr = (u8*)data + off_param + offset;
+ *(u8**)((u8*)ana+tptr->off_ptr) = ptr;
+ DASSERT( n_ptr < sizeof(ptr_list)/sizeof(*ptr_list) );
+ ptr_list[n_ptr++] = ptr;
+ }
+ }
+
+ ana->beg_of_data = (u8*)p + min;
+ ana->end_of_data = (u8*)p + max;
+
+ memcpy(ana->lpar.engine,p->engine,sizeof(ana->lpar.engine));
+ ana->lpar.enable_200cc = p->enable_200cc;
+ ana->lpar.enable_perfmon = p->enable_perfmon;
+ }
+
+ if ( param_size >= sizeof(le_binpar_v1_37_t) )
+ {
+ le_binpar_v1_37_t *p = (le_binpar_v1_37_t*)(data+off_param);
+ ana->lpar.enable_custom_tt = p->enable_custom_tt;
+ ana->lpar.enable_xpflags = p->enable_xpflags;
+ }
+
+ if ( param_size >= sizeof(le_binpar_v1_f8_t) )
+ {
+ le_binpar_v1_f8_t *p = (le_binpar_v1_f8_t*)(data+off_param);
+ ana->lpar.block_track = p->block_track;
+ be16n(ana->lpar.chat_mode_1,p->chat_mode,BMG_N_CHAT);
+ }
+ else
+ memcpy(ana->lpar.chat_mode_1,chat_mode_legacy,sizeof(ana->lpar.chat_mode_1));
+ break;
+
+ if ( param_size >= sizeof(le_binpar_v1_1b8_t) )
+ {
+ le_binpar_v1_1b8_t *p = (le_binpar_v1_1b8_t*)(data+off_param);
+ be16n(ana->lpar.chat_mode_2,p->chat_mode_2,BMG_N_CHAT);
+ }
+ break;
+
+ default:
+ // at least v1
+ if ( ana->param_vers > 1 && param_size >= sizeof(le_binary_param_t) )
+ {
+ ana->valid |= LE_PARAM_VALID;
+ }
+ break;
+ }
+ }
+
+ ana->n_cup_track = ana->cup_par ? ntohl(ana->cup_par->n_racing_cups) : 0;
+ ana->n_cup_arena = ana->cup_par ? ntohl(ana->cup_par->n_battle_cups) : 0;
+ ana->n_slot = ana->course_par ? ntohl(ana->course_par->n_slot) : 0;
+ }
+
+
+ if (ana->end_of_data)
+ {
+ const int size = ana->data + ana->size - ana->end_of_data;
+ if ( size >= 0 )
+ {
+ ana->bin_data = ana->end_of_data;
+ ana->bin_size = size;
+ }
+ }
+
+
+ //--- analyse n+max
+
+ typedef struct ana_n_max_t
+ {
+ int off_ptr;
+ int off_n;
+ int off_max;
+ uint elem_size;
+ }
+ ana_n_max_t;
+
+ #undef DEF_TAB
+ #define DEF_TAB(p,n,m,s) { offsetof(le_analyse_t,p), \
+ offsetof(le_analyse_t,n), \
+ offsetof(le_analyse_t,m), \
+ sizeof(*ana->p)*s },
+
+ static const ana_n_max_t n_max_tab[] =
+ {
+ DEF_TAB( cup_track, n_cup_track, max_cup_track, 4 )
+ DEF_TAB( cup_arena, n_cup_arena, max_cup_arena, 5 )
+ DEF_TAB( property, n_slot, max_property, 1 )
+ DEF_TAB( music, n_slot, max_music, 1 )
+ DEF_TAB( flags, n_slot, max_flags, 1 )
+ {-1,-1,-1}
+ };
+
+ #undef DEF_TAB
+
+ const ana_n_max_t *pnm;
+
+ // initialize max
+ for ( pnm = n_max_tab; pnm->off_ptr >= 0; pnm++ )
+ *(uint*)((u8*)ana+pnm->off_max) = ~0;
+
+ // calculate max
+ for ( pnm = n_max_tab; pnm->off_ptr >= 0; pnm++ )
+ {
+ const u8 *ptr = *(u8**)((u8*)ana+pnm->off_ptr);
+ const u8 *end = data + ana->head_size;
+ uint i;
+ for ( i = 0; i < n_ptr; i++ )
+ if ( ptr_list[i] > ptr && ptr_list[i] < end )
+ end = ptr_list[i];
+
+ const uint max = (end-ptr) / pnm->elem_size;
+ if ( *(uint*)((u8*)ana+pnm->off_max) > max )
+ *(uint*)((u8*)ana+pnm->off_max) = max;
+ }
+
+ CalculateStatsLE(ana);
+
+
+ //--- return
+
+ return ana->valid & LE_PARAM_VALID ? ERR_OK :
+ ana->valid & LE_HEAD_VALID ? ERR_MISSING_PARAM
+ : ERR_INVALID_DATA;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CalculateStatsLE ( le_analyse_t *ana )
+{
+ ResetLEAnalyseUsage(ana);
+
+ ana->used_rslots = 0;
+ ana->max_rslots = 0;
+ ana->used_bslots = 0;
+ ana->max_bslots = 0;
+
+ ana->max_slot = ana->max_property < ana->max_music
+ ? ana->max_property : ana->max_music;
+ if ( ana->max_slot > ana->max_flags )
+ ana->max_slot = ana->max_flags;
+ if ( ana->max_slot < LE_FIRST_LOWER_CT_SLOT )
+ return;
+
+
+ //-- count racing slots
+
+ uint used = 0, total = 0, slot;
+ for ( slot = 0; slot < MKW_N_TRACKS; slot++ )
+ {
+ total++;
+ if (IsLESlotUsed(ana,slot))
+ used++;
+ }
+
+ for ( slot = LE_FIRST_LOWER_CT_SLOT; slot < ana->n_slot; slot++ )
+ {
+ total++;
+ if (IsLESlotUsed(ana,slot))
+ used++;
+ }
+ ana->used_rslots = used;
+ ana->max_rslots = total;
+
+
+ //-- count battle slots
+
+ used = total = 0;
+ for ( slot = MKW_ARENA_BEG; slot < MKW_ARENA_END; slot++ )
+ {
+ total++;
+ if (IsLESlotUsed(ana,slot))
+ used++;
+ }
+ ana->used_bslots = used;
+ ana->max_bslots = total;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const le_usage_t * GetLEUsage ( const le_analyse_t *ana0, bool force_recalc )
+{
+ le_analyse_t *ana = (le_analyse_t*)ana0;
+ if ( force_recalc && ana->usage )
+ ResetLEAnalyseUsage(ana);
+
+ if ( ana->usage || ana->max_slot < LE_FIRST_LOWER_CT_SLOT )
+ return ana->usage;
+
+ ana->usage_size = ana->n_slot > 0x100 ? ana->n_slot : 0x100;
+ le_usage_t *usage = CALLOC(ana->usage_size,sizeof(*usage));
+ ana->usage = usage;
+ memset(usage,LEU_S_UNUSED,ana->usage_size);
+
+
+ //-- special slots
+
+ memset(usage+0x36,LEU_S_SPECIAL,5);
+ usage[0x42] = LEU_S_SPECIAL;
+ usage[0x43] = LEU_S_NETWORK|LEU_F_ONLINE;
+
+
+ //-- random slots
+
+ memset( usage + LE_FIRST_RANDOM_SLOT,
+ LEU_S_WIIMM|LEU_F_ONLINE,
+ LE_LAST_RANDOM_SLOT - LE_FIRST_RANDOM_SLOT+1 );
+
+
+ //-- check racing slots
+
+ uint slot;
+ for ( slot = 0; slot < MKW_N_TRACKS; slot++ )
+ usage[slot] = IsLESlotUsed(ana,slot)
+ ? LEU_S_TRACK|LEU_F_ONLINE : LEU_F_ONLINE;
+
+ for ( slot = LE_FIRST_LOWER_CT_SLOT; slot < ana->usage_size; slot++ )
+ {
+ if (IsLESlotUsed(ana,slot))
+ {
+ const le_flags_t flags = ana->flags ? ana->flags[slot] : 0;
+ usage[slot] = flags & LETF_ALIAS ? LEU_S_ALIAS
+ : flags & LETF_RND_HEAD ? LEU_S_TRACK_RND
+ : flags & LETF_RND_GROUP ? LEU_S_TRACK_HIDE
+ : LEU_S_TRACK;
+ }
+
+ if ( ana->version > 1 || slot < 0xff
+ || slot >= LE_FIRST_UPPER_CT_SLOT && slot <= LE_LAST_UPPER_CT_SLOT )
+ usage[slot] |= LEU_F_ONLINE;
+ }
+
+
+ //-- check battle slots
+
+ for ( slot = MKW_ARENA_BEG; slot < MKW_ARENA_END; slot++ )
+ {
+ if (IsLESlotUsed(ana,slot))
+ {
+ const le_flags_t flags = ana->flags ? ana->flags[slot] : 0;
+ usage[slot] = flags & LETF_ALIAS ? LEU_S_ALIAS
+ : flags & LETF_RND_HEAD ? LEU_S_ARENA_RND
+ : flags & LETF_RND_GROUP ? LEU_S_ARENA_HIDE
+ : LEU_S_ARENA;
+ }
+ }
+
+
+ //--- override Network.random
+
+ if ( ana->version == 1 )
+ usage[0xff] = LEU_S_NETWORK|LEU_F_ONLINE;
+
+ return usage;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char usage_ch[] = "-AraTRtsn*W>----";
+
+char GetLEUsageChar ( le_usage_t usage )
+{
+ return usage_ch[usage & LEU_S_MASK];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetLEUsageCharCol ( le_usage_t usage, const ColorSet_t *colset, ccp * prev_col )
+{
+ const char ch = usage_ch[usage & LEU_S_MASK];
+
+ if ( colset && colset->colorize )
+ {
+ ccp col;
+ switch ( usage & LEU_S_MASK )
+ {
+ case LEU_S_UNUSED: col = colset->reset; break;
+
+ case LEU_S_ARENA: col = colset->b_cyan; break;
+ case LEU_S_ARENA_RND: col = colset->b_yellow; break;
+ case LEU_S_ARENA_HIDE: col = colset->b_green; break;
+ case LEU_S_TRACK: col = colset->b_cyan_blue; break;
+ case LEU_S_TRACK_RND: col = colset->b_yellow; break;
+ case LEU_S_TRACK_HIDE: col = colset->green_cyan; break;
+
+ case LEU_S_SPECIAL: col = colset->reset; break;
+ case LEU_S_NETWORK: col = colset->b_orange; break;
+ case LEU_S_RANDOM: col = colset->b_yellow; break;
+ case LEU_S_WIIMM: col = colset->b_orange; break;
+ case LEU_S_ALIAS: col = colset->b_red; break;
+ default: col = colset->b_magenta; break;
+ }
+
+ if ( !prev_col || *prev_col != col )
+ {
+ if (prev_col)
+ *prev_col = col;
+ const int clen = strlen(col);
+ char *buf = GetCircBuf(clen+2);
+ memcpy(buf,col,clen);
+ buf[clen] = ch;
+ buf[clen+1] = 0;
+ return buf;
+ }
+ }
+
+ return CopyCircBuf0(&ch,1);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// dump ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+typedef enum
+{
+ CHECK_OFF, // don't check
+ CHECK_TRACK, // check for racing tracks
+ CHECK_ARENA // check for battle arenas
+}
+slot_check_t;
+
+//-----------------------------------------------------------------------------
+
+ccp GetLEValid ( le_valid_t valid )
+{
+ char *buf = GetCircBuf(7);
+ DASSERT(buf);
+
+ buf[0] = valid & LE_HEAD_FOUND ? 'F' : '-';
+ buf[1] = valid & LE_HEAD_VALID ? 'V' : '-';
+ buf[2] = valid & LE_HEAD_KNOWN ? 'K' : '-';
+ buf[3] = valid & LE_PARAM_FOUND ? 'f' : '-';
+ buf[4] = valid & LE_PARAM_VALID ? 'v' : '-';
+ buf[5] = valid & LE_PARAM_KNOWN ? 'k' : '-';
+ buf[6] = 0;
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+
+static ccp GetSlotInfo
+(
+ const le_analyse_t *ana, // valid structure
+ int tid, // track id
+ ColorSet_t *col, // NULL or color set for warnings
+ slot_check_t check, // check modus
+ uint *warnings // not NULL: increment on warnings
+)
+{
+ if ( tid < ana->n_slot )
+ {
+ uint warn = 0;
+
+ //-- property slot
+
+ ccp prop1 = "", prop0 = "";
+ const int prop = ana->property ? ana->property[tid] : -1;
+ ccp propx = GetMkwTrackName3(prop);
+ if ( check == CHECK_TRACK && ( IsMkwArena(tid) || IsMkwArena(prop) )
+ || check == CHECK_ARENA && ( !IsMkwArena(tid) || !IsMkwArena(prop) )
+ )
+ {
+ warn |= 2;
+ if (col)
+ {
+ prop1 = col->bad;
+ prop0 = col->reset;
+ }
+ }
+ else if ( IsMkwOriginal(tid) && tid != prop )
+ {
+ warn |= 1;
+ if (col)
+ {
+ prop1 = col->hint;
+ prop0 = col->reset;
+ }
+ }
+
+
+ //-- music slot
+
+ ccp music1 = "", music0 = "";
+ const int music = ana->music ? ana->music[tid] : -1;
+ ccp musicx = GetMkwMusicName3(music);
+ if ( tid < MKW_ORIGINAL_END || tid >= LE_FIRST_LOWER_CT_SLOT )
+ {
+ if ( music < MKW_MUSIC_MIN_ID || music > MKW_MUSIC_MAX_ID )
+ {
+ warn |= 2;
+ if (col)
+ {
+ music1 = col->bad;
+ music0 = col->reset;
+ }
+ }
+ else if (!(music&1))
+ {
+ warn |= 1;
+ if (col)
+ {
+ music1 = col->hint;
+ music0 = col->reset;
+ }
+ }
+ }
+
+
+ //-- flags slot
+
+ ccp flags;
+ char flags_buf[8];
+ bool is_alias = false;
+ if (ana->flags)
+ {
+ const le_flags_t fl = ana->flags[tid];
+ if ( fl & ~LETF__ALL )
+ snprintf(flags_buf,sizeof(flags_buf),"?%03x",fl);
+ else
+ {
+ if ( fl & LETF_ALIAS )
+ is_alias = true;
+
+ snprintf(flags_buf,sizeof(flags_buf),"%c%c%c%c",
+ fl & LETF_NEW ? 'N' : '-',
+ fl & LETF_RND_HEAD ? 'H' : '-',
+ fl & LETF_RND_GROUP ? 'G' : '-',
+ fl & LETF_ALIAS ? 'A' : '-' );
+ }
+ flags = flags_buf;
+ }
+ else
+ flags = "-?-";
+
+ static const char warn_list[] = " <!!";
+ char buf[40];
+ const int len = is_alias
+ ? snprintf(buf,sizeof(buf),"[%s->%4x %s,%s] ",
+ col->differ, TrackByAliasLE(prop,music),
+ col->reset, flags )
+ : snprintf(buf,sizeof(buf),"[%s%s%s,%s%s%s,%s]%c",
+ prop1, propx, prop0,
+ music1, musicx, music0,
+ flags, warn_list[warn] );
+ if ( len > 0 )
+ return CopyCircBuf0(buf,len);
+ }
+
+ if (col)
+ {
+ static char fail[40] = {0};
+ if (!*fail)
+ snprintf(fail,sizeof(fail),"[%s---,---,----%s]!",col->bad,col->reset);
+ return fail;
+ }
+ return "[---,---,---]!";
+}
+
+//-----------------------------------------------------------------------------
+
+static le_cup_track_t * DumpLECup
+(
+ FILE *f, // print file
+ const le_analyse_t *ana, // valid structure
+ const le_cup_track_t *cp, // pointer to cup
+ uint n_track, // number of tracks in cup
+ ColorSet_t *col, // NULL or color set for warnings
+ slot_check_t check, // check modus
+ uint *warnings, // not NULL: store numebr of tracks warnings
+ u16 *done // not NULL: mark slot as done
+)
+{
+ if (warnings)
+ *warnings = 0;
+
+ uint tr;
+ for ( tr = 0; tr < n_track; tr++ )
+ {
+ const uint tid = htonl(*cp++);
+ fprintf(f," %3x%s",tid,GetSlotInfo(ana,tid,col,check,warnings));
+
+ if ( done && tid < ana->n_slot )
+ done[tid]++;
+ }
+ return (le_cup_track_t*)cp;
+}
+
+//-----------------------------------------------------------------------------
+
+static void DumpLETracks
+ ( FILE *f, uint indent, ColorSet_t *col, const le_analyse_t *ana )
+{
+ DASSERT(f);
+ DASSERT(indent>=2);
+ DASSERT(col);
+ DASSERT(ana);
+
+ u16 *done = CALLOC(sizeof(*done),ana->n_slot);
+ DASSERT(done);
+ if ( ana->version == 1 )
+ {
+ uint max = ana->n_slot < LE_FIRST_UPPER_CT_SLOT
+ ? ana->n_slot : LE_FIRST_UPPER_CT_SLOT;
+ uint i;
+ for ( i = 0xff; i < max; i++ )
+ done[i]++;
+ }
+
+ static const char id_info[] = "id[prop,music,new+head+group+alias]";
+
+
+ //--- battle cups
+
+ if ( ana->n_cup_track && ana->cup_track )
+ {
+ fprintf(f,"\n%*s%s" "%u cup%s with battle tracks (%s):%s\n",
+ indent-2,"", col->heading,
+ ana->n_cup_arena, ana->n_cup_arena == 1 ? "" : "s",
+ id_info, col->reset );
+
+ uint cup, hidden = 0;
+ const le_cup_track_t *cp = ana->cup_arena;
+ for ( cup = 0; cup < ana->n_cup_arena; cup++ )
+ {
+ if ( cp[0] || cp[1] || cp[2] || cp[3] || cp[4] )
+ {
+ fprintf(f,"%*s" "Cup %2u:",indent,"",cup);
+ uint warnings;
+ DumpLECup(f,ana,cp,5,col,CHECK_ARENA,&warnings,done);
+ if (warnings)
+ fputs(" <<<\n",f);
+ else
+ fputc('\n',f);
+ }
+ else
+ hidden++;
+ cp += 5;
+ }
+ if (hidden)
+ fprintf(f,"%*s > %u unused battle cup%s not listed.\n",
+ indent,"", hidden, hidden==1 ? "" : "s" );
+ }
+
+
+ //--- racing cups
+
+ if ( ana->n_cup_track && ana->cup_track )
+ {
+ fprintf(f,"\n%*s%s" "%u cup%s with racing tracks (%s):%s\n",
+ indent-2,"", col->heading,
+ ana->n_cup_track, ana->n_cup_track == 1 ? "" : "s",
+ id_info, col->reset );
+
+ uint cup, hidden = 0;
+ const le_cup_track_t *cp = ana->cup_track;
+ for ( cup = 0; cup < ana->n_cup_track; cup++ )
+ {
+ if ( cp[0] || cp[1] || cp[2] || cp[3] )
+ {
+ fprintf(f,"%*s" "Cup %2u:",indent,"",cup);
+ uint warnings;
+ DumpLECup(f,ana,cp,4,col,CHECK_TRACK,&warnings,done);
+ if (warnings)
+ fputs(" <<<\n",f);
+ else
+ fputc('\n',f);
+ }
+ else
+ hidden++;
+ cp += 4;
+ }
+ if (hidden)
+ fprintf(f,"%*s > %u unused racing cup%s not listed.\n",
+ indent,"", hidden, hidden==1 ? "" : "s" );
+ }
+
+
+ //--- count multi/group/unused slots
+
+ #if 0 && defined(TEST) // for tests only
+ done[15] = 99;
+ done[16] = 0;
+ done[17] = 99;
+ done[18] = 0;
+ #endif
+
+ uint i, max_used_slot = 0, n_multi = 0, n_group = 0, n_unused = 0;
+ for ( i = 0; i < ana->n_slot; i++ )
+ if (IsRacingTrackLE(i))
+ {
+ if (done[i])
+ {
+ max_used_slot = i;
+ if ( done[i] > 1 )
+ n_multi++;
+ }
+ else if ( !done[i] )
+ {
+ if ( ana->flags && ana->flags[i] & LETF_RND_GROUP )
+ n_group++;
+ else
+ n_unused++;
+ }
+ }
+ PRINT0("m=%d, g=%d, u=%d /%d\n",n_multi,n_group,n_unused,max_used_slot);
+
+
+ //--- multi used tracks
+
+ if (n_multi)
+ {
+ fprintf(f,"\n%*s%s" "%u multiple used track slot%s (%s):%s\n",
+ indent-2,"", col->heading,
+ n_multi, n_multi == 1 ? "" : "s",
+ id_info, col->reset );
+
+ for ( i = 0; i < ana->n_slot; i++ )
+ if ( done[i] > 1 && IsRacingTrackLE(i) )
+ fprintf(f,"%*sSlot %4d/dec : %3x%s : %3ux\n",
+ indent,"", i, i, GetSlotInfo(ana,i,col,CHECK_OFF,0), done[i]);
+ }
+
+
+ //--- random group tracks
+
+ if (n_group)
+ {
+ fprintf(f,"\n%*s%s" "%u track slot%s reserved for random groups (%s):%s\n",
+ indent-2,"", col->heading,
+ n_group, n_group == 1 ? "" : "s",
+ id_info, col->reset );
+
+ for ( i = 0; i < ana->n_slot; i++ )
+ if ( !done[i] && IsRacingTrackLE(i) && ana->flags[i] & LETF_RND_GROUP )
+ {
+ done[i]++;
+ fprintf(f,"%*sSlot %4d/dec : %3x%s\n",
+ indent,"", i, i, GetSlotInfo(ana,i,col,CHECK_OFF,0));
+ }
+ }
+
+
+ //--- unused used track slots
+
+ if ( n_unused)
+ {
+ fprintf(f,"\n%*s%s" "%u unused track slot%s (%s):%s\n",
+ indent-2,"", col->heading,
+ n_unused, n_unused == 1 ? "" : "s",
+ id_info, col->reset );
+ for ( i = 0; i <= max_used_slot; i++ )
+ {
+ if ( !done[i] && IsRacingTrackLE(i) )
+ fprintf(f,"%*sSlot %4d/dec : %3x%s\n",
+ indent,"", i, i, GetSlotInfo(ana,i,col,CHECK_OFF,0) );
+ }
+
+
+ uint first_unused_slot = max_used_slot + 1;
+ if (!IsRacingTrackLE(first_unused_slot))
+ first_unused_slot = LE_FIRST_LOWER_CT_SLOT;
+ const int unused_slots = ana->n_slot - first_unused_slot;
+ if ( unused_slots > 0 )
+ fprintf(f,"%*s > %u slot%s beginning with slot %u (%x/hex) are not used.\n",
+ indent,"",
+ unused_slots, unused_slots == 1 ? "" : "s",
+ first_unused_slot, first_unused_slot );
+ }
+
+
+ //--- term
+
+ FREE(done);
+}
+
+//-----------------------------------------------------------------------------
+
+static void DumpLEUsageMap
+ ( FILE *f, uint indent, ColorSet_t *col, const le_analyse_t *ana )
+{
+ DASSERT(f);
+ DASSERT(indent>=2);
+ DASSERT(col);
+ DASSERT(ana);
+
+ const le_usage_t *usage = GetLEUsage(ana,false);
+ if (!usage)
+ return;
+
+ fprintf(f,"\n%*s%s" "Slot usage map for %u slots%s\n"
+ "%*s" "%s%s=Arena, %s%s=Random Arena, %s%s=Hidden Arena, "
+ "%s%s=Wiimm Cup (random), %s%s=Alias,%s\n"
+ "%*s" "%s%s=Track, %s%s=Random Track, %s%s=Hidden Track, "
+ "%s%s=Network, %s%s=Special, %s%s=unused.%s\n",
+ indent-2,"", col->heading, ana->n_slot, col->reset,
+ indent,"",
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ARENA, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ARENA_RND, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ARENA_HIDE, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_WIIMM, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ALIAS, col,0 ), col->info,
+ col->reset,
+ indent,"",
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_TRACK, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_TRACK_RND, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_TRACK_HIDE, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_NETWORK, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_S_SPECIAL, col,0 ), col->info,
+ GetLEUsageCharCol( LEU_S_UNUSED, col,0 ), col->info,
+ col->reset );
+
+ uint slot;
+ ccp prev_col = 0;
+ for ( slot = 0; slot < ana->usage_size; slot++ )
+ {
+ if (!(slot&63))
+ {
+ fprintf(f,"%s\n%*s%3x: ",col->reset,indent,"",slot);
+ prev_col = 0;
+ }
+ else if (!(slot&7))
+ fputc(' ',f);
+ fputs(GetLEUsageCharCol(*usage++,col,&prev_col),f);
+ }
+ fprintf(f,"%s\n\n",col->reset);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void DumpLEAnalyse ( FILE *f, uint indent, const le_analyse_t *ana )
+{
+ DASSERT(f);
+ DASSERT(ana);
+ indent = NormalizeIndent(indent);
+
+ ColorSet_t col;
+ SetupColorSet(&col,f);
+
+ fprintf(f,"%*s%sLE-CODE binary, valid=%s, file size: %x/hex = %u%s\n",
+ indent,"",
+ col.caption, GetLEValid(ana->valid), ana->size, ana->size,
+ col.reset );
+ indent += 4;
+
+ #if 0 // TEST only!
+ {
+ static int done = 0;
+ if (!done++)
+ {
+ int i;
+ for ( i = -1; i < MKW_MUSIC_MAX_ID-MKW_MUSIC_MIN_ID + 4; i++ )
+ {
+ uint m = i < 0 ? i : i + MKW_MUSIC_MIN_ID - 2;
+ printf(" %3d = 0x%02x : %-8s / %3d = 0x%02x : %s\n",
+ i, i&0xff, GetMkwTrackName3(i), m, m&0xff, GetMkwMusicName3(m) );
+ }
+
+ #if 0
+ for ( i = 0; i <= MKW_MUSIC_MAX_ID - MKW_MUSIC_MIN_ID; i+=2 )
+ {
+ ccp name = GetMkwTrackName3(music_info[i/2].track);
+ printf("\"%s\", \"%c%s\", \n",name,tolower(*name),name+1);
+ }
+ #endif
+ }
+ }
+ #endif
+
+
+ //--- file header
+
+ if ( ana->valid & LE_HEAD_VALID )
+ {
+ const le_binary_head_t *h = ana->head;
+ DASSERT(h);
+ fprintf(f,
+ "\n%*s%s" "File header:%s\n"
+ "%*s" "Magic: %.4s\n"
+ "%*s" "Version: %u\n"
+ "%*s" "Build number: %u\n"
+ "%*s" "Base address: %8x/hex\n"
+ "%*s" "Entry point: %8x/hex\n"
+ "%*s" "Total size: %8x/hex = %u\n"
+ "%*s" "Offset param: %8x/hex\n"
+ "%*s" "Region code: %c (%s)\n"
+ "%*s" "Debug flag: %c (%s)\n"
+ "%*s" "LE version/phase: %u\n"
+ ,indent-2,"", col.heading, col.reset
+ ,indent,"", h->magic
+ ,indent,"", ntohl(h->version)
+ ,indent,"", ntohl(h->build_number)
+ ,indent,"", ntohl(h->base_address)
+ ,indent,"", ntohl(h->entry_point)
+ ,indent,"", ntohl(h->size), ntohl(h->size)
+ ,indent,"", ntohl(h->off_param)
+ ,indent,"", h->region
+ ,h->region == 'P' ? "PAL"
+ :h->region == 'E' ? "USA"
+ :h->region == 'J' ? "Japan"
+ :h->region == 'K' ? "Korea"
+ : "?"
+ ,indent,"", h->debug
+ ,h->debug == 'R' ? "Release"
+ :h->debug == 'D' ? "Debug"
+ : "?"
+ ,indent,"", h->phase
+ );
+ }
+
+ switch (ana->head_vers)
+ {
+ case 4:
+ {
+ const le_binary_head_v4_t *h = ana->head_v4;
+ DASSERT(h);
+ fprintf(f,
+ "%*s" "Timestamp: %s\n"
+ ,indent,"", h->timestamp
+ );
+ }
+ break;
+
+ default:
+ fprintf(f,"%*s" ">>> other parameters unknown!\n",indent,"");
+ break;
+ }
+
+
+ //--- parameters
+
+ if ( ana->valid & LE_PARAM_VALID )
+ {
+ const le_binary_param_t *h = ana->param;
+ DASSERT(h);
+ fprintf(f,
+ "\n%*s%s" "Parameters:%s\n"
+ "%*s" "Magic: %.4s\n"
+ "%*s" "Version: %u\n"
+ "%*s" "Param size: %x/hex = %u\n"
+ ,indent-2,"", col.heading, col.reset
+ ,indent,"", h->magic
+ ,indent,"", ntohl(h->version)
+ ,indent,"", ntohl(h->size), ntohl(h->size)
+ );
+ }
+
+ switch (ana->param_vers)
+ {
+ case 1:
+ {
+ const le_binpar_v1_t *h = ana->param_v1;
+ DASSERT(h);
+ fprintf(f,
+ "%*s" "Engine chances: "
+ "%u + %u + %u = %u (1%s0cc,mirror)\n"
+ "%*s" "200cc: %u (%sabled)\n"
+ "%*s" "Perform. monitor: %u (%s)\n"
+ ,indent,""
+ ,h->engine[0], h->engine[1], h->engine[2]
+ ,h->engine[0] + h->engine[1] + h->engine[2]
+ ,h->enable_200cc ? "50cc,20" : "00cc,15"
+ ,indent,"", h->enable_200cc, h->enable_200cc ? "en" :"dis"
+ ,indent,"", h->enable_perfmon,
+ h->enable_perfmon > 1 ? "Wii+Dolphin"
+ : h->enable_perfmon ? "Wii only" :"disabled"
+ );
+
+ if ( ana->param_size >= sizeof(le_binpar_v1_37_t) )
+ fprintf(f,
+ "%*s" "Custom Timetrial: %u (%sabled)\n"
+ "%*s" "Extended P-Flags: %u (%sabled)\n"
+ "%*s" "Block tracks for: %u races\n"
+ ,indent,"", h->enable_custom_tt, h->enable_custom_tt ? "en" :"dis"
+ ,indent,"", h->enable_xpflags, h->enable_xpflags ? "en" :"dis"
+ ,indent,"", h->block_track
+ );
+
+ #if defined(DEBUG) && defined(TEST) && 0
+ const uint po = ana->param_offset;
+ fprintf(f,
+ "%*s" "Offset cup params: %5x/hex => %5x/hex\n"
+ "%*s" "Offset cup tracks: %5x/hex => %5x/hex\n"
+ "%*s" "Offset cup arenas: %5x/hex => %5x/hex\n"
+ "%*s" "Offset course par: %5x/hex => %5x/hex\n"
+ "%*s" "Offset properties: %5x/hex => %5x/hex\n"
+ "%*s" "Offset music list: %5x/hex => %5x/hex\n"
+ ,indent,"", ntohl(h->off_cup_par), ntohl(h->off_cup_par) + po
+ ,indent,"", ntohl(h->off_cup_track), ntohl(h->off_cup_track) + po
+ ,indent,"", ntohl(h->off_cup_arena), ntohl(h->off_cup_arena) + po
+ ,indent,"", ntohl(h->off_course_par), ntohl(h->off_course_par) + po
+ ,indent,"", ntohl(h->off_property), ntohl(h->off_property) + po
+ ,indent,"", ntohl(h->off_music), ntohl(h->off_music) + po
+ );
+ #endif
+
+
+ const bool hard_coded = ana->param_size < sizeof(le_binpar_v1_f8_t);
+ const u16 *chat_mode_1 = hard_coded ? chat_mode_legacy : ana->lpar.chat_mode_1;
+ const u16 *chat_mode_2 = hard_coded ? chat_mode_legacy : ana->lpar.chat_mode_2;
+
+ uint i, n_special = 0;
+ for ( i = 0; i < BMG_N_CHAT; i++ )
+ if ( chat_mode_1[i] || chat_mode_2[i] )
+ n_special++;
+
+ if (n_special)
+ {
+ fprintf(f,"\n%*s%s" "%u special chat message%s%s:%s\n",
+ indent-2,"", col.heading,
+ n_special, n_special == 1 ? "" : "s",
+ hard_coded ? " (hard coded)" : "",
+ col.reset );
+
+ for ( i = 0; i < BMG_N_CHAT; i++ )
+ {
+ ccp info = PrintChatMode(i,chat_mode_1[i],chat_mode_2[i],0);
+ if (info)
+ fprintf(f,"%*s%s\n", indent,"", info );
+ }
+ }
+ }
+ break;
+
+ default:
+ fprintf(f,"%*s" ">>> other parameters unknown!\n",indent,"");
+ break;
+ }
+
+
+ //--- section offsets
+
+ char fail[50];
+ snprintf(fail,sizeof(fail)," %s FAIL! %s",col.fail2,col.reset);
+
+ fprintf(f,"\n%*s%s" "Sections (offset,n/max):%s\n",
+ indent-2,"", col.heading, col.reset );
+
+ if ( ana->cup_par )
+ fprintf(f,
+ "%*s" "Cup parameters: %5x/hex\n",
+ indent,"", PTR_DISTANCE_INT(ana->cup_par,ana->data) );
+
+ if ( ana->cup_track )
+ fprintf(f,
+ "%*s" "Racing track cups: %5x/hex, %4u / %4u%s\n",
+ indent,"", PTR_DISTANCE_INT(ana->cup_track,ana->data),
+ ana->n_cup_track, ana->max_cup_track,
+ ana->n_cup_track > ana->max_cup_track ? fail : "" );
+
+ if ( ana->cup_arena )
+ fprintf(f,
+ "%*s" "Battle arena cups: %5x/hex, %4u / %4u%s\n",
+ indent,"", PTR_DISTANCE_INT(ana->cup_arena,ana->data),
+ ana->n_cup_arena, ana->max_cup_arena,
+ ana->n_cup_arena > ana->max_cup_arena ? fail : "" );
+
+ if ( ana->course_par )
+ fprintf(f,
+ "%*s" "Course parameters: %5x/hex\n",
+ indent,"", PTR_DISTANCE_INT(ana->course_par,ana->data) );
+
+ if ( ana->property )
+ fprintf(f,
+ "%*s" "Property list: %5x/hex, %4u / %4u%s\n",
+ indent,"", PTR_DISTANCE_INT(ana->property,ana->data),
+ ana->n_slot, ana->max_property,
+ ana->n_slot > ana->max_property ? fail : "" );
+
+ if ( ana->music )
+ fprintf(f,
+ "%*s" "Music list: %5x/hex, %4u / %4u%s\n",
+ indent,"", PTR_DISTANCE_INT(ana->music,ana->data),
+ ana->n_slot, ana->max_music,
+ ana->n_slot > ana->max_music ? fail : "" );
+
+ if ( ana->flags )
+ fprintf(f,
+ "%*s" "Flags list: %5x/hex, %4u / %4u%s\n",
+ indent,"", PTR_DISTANCE_INT(ana->flags,ana->data),
+ ana->n_slot, ana->max_flags,
+ ana->n_slot > ana->max_flags ? fail : "" );
+
+ if ( ana->end_of_data )
+ fprintf(f,
+ "%*s" "End of data: %5x/hex\n",
+ indent,"", PTR_DISTANCE_INT(ana->end_of_data,ana->data) );
+
+ fprintf(f,
+ "%*s" "End of file: %5x/hex\n",
+ indent,"", ana->size );
+
+
+ //--- settings
+
+ if ( ana->cup_par || ana->course_par )
+ {
+ fprintf(f,"\n%*s%s" "Cup and course parameters (n/max):%s\n",
+ indent-2,"", col.heading, col.reset );
+
+ if ( ana->cup_par )
+ fprintf(f,
+ "%*s" "Cup magic: %s\n"
+ "%*s" "Racing track cups: %4u / %4u%s\n"
+ "%*s" "Battle arena cups: %4u / %4u%s\n"
+ ,indent,"", ana->cup_par->magic
+ ,indent,"", ana->n_cup_track, ana->max_cup_track,
+ ana->n_cup_track > ana->max_cup_track ? fail : ""
+ ,indent,"", ana->n_cup_arena, ana->max_cup_arena,
+ ana->n_cup_arena > ana->max_cup_arena ? fail : ""
+ );
+
+ if ( ana->course_par )
+ fprintf(f,
+ "%*s" "Course magic: %s\n"
+ "%*s" "Total track slots: %4u / %4u%s\n"
+ "%*s" "Used racing slots: %4u / %4u\n"
+ "%*s" "Used battle slots: %4u / %4u\n"
+ ,indent,"", ana->course_par->magic
+ ,indent,"", ana->n_slot, ana->max_slot,
+ ana->n_slot > ana->max_slot ? fail : ""
+ ,indent,"", ana->used_rslots, ana->max_rslots
+ ,indent,"", ana->used_bslots, ana->max_bslots
+ );
+ }
+
+ if ( long_count > 0 )
+ DumpLETracks(f,indent,&col,ana);
+ if ( verbose > 0 )
+ DumpLEUsageMap(f,indent,&col,ana);
+
+ fputc('\n',f);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// copy track files ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void CopyTrackFile ( uint tid, ccp name )
+{
+ if ( opt_track_dest
+ && IsDirectory(opt_track_dest,false)
+ && opt_track_source.used )
+ {
+ char src[PATH_MAX];
+ char dest[PATH_MAX];
+
+ ParamFieldItem_t *ptr = opt_track_source.field, *end;
+ for ( end = ptr + opt_track_source.used; ptr < end; ptr++ )
+ {
+ PathCatBufPPE(src,sizeof(src),ptr->key,name,".szs");
+
+ struct stat st;
+ if ( !stat(src,&st) && S_ISREG(st.st_mode) )
+ {
+ snprintf(dest,sizeof(dest),"%s/%03x.szs",opt_track_dest,tid);
+ if (TransferFile(src,dest,ptr->num,0666))
+ {
+ PathCatBufPPE(src,sizeof(src),ptr->key,name,"_d.szs");
+ if ( !stat(src,&st) && S_ISREG(st.st_mode) )
+ {
+ snprintf(dest,sizeof(dest),"%s/%03x_d.szs",opt_track_dest,tid);
+ TransferFile(src,dest,ptr->num,0666);
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LECODE file ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool le_file_done = false;
+static ctcode_t *le_file = 0;
+
+///////////////////////////////////////////////////////////////////////////////
+
+const ctcode_t * LoadLEFile ( uint le_phase )
+{
+ const ct_mode_t ct_mode = le_phase == 2 ? CTM_LECODE2 : CTM_LECODE1;
+ if ( le_file && le_file->ctb.ct_mode != ct_mode )
+ {
+ // rescan nessessary
+ ResetCTCODE(le_file);
+ FREE(le_file);
+ le_file = 0;
+ le_file_done = false;
+ }
+
+ if ( !le_file_done && opt_le_define && *opt_le_define )
+ {
+ PRINT("le=%d, ctm=%d[%s]\n",
+ le_phase, ct_mode, GetCtModeNameBMG(ct_mode,true) );
+ if ( logging > 1 )
+ fprintf(stdlog,"Load LE definition for phase %d (%s)\n",
+ le_phase, GetCtModeNameBMG(ct_mode,true) );
+
+ le_file_done = true;
+ le_file = MALLOC(sizeof(*le_file));
+ InitializeCTCODE(le_file,ct_mode);
+
+ raw_data_t raw;
+ if ( LoadRawData(&raw,true,opt_le_define,0,false,0)
+ || ScanRawDataCTCODE(le_file,CTM_NO_INIT,&raw,0) )
+ {
+ ResetCTCODE(le_file);
+ FREE(le_file);
+ le_file = 0;
+ }
+ ResetRawData(&raw);
+ }
+ return le_file;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ApplyLEFile ( le_analyse_t * ana )
+{
+ DASSERT(ana);
+ const ctcode_t *ctcode = LoadLEFile(ana->version);
+ return ctcode ? ApplyCTCODE(ana,ctcode) : ERR_ERROR;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ApplyCTCODE() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ApplyCTCODE ( le_analyse_t * ana, const ctcode_t * ctcode )
+{
+ DASSERT(ana);
+ DASSERT(ctcode);
+
+ //--- setup
+
+ ana->n_cup_track = 0;
+ ana->n_cup_arena = 0;
+ ana->n_slot = 0;
+
+ le_property_t *prop = ana->property;
+ le_music_t *music = ana->music;
+ le_flags_t *flags = ana->flags;
+
+ if ( prop && music && flags )
+ {
+ const uint max = ana->max_slot < LE_FIRST_LOWER_CT_SLOT
+ ? ana->max_slot : LE_FIRST_LOWER_CT_SLOT;
+ uint tid;
+ for ( tid = 0; tid < max; tid++ )
+ {
+ *prop++ = tid;
+ *music++ = GetMkwMusicSlot(tid);
+ *flags++ = 0;
+ }
+ //HexDump16(stdout,5,0,ana->property,max);
+ //HexDump16(stdout,6,0,ana->music,max);
+ //HexDump16(stdout,7,0,ana->flags,max);
+ }
+
+
+ //--- copy racing cups
+
+ const ctcode_cup1_data_t *cup = ctcode->cup_racing;
+ le_cup_track_t *cup_dest = ana->cup_track;
+ if ( cup && cup_dest )
+ {
+ uint cup_idx, max = ctcode->n_racing_cups;
+ if ( max > ana->max_cup_track )
+ max = ana->max_cup_track ;
+
+ for ( cup_idx = 0; cup_idx < max; cup_idx++, cup++ )
+ {
+ *cup_dest++ = cup->track_id[0];
+ *cup_dest++ = cup->track_id[1];
+ *cup_dest++ = cup->track_id[2];
+ *cup_dest++ = cup->track_id[3];
+ ana->n_cup_track++;
+ }
+ }
+
+
+ //--- copy battle cups
+
+ cup = ctcode->cup_battle;
+ cup_dest = ana->cup_arena;
+ if ( cup && cup_dest )
+ {
+ uint cup_idx, max = ctcode->n_battle_cups;
+ if ( max > ana->max_cup_arena )
+ max = ana->max_cup_arena ;
+
+ for ( cup_idx = 0; cup_idx < max; cup_idx++, cup++ )
+ {
+ *cup_dest++ = cup->track_id[0];
+ *cup_dest++ = cup->track_id[1];
+ *cup_dest++ = cup->track_id[2];
+ *cup_dest++ = cup->track_id[3];
+ *cup_dest++ = cup->track_id[4];
+ ana->n_cup_arena++;
+ }
+ }
+
+
+ //--- copy track properties
+
+ const ctcode_crs1_data_t *td = ctcode->crs->data;
+ prop = ana->property;
+ music = ana->music;
+ flags = ana->flags;
+
+ if ( td && prop && music && flags )
+ {
+ uint tidx, max = ctcode->n_tracks;
+ if ( max > ana->max_slot )
+ max = ana->max_slot ;
+
+ for ( tidx = 0; tidx < max; tidx++, td++ )
+ {
+ *prop++ = ntohl(td->property_id);
+ *music++ = ntohl(td->music_id);
+ *flags++ = ctcode->le_flags[tidx];
+ ana->n_slot++;
+ }
+ }
+
+ switch (ana->param_vers)
+ {
+ case 1:
+ ana->cup_par->n_racing_cups = htonl(ana->n_cup_track);
+ ana->cup_par->n_battle_cups = htonl(ana->n_cup_arena);
+ ana->course_par->n_slot = htonl(ana->n_slot);
+ break;
+ }
+
+
+ //--- copy/move/link track files
+
+ char buf[500];
+ const bmg_item_t * bi = ctcode->track_file.item;
+ const bmg_item_t * bi_end = bi + ctcode->track_file.item_used;
+ for ( ; bi < bi_end; bi++ )
+ {
+ PrintString16BMG( buf, sizeof(buf), bi->text, bi->len,
+ BMG_UTF8_MAX, 0, true );
+ CopyTrackFile(bi->mid - ctcode->ctb.track_name1.beg,buf);
+ }
+
+ CalculateStatsLE(ana);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool DefineAliasLE ( le_analyse_t * ana, uint slot, uint alias )
+{
+ if ( !ana->property || !ana->music || !ana->flags )
+ return false;
+
+ DASSERT(ana);
+ if ( slot >= ana->n_slot )
+ {
+ printf("ALIAS: Invalid slot: %x = %x\n",slot,alias);
+ return false;
+ }
+
+ for(;;)
+ {
+ if ( alias >= ana->n_slot
+ || alias >= MKW_ARENA_BEG && alias < LE_FIRST_LOWER_CT_SLOT )
+ {
+ printf("ALIAS: Invalid alias: %x = %x\n",slot,alias);
+ return false;
+ }
+
+ const le_flags_t fl = ana->flags[alias];
+ if ( !(fl & LETF_ALIAS) )
+ {
+ ana->flags[slot] |= LETF_ALIAS;
+ ana->property[slot] = alias >> 8;
+ ana->music[slot] = alias & 0xff;
+ return true;
+ }
+
+ alias = TrackByAliasLE(ana->property[alias],ana->music[alias]);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint PatchAliasLE ( le_analyse_t * ana, ccp list )
+{
+ DASSERT(ana);
+
+ if (!list)
+ return 0;
+
+ uint count = 0;
+
+ ccp ptr = (char*)list;
+ while (*ptr)
+ {
+ while ( isspace((int)*ptr) || *ptr == ',' )
+ ptr++;
+
+ char *end;
+ const uint slot = str2ul(ptr,&end,16);
+ if ( end > ptr )
+ {
+ ptr = end;
+ while ( isspace((int)*ptr) )
+ ptr++;
+
+ if ( *ptr == '=' )
+ {
+ const uint alias = str2ul(ptr+1,&end,16);
+ if ( end > ptr )
+ {
+ ptr = end;
+ count += DefineAliasLE(ana,slot,alias);
+ }
+ }
+ }
+ while ( *ptr && *ptr != ',' )
+ ptr++;
+ }
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void PatchLECODE ( le_analyse_t * ana )
+{
+ DASSERT(ana);
+ PRINT("PatchLECODE() opt_lpar=%s)\n",opt_lpar);
+
+ if (opt_lpar)
+ LoadLPAR(&ana->lpar,false,0,false);
+
+ switch (ana->param_vers)
+ {
+ case 1:
+ {
+ if (opt_le_alias)
+ PatchAliasLE(ana,opt_le_alias);
+
+ le_lpar_t *lp = &ana->lpar;
+ if (opt_engine_valid)
+ memcpy(lp->engine,opt_engine,sizeof(lp->engine));
+ if ( opt_200cc != OFFON_AUTO )
+ lp->enable_200cc = opt_200cc >= OFFON_ON;
+ if ( opt_perfmon != OFFON_AUTO )
+ lp->enable_perfmon = opt_perfmon >= OFFON_FORCE ? 2
+ : opt_perfmon >= OFFON_ON;
+ if ( opt_custom_tt != OFFON_AUTO )
+ lp->enable_custom_tt = opt_custom_tt >= OFFON_ON;
+ if ( opt_xpflags != OFFON_AUTO )
+ lp->enable_xpflags = opt_xpflags >= OFFON_ON;
+ }
+ break;
+ }
+
+ CopyLPAR2Data(ana);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextLPAR() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError LoadLPAR
+(
+ le_lpar_t *lpar, // LE-CODE parameters
+ bool init_lpar, // true: initialize 'lpar' first
+ ccp fname, // filename of source, if NULL: use opt_lpar
+ bool ignore_no_file // ignore if file does not exists
+ // and return warning ERR_NOT_EXISTS
+)
+{
+ DASSERT(lpar);
+ if (init_lpar)
+ InitializeLPAR(lpar,false);
+
+ bool use_lpar = fname == 0;
+ if (use_lpar)
+ {
+ if ( !opt_lpar || !*opt_lpar )
+ return ERR_OK;
+ fname = opt_lpar;
+ }
+
+ //--- load and scan data
+
+ raw_data_t raw;
+ enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0);
+ if ( !err && raw.fform == FF_LPAR )
+ err = ScanTextLPAR(lpar,false,fname,raw.data,raw.data_size);
+
+ if ( err && use_lpar )
+ opt_lpar = 0;
+
+ ResetRawData(&raw);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanTextLPAR
+(
+ le_lpar_t *lpar, // LE-CODE parameters
+ bool init_lpar, // true: initialize 'lpar' first
+ ccp fname, // NULL or filename for error messages
+ const void * data, // data to scan
+ uint data_size // size of 'data'
+)
+{
+ PRINT("ScanTextLPAR(init=%d,size=%u)\n",init_lpar,data_size);
+ DASSERT(lpar);
+ if (init_lpar)
+ InitializeLPAR(lpar,false);
+
+ enum { C_PARAM, C_CHAT, C_END };
+ static const KeywordTab_t section_name[] =
+ {
+ { C_PARAM, "LECODE-PARAMETERS", 0, 0 },
+ { C_CHAT, "CHAT-MESSAGE-MODES", 0, 0 },
+ { C_END, "END", 0, 0 },
+ {0,0,0,0}
+ };
+
+ ScanInfo_t si;
+ InitializeSI(&si,data,data_size,fname,0);
+ si.predef = SetupVarsLEX();
+
+ bool is_pass2 = false;
+ enumError max_err = ERR_OK;
+
+ for(;;)
+ {
+ PRINT("----- SCAN KMP SECTIONS, PASS%u ...\n",is_pass2+1);
+
+ max_err = ERR_OK;
+ si.no_warn = !is_pass2;
+ si.total_err = 0;
+ DefineIntVar(&si.gvar,"$PASS",is_pass2+1);
+
+ for(;;)
+ {
+ char ch = NextCharSI(&si,true);
+ if (!ch)
+ break;
+
+ if ( ch != '[' )
+ {
+ NextLineSI(&si,true,false);
+ continue;
+ }
+ ResetLocalVarsSI(&si,0);
+
+ si.cur_file->ptr++;
+ char name[20];
+ ScanNameSI(&si,name,sizeof(name),true,true,0);
+ PRINT("--> pass=%u: #%04u: %s\n",is_pass2+1,si.cur_file->line,name);
+
+ int abbrev_count;
+ const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,section_name);
+ if ( !cmd || abbrev_count )
+ continue;
+ NextLineSI(&si,false,false);
+ noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr);
+
+ enumError err = ERR_OK;
+ switch (cmd->id)
+ {
+ case C_PARAM:
+ err = ScanTextLPAR_PARAM(lpar,&si,is_pass2);
+ break;
+
+ case C_CHAT:
+ err = ScanTextLPAR_CHAT(lpar,&si,is_pass2);
+ break;
+
+ default:
+ // ignore all other section without any warnings
+ break;
+ }
+
+ if ( max_err < err )
+ max_err = err;
+ }
+
+ if (is_pass2)
+ break;
+ is_pass2 = true;
+
+ RestartSI(&si);
+ }
+
+ #if HAVE_PRINT0
+ printf("VAR DUMP/GLOBAL:\n");
+ DumpVarMap(stdout,3,&si.gvar,false);
+ #endif
+
+ CheckLevelSI(&si);
+ if ( max_err < ERR_WARNING && si.total_err )
+ max_err = ERR_WARNING;
+ PRINT("ERR(ScanTextKMP) = %u (errcount=%u)\n", max_err, si.total_err );
+ ResetSI(&si);
+
+ return max_err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanTextLPAR_PARAM
+(
+ le_lpar_t *lpar, // LE-CODE parameters
+ ScanInfo_t *si, // valid data
+ bool is_pass2 // false: pass1; true: pass2
+)
+{
+ DASSERT(lpar);
+ DASSERT(si);
+ PRINT(">> ScanTextLPAR_PARAM(pass=%u)\n",is_pass2+1);
+
+
+ //--- setup data
+
+ int limit_mode = -1;
+ const ScanParam_t ptab[] =
+ {
+ { "LIMIT-MODE", SPM_INT, &limit_mode },
+ { "ENGINE", SPM_U8, lpar->engine, 3,3 },
+ { "ENABLE-200CC", SPM_U8, &lpar->enable_200cc },
+ { "PERF-MONITOR", SPM_U8, &lpar->enable_perfmon },
+ { "CUSTOM-TT", SPM_S8, &lpar->enable_custom_tt },
+ { "XPFLAGS", SPM_U8, &lpar->enable_xpflags },
+ { "BLOCK-TRACK", SPM_U8, &lpar->block_track },
+ {0}
+ };
+
+
+ //--- main loop
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ ScanParamSI(si,ptab);
+ }
+ CheckLevelSI(si);
+
+ if ( limit_mode >= 0 )
+ LimitToLparMode(lpar,limit_mode);
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanTextLPAR_CHAT
+(
+ le_lpar_t *lpar, // LE-CODE parameters
+ ScanInfo_t * si, // valid data
+ bool is_pass2 // false: pass1; true: pass2
+)
+{
+ PRINT(">> ScanTextLPAR_CHAT(pass=%u)\n",is_pass2+1);
+ // [[2do]] ???
+
+
+ //--- setup data
+
+ bool reset = true, legacy = false, mkwfun = false;
+ const ScanParam_t ptab[] =
+ {
+ { "RESET", SPM_BOOL, &reset },
+ { "LEGACY", SPM_BOOL, &legacy },
+ { "MKW-FUN", SPM_BOOL, &mkwfun },
+ {0}
+ };
+
+
+ //--- main loop
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ if (reset)
+ {
+ reset = false;
+ ClearLparChat(lpar);
+ }
+
+ if ( legacy || mkwfun )
+ {
+ const u16 *chat_mode = legacy ? chat_mode_legacy : chat_mode_mkwfun;
+ uint i;
+ for ( i = 0; i < BMG_N_CHAT; i++ )
+ if (!lpar->chat_mode_1[i])
+ lpar->chat_mode_1[i] = chat_mode[i];
+ legacy = mkwfun = false;
+ }
+ continue;
+ }
+
+ if (reset)
+ {
+ reset = false;
+ ClearLparChat(lpar);
+ }
+
+ char name[20];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+ if ( name[0] != 'M' )
+ {
+ wrong_name:;
+ ScanFile_t *sf = si->cur_file;
+ sf->ptr = sf->prev_ptr;
+ WarnIgnoreSI(si,"Message name (M01..M96) expected: %s\n",name);
+ continue;
+ }
+
+ char *end;
+ long num = strtol(name+1,&end,10);
+ if ( *end || num < 1 || num > BMG_N_CHAT )
+ goto wrong_name;
+
+ CheckWarnSI(si,'=',ERR_OK);
+
+ DEFINE_VAR(var);
+ if (ScanExprSI(si,&var))
+ continue;
+ lpar->chat_mode_1[num-1] = GetIntV(&var);
+
+ if ( NextCharSI(si,false) == ',' )
+ {
+ si->cur_file->ptr++;
+ if (!ScanExprSI(si,&var))
+ lpar->chat_mode_2[num-1] = GetIntV(&var);
+ }
+
+ FreeV(&var);
+ CheckEolSI(si);
+ }
+ CheckLevelSI(si);
+
+ if (reset)
+ ClearLparChat(lpar);
+
+ //HEXDUMP16(0,0,fb->buf,fb->ptr-fb->buf);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// lecode extension LEX ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanLexFile
+ ( cvp data, uint data_size, lex_stream_func func,
+ cvp user_ptr, int user_int )
+{
+ if ( !data || data_size < sizeof(lex_header_t) )
+ return ERR_INVALID_DATA;
+
+ const lex_header_t *hd = (lex_header_t*)data;
+ if (memcmp(hd->magic,LEX_BIN_MAGIC,4))
+ return ERR_INVALID_DATA;
+
+ if ( ntohs(hd->major_version) != LEX_MAJOR_VERSION )
+ return ERR_INVALID_VERSION;
+
+ const uint size = ntohl(hd->size);
+ const uint off = ntohl(hd->element_off);
+ if ( (size&3) || size > data_size || (off&3) || off + 4 > size )
+ return ERR_INVALID_DATA;
+
+ return ScanLexElements((u8*)data+off,size-off,func,user_ptr,user_int);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanLexElements
+ ( cvp data, uint data_size, lex_stream_func func,
+ cvp user_ptr, int user_int )
+{
+ if ( !data || !data_size || (data_size&3) )
+ return ERR_INVALID_DATA;
+
+ enumError max_err = ERR_OK;
+
+ uint off = 0;
+ while ( off < data_size )
+ {
+ const lex_element_t *s = (lex_element_t*)( (u8*)data + off );
+
+ const u32 magic = ntohl(s->magic);
+ if (!magic)
+ break;
+
+ const uint size = ntohl(s->size);
+ off += sizeof(lex_element_t) + size;
+ if ( (size&3) || off > data_size )
+ return ERR_INVALID_DATA;
+
+ PRINT("LEX SECTION: %.4s, size:%x\n",PrintID(&s->magic,4,0),size);
+ if (func)
+ {
+ const int err = func(magic,s->data,size,user_ptr,user_int);
+ if (err<0)
+ return -err;
+ if ( max_err < err )
+ max_err = err;
+ }
+ }
+
+ return max_err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+lex_element_t * FindLexElement ( lex_header_t *head, uint data_size, u32 magic )
+{
+ if ( !head || data_size <= sizeof(lex_header_t) || (data_size&3) )
+ return 0;
+
+ uint file_size = ntohl(head->size);
+ if ( file_size > data_size )
+ return 0;
+ file_size -= sizeof(lex_element_t);
+
+ uint offset = ntohl(head->element_off);
+ if ( offset > file_size )
+ return 0;
+
+ magic = htonl(magic);
+ for(;;)
+ {
+ lex_element_t *elem = (lex_element_t*)((u8*)head + offset);
+ offset += 8 + ntohl(elem->size);
+ if ( offset > file_size )
+ return 0;
+ if ( elem->magic == magic )
+ return elem;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// fix lex items ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*FixElementFunc) ( lex_t * lex, lex_item_t * item );
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void FixLEXElement_SET1 ( lex_set1_t *set1 )
+{
+ DASSERT(set1);
+
+ uint i;
+ for ( i = 0; i < 3; i++ )
+ {
+ float f = bef4(set1->item_factor.v+i);
+ if ( !isnormal(f) || f < 1.0 )
+ write_bef4(set1->item_factor.v+i,1.0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static void FixLEXItem_SET1
+(
+ lex_t * lex, // LEX data structure
+ lex_item_t * item // item to fix
+)
+{
+ DASSERT(lex);
+ DASSERT(item);
+ DASSERT( ntohl(item->elem.size) >= sizeof(lex_set1_t) );
+
+ lex_set1_t *set1 = (lex_set1_t*)item->elem.data;
+ FixLEXElement_SET1(set1);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// lex_t helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static lex_item_t * FindElementLEX
+(
+ const lex_t * lex, // LEX data structure
+ u32 magic // magic
+)
+{
+ magic = htonl(magic);
+
+ uint i;
+ lex_item_t **ss;
+ for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ )
+ if ( (*ss)->elem.magic == magic )
+ return *ss;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint GetLexSortOrder ( u32 magic )
+{
+ switch(magic)
+ {
+ case LEXS_SET1: return 1;
+ case LEXS_TEST: return 9;
+ default: return 2;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void UpdateHaveLex ( lex_t *lex )
+{
+ DASSERT(lex);
+ uint have_lex = 0;
+
+ uint i;
+ lex_item_t **ss;
+ for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ )
+ {
+ const u32 magic = ntohl((*ss)->elem.magic);
+ const uint size = ntohl((*ss)->elem.size);
+ uint j;
+ for ( j = 0; j < HAVELEX__N; j++ )
+ if ( magic == have_lex_info[j].magic && size >= have_lex_info[j].min_size )
+ {
+ have_lex |= 1 << j;
+ break;
+ }
+ }
+
+ lex->have_lex = have_lex;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static lex_item_t * AppendElementLEX
+(
+ lex_t * lex, // LEX data structure
+ u32 magic, // magic
+ const void * data, // data of section
+ uint size, // size of 'data'
+ bool overwrite // false: abort if exists and return 0
+ // true: overwrite
+)
+{
+ DASSERT(lex);
+ DASSERT( data || !size );
+
+
+ //--- add have_lex
+
+ uint i;
+ for ( i = 0; i < HAVELEX__N; i++ )
+ if ( magic == have_lex_info[i].magic && size >= have_lex_info[i].min_size )
+ {
+ lex->have_lex |= 1 << i;
+ break;
+ }
+
+
+ //--- check if already exists
+
+ lex_item_t **ss;
+ const u32 be_magic = htonl(magic);
+ for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ )
+ if ( (*ss)->elem.magic == be_magic )
+ {
+ if (!overwrite)
+ return 0;
+ break;
+ }
+
+
+ //--- grow item list if necessary
+
+ if ( i == lex->item_size )
+ {
+ lex->item_size = lex->item_size + 30;
+ lex->item = REALLOC( lex->item, lex->item_size * sizeof(*lex->item) );
+ // 'ss' becomes invalid!
+ ss = lex->item + i;
+ }
+
+ if ( i < lex->item_used )
+ FREE(*ss);
+ else
+ lex->item_used++;
+
+
+ //--- fix item size
+
+ uint size4 = ALIGN32(size,4);
+ FixElementFunc fix_func = 0;
+
+ switch(magic)
+ {
+ case LEXS_SET1:
+ fix_func = FixLEXItem_SET1;
+ if ( size4 < sizeof(lex_set1_t) )
+ size4 = ALIGN32(sizeof(lex_set1_t),4);
+ break;
+
+ case LEXS_TEST:
+ if ( size4 < sizeof(lex_test_t) )
+ size4 = ALIGN32(sizeof(lex_test_t),4);
+ break;
+ }
+
+
+ //--- setup item
+
+ lex_item_t *s = MALLOC(sizeof(lex_item_t)+size4);
+ *ss = s;
+ s->sort_order = GetLexSortOrder(magic);
+ s->insert_order = i;
+ s->elem.magic = be_magic;
+ s->elem.size = htonl(size4);
+ memcpy(s->elem.data,data,size);
+ if ( size < size4 )
+ memset(s->elem.data+size,0,size4-size);
+
+ if (fix_func)
+ fix_func(lex,s);
+
+ return s;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool RemoveElementLEX
+(
+ lex_t *lex, // LEX data structure
+ const lex_item_t *li // element to remove (robust)
+)
+{
+ if ( !li || !li->elem.magic )
+ return 0;
+
+ uint i;
+ lex_item_t **ss;
+ for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ )
+ if ( *ss == li )
+ {
+ memmove(ss,ss+1,lex->item_used-i);
+ lex->item_used--;
+ FREE((lex_item_t*)li);
+ UpdateHaveLex(lex);
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+
+static uint RemoveElementByMagicLEX
+(
+ lex_t *lex, // LEX data structure
+ u32 magic // magic
+)
+{
+ if (!magic)
+ return 0;
+
+ uint count = 0;
+ for(;;)
+ {
+ lex_item_t *li = FindElementLEX(lex,magic);
+ if (!li)
+ return count;
+ count += RemoveElementLEX(lex,li);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int cmp_lex_item ( const lex_item_t ** pa, const lex_item_t ** pb )
+{
+ const lex_item_t *a = *pa;
+ const lex_item_t *b = *pb;
+ noPRINT(">> SORT: %u.%u : %u.%u\n",
+ a->sort_order, a->insert_order,
+ b->sort_order, b->insert_order );
+
+ int res = (int)a->sort_order - (int)b->sort_order;
+ return res ? res : (int)a->insert_order - (int)b->insert_order;
+}
+
+//-----------------------------------------------------------------------------
+
+static void SortItemsLEX
+(
+ lex_t * lex // LEX data structure
+)
+{
+ if ( lex->item_used > 1 )
+ qsort( lex->item, lex->item_used, sizeof(*lex->item),
+ (qsort_func)cmp_lex_item );
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// have lex support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// order is important, compare have_lex_t and ct.wiimm.de
+
+const have_lex_info_t have_lex_info[HAVELEX__N] =
+{
+ { LEXS_SET1, "set1", 1 },
+ { LEXS_CANN, "cannon", 0 },
+ { LEXS_TEST, "test", 1 },
+ { LEXS_HIPT, "hidepos", sizeof(lex_hipt_rule_t) },
+ //--- add new sections here (order is important)
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp CreateSectionInfoLEX
+ ( have_lex_t special, bool add_value, ccp return_if_empty )
+{
+ static char buf[500];
+ char *dest = buf;
+
+ if (add_value)
+ dest = snprintfE( dest, buf+sizeof(buf), "%u=" , special );
+
+ uint i, mask;
+ ccp sep = "";
+ for ( i = 0, mask = 1; i < HAVELEX__N; i++, mask <<= 1 )
+ if ( special & mask )
+ {
+ dest = StringCat2E(dest,buf+sizeof(buf),sep,have_lex_info[i].name);
+ sep = ",";
+ }
+
+ return dest == buf ? return_if_empty : CopyCircBuf0(buf,dest-buf);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LEX setup ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeLEX ( lex_t * lex )
+{
+ DASSERT(lex);
+ memset(lex,0,sizeof(*lex));
+
+ lex->fname = EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetLEX ( lex_t * lex )
+{
+ if (lex)
+ {
+ FreeString(lex->fname);
+ if (lex->raw_data)
+ FREE(lex->raw_data);
+
+ if (lex->item)
+ {
+ uint i;
+ for ( i = 0; i < lex->item_used; i++ )
+ if (lex->item[i])
+ FREE(lex->item[i]);
+ FREE(lex->item);
+ }
+ InitializeLEX(lex);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void UpdateLEX ( lex_t * lex, bool add_missing, bool add_test )
+{
+ DASSERT(lex);
+
+ if ( add_missing && lex->develop )
+ {
+ }
+
+ if (add_missing)
+ {
+ //--- SET1
+ AppendSet1LEX(lex,false);
+
+ //--- CANN
+ AppendCannLEX(lex,false);
+
+ //--- HIPT
+ AppendHiptLEX(lex,false);
+ }
+
+ if (add_test) //--- TEST
+ AppendTestLEX(lex,false);
+
+ SortItemsLEX(lex);
+}
+
+//-----------------------------------------------------------------------------
+
+lex_item_t * AppendSet1LEX ( lex_t * lex, bool overwrite )
+{
+ lex_set1_t set1;
+ memset(&set1,0,sizeof(set1));
+ // AppendElementLE() do call FixLEXItem_SET1()
+ return AppendElementLEX(lex,LEXS_SET1,&set1,sizeof(set1),false);
+}
+
+//-----------------------------------------------------------------------------
+
+lex_item_t * AppendCannLEX ( lex_t * lex, bool overwrite )
+{
+ return AppendElementLEX( lex, LEXS_CANN, CannonDataLEX,
+ sizeof(CannonDataLEX), overwrite);
+}
+
+//-----------------------------------------------------------------------------
+
+lex_item_t * AppendHiptLEX ( lex_t * lex, bool overwrite )
+{
+ return AppendElementLEX( lex, LEXS_HIPT, 0, 0, overwrite);
+}
+
+//-----------------------------------------------------------------------------
+
+lex_item_t * AppendTestLEX ( lex_t * lex, bool overwrite )
+{
+ return AppendElementLEX( lex, LEXS_TEST, TestDataLEX,
+ sizeof(TestDataLEX), overwrite);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void DumpElementsLEX ( FILE *f, lex_t *lex, bool hexdump )
+{
+ DASSERT(f);
+ DASSERT(lex);
+
+ fprintf(f,"LEX SECTIONS: %u/%u\n",lex->item_used,lex->item_size);
+
+ uint i;
+ for ( i = 0; i < lex->item_used; i++ )
+ {
+ lex_item_t *s = lex->item[i];
+ const uint size = ntohl(s->elem.size);
+ fprintf(f," %-4.4s %08x, size: %5u = 0x%04x\n",
+ PrintID(&s->elem.magic,4,0), s->elem.magic, size, size );
+ if ( size && hexdump )
+ HexDump16(f,8,0,s->elem.data,size);
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// lex parser functions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError F_isLEX
+(
+ struct Var_t * res, // store result here
+ struct Var_t * param, // parameters, modified
+ uint n_param, // number of parameters
+ struct ScanInfo_t * si, // not NULL: use for error messages
+ const FuncParam_t * fpar // pointer to user parameter
+)
+{
+ DASSERT(res);
+ DASSERT(param);
+
+ uint result = 0;
+ if ( si && si->lex )
+ {
+ result = 1;
+
+ if (si->lex->fname)
+ {
+ ccp fname = strrchr(si->lex->fname,'/');
+ if (fname)
+ fname++;
+ else
+ fname = si->lex->fname;
+
+ if ( !strcasecmp(fname,"course.lex")
+ || !strcasecmp(fname,"course.txt")
+ || !strcasecmp(fname,"course.txt.lex") )
+ {
+ result = 2;
+ }
+ }
+ }
+
+ res->i = result;
+ res->mode = VAR_INT;
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const struct FuncTable_t lex_func_tab[] =
+{
+ { 0, 0, "ISLEX", F_isLEX, 0, // replace only function call
+ "int", "isLEX()", 0 }, // but not info
+
+ {0,0,0,0,0,0,0,0}
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// setup lex parser ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const VarMap_t * SetupVarsLEX()
+{
+ static VarMap_t vm = {0};
+ if (!vm.used)
+ {
+ DefineMkwVars(&vm);
+ DefineParserFuncTab(lex_func_tab,FF_LEX);
+ DefineParserVars(&vm);
+ }
+
+ return &vm;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanRawLEX() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError DumpLEXFunc
+ ( u32 magic, const u8 *data, uint size, cvp user_ptr, int user_int )
+{
+ u32 m = htonl(magic);
+ printf(" %s 0x%08x, %5u = 0x%04x bytes\n",
+ PrintID(&m,4,0), magic, size, size );
+ if ( user_int < 0 || user_int >= 0x10 )
+ {
+ if ( user_int > 0 && size > user_int )
+ size = user_int;
+ HexDump16(stdout,5,0,data,size);
+ putchar('\n');
+ }
+ return ERR_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+enumError ScanLEXFunc
+ ( u32 magic, const u8 *data, uint size, cvp user_ptr, int user_int )
+{
+ #if HAVE_PRINT
+ u32 m = htonl(magic);
+ PRINT(">>>>> %-4.4s %08x : size: %u = 0x%x\n",
+ PrintID(&m,4,0), magic, size, size );
+ #endif
+
+ lex_t *lex = (lex_t*)user_ptr;
+ DASSERT(lex);
+
+ switch (magic)
+ {
+ case LEXS_TERMINATE:
+ case LEXS_IGNORE:
+ // ignore
+ break;
+
+ default:
+ AppendElementLEX(lex,magic,data,size,true);
+ #if HAVE_PRINT0
+ if (logging>1)
+ DumpElementsLEX(stdout,lex,logging>2);
+ #endif
+ }
+ return ERR_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+enumError ScanRawLEX
+(
+ lex_t * lex, // LEX data structure
+ bool init_lex, // true: initialize 'lex' first
+ const void * data, // data to scan
+ uint data_size, // size of 'data'
+ uint dump_mode // 0: add element to lex->item
+ // 1: dump scanned elements
+ // >=0x10: add hexdump of max # bytes
+)
+{
+ DASSERT(lex);
+ if (init_lex)
+ InitializeLEX(lex);
+
+ if ( IsValidLEX(data,data_size,data_size,lex->fname) >= VALID_ERROR )
+ {
+ return ERROR0(ERR_INVALID_DATA,
+ "Invalid LEX file: %s\n"
+ "Add option --lex=force or --force to ignore some validity checks.",
+ lex->fname ? lex->fname : "?" );
+ }
+
+ lex->fform = FF_LEX;
+ enumError err = ScanLexFile( data, data_size,
+ dump_mode ? DumpLEXFunc : ScanLEXFunc, lex, dump_mode );
+ if (err)
+ ERROR0(err,"Scanning of LEX file failed: %s\n",lex->fname);
+ else
+ UpdateLEX(lex,opt_complete,false);
+
+ if (logging>0)
+ DumpElementsLEX(stdout,lex,logging>2);
+ return err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextLEX() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[lex_section_id]]
+
+typedef enum lex_section_id
+{
+ LID_UNKNOWN,
+ LID_IGNORE,
+ LID_SETUP,
+ LID_HEXDUMP,
+
+ LID_SETTINGS1,
+ LID_CANNON,
+ LID_HIDE_PT,
+ LID_TEST,
+}
+lex_section_id;
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t lex_section_name[] =
+{
+ //------------------------------------------------------------------------
+ // ID NAME INFO unused
+ //------------------------------------------------------------------------
+
+ { LID_IGNORE, "END", "--", 0 },
+ { LID_SETUP, "SETUP", "Setup", 0 },
+ { LID_HEXDUMP, "HEXDUMP", "Hexdump of unknown section", 0 },
+
+ { LID_SETTINGS1, "SET1", "Basic settings", 0 },
+ { LID_CANNON, "CANN", "Cannon settings", 0 },
+ { LID_HIDE_PT, "HIPT", "Hide position tracker", 0 },
+ { LID_TEST, "TEST", "Test case", 0 },
+
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t lex_offline_online[] =
+{
+ { LEX_OO_AUTO, "AUTO", "0", 0 },
+ { LEX_OO_OFFLINE, "OFFLINE", "1", 0 },
+ { LEX_OO_OFFLINE, "NEVER", 0, 0 },
+ { LEX_OO_ONLINE, "ONLINE", "2", 0 },
+ { LEX_OO_ONLINE, "ALWAYS", 0, 0 },
+ { -9, "OFF", "-1", -1 },
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t lex_game_mode[] =
+{
+ { LEX_GMODE_AUTO, "AUTO", "0", -1 },
+ { LEX_GMODE_STD, "STANDARD", "1", KMP_GMODE_STD },
+ { LEX_GMODE_STD, "STD", 0, KMP_GMODE_STD },
+ { LEX_GMODE_BALLOON, "BALLOON", "2", KMP_GMODE_BALLOON },
+ { LEX_GMODE_COIN, "COIN", "3", KMP_GMODE_COIN },
+ { LEX_GMODE_VERSUS, "VERSUS", "4", KMP_GMODE_VERSUS },
+ { LEX_GMODE_ITEMRAIN, "ITEMRAIN", "5", KMP_GMODE_ITEMRAIN },
+ { -1, "OFF", "-1", -1 },
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t lex_engine[] =
+{
+ { LEX_ENGINE_AUTO, "AUTO", "0", -1 },
+ { LEX_ENGINE_BATTLE, "BATTLE", 0, ENGINE_BATTLE },
+ { LEX_ENGINE_50, "50CC", 0, ENGINE_50 },
+ { LEX_ENGINE_100, "100CC", 0, ENGINE_100 },
+ { LEX_ENGINE_150, "150CC", 0, ENGINE_150 },
+ { LEX_ENGINE_200, "200CC", 0, ENGINE_200 },
+ { LEX_ENGINE_150M, "150M", "150MIRROR", ENGINE_150M },
+ { LEX_ENGINE_200M, "200M", "200MIRROR", ENGINE_200M },
+ { -1, "OFF", "-1", -1 },
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanLEXElementHexdump
+(
+ lex_t * lex, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(lex);
+ DASSERT(si);
+ TRACE("ScanLEXElementHexdump()\n");
+
+
+ //--- setup data
+
+ u32 magic = LEXS_IGNORE;
+ ScanParam_t ptab[] =
+ {
+ { "MAGIC", SPM_U32, &magic, },
+ {0}
+ };
+
+ char fb_buf[1000];
+ FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf));
+
+
+ //--- main loop
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ ScanParamSI(si,ptab);
+ else
+ ScanHexlineSI(si,fb,true);
+ }
+ CheckLevelSI(si);
+
+ if ( magic != LEXS_IGNORE || magic != LEXS_TERMINATE )
+ AppendElementLEX(lex,magic,fb->buf,fb->ptr-fb->buf,true);
+
+ ResetFastBuf(fb);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanLEXElement_SET1
+(
+ lex_t * lex, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(lex);
+ DASSERT(si);
+ TRACE("ScanLEXElement_SET1()\n");
+
+
+ //--- setup data
+
+ lex_set1_t set1;
+ memset(&set1,0,sizeof(set1));
+
+ const ScanParam_t ptab[] =
+ {
+ { "ITEM-POS-FACTOR", SPM_FLOAT3_BE, &set1.item_factor },
+ {0}
+ };
+
+ char fb_buf[1000];
+ FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf));
+ AppendFastBuf(fb,&set1,sizeof(set1));
+
+
+ //--- main loop
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ ScanParamSI(si,ptab);
+ else
+ ScanHexlineSI(si,fb,true);
+ }
+ CheckLevelSI(si);
+
+ memcpy(fb->buf,&set1,sizeof(set1));
+ HEXDUMP16(0,0,fb->buf,fb->ptr-fb->buf);
+ AppendElementLEX(lex,LEXS_SET1,fb->buf,fb->ptr-fb->buf,true);
+
+ ResetFastBuf(fb);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanLEXElement_CANN
+(
+ lex_t * lex, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(lex);
+ DASSERT(si);
+ TRACE("ScanLEXElement_CANN()\n");
+
+
+ //--- setup data
+
+ ScanParam_t ptab[] =
+ {
+ {0}
+ };
+
+ char fb_buf[1000];
+ FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf));
+ AppendFastBuf(fb,EmptyString,4);
+
+
+ //--- main loop
+
+ uint n_cannon = 0;
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,n_cannon,0,2);
+ if (err)
+ break;
+
+ float f[4];
+ ScanFloatSI(si,f,4);
+
+ uint i;
+ u32 *fp = (u32*)f;
+ for ( i = 0; i < 4; i++, fp++ )
+ *fp = htonl(*fp);
+ AppendFastBuf(fb,f,sizeof(f));
+ n_cannon++;
+ CheckEolSI(si);
+ }
+
+ if ( n_cannon > 0 )
+ {
+ write_be32(fb->buf,n_cannon);
+ //HexDump16(stdout,0,0,fb->buf,fb->ptr-fb->buf);
+ AppendElementLEX(lex,LEXS_CANN,fb->buf,fb->ptr-fb->buf,true);
+ }
+
+ ResetFastBuf(fb);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanLEXElement_HIPT
+(
+ lex_t * lex, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(lex);
+ DASSERT(si);
+ TRACE("ScanLEXElement_HIPT()\n");
+
+
+ //--- setup data
+
+ ScanParam_t ptab[] =
+ {
+ {0}
+ };
+
+ char fb_buf[1000];
+ FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf));
+
+
+ //--- main loop
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ u8 num[sizeof(lex_hipt_rule_t)];
+ ScanU8SI(si,num,sizeof(num),false);
+ AppendFastBuf(fb,num,sizeof(num));
+ CheckEolSI(si);
+ }
+
+ AppendElementLEX(lex,LEXS_HIPT,fb->buf,fb->ptr-fb->buf,true);
+ ResetFastBuf(fb);
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanLEXElement_TEST
+(
+ lex_t * lex, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(lex);
+ DASSERT(si);
+ TRACE("ScanLEXElement_TEST()\n");
+
+
+ //--- setup data
+
+ lex_test_t test;
+ memcpy(&test,TestDataLEX,sizeof(test));
+
+ const ScanParam_t ptab[] =
+ {
+ { "OFFLINE-ONLINE", SPM_U8, &test.offline_online },
+ { "N-OFFLINE", SPM_U8, &test.n_offline },
+ { "N-ONLINE", SPM_U8, &test.n_online },
+ { "COND-BIT", SPM_S8, &test.cond_bit },
+ { "GAME-MODE", SPM_U8, &test.game_mode },
+ { "ENGINE", SPM_U8, &test.engine },
+ { "RANDOM", SPM_U8, &test.random },
+ {0}
+ };
+
+ char fb_buf[1000];
+ FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf));
+ AppendFastBuf(fb,&test,sizeof(test));
+
+
+ //--- main loop
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ ScanParamSI(si,ptab);
+ else
+ ScanHexlineSI(si,fb,true);
+ }
+ CheckLevelSI(si);
+
+ memcpy(fb->buf,&test,sizeof(test));
+ HEXDUMP16(0,0,fb->buf,fb->ptr-fb->buf);
+ AppendElementLEX(lex,LEXS_TEST,fb->buf,fb->ptr-fb->buf,true);
+
+ ResetFastBuf(fb);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanTextLEX
+(
+ lex_t * lex, // LEX data structure
+ bool init_lex, // true: initialize 'lex' first
+ const void * data, // data to scan
+ uint data_size // size of 'data'
+)
+{
+ PRINT("ScanTextLEX(init=%d)\n",init_lex);
+
+ DASSERT(lex);
+ if (init_lex)
+ InitializeLEX(lex);
+
+ ScanInfo_t si;
+ InitializeSI(&si,data,data_size,lex->fname,lex->revision);
+ si.predef = SetupVarsLEX();
+
+ enumError max_err = ERR_OK;
+ lex->is_pass2 = false;
+
+ for(;;)
+ {
+ PRINT("----- SCAN LEX SECTIONS, PASS%u ...\n",lex->is_pass2+1);
+
+ max_err = ERR_OK;
+ si.lex = lex;
+ si.no_warn = !lex->is_pass2;
+ si.total_err = 0;
+ DefineIntVar(&si.gvar, "$PASS", lex->is_pass2+1 );
+
+ for(;;)
+ {
+ char ch = NextCharSI(&si,true);
+ if (!ch)
+ break;
+
+ if ( ch != '[' )
+ {
+ NextLineSI(&si,true,false);
+ continue;
+ }
+ ResetLocalVarsSI(&si,lex->revision);
+
+ si.cur_file->ptr++;
+ char sect_name[20];
+ ScanNameSI(&si,sect_name,sizeof(sect_name),true,true,0);
+ PRINT0("--> pass=%u: #%04u: %s\n",lex->is_pass2+1,si.cur_file->line,sect_name);
+
+ int abbrev_count;
+ const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,sect_name,lex_section_name);
+ if ( !cmd || abbrev_count )
+ continue;
+ NextLineSI(&si,false,false);
+ PRINT0("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr);
+
+ enumError err = ERR_OK;
+ switch (cmd->id)
+ {
+ case LID_IGNORE:
+ // ignore it without warning
+ break;
+
+ case LID_SETUP:
+ // nothing to do at the moment
+ break;
+
+ case LID_HEXDUMP:
+ err = ScanLEXElementHexdump(lex,&si);
+ break;
+
+ case LID_SETTINGS1:
+ err = ScanLEXElement_SET1(lex,&si);
+ break;
+
+ case LID_CANNON:
+ err = ScanLEXElement_CANN(lex,&si);
+ break;
+
+ case LID_HIDE_PT:
+ err = ScanLEXElement_HIPT(lex,&si);
+ break;
+
+ case LID_TEST:
+ err = ScanLEXElement_TEST(lex,&si);
+ break;
+
+ default:
+ err = ERROR0(ERR_WARNING,"Unknown section (ignored): %s\n",sect_name);
+ break;
+ }
+
+ if ( max_err < err )
+ max_err = err;
+ }
+
+ if (lex->is_pass2)
+ break;
+
+ lex->is_pass2 = true;
+ RestartSI(&si);
+ }
+
+ ResetSI(&si);
+
+ UpdateLEX(lex,opt_complete,false);
+ return max_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LEX: scan and load ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanLEX
+(
+ lex_t * lex, // LEX data structure
+ bool init_lex, // true: initialize 'lex' first
+ const void * data, // data to scan
+ uint data_size // size of 'data'
+)
+{
+ DASSERT(lex);
+ PRINT("ScanLEX() size=%u\n",data_size);
+ HEXDUMP16(0,0,data,16);
+
+ enumError err;
+// [[analyse-magic]]
+ switch (GetByMagicFF(data,data_size,data_size))
+ {
+ case FF_LEX:
+ lex->fform = FF_LEX;
+ err = ScanRawLEX(lex,init_lex,data,data_size,0);
+ break;
+
+ case FF_LEX_TXT:
+ lex->fform = FF_LEX_TXT;
+ err = ScanTextLEX(lex,init_lex,data,data_size);
+ break;
+
+ default:
+ if (init_lex)
+ InitializeLEX(lex);
+ return ERROR0(ERR_INVALID_DATA,
+ "No LEX file: %s\n", lex->fname ? lex->fname : "?");
+ }
+
+ PatchLEX(lex);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanRawDataLEX
+(
+ lex_t * lex, // LEX data structure
+ bool init_lex, // true: initialize 'lex' first
+ struct raw_data_t * raw // valid raw data
+)
+{
+ DASSERT(lex);
+ DASSERT(raw);
+ if (init_lex)
+ InitializeLEX(lex);
+ else
+ ResetLEX(lex);
+
+ lex->fname = raw->fname;
+ raw->fname = 0;
+
+ return raw->is_0 ? ERR_OK : ScanLEX(lex,false,raw->data,raw->data_size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError LoadLEX
+(
+ lex_t * lex, // LEX data structure
+ bool init_lex, // true: initialize 'lex' first
+ ccp fname, // valid pointer to filenname
+ bool ignore_no_file // ignore if file does not exists
+ // and return warning ERR_NOT_EXISTS
+)
+{
+ DASSERT(lex);
+ DASSERT(fname);
+ if (init_lex)
+ InitializeLEX(lex);
+ else
+ ResetLEX(lex);
+
+ //--- load and scan data
+
+ raw_data_t raw;
+ enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0);
+ if (!err)
+ {
+ lex->fname = raw.fname;
+ raw.fname = 0;
+ err = ScanLEX(lex,false,raw.data,raw.data_size);
+ }
+
+ ResetRawData(&raw);
+ return err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// CreateRawLEX() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError CreateRawLEX
+(
+ lex_t * lex // pointer to valid LEX
+)
+{
+ DASSERT(lex);
+ if (lex->raw_data)
+ FREE(lex->raw_data);
+
+ uint total_size = sizeof(lex_header_t) + sizeof(lex_element_t); // final term!
+
+ uint i;
+ for ( i = 0; i < lex->item_used; i++ )
+ {
+ lex_item_t *s = lex->item[i];
+ total_size += sizeof(lex_element_t) + ntohl(s->elem.size);
+ }
+
+ lex->raw_data = MALLOC(total_size);
+ lex->raw_data_size = total_size;
+
+ lex_header_t *hd = (lex_header_t*)lex->raw_data;
+ memcpy(hd->magic,LEX_BIN_MAGIC,sizeof(hd->magic));
+ hd->major_version = htons(LEX_MAJOR_VERSION);
+ hd->minor_version = htons(LEX_MINOR_VERSION);
+ hd->size = htonl(total_size);
+ hd->element_off = htonl(sizeof(lex_header_t));
+
+ u8 *dest = lex->raw_data + sizeof(lex_header_t);
+ for ( i = 0; i < lex->item_used; i++ )
+ {
+ lex_item_t *s = lex->item[i];
+ const uint size = sizeof(lex_element_t) + ntohl(s->elem.size);
+ ASSERT ( dest + size < lex->raw_data + lex->raw_data_size );
+ memcpy(dest,&s->elem,size);
+ dest += size;
+ }
+ ASSERT ( dest + sizeof(lex_element_t) == lex->raw_data + lex->raw_data_size );
+ memset(dest,0,sizeof(lex_element_t));
+
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SaveRawLEX() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SaveRawLEX
+(
+ lex_t * lex, // pointer to valid LEX
+ ccp fname, // filename of destination
+ bool set_time // true: set time stamps
+)
+{
+ DASSERT(lex);
+ DASSERT(fname);
+ PRINT("SaveRawLEX(%s,%d)\n",fname,set_time);
+
+ //--- create raw data
+
+ enumError err = CreateRawLEX(lex);
+ if (err)
+ return err;
+ DASSERT(lex->raw_data);
+ DASSERT(lex->raw_data_size);
+
+
+ //--- write to file
+
+ File_t F;
+ err = CreateFileOpt(&F,true,fname,testmode,lex->fname);
+ if ( err > ERR_WARNING || !F.f )
+ return err;
+ SetFileAttrib(&F.fatt,&lex->fatt,0);
+
+ if ( fwrite(lex->raw_data,1,lex->raw_data_size,F.f) != lex->raw_data_size )
+ FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname);
+ return ResetFile(&F,set_time);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SaveTextLEX() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError SaveTextLEX_SET1
+(
+ FILE * f, // file to write
+ const lex_t * lex, // pointer to valid LEX
+ lex_item_t * item // item to save
+)
+{
+ DASSERT(f);
+ DASSERT(lex);
+ DASSERT(item);
+
+ const uint size = ntohl(item->elem.size);
+ noPRINT("SaveTextLEX_SET1(), size=%u\n",size);
+ DASSERT( size >= sizeof(lex_set1_t) );
+ if ( size < sizeof(lex_set1_t))
+ return ERR_OK;
+
+ const u8 *data = item->elem.data;
+ const lex_set1_t *set1 = (lex_set1_t*)data;
+
+ fprintf(f,text_lex_elem_set1_cr
+ ,item->sort_order
+ ,bef4(&set1->item_factor.x)
+ ,bef4(&set1->item_factor.y)
+ ,bef4(&set1->item_factor.z)
+ );
+
+ if ( size > sizeof(lex_set1_t) )
+ {
+ fputs("\n# Hex dump for unknown settings:\n",f);
+ HexDump(f,0,sizeof(lex_set1_t),5,16,
+ item->elem.data + sizeof(lex_set1_t),
+ size - sizeof(lex_set1_t) );
+ }
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError SaveTextLEX_CANN
+(
+ FILE * f, // file to write
+ const lex_t * lex, // pointer to valid LEX
+ lex_item_t * item // item to save
+)
+{
+ DASSERT(f);
+ DASSERT(lex);
+ DASSERT(item);
+
+ fprintf(f,text_lex_elem_cann_cr,item->sort_order);
+ if ( item->elem.size < 4 + 16 )
+ return ERR_OK;
+
+ const u8 *data = item->elem.data;
+ uint n = be32(data);
+ uint max = ( ntohl(item->elem.size) - 4 ) / 16;
+ if ( n > max )
+ n = max;
+
+ uint i, off = 4;
+ for ( i = 0; i < n; i++, off += 16 )
+ fprintf(f,"%4u %10.3f %10.3f %10.3f %10.3f\r\n",
+ i, bef4(data+off), bef4(data+off+4),
+ bef4(data+off+8), bef4(data+off+12) );
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError SaveTextLEX_HIPT
+(
+ FILE * f, // file to write
+ const lex_t * lex, // pointer to valid LEX
+ lex_item_t * item // item to save
+)
+{
+ DASSERT(f);
+ DASSERT(lex);
+ DASSERT(item);
+
+ fprintf(f,text_lex_elem_hipt_cr,item->sort_order);
+ if ( item->elem.size < 4 + 16 )
+ return ERR_OK;
+
+ const lex_hipt_rule_t *rule = (lex_hipt_rule_t*)item->elem.data;
+ int n = ntohl(item->elem.size) / sizeof(*rule) ;
+
+ int i, last_lap = 1000;
+ for ( i = 0; i < n; i++, rule++ )
+ {
+ if ( last_lap != rule->lap )
+ {
+ last_lap = rule->lap;
+ fputs("\r\n",f);
+ }
+
+ fprintf(f,"%5u %4d %4u %4u %4u\r\n",
+ rule->cond, rule->lap, rule->from, rule->to, rule->mode );
+ }
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError SaveTextLEX_TEST
+(
+ FILE * f, // file to write
+ const lex_t * lex, // pointer to valid LEX
+ lex_item_t * item // item to save
+)
+{
+ DASSERT(f);
+ DASSERT(lex);
+ DASSERT(item);
+
+ const uint size = ntohl(item->elem.size);
+ PRINT("SaveTextLEX_TEST(), size=%u\n",size);
+ DASSERT( size >= sizeof(lex_test_t) );
+ if ( size < sizeof(lex_test_t))
+ return ERR_OK;
+
+ const u8 *data = item->elem.data;
+ const lex_test_t *test = (lex_test_t*)data;
+
+ const KeywordTab_t *off_on;
+ for ( off_on = lex_offline_online; off_on->name1; off_on++ )
+ if ( off_on->id == test->offline_online && off_on->opt >= 0 )
+ break;
+
+ const KeywordTab_t *gmode;
+ for ( gmode = lex_game_mode; gmode->name1; gmode++ )
+ if ( gmode->id == test->game_mode && gmode->opt >= 0 )
+ break;
+
+ const KeywordTab_t *engine;
+ for ( engine = lex_engine; engine->name1; engine++ )
+ if ( engine->id == test->engine && engine->opt >= 0 )
+ break;
+
+ fprintf(f,text_lex_elem_test_cr
+ ,item->sort_order
+ ,off_on->name1 ? off_on->name1 : "AUTO"
+ ,test->n_offline
+ ,test->n_online
+ ,test->cond_bit
+ ,gmode->name1 ? gmode->name1 : "AUTO"
+ ,engine->name1 ? engine->name1 : "AUTO"
+ ,test->random
+ );
+
+ if ( size > sizeof(lex_test_t) )
+ {
+ fputs("\n# Hex dump for unknown settings:\n",f);
+ HexDump(f,0,sizeof(lex_test_t),5,16,
+ item->elem.data + sizeof(lex_test_t),
+ size - sizeof(lex_test_t) );
+ }
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SaveTextLEX
+(
+ const lex_t * lex, // pointer to valid LEX
+ ccp fname, // filename of destination
+ bool set_time // true: set time stamps
+)
+{
+ // use DOS/Windows line format -> unix can handle it ;)
+
+ DASSERT(lex);
+ DASSERT(fname);
+ PRINT("SaveTextLEX(%s,%d)\n",fname,set_time);
+
+
+ //--- open file
+
+ File_t F;
+ enumError err = CreateFileOpt(&F,true,fname,testmode,lex->fname);
+ if ( err > ERR_WARNING || !F.f )
+ return err;
+ SetFileAttrib(&F.fatt,&lex->fatt,0);
+
+
+ //--- print header + syntax info
+
+ if ( print_header && !brief_count && !export_count )
+ fprintf(F.f,text_lex_head_cr);
+ else
+ fprintf(F.f,
+ "%s\r\n"
+ "# LEX syntax & semantics: https://szs.wiimm.de/info/lex-syntax.html\r\n"
+ ,LEX_TEXT_MAGIC);
+
+
+ //--- print info section & section counter alphabetically
+
+ if ( brief_count || export_count )
+ {
+ fprintf(F.f,
+ "%s[SETUP]\r\n\r\n"
+ "TOOL = %s\r\n"
+ "SYSTEM = %s\r\n"
+ "VERSION = %s\r\n"
+ "REVISION = %u\r\n"
+ "DATE = %s\r\n"
+ "\r\n",
+ section_sep,
+ tool_name, SYSTEM, VERSION, REVISION_NUM, DATE );
+ }
+ else
+ {
+ fprintf(F.f, text_lex_setup_cr,
+ tool_name, SYSTEM, VERSION, REVISION_NUM, DATE );
+
+ uint i;
+ for ( i = 0; i < lex->item_used; i++ )
+ {
+ lex_item_t *s = lex->item[i];
+ const uint size = ntohl(s->elem.size);
+ fprintf(F.f,"# %-4.4s %08x, 0x%04x bytes\r\n",
+ PrintID(&s->elem.magic,4,0), s->elem.magic, size );
+ }
+ }
+
+ enumError max_err = ERR_OK;
+
+ uint i;
+ for ( i = 0; i < lex->item_used; i++ )
+ {
+ lex_item_t *s = lex->item[i];
+ const uint size = ntohl(s->elem.size);
+ const u32 magic = ntohl(s->elem.magic);
+ enumError err = ERR_OK;
+ switch(magic)
+ {
+ case LEXS_SET1:
+ err = SaveTextLEX_SET1(F.f,lex,s);
+ break;
+
+ case LEXS_CANN:
+ err = SaveTextLEX_CANN(F.f,lex,s);
+ break;
+
+ case LEXS_HIPT:
+ err = SaveTextLEX_HIPT(F.f,lex,s);
+ break;
+
+ case LEXS_TEST:
+ err = SaveTextLEX_TEST(F.f,lex,s);
+ break;
+
+ default:
+ hexdump_eol = "\r\n";
+ fprintf(F.f,text_lex_hexdump_cr,
+ s->sort_order, magic, PrintID(&s->elem.magic,4,0) );
+ HexDump(F.f,0,0,5,16,s->elem.data,size);
+ break;
+ }
+
+ if ( max_err < err )
+ max_err = err;
+ }
+
+ fputs(section_end,F.f);
+ ResetFile(&F,set_time);
+ return max_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LEX options ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+bool opt_lex_purge = false;
+bool force_lex_test = false;
+bool opt_lt_clear = false;
+int opt_lt_online = -9;
+int opt_lt_n_offline = -9;
+int opt_lt_n_online = -9;
+int opt_lt_cond_bit = -9;
+int opt_lt_game_mode = -9;
+int opt_lt_engine = -9;
+int opt_lt_random = -9;
+
+//-----------------------------------------------------------------------------
+
+int ScanOptLtOnline ( ccp arg )
+{
+ if ( !arg || !*arg )
+ opt_lt_online = -9;
+ else
+ {
+ int abbrev;
+ const KeywordTab_t *key = ScanKeyword(&abbrev,arg,lex_offline_online);
+ if (!key)
+ {
+ PrintKeywordError(lex_offline_online,arg,abbrev,0,
+ "keyword for option --lt-online");
+ return 1;
+ }
+ opt_lt_online = key->id;
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptLtNPlayers ( ccp arg )
+{
+ if ( !arg || !*arg )
+ opt_lt_n_offline = opt_lt_n_online = -9;
+ else
+ {
+ ccp comma = strchr(arg,',');
+ if (comma)
+ {
+ opt_lt_n_offline = str2l(arg,0,10);
+ opt_lt_n_online = str2l(comma+1,0,10);
+ }
+ else
+ {
+ int n = str2l(arg,0,10);
+ if ( n > 4 )
+ {
+ opt_lt_n_offline = -9;
+ opt_lt_n_online = n;
+ }
+ else
+ {
+ opt_lt_n_offline = n;
+ opt_lt_n_online = -9;
+ }
+ }
+
+ if ( opt_lt_n_offline < 0 )
+ opt_lt_n_offline = -9;
+ else if ( opt_lt_n_offline > 4 )
+ opt_lt_n_offline = 4;
+
+ if ( opt_lt_n_online < 0 )
+ opt_lt_n_online = -9;
+ else if ( opt_lt_n_online > 99 )
+ opt_lt_n_online = 99;
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptLtCondBit ( ccp arg )
+{
+ if ( !arg || !*arg )
+ opt_lt_cond_bit = -9;
+ else
+ {
+ int n = str2l(arg,0,10);
+ opt_lt_cond_bit = n < -1 || n >= 16 ? -9 : n;
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptLtGameMode ( ccp arg )
+{
+ if ( !arg || !*arg )
+ opt_lt_game_mode = -1;
+ else
+ {
+ int abbrev;
+ const KeywordTab_t *key = ScanKeyword(&abbrev,arg,lex_game_mode);
+ if (!key)
+ {
+ PrintKeywordError(lex_game_mode,arg,abbrev,0,
+ "keyword for option --lt-game-mode");
+ return 1;
+ }
+ opt_lt_game_mode = key->id;
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptLtEngine ( ccp arg )
+{
+ if ( !arg || !*arg )
+ opt_lt_engine = -1;
+ else
+ {
+ int abbrev;
+ const KeywordTab_t *key = ScanKeyword(&abbrev,arg,lex_engine);
+ if (!key)
+ {
+ PrintKeywordError(lex_engine,arg,abbrev,0,
+ "keyword for option --lt-engine");
+ return 1;
+ }
+ opt_lt_engine = key->id;
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptLtRandom ( ccp arg )
+{
+ if ( !arg || !*arg )
+ opt_lt_random = -1;
+ else
+ {
+ int n = str2l(arg,0,10);
+ opt_lt_random = n < 0 || n > 6 ? -9 : n;
+ }
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// lex_info_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SetupLexInfo ( lex_info_t *info, const lex_t *lex )
+{
+ DASSERT(info);
+ InitializeLexInfo(info);
+
+ if (lex)
+ {
+ info->lex_found = true;
+ lex_item_t *item;
+
+ item = FindElementLEX(lex,LEXS_SET1);
+ if (item)
+ {
+ info->set1_found = true;
+ memcpy(&info->set1,item->elem.data,sizeof(info->set1));
+ bef4n(info->set1.item_factor.v,info->set1.item_factor.v,3);
+ }
+
+ item = FindElementLEX(lex,LEXS_TEST);
+ if (item)
+ {
+ info->test_found = true;
+ memcpy(&info->test,item->elem.data,sizeof(info->test));
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SetupLexInfoByData ( lex_info_t *info, cvp data, uint data_size )
+{
+ lex_t lex;
+ const enumError err = ScanLEX(&lex,true,data,data_size);
+ if (!err)
+ SetupLexInfo(info,&lex);
+ else
+ InitializeLexInfo(info);
+ ResetLEX(&lex);
+ return err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LEX patching ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+// return TRUE if any option set
+bool HavePatchTestLEX()
+{
+ return force_lex_test
+ || opt_lt_clear
+ || opt_lt_online >= 0
+ || opt_lt_n_offline >= 0
+ || opt_lt_n_online >= 0
+ || opt_lt_game_mode >= 0
+ || opt_lt_engine >= 0
+ || opt_lt_random >= 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+// return TRUE if any option set and not default
+bool HaveActivePatchTestLEX()
+{
+ return force_lex_test
+ || opt_lt_online > 0
+ || opt_lt_n_offline > 0
+ || opt_lt_n_online > 0
+ || opt_lt_game_mode > LEX_GMODE_AUTO
+ || opt_lt_engine > LEX_ENGINE_AUTO
+ || opt_lt_random >= 0;
+}
+
+//-----------------------------------------------------------------------------
+
+// return TRUE if modified / set optional 'empty' to true, if test section is empty
+bool PatchTestLEX ( lex_test_t *lt, bool *empty )
+{
+ DASSERT(lt);
+ PRINT0("--lt-*: clear=%d, online=%d, n=%d,%d, gm=%d, en=%d, rnd=%d\n",
+ opt_lt_clear, opt_lt_online, opt_lt_n_offline, opt_lt_n_online,
+ opt_lt_game_mode, opt_lt_engine, opt_lt_random );
+
+ lex_test_t temp;
+ memcpy( &temp, opt_lt_clear ? (lex_test_t*)&TestDataLEX : lt, sizeof(temp) );
+
+ if ( opt_lt_online >= LEX_OO_AUTO && opt_lt_online <= LEX_OO_ONLINE )
+ temp.offline_online = opt_lt_online;
+
+ if ( opt_lt_n_offline >= 0 )
+ temp.n_offline = opt_lt_n_offline > 4 ? 4 : opt_lt_n_offline;
+
+ if ( opt_lt_n_online >= 0 )
+ temp.n_online = opt_lt_n_online > 99 ? 99 : opt_lt_n_online;
+
+ if ( opt_lt_cond_bit >= -1 && opt_lt_cond_bit < 16 )
+ temp.cond_bit = opt_lt_cond_bit;
+
+ if ( opt_lt_game_mode >= 0 )
+ temp.game_mode = opt_lt_game_mode;
+
+ if ( opt_lt_engine >= 0 )
+ temp.engine = opt_lt_engine;
+
+ if ( opt_lt_random >= 0 )
+ temp.random = opt_lt_random;
+
+ if (empty)
+ *empty = !memcmp(&temp,&TestDataLEX,sizeof(temp));
+ if (memcmp(lt,&temp,sizeof(*lt)))
+ {
+ memcpy(lt,&temp,sizeof(*lt));
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+
+bool PatchLEX ( lex_t * lex )
+{
+ DASSERT(lex);
+
+ bool modified = false;
+ if (HavePatchTestLEX())
+ {
+ lex_item_t *li = FindElementLEX(lex,LEXS_TEST);
+ const bool had_test = li != 0;
+ if (!had_test)
+ {
+ if (!HaveActivePatchTestLEX())
+ return 0;
+ li = AppendTestLEX(lex,false);
+ }
+ DASSERT(li);
+ lex_test_t orig;
+ memcpy(&orig,li->elem.data,sizeof(orig));
+
+ bool empty;
+ modified = PatchTestLEX((lex_test_t*)li->elem.data,&empty);
+ PRINT("PATCH+ LEX/TEST: had=%d, empty=%d, modified=%d => have_lex:%x\n",
+ had_test, empty, modified, lex->have_lex );
+ if ( empty && !force_lex_test )
+ {
+ if ( RemoveElementLEX(lex,li) && had_test )
+ modified = true;
+ }
+ else if (!had_test)
+ modified = true;
+
+ PRINT("PATCH- LEX/TEST: had=%d, empty=%d, modified=%d => have_lex:%x\n",
+ had_test, empty, modified, lex->have_lex );
+
+ if (modified)
+ lex->modified = true;
+ }
+
+ if (opt_lex_purge)
+ modified |= PurgeLEX(lex);
+
+ return modified;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// LEX purging ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+bool PurgeLEX ( lex_t * lex )
+{
+ DASSERT(lex);
+
+ bool modified = false;
+
+ uint i = 0;
+ while ( i < lex->item_used )
+ {
+ lex_item_t **ss = lex->item+i;
+ const uint size = ntohl((*ss)->elem.size);
+ const u8 *data = (*ss)->elem.data;
+
+ bool remove = false;
+ switch(ntohl((*ss)->elem.magic))
+ {
+ case LEXS_SET1:
+ {
+ lex_set1_t set1;
+ memset(&set1,0,sizeof(set1));
+ FixLEXElement_SET1(&set1);
+ remove = !memcmp(data,&set1,sizeof(set1));
+ }
+ break;
+
+ case LEXS_CANN:
+ remove = size <= sizeof(CannonDataLEX)
+ && !memcmp(data,&CannonDataLEX,size);
+ break;
+
+ case LEXS_HIPT:
+ remove = size < sizeof(lex_hipt_rule_t);
+ break;
+
+ case LEXS_TEST:
+ remove = !memcmp(data,TestDataLEX,sizeof(TestDataLEX));
+ break;
+
+ default:
+ remove = true;
+ break;
+ }
+
+ if (remove)
+ modified |= RemoveElementLEX(lex,*ss);
+ else
+ i++;
+ }
+
+ if (modified)
+ UpdateHaveLex(lex);
+
+ return modified;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+