diff options
Diffstat (limited to '')
| -rw-r--r-- | project/src/lib-lex.c | 2952 |
1 files changed, 2952 insertions, 0 deletions
diff --git a/project/src/lib-lex.c b/project/src/lib-lex.c new file mode 100644 index 0000000..357e062 --- /dev/null +++ b/project/src/lib-lex.c @@ -0,0 +1,2952 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * 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-2021 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-lex.h" +#include "lib-std.h" +#include "lib-mkw.h" +#include "lib-szs.h" +#include "lex.inc" + +#include <stddef.h> + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DEBUG settings /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef CHECK_FEATURES + +#if defined(TEST) || defined(DEBUG) || defined(HAVE_WIIMM_EXT) + #define CHECK_FEATURES 1 +#else + #define CHECK_FEATURES 0 +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 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; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// features_szs_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[feature_var_t]] + +typedef struct feature_var_t +{ + u16 offset; // offset for features_szs_t + u8 index; // index or bit number of related analyse_szs_t + u8 no_lex; // suppress output for lex files + ccp name; // name of variable + ccp comment; // comment for variable +} +feature_var_t; + +#undef DEF_VAR +#define DEF_VAR(l,i,v,c) { offsetof(features_szs_t,v),i,l,#v,c } + +//----------------------------------------------------------------------------- + +static const feature_var_t szs_feature_list[] = +{ + DEF_VAR( 1,HAVESZS_COURSE_LEX, f_lex, "File course.lex" ), + DEF_VAR( 0,HAVESZS_ITEM_SLOT_TABLE, f_item_slot_table, "File ItemSlotTable/ItemSlotTable.slt" ), + DEF_VAR( 0,HAVESZS_OBJFLOW, f_objflow, "File common/ObjFlow.bin" ), + DEF_VAR( 0,HAVESZS_GHT_ITEM, f_ght_item, "File common/GeoHitTableItem.bin" ), + DEF_VAR( 0,HAVESZS_GHT_ITEM_OBJ, f_ght_item_obj, "File common/GeoHitTableItemObj.bin" ), + DEF_VAR( 0,HAVESZS_GHT_KART, f_ght_kart, "File common/GeoHitTableKart.bin" ), + DEF_VAR( 0,HAVESZS_GHT_KART_OBJ, f_ght_kart_obj, "File common/GeoHitTableKartObj.bin" ), + DEF_VAR( 0,HAVESZS_MINIGAME, f_minigame, "File common/minigame.kmg" ), + + {0,0,0,0,0} +}; + +//----------------------------------------------------------------------------- + +static const feature_var_t kmp_feature_list[] = +{ + DEF_VAR( 0,HAVEKMP_WOODBOX_HT, kmp_woodbox_ht, "KMP: Woodbox with fall height" ), + DEF_VAR( 0,HAVEKMP_MUSHROOM_CAR, kmp_mushroom_car, "KMP: penguin_m as mushroom car" ), + DEF_VAR( 0,HAVEKMP_PENGUIN_POS, kmp_penguin_pos, "KMP: Alternative start of penguin_m" ), + DEF_VAR( 0,HAVEKMP_SECOND_KTPT, kmp_second_ktpt, "KMP: Second KTPT with index -1" ), + DEF_VAR( 0,HAVEKMP_X_PFLAGS, kmp_extended_pflags, "KMP: Extended presence flags (XPF) used" ), + DEF_VAR( 0,HAVEKMP_X_COND, kmp_xpf_cond_obj, "KMP: XPF: Conditional object used" ), + DEF_VAR( 0,HAVEKMP_X_DEFOBJ, kmp_xpf_def_obj, "KMP: XPF: Definition object used" ), + DEF_VAR( 0,HAVEKMP_X_RANDOM, kmp_xpf_random, "KMP: XPF: Random scenarios used" ), + DEF_VAR( 0,HAVEKMP_EPROP_SPEED, kmp_eprop_speed, "KMP: Epropeller with speed setting" ), + DEF_VAR( 0,HAVEKMP_COOB_R, kmp_coob_riidefii, "KMP: Conditional OOB by Riidefi" ), + DEF_VAR( 0,HAVEKMP_COOB_K, kmp_coob_khacker, "KMP: Conditional OOB by kHacker35000vr" ), + DEF_VAR( 0,HAVEKMP_UOOB, kmp_uncond_oob, "KMP: Unconditional OOB" ), + + {0,0,0,0,0} +}; + +//----------------------------------------------------------------------------- + +static const feature_var_t lex_section_list[] = +{ + DEF_VAR( 1,HAVELEXS_FEA0, lex_sect_fea0, "LEX section FEA0" ), + DEF_VAR( 0,HAVELEXS_TEST, lex_sect_test, "LEX section TEST" ), + DEF_VAR( 0,HAVELEXS_SET1, lex_sect_set1, "LEX section SET1" ), + DEF_VAR( 0,HAVELEXS_CANN, lex_sect_cann, "LEX section CANN" ), + DEF_VAR( 0,HAVELEXS_HIPT, lex_sect_hipt, "LEX section HIPT" ), + // move HAVELEXS_TEST to here + + {0,0,0,0,0} +}; +//----------------------------------------------------------------------------- + +static const feature_var_t lex_feature_list[] = +{ + DEF_VAR( 0,HAVELEXF_TEST_ACTIVE, lex_test_active, "LEX: Active test scenario" ), + DEF_VAR( 0,HAVELEXF_ITEM_RANGE, lex_item_range, "LEX: Expand the range of item positions" ), + DEF_VAR( 0,HAVELEXF_CANNON, lex_cannon, "LEX: Alternative cannon settings" ), + DEF_VAR( 0,HAVELEXF_HIDE_POS, lex_hide_pos, "LEX: Hide position tracker conditionally" ), + // move HAVELEXF_TEST_ACTIVE to here + {0,0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// +// [[feature_list_var_type]] + +enum feature_list_var_type +{ + FVT_BOOL_ARRAY, + FVT_BYTE_ARRAY, + FVT_UINT_BITS, +}; + +//----------------------------------------------------------------------------- +// [[feature_list_t]] + +typedef struct feature_list_t +{ + const feature_var_t *var; // var list + uint var_type; // one of enum feature_list_var_type + uint offset; // offset in analyse_szs_t + ccp ref_prefix; // prefix for references + ccp heading1; // heading1 + ccp heading2; // heading2 +} +feature_list_t; + +//----------------------------------------------------------------------------- + +static const feature_list_t feature_lists[] = +{ + { szs_feature_list, + FVT_BYTE_ARRAY, + offsetof(szs_have_t,szs), + "SZS file", + "Additional sub-files found in SZS file", + "0:no file, 1:unmodified file, 2:modified file" }, + + { kmp_feature_list, + FVT_BOOL_ARRAY, + offsetof(szs_have_t,kmp), + "KMP feature", + "Special features found in KMP (sub-file course.kmp)", + 0 }, + + { lex_section_list, + FVT_UINT_BITS, + offsetof(szs_have_t,lex_sect), + "LEX section", + "Sections found in LEX (sub-file course.lex)", + 0 }, + + { lex_feature_list, + FVT_UINT_BITS, + offsetof(szs_have_t,lex_feat), + "LEX feature", + "Special features found in LEX (sub-file course.lex)", + 0 }, + + {0,0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeFeaturesSZS ( features_szs_t * fs ) +{ + DASSERT(fs); + memset(fs,0,sizeof(*fs)); + fs->size_be = htons(sizeof(*fs)); +} + +/////////////////////////////////////////////////////////////////////////////// +#if CHECK_FEATURES + + static uint CheckUsageFeaturesSZS() + { + GetFeaturesModes(); + + static uint err_count = 0; + static bool done = false; + if (done) + return err_count; + done = true; + + features_szs_t fs; + InitializeFeaturesSZS(&fs); + u8 *fs_data = (u8*)&fs; + + ccp err_msg = "Invalid feature_var_t[]\n"; + + const feature_list_t *list; + for ( list = feature_lists; list->var; list++ ) + { + const feature_var_t *var; + for ( var = list->var; var->offset; var++ ) + { + uint offset = var->offset; + if ( offset < FIRST_FEATURE_SZS_OFFSET || offset > sizeof(features_szs_t) ) + { + if (!err_count++) + ERROR0(ERR_INVALID_DATA,err_msg,0); + fprintf(stderr,"!! * Offset %d out of range.\n",offset); + } + else + fs_data[offset]++; + } + } + + uint offset; + for ( offset = FIRST_FEATURE_SZS_OFFSET; offset < sizeof(features_szs_t); offset++ ) + { + const u8 count = fs_data[offset]; + if ( count != 1 ) + { + if (!err_count++) + ERROR0(ERR_INVALID_DATA,err_msg,0); + if (!count) + fprintf(stderr,"!! * Offset %d not used.\n",offset); + else + fprintf(stderr,"!! * Offset %d used %u times.\n",offset,count); + } + } + + ResetFeaturesSZS(&fs); + return err_count; + } + +#endif // CHECK_FEATURES +/////////////////////////////////////////////////////////////////////////////// + +void SetupFeaturesSZS ( features_szs_t *fs, const szs_have_t *have, bool is_lex ) +{ + #if CHECK_FEATURES + CheckUsageFeaturesSZS(); + #endif + + const feature_list_t *list; + const feature_var_t *var; + + for ( list = feature_lists; list->var; list++ ) + { + switch (list->var_type) + { + case FVT_BOOL_ARRAY: + { + const u8 *src = (u8*)have + list->offset; + for ( var = list->var; var->offset; var++ ) + ((u8*)fs)[var->offset] + = is_lex && var->no_lex + ? 1 + : src[var->index] > 0; + } + break; + + case FVT_BYTE_ARRAY: + { + const u8 *src = (u8*)have + list->offset; + for ( var = list->var; var->offset; var++ ) + ((u8*)fs)[var->offset] + = is_lex && var->no_lex + ? 1 + : src[var->index]; + } + break; + + case FVT_UINT_BITS: + { + const uint val = *(uint*)((u8*)have + list->offset); + for ( var = list->var; var->offset; var++ ) + ((u8*)fs)[var->offset] + = is_lex && var->no_lex + ? 1 + : ( val & 1 << var->index ) != 0; + } + break; + + default: + ASSERT(0); + } + } + + // ??? [[2do]] +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetIsLexFeaturesSZS ( features_szs_t *fs ) +{ + DASSERT(fs); + + #if 1 + // short cut for code below + fs->f_lex = fs->lex_sect_fea0 = 1; + #else + // non optimized code + const feature_list_t *list; + for ( list = feature_lists; list->var; list++ ) + { + const feature_var_t *var; + for ( var = list->var; var->offset; var++ ) + if (var->no_lex) + ((u8*)fs)[var->offset] = 1; + } + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetFeaturesStatusSZS ( const features_szs_t *fs ) +{ + // returns + // 0: all features set to null + // 1: all features except f_lex + lex_sect_fea0 are set to null + // 2: any features except f_lex + lex_sect_fea0 is not NULL + + DASSERT(fs); + + features_szs_t temp; + InitializeFeaturesSZS(&temp); + if (!memcmp(&temp,fs,sizeof(temp))) + return 0; + + temp.f_lex = fs->f_lex; + temp.lex_sect_fea0 = fs->lex_sect_fea0; + return memcmp(&temp,fs,sizeof(temp)) ? 2 : 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintFeaturesSZS +( + PrintScript_t *ps, // valid output definition + const features_szs_t *fs, // valid source + bool is_lex, // TRUE: Create output for a LEX file + int comments, // >0: add extended comments + int print_modes, // >0: append ",MODES" + u8 include, // print only if all bits match + u8 exclude // exclude if any bit match +) +{ + DASSERT(ps); + DASSERT(fs); + + if ( ps->fform == PSFF_UNKNOWN && comments >= 0 ) + ps->fform = PSFF_ASSIGN; + + int head_mode = 0; + const u8 *modes = (u8*)GetFeaturesModes(); + const feature_list_t *list; + + for ( list = feature_lists; list->var; list++ ) + { + if ( comments >= 0 ) + head_mode = 2; + else if ( comments >= -1 ) + head_mode = 1; + + const feature_var_t *var; + for ( var = list->var; var->offset; var++ ) + { + if ( is_lex && var->no_lex ) + continue; + + const u8 mode = modes[var->offset]; + if ( ( mode & include ) != include || mode & exclude ) + continue; + + const u8 value = ((u8*)fs)[var->offset]; + if ( value || comments >= -2 ) + { + if ( head_mode == 1 ) + fputc('\n',ps->f); + else if ( head_mode == 2 ) + { + const int hlen1 = strlen(list->heading1); + const int hlen2 = list->heading2 ? strlen(list->heading2) : 0; + const int sep_len = ( hlen1 > hlen2 ? hlen1 : hlen2 ) + 2; + PrintScriptVars(ps,0,"\n#%.*s\n# %s\n", sep_len, Minus300, list->heading1 ); + if (list->heading2) + PrintScriptVars(ps,0,"# %s\n", list->heading2 ); + PrintScriptVars(ps,0,"#%.*s\n", sep_len, Minus300 ); + } + head_mode = 0; + + if ( comments >= 0 ) + { + fputc('\n',ps->f); + if ( comments >= 2 ) + PrintScriptVars(ps,0,"# %s #%u, feature offset %u, source index %u\n", + list->ref_prefix, (int)(var-list->var), + var->offset, var->index ); + PrintScriptVars(ps,0,"# %s\n",var->comment); + ccp effect = GetFeaturesEffects(mode); + if (effect) + PrintScriptVars(ps,0,"# %s\n",effect); + } + if ( print_modes > 0 ) + PrintScriptVars(ps,0,"%s=\"%u,%s\"\n", + var->name, value, GetFeaturesMode(mode) ); + else + PrintScriptVars(ps,0,"%s=%u\n",var->name,value); + } + } + } + + if ( comments >= -1 ) + fputc('\n',ps->f); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// features_szs_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const features_szs_t * GetFeaturesModes() +{ + static features_szs_t m = {0}; // keep name short! + + if (!m.size_be) + { + InitializeFeaturesSZS(&m); + + const u8 invalid = FZM_SECTION | FZM_BATTLE | FZM_TIMETRIAL; + memset((u8*)&m+sizeof(m.size_be),invalid,sizeof(m)-sizeof(m.size_be)); + + const u8 no_timetrial = FZM_OFFLINE | FZM_ONLINE; + + m.f_lex = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.f_item_slot_table = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.f_objflow = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.f_ght_item = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.f_ght_item_obj = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.f_ght_kart = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.f_ght_kart_obj = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.f_minigame = FZM_GAMEPLAY | FZM_BATTLE | FZM_M_WHERE; + + m.kmp_woodbox_ht = FZM_GAMEPLAY | FZM_M_TYPE | no_timetrial; + m.kmp_mushroom_car = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_penguin_pos = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_second_ktpt = FZM_VISUAL | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_extended_pflags = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_xpf_cond_obj = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_xpf_def_obj = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_xpf_random = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | no_timetrial; + m.kmp_eprop_speed = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_coob_riidefii = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_coob_khacker = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.kmp_uncond_oob = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + + m.lex_sect_fea0 = FZM_SECTION; + m.lex_sect_test = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | no_timetrial | FZM_SECTION; + m.lex_sect_set1 = FZM_GAMEPLAY | FZM_M_TYPE | FZM_ONLINE | FZM_SECTION; + m.lex_sect_cann = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE | FZM_SECTION; + m.lex_sect_hipt = FZM_VISUAL | FZM_M_TYPE | no_timetrial | FZM_SECTION; + + m.lex_test_active = FZM_VISUAL | FZM_GAMEPLAY | FZM_M_TYPE | no_timetrial; + m.lex_item_range = FZM_GAMEPLAY | FZM_M_TYPE | FZM_ONLINE; + m.lex_cannon = FZM_GAMEPLAY | FZM_M_TYPE | FZM_M_WHERE; + m.lex_hide_pos = FZM_VISUAL | FZM_M_TYPE | no_timetrial; + + #if CHECK_FEATURES + uint err_count = 0; + const feature_list_t *list; + for ( list = feature_lists; list->var; list++ ) + { + const feature_var_t *var; + for ( var = list->var; var->offset; var++ ) + { + u8 value = ((u8*)&m)[var->offset]; + if ( value == invalid ) + { + if (!err_count++) + ERROR0(ERR_INVALID_DATA,"Invalid features modes\n"); + fprintf(stderr,"!! * Mode %s not set.\n",var->name); + } + } + } + #endif + } + return &m; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFeaturesMode ( features_szs_mode_t mode ) +{ + char *buf = GetCircBuf(CHAR_BIT+1); + char *src = "SVGBRTLO", *dest = buf; + u8 mask; + for ( mask = 1; mask; mask <<= 1, src++ ) + if ( mode & mask ) + *dest++ = *src; + *dest = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +ccp GetFeaturesEffects ( features_szs_mode_t mode ) +{ + struct tab_t { u8 not; u8 mode; ccp info; }; + static const struct tab_t *ptr, tab[] = + { + { 0,FZM_SECTION, "section" }, + + { 1,FZM_M_KIND, "no effects" }, + { 0,FZM_M_KIND, "visual and gameplay effects" }, + { 0,FZM_VISUAL, "visual effects only" }, + { 0,FZM_GAMEPLAY, "gameplay effects" }, + + { 0,FZM_M_TYPE, 0 }, + { 0,FZM_BATTLE, "battle only" }, + { 0,FZM_RACING, "racing only" }, + + { 0,FZM_M_WHERE, 0 }, + { 0,FZM_OFFLINE|FZM_ONLINE, "except time trial" }, + { 0,FZM_OFFLINE|FZM_TIMETRIAL, "offline incl. time trial" }, + { 0,FZM_ONLINE|FZM_TIMETRIAL, "time trial and online" }, + { 0,FZM_TIMETRIAL, "time trial only" }, + { 0,FZM_OFFLINE, "offline except time trial" }, + { 0,FZM_ONLINE, "online only" }, + + {0,0} + }; + + char buf[200], *dest = buf, *sep = ""; + for ( ptr = tab; ptr->mode; ptr++ ) + { + const u8 mode1 = ptr->not ? ~mode : mode; + if ( (mode1 & ptr->mode) == ptr->mode ) + { + if (ptr->info) + { + dest = StringCat2E(dest,buf+sizeof(buf),sep,ptr->info); + sep = ", "; + } + if (!ptr->not) + mode &= ~ptr->mode; + } + } + + return dest == buf ? 0 : CopyCircBuf0(buf,dest-buf); +} + +//----------------------------------------------------------------------------- + +ccp GetFeaturesEffectsByOffset ( uint offset ) +{ + if ( offset < 2 && offset >= sizeof(features_szs_t) ) + return 0; + + const features_szs_t *fs = GetFeaturesModes(); + return GetFeaturesEffects(((u8*)fs)[offset]); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 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); +} + +//----------------------------------------------------------------------------- +// [[+]] + +static void ClearLEXElement_SET1 ( lex_set1_t *set1 ) +{ + DASSERT(set1); + memset(set1,0,sizeof(*set1)); + FixLEXElement_SET1(set1); +} + +//----------------------------------------------------------------------------- +// [[+]] + +static bool IsActiveLEXElement_SET1 ( const lex_set1_t *set1 ) +{ + DASSERT(set1); + lex_set1_t empty, test = *set1; + ClearLEXElement_SET1(&empty); + FixLEXElement_SET1(&test); + return memcmp(&empty,&test,sizeof(empty)) != 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[+]] + +static void FixLEXElement_TEST ( lex_test_t *lt ) +{ + DASSERT(lt); + + if ( lt->offline_online < LEX_OO_AUTO || lt->offline_online > LEX_OO_ONLINE ) + lt->offline_online = LEX_OO_AUTO; + + if ( lt->n_offline > 4 ) + lt->n_offline = 4; + + if ( lt->n_online > 99 ) + lt->n_online = 99; + + if ( lt->cond_bit < -1 || lt->cond_bit > 15 ) + lt->cond_bit = -1; + + if ( lt->game_mode >= LEX_GMODE__N ) + lt->game_mode = LEX_GMODE_AUTO; + + if ( lt->random > 8 ) + lt->random = 0; + + if ( lt->engine > ENGINE__N ) + lt->engine = 0; + + lt->padding = 0; +} + +//----------------------------------------------------------------------------- +// [[+]] + +static void FixLEXItem_TEST +( + lex_t * lex, // LEX data structure + lex_item_t * item // item to fix +) +{ + DASSERT(lex); + DASSERT(item); + DASSERT( ntohl(item->elem.size) >= sizeof(lex_test_t) ); + + lex_test_t *lt = (lex_test_t*)item->elem.data; + FixLEXElement_TEST(lt); +} + +//----------------------------------------------------------------------------- +// [[+]] + +static void ClearLEXElement_TEST ( lex_test_t *lt ) +{ + DASSERT(lt); + memcpy(lt,TestDataLEX,sizeof(*lt)); +} + +//----------------------------------------------------------------------------- +// [[+]] + +static bool IsActiveLEXElement_TEST ( const lex_test_t *lt ) +{ + DASSERT(lt); + lex_test_t empty, test = *lt; + ClearLEXElement_TEST(&empty); + FixLEXElement_TEST(&test); + return memcmp(&empty,&test,sizeof(empty)) != 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 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_FEA0: return 1; + case LEXS_SET1: return 2; + case LEXS_TEST: return 9; + default: return 5; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void UpdateHaveLex ( lex_t *lex ) +{ + DASSERT(lex); + uint have_sect = 0; + uint have_feat = 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 < HAVELEXS__N; j++ ) + if ( magic == have_lex_sect_info[j].magic + && size >= have_lex_sect_info[j].min_size ) + { + have_sect |= 1 << j; + have_feat |= GetLexFeatures(&(*ss)->elem); + break; + } + } + + lex->have_sect = have_sect; + lex->have_feat = have_feat; +} + +/////////////////////////////////////////////////////////////////////////////// + +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 ); + PRINT("AppendElementLEX(%.4s) have=%02x,%02x, n=%u\n", + (ccp)&magic, lex->have_sect, lex->have_feat, lex->item_used); + + + //--- check if already exists + + lex_item_t **ss; + const u32 be_magic = htonl(magic); + uint i; + 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_FEA0: + if ( size4 < sizeof(features_szs_t) ) + size4 = ALIGN32(sizeof(features_szs_t),4); + break; + + 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); + + PRINT("AppendElementLEX(%.4s) have=%02x,%02x, n=%u\n", + (ccp)&magic, lex->have_sect, lex->have_feat, lex->item_used); + UpdateHaveLex(lex); + 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 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX features /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint GetLexFeaturesSET1 ( const lex_element_t *elem ) +{ + uint have_feat = 0; + const uint size = ntohl(elem->size); + const lex_set1_t *set1 = (lex_set1_t*)elem->data; + + if ( size >= offsetof(lex_set1_t,item_factor) + sizeof(set1->item_factor) ) + { + if ( bef4(set1->item_factor.v+0) != 1.0 + || bef4(set1->item_factor.v+1) != 1.0 + || bef4(set1->item_factor.v+2) != 1.0 + ) + { + have_feat |= 1 << HAVELEXF_ITEM_RANGE; + } + } + return have_feat; +} + +//----------------------------------------------------------------------------- + +static uint GetLexFeaturesCANN ( const lex_element_t *elem ) +{ + return ntohl(elem->size) != sizeof(CannonDataLEX) + || memcmp(elem->data,CannonDataLEX,sizeof(CannonDataLEX)) + ? 1 << HAVELEXF_CANNON : 0; +} + +//----------------------------------------------------------------------------- + +static uint GetLexFeaturesHIPT ( const lex_element_t *elem ) +{ + return ntohl(elem->size) >= sizeof(lex_hipt_rule_t) + ? 1 << HAVELEXF_HIDE_POS : 0; +} + +//----------------------------------------------------------------------------- + +static uint GetLexFeaturesTEST ( const lex_element_t *elem ) +{ + DASSERT(elem); + return IsActiveLEXElement_TEST((lex_test_t*)elem->data) + ? 1 << HAVELEXF_TEST_ACTIVE : 0; +} + +//----------------------------------------------------------------------------- + +uint GetLexFeatures ( const lex_element_t *elem ) +{ + switch(ntohl(elem->magic)) + { + case LEXS_SET1: return GetLexFeaturesSET1(elem); + case LEXS_CANN: return GetLexFeaturesCANN(elem); + case LEXS_HIPT: return GetLexFeaturesHIPT(elem); + case LEXS_TEST: return GetLexFeaturesTEST(elem); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const have_lex_info_t have_lex_feat_info[HAVELEXF__N] = +{ + { HAVELEXF_TEST_ACTIVE, "test", 0 }, + { HAVELEXF_ITEM_RANGE, "itempos", 0 }, + { HAVELEXF_CANNON, "cannon", 0 }, + { HAVELEXF_HIDE_POS, "hipt", 0 }, + //--- add new sections here (order is important) +}; + +/////////////////////////////////////////////////////////////////////////////// + +ccp CreateFeatureInfoLEX + ( have_lex_feat_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 < HAVELEXF__N; i++, mask <<= 1 ) + if ( special & mask ) + { + dest = StringCat2E(dest,buf+sizeof(buf),sep,have_lex_feat_info[i].name); + sep = ","; + } + + return dest == buf ? return_if_empty : CopyCircBuf0(buf,dest-buf); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// have lex sections /////////////// +/////////////////////////////////////////////////////////////////////////////// +// order is important, compare have_lex_sect_t and ct.wiimm.de + +const have_lex_info_t have_lex_sect_info[HAVELEXS__N] = +{ + { LEXS_SET1, "set1", 1 }, // HAVELEXS_SET1 + { LEXS_CANN, "cannon", 0 }, // HAVELEXS_CANN + { LEXS_TEST, "test", 1 }, // HAVELEXS_TEST + { LEXS_HIPT, "hidepos", sizeof(lex_hipt_rule_t) }, // HAVELEXS_HIPT + { LEXS_FEA0, "features", 2 }, // HAVELEXS_FEA0 + //--- add new sections here (order is important) +}; + +/////////////////////////////////////////////////////////////////////////////// + +ccp CreateSectionInfoLEX + ( have_lex_sect_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 < HAVELEXS__N; i++, mask <<= 1 ) + if ( special & mask ) + { + dest = StringCat2E(dest,buf+sizeof(buf),sep,have_lex_sect_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 ) + { + //--- FEA0 + AppendFea0LEX(lex,false,0); + } + + 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 * AppendFea0LEX + ( lex_t * lex, bool overwrite, const features_szs_t *src_fs ) +{ + features_szs_t fs; + if (src_fs) + fs = *src_fs; + else + InitializeFeaturesSZS(&fs); + SetIsLexFeaturesSZS(&fs); + + return AppendElementLEX(lex,LEXS_FEA0,&fs,sizeof(fs),overwrite); +} + +//----------------------------------------------------------------------------- + +lex_item_t * AppendSet1LEX ( lex_t * lex, bool overwrite ) +{ + lex_set1_t set1; + memset(&set1,0,sizeof(set1)); + // AppendElementLEX() does call FixLEXItem_SET1() + return AppendElementLEX(lex,LEXS_SET1,&set1,sizeof(set1),overwrite); +} + +//----------------------------------------------------------------------------- + +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 = {0}; + + const ScanParam_t ptab[] = + { + { "ITEM-POS-FACTOR", SPM_FLOAT3_BE, &set1.item_factor }, + { "TEST1", SPM_U8, set1.test+0 }, + { "TEST2", SPM_U8, set1.test+1 }, + { "TEST3", SPM_U8, set1.test+2 }, + { "TEST4", SPM_U8, set1.test+3 }, + {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,0); + 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->fatt = raw->fatt; + 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_FEA0 +( + 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(features_szs_t)) + return ERR_OK; + + fprintf(f,text_lex_elem_feat_cr,item->sort_order); + const features_szs_t *fs = (features_szs_t*)item->elem.data; + + PrintScript_t ps; + InitializePrintScript(&ps); + ps.f = f; + ps.fform = PSFF_ASSIGN; + ps.force_case = LOUP_UPPER; + PrintFeaturesSZS(&ps,fs,true,1,0,0,0); + ResetPrintScript(&ps); + + uint data_size = ntohs(fs->size_be); + if ( data_size > size ) + data_size = size; + + if ( data_size > sizeof(features_szs_t) ) + { + u8 size = data_size - sizeof(features_szs_t); + u8 *ptr = item->elem.data + sizeof(features_szs_t); + if ( size >= 4 || !IsMemConst(ptr,size,0) ) + { + fputs("\n# Hex dump for unknown settings:\n",f); + HexDump(f,0,sizeof(features_szs_t),5,16,ptr,size); + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +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 (*(u32*)set1->test) + fprintf(f,text_lex_elem_set1_develop_cr + ,set1->test[0],set1->test[1],set1->test[2],set1->test[3] + ); + + 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" + "SYSTEM2 = %s\r\n" + "VERSION = %s\r\n" + "REVISION = %u\r\n" + "DATE = %s\r\n" + "\r\n", + section_sep, + tool_name, SYSTEM2, VERSION, REVISION_NUM, DATE ); + } + else + { + fprintf(F.f, text_lex_setup_cr, + tool_name, SYSTEM2, 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_FEA0: err = SaveTextLEX_FEA0(F.f,lex,s); break; + 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_features = false; +bool opt_lex_rm_features = false; +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; + info->have_sect = lex->have_sect; + info->have_feat = lex->have_feat; + + 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; +} + +//----------------------------------------------------------------------------- +// return TRUE if modified / set optional 'empty' to true, if test section is empty + +bool PatchFeaturesLEX ( lex_t * lex, const szs_have_t *have, bool *empty ) +{ + DASSERT(lex); + DASSERT(have); + DASSERT(have->valid); + + features_szs_t fs; + SetupFeaturesSZS(&fs,have,true); + const bool need_features = GetFeaturesStatusSZS(&fs) > 1; + + lex_item_t *li = FindElementLEX(lex,LEXS_FEA0); + const bool have_features = li != 0; + + PRINT("--lex-features = %d, --lex-rm-features = %d : have=%d, need=%d\n", + opt_lex_features, opt_lex_rm_features, have_features, need_features ); + + bool modified = false; + if ( opt_lex_rm_features && RemoveElementByMagicLEX(lex,LEXS_FEAT) ) + modified = true; // FEA0 + + if ( have_features && !need_features ) + { + if (RemoveElementLEX(lex,li)) + modified = true; + } + else if ( opt_lex_features + && need_features + && ( !have_features || memcmp(&fs,li->elem.data,sizeof(fs)) )) + { + AppendFea0LEX(lex,true,&fs); + modified = true; + } + + ResetFeaturesSZS(&fs); + + if (empty) + *empty = !need_features; + if (modified) + lex->modified = true; + return modified; +} + +//----------------------------------------------------------------------------- + +bool PatchLEX ( lex_t * lex, const szs_have_t *have ) +{ + DASSERT(lex); + + bool modified = false; + + //--- section TEST + + 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,%x\n", + had_test, empty, modified, lex->have_sect, lex->have_feat ); + 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,%x\n", + had_test, empty, modified, lex->have_sect, lex->have_feat ); + + } + + + //--- section FEA0 + + if ( HavePatchFeaturesLEX() && have && have->valid ) + { + szs_have_t temp = *have; + temp.lex_sect = lex->have_sect; + temp.lex_feat = lex->have_feat; + + if (PatchFeaturesLEX(lex,&temp,0)) + modified = true; + } + + + //--- terminate + + if (opt_lex_purge) + modified |= PurgeLEX(lex); + + if (modified) + lex->modified = true; + + 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_FEA0: + remove = GetFeaturesStatusSZS((features_szs_t*)data) < 2; + break; + + case LEXS_SET1: + remove = !IsActiveLEXElement_SET1((lex_set1_t*)data); + 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 = !IsActiveLEXElement_TEST((lex_test_t*)data); + break; + + default: + remove = true; + break; + } + + if (remove) + modified |= RemoveElementLEX(lex,*ss); + else + i++; + } + + if (modified) + UpdateHaveLex(lex); + + return modified; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + |
