diff options
Diffstat (limited to '')
| -rw-r--r-- | project/src/lib-kmp-pflags.c | 1965 |
1 files changed, 1965 insertions, 0 deletions
diff --git a/project/src/lib-kmp-pflags.c b/project/src/lib-kmp-pflags.c new file mode 100644 index 0000000..07be6c6 --- /dev/null +++ b/project/src/lib-kmp-pflags.c @@ -0,0 +1,1965 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * 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 "lib-kmp.h" +#include "lib-szs.h" +#include "dclib-xdump.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +opt_gamemode_t opt_gamemodes = GMD_M_DEFAULT; + +static const KeywordTab_t opt_gamemodes_tab[] = +{ + { 0, "CLEAR", "RESET", GMD_M_ALL | GMD_F_HIDE }, + { GMD_M_DEFAULT, "DEFAULT", 0, GMD_M_ALL | GMD_F_HIDE }, + + { GMD_STD, "STANDARD", "STD", 0 }, + { GMD_BALLOON, "BALLOON", 0, 0 }, + { GMD_COIN, "COIN", 0, 0 }, + { GMD_MD_BATTLE, "BATTLE", 0, 0 }, + { GMD_VERSUS, "VERSUS", "VS", 0 }, + { GMD_ITEMRAIN, "ITEMRAIN", 0, 0 }, + { GMD_MD_RACE, "RACE", 0, 0 }, + { GMD_MD_ALL, "ALL", 0, 0 }, + + { GMD_NO_MODES, "NO-MODES", "NOMODES", 0 }, + { GMD_OFFLINE, "OFFLINE", 0, 0 }, + { GMD_ONLINE, "ONLINE", 0, 0 }, + + { GMD_AUTO, "AUTO", 0, 0 }, + { GMD_STD_FIRST, "1STANDARD", "1STD", 0 }, + { GMD_ENGINE, "ENGINE", 0, 0 }, + { 0, "NO-ENGINE", "NOENGINE", GMD_ENGINE }, + { GMD_RANDOM, "RANDOM", 0, 0 }, + { 0, "NO-RANDOM", "NORANDOM", GMD_RANDOM }, + { GMD_NO_TT, "NO-TT", "NOTT", 0 }, + { GMD_FORCE_TT, "TT", 0, 0 }, + { GMD_FULL, "FULL", 0, 0 }, + + { GMD_NSORT, "SORT", "NSORT", GMD_M_SORT }, + { GMD_ISORT, "ISORT", 0, GMD_M_SORT }, + { 0, "NO-SORT", "NOSORT", GMD_M_SORT }, + + { GMD_DEBUG, "DEBUG", 0, 0 }, + { GMD_DUMP, "DUMP", 0, 0 }, + + {0,0,0,0} +}; + +//----------------------------------------------------------------------------- + +int ScanOptGamemodes ( ccp arg ) +{ + if (!arg) + return 0; + + s64 stat = ScanKeywordList(arg,opt_gamemodes_tab,0,true,0,GMD_M_OPTIONS, + "Option --gamemodes",ERR_SYNTAX); + if ( stat != -1 ) + { + opt_gamemodes = stat; + if (!(opt_gamemodes&GMD_MD_ALL)) + opt_gamemodes |= GMD_MD_DEFAULT; + if (!(opt_gamemodes&GMD_FI_ALL)) + opt_gamemodes |= GMD_FI_DEFAULT; + + // do this after evaluating GMD_MD_ALL! + if (opt_gamemodes&GMD_STD_FIRST) + opt_gamemodes |= GMD_STD; + + if ( opt_gamemodes & GMD_DEBUG ) + { + char buf[200] = {0}; + PrintKeywordList( buf, sizeof(buf), 0, opt_gamemodes_tab, + opt_gamemodes, GMD_M_DEFAULT, GMD_F_HIDE ); + fflush(stdout); + fprintf(stderr,"GAMEMODES: %s\n",buf); + fflush(stderr); + } + return 0; + } + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_pflags_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint CountPresenceFlags ( const kmp_t *kmp, kmp_pflags_t *pf ) +{ + DASSERT(pf); + DASSERT(kmp); + + memset(pf,0,sizeof(*pf)); + + const uint n = kmp->dlist[KMP_GOBJ].used; + pf->n_gobj = n; + + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint i; + for ( i = 0; i < n; i++, gobj++ ) + { + const u16 pflags = gobj->pflags; + const u16 std = pflags & GOBJ_M_PF_STD; + const u16 mode = ( pflags & GOBJ_M_PF_MODE ) >> GOBJ_S_PF_MODE; + + if ( std == pflags ) + pf->n_standard++; + + if ( mode > 0 ) + pf->n_xpflags++; + + if (!pf->n_mode[mode]++) + pf->n_cat_mode++; + + if (!pf->n_std[std]++) + pf->n_cat_std++; + + if ( mode == 1 ) + { + if (IsConditionRefRelevantRandomId(gobj->ref_id)) + pf->n_cond_rnd++; + else if (IsConditionRefId(gobj->ref_id)) + pf->n_cond_ref++; + + if (IsDefinitionObjectId(gobj->ref_id)) + pf->n_obj_ref++; + } + + if ( IsDefinitionObjectIdBITS(gobj->obj_id) ) + pf->n_defobj_bits++; + else if ( IsDefinitionObjectIdOR(gobj->obj_id) ) + pf->n_defobj_or++; + else if ( IsDefinitionObjectIdAND(gobj->obj_id) ) + pf->n_defobj_and++; + } + + pf->n_defobj = pf->n_defobj_bits + pf->n_defobj_or + pf->n_defobj_and; + return n; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// detect, fix and sort definition-objects /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyseDefObject ( kmp_ana_defobj_t *ad, const kmp_gobj_entry_t *gobj ) +{ + DASSERT(ad); + DASSERT(gobj); + memset(ad,0,sizeof(*ad)); + + if (IsDefinitionObjectId(gobj->obj_id)) + { + ad->is_defobj = true; + ad->is_pure_defobj = + ( gobj->ref_id == 0 || IsConditionRefId(gobj->ref_id) ) + && gobj->position[0] == 0.0 + && gobj->position[1] == 0.0 + && gobj->position[2] == 0.0 + && gobj->rotation[0] == 0.0 + && gobj->rotation[1] == 0.0 + && gobj->rotation[2] == 0.0 + && gobj->scale[0] == 0.0 + && gobj->scale[1] == 0.0 + && gobj->scale[2] == 0.0 + && gobj->route_id == 0xffff + && ( gobj->pflags == 0 + || gobj->pflags == GOBJ_PF_MODE1 ); + } + + if ( IsDefinitionObjectIdOR(gobj->obj_id) ) + { + ad->cmd_name = "OR"; + logical: + ad->par_info = "obj_id, condition_1 [,conditions_2_8]..."; + ad->is_logical = true; + ad->index_beg = 0; + ad->index_end = 8; + return; + } + else if ( IsDefinitionObjectIdAND(gobj->obj_id) ) + { + ad->cmd_name = "AND"; + goto logical; + } + + u16 set0 = gobj->setting[0]; + uint idx = 1; + while ( idx < 8 && gobj->setting[idx] == set0 ) + idx++; + + if ( idx > 2 ) + { + ad->cmd_name = "RACE"; + ad->par_info = "obj_id, versus, itemrain"; + ad->index_beg = 2; + } + else + { + ad->cmd_name = "BATTLE"; + ad->par_info = "obj_id, balloon, coin, versus, itemrain"; + ad->index_beg = 0; + } + + while ( idx < 8 ) + { + const u16 set1 = gobj->setting[idx++]; + if ( set0 != set1 ) + { + ad->index_end = idx; + set0 = set1; + } + } + + if ( ad->index_end <= ad->index_beg ) + ad->index_end = ad->index_beg + 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NormalizeDefObj ( kmp_gobj_entry_t *gobj ) +{ + DASSERT(gobj); + bool stat = false; + if (IsDefinitionObjectId(gobj->obj_id)) + { + kmp_gobj_entry_t temp; + memcpy(&temp,gobj,sizeof(temp)); + + gobj->position[0] = 0.0; + gobj->position[1] = 0.0; + gobj->position[2] = 0.0; + gobj->rotation[0] = 0.0; + gobj->rotation[1] = 0.0; + gobj->rotation[2] = 0.0; + gobj->scale[0] = 0.0; + gobj->scale[1] = 0.0; + gobj->scale[2] = 0.0; + gobj->route_id = 0xffff; + gobj->pflags = 0; + + if (IsDefinitionObjectIdBITS(gobj->obj_id)) + { + const u16 val = gobj->setting[2]; + gobj->setting[4] = val; + gobj->setting[5] = val; + gobj->setting[6] = val; + gobj->setting[7] = val; + } + + stat = memcmp(&temp,gobj,sizeof(temp)) != 0; + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountDefObjUsage ( const kmp_t *kmp, u16 obj_id ) +{ + DASSERT(kmp); + + uint i, count = 0; + const List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + const kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + for ( i = 0; i < n; i++, gobj++ ) + { + if ( (gobj->ref_id & ~GOBJ_DEF_F_NOT) == obj_id ) + { + kmp_ana_ref_t ar; + AnalyseRefObject(&ar,gobj); + if (ar.is_valid) + count++; + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetDefObjConditionName ( u16 cond ) +{ + const gobj_cond_ref_t *rc; + for ( rc = cond_ref_tab; rc->ref_id; rc++ ) + if ( rc->cond_mask == cond && !(rc->option & 4) ) + return PrintCircBuf("C$%s",rc->name); + + return PrintCircBuf("0x%04x",cond); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FixDefObj +( + kmp_t * kmp, // pointer to valid KMP + int fix_mode // >0: clear unused parameters +) +{ + DASSERT(kmp); + if ( fix_mode < 1 ) + return false; + + uint i, fix_count = 0; + List_t *dlist = kmp->dlist + KMP_GOBJ; + kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + for ( i = 0; i < dlist->used; i++, gobj++ ) + if (NormalizeDefObj(gobj)) + fix_count++; + + return fix_count>0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int sort_defobj_begin + ( const kmp_gobj_entry_t *a, const kmp_gobj_entry_t *b ) +{ + if (IsDefinitionObjectId(a->obj_id)) + { + if (!IsDefinitionObjectId(b->obj_id)) + return -1; + + return a->obj_id < b->obj_id ? -1 + : a->obj_id > b->obj_id ? 1 + : a < b ? -1 : 1; + } + + if (IsDefinitionObjectId(b->obj_id)) + return 1; + return a < b ? -1 : 1; +} + +//----------------------------------------------------------------------------- + +static int sort_defobj_end + ( const kmp_gobj_entry_t *a, const kmp_gobj_entry_t *b ) +{ + if (IsDefinitionObjectId(a->obj_id)) + { + if (!IsDefinitionObjectId(b->obj_id)) + return 1; + + return a->obj_id < b->obj_id ? -1 + : a->obj_id > b->obj_id ? 1 + : a < b ? -1 : 1; + } + + if (IsDefinitionObjectId(b->obj_id)) + return -1; + return a < b ? -1 : 1; +} + +//----------------------------------------------------------------------------- + +bool SortDefObj +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // sort if KMP_DOB_BEGIN || KMP_DOB_END +) +{ + DASSERT(kmp); + if ( sort_mode != KMP_DOB_BEGIN && sort_mode != KMP_DOB_END ) + return false; + + List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + if ( n < 2 ) + return false; + + kmp_gobj_entry_t *base = (kmp_gobj_entry_t*)dlist->list; + if ( sort_mode == KMP_DOB_BEGIN ) + qsort( base, n, sizeof(*base), (qsort_func)sort_defobj_begin ); + else + qsort( base, n, sizeof(*base), (qsort_func)sort_defobj_end ); + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// detect, fix and sort references /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyseRefObject ( kmp_ana_ref_t *ar, const kmp_gobj_entry_t *gobj ) +{ + DASSERT(ar); + DASSERT(gobj); + memset(ar,0,sizeof(*ar)); + + ar->mode = GetPFlagsMode(gobj->pflags); + ar->mode_valid = ar->mode == 1; + + ar->condref_valid = IsConditionRefId(gobj->ref_id); + ar->is_condref = ar->mode_valid && ar->condref_valid; + + ar->condeng_valid = IsConditionRefEngineId(gobj->ref_id); + ar->is_condeng = ar->mode_valid && ar->condeng_valid; + + ar->condrnd_valid = IsConditionRefRandomId(gobj->ref_id); + ar->is_condrnd = ar->mode_valid && ar->condrnd_valid; + + ar->condtest_valid = IsConditionRefTestId(gobj->ref_id); + ar->is_condtest = ar->mode_valid && ar->condtest_valid; + + ar->objref_valid = IsDefinitionObjectId(gobj->ref_id); + ar->is_objref = ar->mode_valid && ar->objref_valid; + + ar->is_valid = ar->objref_valid || ar->is_objref; +} + +/////////////////////////////////////////////////////////////////////////////// + +kmp_gobj_entry_t * FindRefObjectByID ( const kmp_t *kmp, u16 ref_id ) +{ + DASSERT(kmp); + ref_id &= ~GOBJ_DEF_F_NOT; + if (!IsDefinitionObjectId(ref_id)) + return 0; + + const List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + uint i; + for ( i = 0; i < n; i++, gobj++ ) + if ( gobj->obj_id == ref_id ) + return gobj; + return 0; +} + +//----------------------------------------------------------------------------- + +kmp_gobj_entry_t * FindRefObject ( const kmp_t *kmp, const kmp_gobj_entry_t *gobj ) +{ + DASSERT(kmp); + DASSERT(gobj); + + kmp_ana_ref_t ar; + AnalyseRefObject(&ar,gobj); + return ar.is_valid ? FindRefObjectByID(kmp,gobj->ref_id) : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FixReference +( + kmp_t * kmp, // pointer to valid KMP + int fix_mode // >0: fix if valid mode + // >1: fix for all modes +) +{ + DASSERT(kmp); + if ( fix_mode < 1 ) + return false; + + uint i, fix_count = 0; + List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + for ( i = 0; i < n; i++, gobj++ ) + { + kmp_ana_ref_t ar; + AnalyseRefObject(&ar,gobj); + if ( !ar.is_valid && ( fix_mode > 1 || ar.mode_valid )) + { + gobj->ref_id = 0; + fix_count++; + } + } + return fix_count>0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int sort_reference ( const u32 *a, const u32 *b ) +{ + return (s32)*a - (s32)*b; +} + +//----------------------------------------------------------------------------- + +bool SortReference +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // sort if KMP_DOB_BEFORE || KMP_DOB_BEHIND +) +{ + DASSERT(kmp); + if ( sort_mode != KMP_DOB_BEFORE && sort_mode != KMP_DOB_BEHIND ) + return false; + + List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + if ( n < 2 ) + return false; + + kmp_gobj_entry_t *base = (kmp_gobj_entry_t*)dlist->list; + u32 *keys = MALLOC(n*sizeof(*keys)); + uint i; + const kmp_gobj_entry_t *gobj = base; + if ( sort_mode == KMP_DOB_BEFORE ) + { + for ( i = 0; i < n; i++, gobj++ ) + { + const kmp_gobj_entry_t *found = FindRefObject(kmp,gobj); + if (!found) + keys[i] = i << 16 | 0x8000 | i; + else + keys[i] = (uint)(found-base) << 16 | i; + } + } + else + { + for ( i = 0; i < n; i++, gobj++ ) + { + const kmp_gobj_entry_t *found = FindRefObject(kmp,gobj); + if (!found) + keys[i] = i << 16 | i; + else + keys[i] = (uint)(found-base) << 16 | 0x8000 | i; + } + } + + qsort ( keys, n, sizeof(*keys), (qsort_func)sort_reference ); + + kmp_gobj_entry_t *temp = MALLOC(n*sizeof(*temp)); + for ( i = 0; i < n; i++ ) + { + const kmp_gobj_entry_t *src = base + ( keys[i] & 0x7fff ); + memcpy(temp+i,src,sizeof(*temp)); + } + memcpy(base,temp,n*sizeof(*temp)); + FREE(temp); + FREE(keys); + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u16 GetActiveObjectId ( const kmp_gobj_entry_t *g ) +{ + if ( !g || g->obj_id >= GOBJ_MIN_DEF ) + return 0; + + const u16 obj_id = g->obj_id & GOBJ_M_OBJECT; + return obj_id < N_KMP_GOBJ + && ( g->pflags & GOBJ_M_PF_STD + || (g->pflags & GOBJ_M_PF_MODE) == GOBJ_PF_MODE1 ) + ? obj_id : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Convert conditions to text /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const gobj_condition_t gobj_condition[] = +{ + { 0xffff,0xff,-1, 1,4, 0,99, + "All conditions." }, + { 0x3fff,0xfb,-1, 1,4, 0,99, + "All conditions except reserved." }, + { 0x7fff,0x04,-1, 1,4, 0,99, + "All conditions except reserved 2." }, + { 0x3fff,0xff,-1, 1,4, 0,99, + "All conditions except time trial and reserved." }, + + //--- offline players + + { 0x000f,0xff,-1, 1,4, 0,0, + "Offline race with 1-4 human players." }, + { 0x0007,0xff,-1, 1,3, 0,0, + "Offline race with 1-3 human players." }, + { 0x0003,0xff,-1, 1,2, 0,0, + "Offline race with 1-2 human players." }, + { 0x0001,0xff, 0, 1,1, 0,0, + "Offline race with 1 human player." }, + + { 0x000e,0xff,-1, 2,4, 0,0, + "Offline race with 2-4 human players." }, + { 0x0006,0xff,-1, 2,3, 0,0, + "Offline race with 2-3 human players." }, + { 0x0002,0xff, 1, 2,2, 0,0, + "Offline race with 2 human players." }, + + { 0x000c,0xff,-1, 3,4, 0,0, + "Offline race with 3-4 human players." }, + { 0x0004,0xff, 2, 3,3, 0,0, + "Offline race with 3 human players." }, + { 0x0008,0xff, 3, 4,4, 0,0, + "Offline race with 4 human players." }, + + + //--- online, 1-N players total + + { 0x3ff0,0xff,-1, 1,2, 1,99, + "Online race with any number of players." }, + { 0x1550,0xff,-1, 1,1, 1,99, + "Online race with 1 human player at Wii." }, + { 0x2aa0,0xff,-1, 2,2, 1,99, + "Online race with 2 human players at Wii." }, + + { 0x0ff0,0xff,-1, 1,2, 1,18, + "Online race with 1-18 players total." }, + { 0x0550,0xff,-1, 1,1, 1,18, + "Online race with 1-18 players total and 1 human at Wii." }, + { 0x0aa0,0xff,-1, 2,2, 1,18, + "Online race with 1-18 players total and 2 humans at Wii." }, + + { 0x03f0,0xff,-1, 1,2, 1,12, + "Online race with 1-12 players total." }, + { 0x0150,0xff,-1, 1,1, 1,12, + "Online race with 1-12 players total and 1 human at Wii." }, + { 0x02a0,0xff,-1, 2,2, 1,12, + "Online race with 1-12 players total and 2 humans at Wii." }, + + { 0x00f0,0xff,-1, 1,2, 1,9, + "Online race with 1-9 players total." }, + { 0x0050,0xff,-1, 1,1, 1,9, + "Online race with 1-9 players total and 1 human at Wii." }, + { 0x00a0,0xff,-1, 2,2, 1,9, + "Online race with 1-9 players total and 2 humans at Wii." }, + + { 0x0030,0xff,-1, 1,2, 1,9, + "Online race with 1-6 players total." }, + { 0x0010,0xff, 4, 1,1, 1,6, + "Online race with 1-6 players total and 1 human at Wii." }, + { 0x0020,0xff, 5, 2,2, 1,6, + "Online race with 1-6 players total and 2 humans at Wii." }, + + + //--- online, 7-N players total + + { 0x3fc0,0xff,-1, 1,2, 7,99, + "Online race with 7 or more players total." }, + { 0x1540,0xff,-1, 1,1, 7,99, + "Online race with 7 or more players total and 1 human at Wii." }, + { 0x2a80,0xff,-1, 2,2, 7,99, + "Online race with 7 or more players total and 2 humans at Wii." }, + + { 0x0fc0,0xff,-1, 1,2, 7,18, + "Online race with 7-18 players total." }, + { 0x0540,0xff,-1, 1,1, 7,18, + "Online race with 7-18 players total and 1 human at Wii." }, + { 0x0a80,0xff,-1, 2,2, 7,18, + "Online race with 7-18 players total and 2 humans at Wii." }, + + { 0x03c0,0xff,-1, 1,2, 7,12, + "Online race with 7-12 players total." }, + { 0x01e0,0xff,-1, 1,1, 7,12, + "Online race with 7-12 players total and 1 human at Wii." }, + { 0x0280,0xff,-1, 2,2, 7,12, + "Online race with 7-12 players total and 2 humans at Wii." }, + + { 0x00c0,0xff,-1, 1,2, 7,9, + "Online race with 7-9 players total." }, + { 0x0040,0xff, 6, 1,1, 7,9, + "Online race with 7-9 players total and 1 human at Wii." }, + { 0x0080,0xff, 7, 2,2, 7,9, + "Online race with 7-9 players total and 2 humans at Wii." }, + + + //--- online, 10-N players total + + { 0x3f00,0xff,-1, 1,2, 10,99, + "Online race with 10 or more players total." }, + { 0x1500,0xff,-1, 1,1, 10,99, + "Online race with 10 or more players total and 1 human at Wii." }, + { 0x2a00,0xff,-1, 2,2, 10,99, + "Online race with 10 or more players total and 2 humans at Wii." }, + + { 0x0f00,0xff,-1, 1,2, 10,18, + "Online race with 10-18 players total." }, + { 0x0500,0xff,-1, 1,1, 10,18, + "Online race with 10-18 players total and 1 human at Wii." }, + { 0x0a00,0xff,-1, 2,2, 10,18, + "Online race with 10-18 players total and 2 humans at Wii." }, + + { 0x0300,0xff,-1, 1,2, 10,12, + "Online race with 10-12 players total." }, + { 0x0100,0xff, 8, 1,1, 10,12, + "Online race with 10-12 players total and 1 human at Wii." }, + { 0x0200,0xff, 9, 2,2, 10,12, + "Online race with 10-12 players total and 2 humans at Wii." }, + + + //--- online, 13-N players total + + { 0x3c00,0xff,-1, 1,2, 13,99, + "Online race with 13 or more players total." }, + { 0x1400,0xff,-1, 1,1, 13,99, + "Online race with 13 or more players total and 1 human at Wii." }, + { 0x2800,0xff,-1, 2,2, 13,99, + "Online race with 13 or more players total and 2 humans at Wii." }, + + { 0x0c00,0xff,-1, 1,2, 13,18, + "Online race with 13-18 players total." }, + { 0x0400,0xff,10, 1,1, 13,18, + "Online race with 13-18 players total and 1 human at Wii." }, + { 0x0800,0xff,11, 2,2, 13,18, + "Online race with 13-18 players total and 2 humans at Wii." }, + + + //--- online, 17-N players total + + { 0x3000,0xff,-1, 1,2, 19,99, + "Online race with 19 or more players total." }, + { 0x1000,0xff,12, 1,1, 19,99, + "Online race with 19 or more players total and 1 human at Wii." }, + { 0x2000,0xff,13, 2,2, 19,99, + "Online race with 19 or more players total and 2 humans at Wii." }, + + + //--- reserved + + { 0x4000,0x04,14, 1,1, -1,-1, + "Time Trial (Reserved 1)." }, + { 0xc000,0xfb,-1, -1,-1, -1,-1, + "Reserved 1 and 2." }, + { 0x4000,0xfb,14, -1,-1, -1,-1, + "Reserved 1." }, + { 0x8000,0xff,15, -1,-1, -1,-1, + "Reserved 2." }, + {0,0,-1,-1,-1,-1,-1,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +uint GetGobjConditions ( gobj_condition_set_t *gcs, int setting, u16 mask ) +{ + DASSERT(gcs); + const u8 setting_mask = setting >= 0 && setting < 8 ? 1<<setting : 0xff; + memset(gcs,0,sizeof(*gcs)); + gcs->mask = mask; + gcs->setting_mask = setting_mask; + + const gobj_condition_t *src = gobj_condition; + gobj_condition_t *dest = gcs->cond; + while (src->info) + { + if ( setting_mask & src->setting + && ( mask & src->mask ) == src->mask + && dest - gcs->cond < GOBJ_COND_SET_MAX + ) + { + memcpy(dest++,src,sizeof(*dest)); + mask &= ~src->mask; + DASSERT( dest - gcs->cond <= GOBJ_COND_SET_MAX ); + } + src++; + } + memcpy(dest,src,sizeof(*dest)); + return gcs->n = dest - gcs->cond; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Analyse GOBJ elements /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetAnaGobj ( kmp_ana_gobj_t *ag ) +{ + if (ag) + { + FREE(ag->info); + memset(ag,0,sizeof(*ag)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static kmp_gobj_status_t CheckRefStatus ( u16 ref_id ) +{ + if (IsConditionRefEngineId(ref_id)) + return KGST_REF_VALID | KGST_ENGINE; + + if (IsConditionRefRandomId(ref_id)) + return KGST_REF_VALID | KGST_RANDOM; + + const gobj_cond_ref_t *cr = FindConditionRef(ref_id); + return cr ? KGST_REF_VALID : 0; +} + +//----------------------------------------------------------------------------- + +void AnalyseGobj ( kmp_ana_gobj_t *ag, const kmp_t *kmp ) +{ + DASSERT(ag); + DASSERT(kmp); + + const u_usec_t start_time = GetTimerUSec(); + + memset(ag,0,sizeof(*ag)); + ag->kmp = kmp; + ag->gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + ag->n_gobj = kmp->dlist[KMP_GOBJ].used; + if ( ag->n_gobj > 0x7fff ) + ag->n_gobj = 0x7fff; + + ag->info = CALLOC(ag->n_gobj,sizeof(*ag->info)); + + + //--- first loop + + uint i; + kmp_gobj_info_t *info; + const kmp_gobj_entry_t *gobj; + for ( i = 0, info = ag->info, gobj = ag->gobj; i < ag->n_gobj; i++, info++, gobj++ ) + { + info->defobj = -1; + if (!gobj->obj_id) + { + info->type = KGTY_NULL; + continue; + } + + info->type = KGTY_INVALID; + if ( gobj->obj_id < GOBJ_MIN_DEF ) + { + const u16 relevant_id = GetRelevantObjectId(gobj->obj_id); + if ( relevant_id >= N_KMP_GOBJ || !ObjectInfo[relevant_id].name ) + continue; + + ag->n_object++; + info->obj_id = relevant_id; + info->type = KGTY_OBJECT; + info->status |= KGST_USED; + + if ( gobj->obj_id < GOBJ_M_OBJECT ) + { + ag->n_enabled++; + info->status |= KGST_ENABLED; + } + else + ag->n_disabled++; + + kmp_gobj_entry_t *defobj = FindRefObjectByID(kmp,gobj->ref_id); + if (defobj) + { + info->ref_id = gobj->ref_id; + info->status |= KGST_REF_VALID; + info->defobj = defobj - ag->gobj; + ag->info[info->defobj].ref_count++; + ag->info[info->defobj].status |= KGST_USED; + } + } + else + { + if ( IsDefinitionObjectIdBITS(gobj->obj_id) ) + { + ag->n_defobj_bits++; + info->type = KGTY_DEFOBJ_BITS; + } + else if ( IsDefinitionObjectIdOR(gobj->obj_id) ) + { + ag->n_defobj_or++; + info->type = KGTY_DEFOBJ_OR; + } + else if ( IsDefinitionObjectIdAND(gobj->obj_id) ) + { + ag->n_defobj_and++; + info->type = KGTY_DEFOBJ_AND; + } + else + continue; + + + // this is a valid def-object + if ( info->type == KGTY_DEFOBJ_OR || info->type == KGTY_DEFOBJ_AND ) + { + kmp_gobj_status_t rs =0; + uint i; + for ( i = 0; i < 8; i++ ) + rs = CheckRefStatus(gobj->setting[i]); + + if (rs) + { + info->status |= rs; + info->ref_id = gobj->ref_id; + } + } + } + + //--- analyse ref_id + + if ( gobj->ref_id ) + { + const kmp_gobj_status_t rs = CheckRefStatus(gobj->ref_id); + if (rs) + { + info->status |= rs; + info->ref_id = gobj->ref_id; + } + else + info->status |= KGST_DISABLED_BY; + } + } + + + //--- second loop + + for ( i = 0, info = ag->info; i < ag->n_gobj; i++, info++ ) + { + if ( info->type == KGTY_OBJECT ) + { + if ( info->defobj >= 0 ) + info->status |= ag->info[info->defobj].status & KGST_M_DERIVE; + + if ( info->status & KGST_ENGINE ) + ag->n_engine++; + if ( info->status & KGST_RANDOM ) + ag->n_random++; + } + } + + + //--- finalize + + ag->n_object = ag->n_disabled + ag->n_enabled; + ag->n_defobj = ag->n_defobj_bits + ag->n_defobj_or + ag->n_defobj_and; + ag->dur_usec = GetTimerUSec() - start_time; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Analyse PFlag Scenarios /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct xpf_bits_t +{ + u16 mask; // mask to select bit + u8 bitnum; // bit number + u8 pflag; // mask to selct presence_flag + u8 n_offline; // num of human players at Wii + u8 n_online; // num of total online players + u16 relevant; // bit field: relevant for setting + bool no_prefix; // true: suppress game mode prefix + char name[5]; // name +} +xpf_bits_t; + +static const xpf_bits_t xpf_bits[16+1] = +{ + { 0x0001, 0, 1, 1, 0, 0x800f, 0, "1" }, + { 0x0002, 1, 2, 2, 0, 0x800f, 0, "2" }, + { 0x0004, 2, 4, 3, 0, 0x800f, 0, "3" }, + { 0x0008, 3, 4, 4, 0, 0x000f, 0, "4" }, + { 0x0010, 4, 1, 1, 2, 0x000f, 0, "1,2" }, + { 0x0020, 5, 2, 2, 2, 0x000f, 0, "2,2" }, + { 0x0040, 6, 1, 1, 7, 0x000f, 0, "1,7" }, + { 0x0080, 7, 2, 2, 7, 0x000f, 0, "2,7" }, + { 0x0100, 8, 1, 1, 10, 0x000f, 0, "1,10" }, + { 0x0200, 9, 2, 2, 10, 0x000f, 0, "2,10" }, + { 0x0400, 10, 1, 1, 13, 0x000f, 0, "1,13" }, + { 0x0800, 11, 2, 2, 13, 0x000f, 0, "2,13" }, + { 0x1000, 12, 1, 1, 19, 0x000f, 0, "1,19" }, + { 0x2000, 12, 2, 2, 19, 0x000f, 0, "2,19" }, + { 0x4000, 14, 1, 1, 0, 0x0004, 1, "TT" }, + { 0x8000, 15, 1, 0, 0, 0x0000, 0, "?" }, + { 0, 0, 0, 0, 0, 0, 0, "" }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPFlagScenarios ( kmp_ana_pflag_t *ap ) +{ + if (ap) + { + ResetAnaGobj(&ap->ag); + + uint i; + for ( i = 0; i < ap->n_res; i++ ) + FreeString(ap->res_list[i].status); + FREE(ap->res_list); + memset(ap,0,sizeof(*ap)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +kmp_ana_pflag_res_t * AppendPFlagScenario ( kmp_ana_pflag_t *ap ) +{ + DASSERT(ap); + if ( ap->n_res == ap->res_size ) + { + ap->res_size = 2 * ap->res_size + 50; + ap->res_list = REALLOC( ap->res_list, ap->res_size*sizeof(*ap->res_list) ); + } + + kmp_ana_pflag_res_t *ptr = ap->res_list + ap->n_res++; + memset(ptr,0,sizeof(*ptr)); + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef SUPPORT_SEP + +#if HAVE_WIIMM_EXT + #define SUPPORT_SEP 1 +#else + #define SUPPORT_SEP 0 +#endif + +// helper consts + +enum +{ + X_INVALID_MODE = -16, + X_INVALID_CONDREF, + X_INVALID_REF, + X_NOTFOUND, + X_DISABLED, + X_ENABLED, + X_DEFOBJ_BITS, + X_DEFOBJ_OR, + X_DEFOBJ_AND, + X_CONDENG, + X_CONDRND, + X_CONDREF_BEG = -0x2000, + X_CONDREF_END = X_CONDREF_BEG + 0xfff, + + CH_DISABLED = '-', + CH_HIDDEN = '=', + CH_ENABLED = '+', + CH_ENGINE = 'e', + CH_RANDOM = 'r', + CH_DEFOBJ_BITS = 'b', + CH_DEFOBJ_OR = 'o', + CH_DEFOBJ_AND = 'a', + CH_SEP = ' ' +}; + +//----------------------------------------------------------------------------- + +static int sort_apf_name + ( const kmp_ana_pflag_res_t * a, const kmp_ana_pflag_res_t * b ) +{ + return strcasecmp(a->name,b->name); +} + +//----------------------------------------------------------------------------- + + +static int sort_apf_index ( const kmp_ana_pflag_res_t * a, const kmp_ana_pflag_res_t * b ) +{ + int stat = (int)a->version - (int)b->version; + return stat ? stat : strcasecmp(a->name,b->name);; +} + +//----------------------------------------------------------------------------- + +static void analyze_helper +( + kmp_ana_pflag_t *ap, // valid struct + const s16 *ref_list, // list with 'ap->ag.n_gobj' elements + char *res, // vector with 'ap->status_len+1' elements + + int gamemode, // selected gamemode + ccp prefix, // gamemode related prefix to use + const xpf_bits_t *xpf, // selected condition + int engine, // <0: off, 0..: select engine + uint random // 0: off, 1-GOBJ_MAX_RANDOM: select random scenario +) +{ + char *res_ptr = res; + const bool is_std = gamemode == KMP_GMODE_STD; + const bool is_tt = gamemode == KMP_GMODE_VERSUS && xpf->bitnum == 14; + + if ( is_std ) + { + if ( random > 1 || engine != -1 && engine != ENGINE_150 ) + return; + engine = -1; + random = 0; + } + else if ( is_tt ) + { + if ( random > 1 || engine != -1 && engine != ENGINE_150 ) + return; + engine = ENGINE_150; + random = 1; + } + else if ( gamemode == KMP_GMODE_BALLOON || gamemode == KMP_GMODE_COIN ) + { + if ( engine != -1 && engine != ENGINE_BATTLE ) + return; + } + else + { + if ( engine == ENGINE_BATTLE ) + return; + } + + if ( ap->opt_gamemode & GMD_DUMP ) + { + printf("gm %2d, bit %2d, engine %2d, random %d\n", + gamemode, xpf->bitnum, engine, random ); + return; + } + + gobj_cond_mask_t cmask = {0}; + cmask.bit_mask = xpf->mask; + cmask.game_mask = 1 << gamemode; + cmask.engine_mask = 1 << ( engine < 0 ? ENGINE_150 : engine ); + cmask.random_mask = 1 << ( random ? random-1 : 0 ); + + int i; + const kmp_gobj_entry_t *gobj; + for ( i = 0, gobj = ap->ag.gobj; i < ap->ag.n_gobj; i++, gobj++ ) + { + u16 obj_id = gobj->obj_id; + u16 pflags = gobj->pflags; + char stat; + + if ( ref_list[i] == X_DEFOBJ_BITS ) + stat = CH_DEFOBJ_BITS; + else if ( ref_list[i] == X_DEFOBJ_OR ) + stat = CH_DEFOBJ_OR; + else if ( ref_list[i] == X_DEFOBJ_AND ) + stat = CH_DEFOBJ_AND; + else if (is_std) + stat = xpf->pflag & pflags ? CH_ENABLED : CH_HIDDEN; + else + { + stat = CH_DISABLED; + switch(ref_list[i]) + { + case X_INVALID_MODE: + case X_INVALID_REF: + stat = xpf->pflag & pflags ? CH_ENABLED : CH_HIDDEN; + break; + + case X_INVALID_CONDREF: + case X_NOTFOUND: + case X_DISABLED: + obj_id = 0; + break; + + case X_ENABLED: + stat = CH_ENABLED; + obj_id = obj_id & ~GOBJ_M_LECODE; + break; + + case X_CONDENG: + if ( engine < 0 ) + { + stat = CH_ENGINE; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + else if ( gobj->ref_id & cmask.engine_mask ) + { + stat = CH_ENABLED; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + break; + + case X_CONDRND: + if ( !random ) + { + stat = CH_RANDOM; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + else if ( gobj->ref_id & cmask.random_mask ) + { + stat = CH_ENABLED; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + break; + + default: + if ( ref_list[i] >= X_CONDREF_BEG && ref_list[i] <= X_CONDREF_END ) + { + const gobj_cond_ref_t *cref + = cond_ref_tab + ( ref_list[i] - X_CONDREF_BEG ); + if (IsConditionRefEnabled(&cmask,gobj->ref_id,cref)) + stat = CH_ENABLED; + } + else + { + bool enabled = false; + const kmp_gobj_entry_t *dob = ap->ag.gobj + ref_list[i]; + if ( !dob->ref_id || IsConditionRefEnabled(&cmask,dob->ref_id,0) ) + { + if (IsDefinitionObjectIdBITS(dob->obj_id)) + { + if ( dob->setting[gamemode] & cmask.bit_mask ) + enabled = true; + } + else if (IsDefinitionObjectIdOR(dob->obj_id)) + { + uint i; + for ( i = 0; i < 8; i++ ) + if ( dob->setting[i] + && IsConditionRefEnabled(&cmask,dob->setting[i],0) ) + { + enabled = true; + break; + } + } + else if (IsDefinitionObjectIdAND(dob->obj_id)) + { + enabled = true; + uint i; + for ( i = 0; i < 8; i++ ) + if ( dob->setting[i] + && !IsConditionRefEnabled(&cmask,dob->setting[i],0) ) + { + enabled = false; + break; + } + } + } + + if ( gobj->ref_id & GOBJ_DEF_F_NOT ) + enabled = !enabled; + if (enabled) + stat = CH_ENABLED; + } + if ( stat == CH_ENABLED || stat == CH_RANDOM ) + obj_id = obj_id & ~GOBJ_M_LECODE; + break; + } + } + + if ( stat != CH_DEFOBJ_BITS && stat != CH_DEFOBJ_OR && stat != CH_DEFOBJ_AND + && (obj_id >= N_KMP_GOBJ || !ObjectInfo[obj_id].name )) + { + stat = CH_DISABLED; + } + + #if SUPPORT_SEP + if ( res_ptr > res + && ( gobj->pflags & 0x100 || ap->auto_sep && !(i%5) )) + { + *res_ptr++ = CH_SEP; + } + #endif + *res_ptr++ = stat; + DASSERT( res_ptr <= res + ap->status_len ); + } + ASSERT( res_ptr <= res + ap->status_len ); + + int ri, found_ri = -1; + for ( ri = 0; ri < ap->n_res; ri++ ) + if (!memcmp(ap->res_list[ri].status,res,ap->status_len)) + { + found_ri = ri; + kmp_ana_pflag_res_t *found = ap->res_list + ri; + if ( is_std && !found->n_std++ ) + { + ap->n_res_std++; + StringCat2S(found->name,sizeof(found->name),found->name,"+"); + } + if ( !is_std && !found->n_ext++ ) + { + ap->n_res_ext++; + StringCat2S(found->name,sizeof(found->name),found->name,"+"); + } + break; + } + + if ( found_ri < 0 + || ap->opt_gamemode & GMD_FULL + || is_tt && ap->opt_gamemode & GMD_FORCE_TT + ) + { + kmp_ana_pflag_res_t *pr = AppendPFlagScenario(ap); + kmp_ana_pflag_res_t *found = found_ri < 0 ? 0 : ap->res_list + found_ri; + ccp pre = xpf->no_prefix || !prefix ? "" : prefix; + pr->status = ALLOCDUP(res,ap->status_len+1); + pr->version = found ? found->version : ap->n_version++; + + memcpy(&pr->lex_test,TestDataLEX,sizeof(pr->lex_test)); + char *name_end = pr->name + sizeof(pr->name); + char *dest = snprintfE(pr->name,name_end,"%s%s",pre,xpf->name); + + if ( engine > 0 ) + { + static const char tab[][5] = { "bat","50c","100c","150c","200c","150m","200m" }; + dest = StringCat2E(dest,name_end,",",tab[engine]); + pr->lex_test.engine = engine; + } + + if ( random && !is_tt ) + { + dest = snprintfE(dest,name_end,",r%u",random); + pr->lex_test.random = random; + } + + pr->lex_test.offline_online = xpf->n_online > 0 + ? LEX_OO_ONLINE : LEX_OO_OFFLINE; + pr->lex_test.n_online = xpf->n_online; + pr->lex_test.n_offline = xpf->n_offline; + if (is_std) + { + pr->lex_test.cond_bit = -1; + pr->lex_test.game_mode = LEX_GMODE_STD; + } + else + { + pr->lex_test.cond_bit = xpf->bitnum; + pr->lex_test.game_mode = gamemode + LEX_GMODE__BASE; + } + + if (found) + { + if ( !is_std && !found->n_ext++ ) + ap->n_res_ext++; + } + else if ( is_std && !pr->n_std++ ) + ap->n_res_std++; + else if ( !is_std && !pr->n_ext++ ) + ap->n_res_ext++; + } +} + +//----------------------------------------------------------------------------- + +void AnalysePFlagScenarios + ( kmp_ana_pflag_t *ap, const kmp_t *kmp, opt_gamemode_t opt_gamemode ) +{ + DASSERT(ap); + DASSERT(kmp); + + const u_usec_t start_time = GetTimerUSec(); + + memset(ap,0,sizeof(*ap)); + AnalyseGobj(&ap->ag,kmp); + ap->opt_gamemode = opt_gamemode; + + struct job_t + { + char prefix[4]; + int gamemode; + int opt_gmd; + }; + + static const struct job_t race_job_list[] = + { + { "std",KMP_GMODE_STD, GMD_STD }, + { "V", KMP_GMODE_VERSUS, GMD_VERSUS }, + { "I", KMP_GMODE_ITEMRAIN, GMD_ITEMRAIN }, + { "B", KMP_GMODE_BALLOON, GMD_BALLOON }, + { "C", KMP_GMODE_COIN, GMD_COIN }, + { "std",KMP_GMODE_STD, GMD_STD }, + { "",-1,0} + }; + + static const struct job_t arena_job_list[] = + { + { "std",KMP_GMODE_STD, GMD_STD }, + { "B", KMP_GMODE_BALLOON, GMD_BALLOON }, + { "C", KMP_GMODE_COIN, GMD_COIN }, + { "V", KMP_GMODE_VERSUS, GMD_VERSUS }, + { "I", KMP_GMODE_ITEMRAIN, GMD_ITEMRAIN }, + { "std",KMP_GMODE_STD, GMD_STD }, + { "",-1,0} + }; + + static const struct job_t auto_job_list[] = + { + { "std",KMP_GMODE_STD, GMD_STD }, + { "a", KMP_GMODE_AUTO, GMD_NO_MODES }, + { "std",KMP_GMODE_STD, GMD_STD }, + { "",-1,0} + }; + + + //--- create reference list for defobj for faster analysis + + #if SUPPORT_SEP + uint n_sep = 0; + #endif + s16 *ref_list = MALLOC(ap->ag.n_gobj*sizeof(*ref_list)); + uint i; + const kmp_gobj_entry_t *gobj; + for ( i = 0, gobj = ap->ag.gobj; i < ap->ag.n_gobj; i++, gobj++ ) + { + #if SUPPORT_SEP + if (gobj->pflags&0x100) + n_sep++; + #endif + if (IsDefinitionObjectIdBITS(gobj->obj_id)) + ref_list[i] = X_DEFOBJ_BITS; + else if (IsDefinitionObjectIdOR(gobj->obj_id)) + ref_list[i] = X_DEFOBJ_OR; + else if (IsDefinitionObjectIdAND(gobj->obj_id)) + ref_list[i] = X_DEFOBJ_AND; + else if ( GetPFlagsMode(gobj->pflags) != 1 ) + ref_list[i] = X_INVALID_MODE; + else if (IsConditionRefId(gobj->ref_id)) + { + if (IsConditionRefEngineId(gobj->ref_id)) + { + const uint engine = gobj->ref_id & GOBJ_MASK_COND_ENGINE; + if (!engine) + ref_list[i] = X_DISABLED; + else if ( engine == GOBJ_MASK_COND_ENGINE ) + ref_list[i] = X_ENABLED; + else + ref_list[i] = X_CONDENG; + } + else if (IsConditionRefRandomId(gobj->ref_id)) + { + const uint rnd = gobj->ref_id & GOBJ_MASK_COND_RND; + if (!rnd) + ref_list[i] = X_DISABLED; + else if ( rnd == GOBJ_MASK_COND_RND ) + ref_list[i] = X_ENABLED; + else + ref_list[i] = X_CONDRND; + } + else + { + const gobj_cond_ref_t *cr = FindConditionRef(gobj->ref_id); + ref_list[i] = cr ? X_CONDREF_BEG + cr-cond_ref_tab : X_INVALID_CONDREF; + } + } + else if (!IsDefinitionObjectId(gobj->ref_id)) + ref_list[i] = X_INVALID_REF; + else + { + const kmp_gobj_entry_t *found = FindRefObject(kmp,gobj); + ref_list[i] = found ? found - ap->ag.gobj : X_NOTFOUND; + } + } + + #if defined(TEST) || defined(HAVE_WIIMM_EXT) + if ( logging > 1 ) + { + XDump_t xd; + InitializeXDump(&xd); + xd.print_format = false; + xd.print_summary = false; + xd.print_text = false; + xd.format = XDUMPF_INT_2; + xd.min_width = 20; + XDump(&xd,ref_list,ap->ag.n_gobj*sizeof(*ref_list),true); + putchar('\n'); + } + #endif + + + //--- add "auto"? + + if ( opt_gamemode & (GMD_AUTO|GMD_FULL) ) + { + kmp_ana_pflag_res_t *pr = AppendPFlagScenario(ap); + StringCopyS(pr->name,sizeof(pr->name),"auto"); + pr->status = EmptyString; + memcpy(&pr->lex_test,TestDataLEX,sizeof(pr->lex_test)); + } + + + //--- separator + + #if SUPPORT_SEP + ap->auto_sep = !n_sep; + if (ap->auto_sep) + n_sep = ap->ag.n_gobj/5; + ap->status_len = ap->ag.n_gobj + n_sep; + #else + ap->status_len = ap->ag.n_gobj; + #endif + char *res = CALLOC(1,ap->status_len+1); + + + //--- engine & random + + int engine_beg, engine_end; + if ( ap->ag.n_engine && ( opt_gamemode & GMD_ENGINE )) + { + engine_beg = ENGINE_BATTLE; + engine_end = ENGINE__N-1; + } + else + engine_beg = engine_end = -1; + + int random_beg, random_end; + if ( ap->ag.n_random && ( opt_gamemode & GMD_RANDOM )) + { + random_beg = 1; + random_end = GOBJ_MAX_RANDOM; + } + else + random_beg = random_end = 0; + + + //--- main loop + + const struct job_t *job + = (opt_gamemode&GMD_NO_MODES) + ? auto_job_list + : CheckBattleModeKMP(kmp) >= ARENA_FOUND + ? arena_job_list + : race_job_list; + if (!(opt_gamemode&GMD_STD_FIRST)) + job++; + + for ( ; job->gamemode >= 0; job++ ) + { + if ( !( opt_gamemode & job->opt_gmd ) ) + continue; + + const xpf_bits_t *xpf; + for ( xpf = xpf_bits; xpf->mask; xpf++ ) + { + if ( job->gamemode == KMP_GMODE_AUTO ) + goto skip; + + if ( !( 1 << job->gamemode & xpf->relevant )) + continue; + + // optimized handling for time trial + const bool is_tt = job->gamemode == KMP_GMODE_VERSUS && xpf->bitnum == 14; + if (is_tt) + { + if ( opt_gamemode & GMD_NO_TT ) + continue; + if ( opt_gamemode & GMD_FORCE_TT ) + analyze_helper(ap,ref_list,res, job->gamemode,job->prefix,xpf,-1,0); + } + + if ( !(opt_gamemode & ( xpf->n_online ? GMD_ONLINE : GMD_OFFLINE )) ) + continue; + + if (is_tt) + { + analyze_helper(ap,ref_list,res, job->gamemode,job->prefix,xpf,-1,0); + continue; + } + + skip:; + int e; + for ( e = engine_beg; e <= engine_end; e++ ) + { + int r; + for ( r = random_beg; r <= random_end; r++ ) + analyze_helper(ap,ref_list,res, job->gamemode,job->prefix,xpf,e,r); + } + } // for xpf + } // for gmode + + if ( ap->n_res > 1 ) + { + if ( opt_gamemode & GMD_ISORT ) + qsort( ap->res_list, ap->n_res, sizeof(*ap->res_list), + (qsort_func)sort_apf_index ); + else if ( opt_gamemode & GMD_NSORT ) + qsort( ap->res_list, ap->n_res, sizeof(*ap->res_list), + (qsort_func)sort_apf_name ); + } + + FREE(res); + FREE(ref_list); + + ap->dur_usec = GetTimerUSec() - start_time; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: repair: global helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp repair_xpf_fname = 0; +static kmp_t *repair_kmp = 0; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRepairXPF ( ccp arg ) +{ + if (repair_xpf_fname) + have_patch_count--, have_kmp_patch_count--; + + repair_xpf_fname = arg && *arg ? arg : 0; + + if (repair_xpf_fname) + have_patch_count++, have_kmp_patch_count++; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +xpf_mode1_t CheckMode1GOBJ ( const kmp_gobj_entry_t *g ) +{ + DASSERT(g); + if (( g->pflags & GOBJ_M_PF_MODE ) != GOBJ_PF_MODE1 ) + return XMODE1_NONE; + + return IsConditionRefId(g->ref_id) ? XMODE1_CONDREF + : IsDefinitionObjectId(g->ref_id) ? XMODE1_OBJREF + : g->ref_id ? XMODE1_INVALID : XMODE1_NULL; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: repair /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[xpf_mode1_elem_t]] + +typedef struct xpf_mode1_elem_t +{ + int index; // index of element wihiin list + xpf_mode1_t mode; // mode + kmp_gobj_entry_t *obj; // pointer to object + kmp_gobj_entry_t norm; // copy of 'obj' with normalized data + + int eval; // evaluation + double diff_sum; // sum of float differences + struct xpf_mode1_elem_t + *hit; // best element found + int match_id; // for logging only: identify matching pair +} +xpf_mode1_elem_t; + +//----------------------------------------------------------------------------- +// [[xpf_mode1_info_t]] + +typedef struct xpf_mode1_info_t +{ + uint broken; // number of broken elements in 'list' + uint used; // number of used elements in 'list' + uint size; // number of available elements in 'list' + xpf_mode1_elem_t list[]; // list of elements +} +xpf_mode1_info_t; + +//----------------------------------------------------------------------------- + +static xpf_mode1_info_t * CreateXpfModelInfo ( const List_t *list ) +{ + DASSERT(list); + uint i, n = 0; + kmp_gobj_entry_t *obj; + + for ( i = 0, obj = (kmp_gobj_entry_t*)list->list; i < list->used; i++, obj++ ) + { + const xpf_mode1_t mode = CheckMode1GOBJ(obj); + if ( mode > XMODE1_NONE ) + n++; + PRINT0("%2d/%d: %u %04x %04x %04x\n", + i, list->used, n, obj->obj_id, obj->ref_id, obj->pflags ); + } + + if (!n) + return 0; + + const uint mem_size = sizeof(xpf_mode1_info_t) + n * sizeof(xpf_mode1_elem_t); + xpf_mode1_info_t *res = CALLOC(1,mem_size); + res->size = res->used = n; + + xpf_mode1_elem_t *elem = res->list; + for ( i = 0, obj = (kmp_gobj_entry_t*)list->list; i < list->used; i++, obj++ ) + { + const xpf_mode1_t mode = CheckMode1GOBJ(obj); + if ( mode > XMODE1_NONE ) + { + elem->index = i; + elem->obj = obj; + elem->mode = mode; + if ( mode <= XMODE1_INVALID ) + res->broken++; + memcpy(&elem->norm,obj,sizeof(elem->norm)); + elem->norm.obj_id &= GOBJ_M_OBJECT; + elem->norm.ref_id = 0; + elem->norm.pflags = 0; + elem++; + } + } + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void LogPrintElement ( xpf_mode1_elem_t *e1, xpf_mode1_elem_t *e2 ) +{ + if ( e2 && e2->hit == e1 ) + fprintf(stdlog," : %s%3d. %s%04x %s%04x %s%04x%s", + e1->index == e2->index ? collog->b_green : collog->b_red, e1->index, + e1->obj->obj_id == e2->obj->obj_id ? collog->b_green + : collog->b_red, e1->obj->obj_id, + e1->obj->ref_id == e2->obj->ref_id ? collog->b_green + : collog->b_red, e1->obj->ref_id, + e1->obj->pflags == e2->obj->pflags ? collog->b_green + : collog->b_red, e1->obj->pflags, + collog->reset ); + else + fprintf(stdlog," : %3d. %04x %04x %04x", + e1->index, e1->obj->obj_id, e1->obj->ref_id, e1->obj->pflags ); +} + +//----------------------------------------------------------------------------- + +static void LogListXPF + ( xpf_mode1_info_t *info, int index, ccp text, int loop, int update_eval ) +{ + int i, count = 0, match_id = 0; + xpf_mode1_elem_t *e; + for ( i = 0, e = info->list; i < info->used; i++, e++ ) + { + if ( e->eval >= 0 ) + { + if (!count++) + fprintf(stdlog,"\nRepair X-PFlags, loop %u, %s:\n",loop,text); + + fprintf(stdlog,"%12.3g %6d", e->diff_sum, e->eval ); + if ( e->eval == update_eval ) + { + LogPrintElement(e,e->hit); + LogPrintElement(e->hit,e); + + if ( index == 1 ) + { + e->match_id = ++match_id; + fprintf(stdlog," [%u]",match_id); + } + else + fprintf(stdlog," [%u]",e->hit->match_id); + } + else + LogPrintElement(e,0); + fputc('\n',stdlog); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint DiffXPF ( xpf_mode1_info_t *l1, xpf_mode1_info_t *l2, int loop ) +{ + DASSERT(l1); + DASSERT(l2); + + //--- setup + + uint i1, i2; + xpf_mode1_elem_t *e1, *e2; + + // clear second list + for ( i2 = 0, e2 = l2->list; i2 < l2->used; i2++, e2++ ) + if ( e2->eval > 0 ) + e2->eval = 0; + + + //--- compare each with each + + int max_eval = 0; + for ( i1 = 0, e1 = l1->list; i1 < l1->used; i1++, e1++ ) + { + if ( e1->eval < 0 ) + continue; + + for ( i2 = 0, e2 = l2->list; i2 < l2->used; i2++, e2++ ) + { + if ( e2->eval < 0 ) + continue; + + int eval = 0; + double diff_sum = 0.0; + + const kmp_gobj_entry_t *o1 = e1->obj; + const kmp_gobj_entry_t *o2 = e2->obj; + + if (!memcmp(o1,o2,sizeof(*o1))) + eval = 990000; + else if (!memcmp(&e1->norm,&e2->norm,sizeof(e1->norm))) + { + eval = 950000; + if ( o1->obj_id == o2->obj_id ) eval += 2000; + if ( o1->pflags == o2->pflags ) eval += 1000; + } + else + { + diff_sum = Length2F(o1->position,o2->position) + + Length2F(o1->rotation,o2->rotation) + + Length2F(o1->scale,o2->scale); + + if ( o1->obj_id == o2->obj_id ) + eval = 400000; + else if ( (o1->obj_id&GOBJ_M_PF_STD) == (o2->obj_id&GOBJ_M_PF_STD) ) + eval = 200000; + + if ( o1->pflags == o2->pflags ) + eval += 100000; + + uint i; + for ( i = 0; i < 8; i++ ) + if ( o1->setting[i] == o2->setting[i] ) + eval += 10000; + + diff_sum = Length2F(o1->position,o2->position) + + Length2F(o1->rotation,o2->rotation) + + Length2F(o1->scale,o2->scale); + + if ( diff_sum > 1.0 ) + { + int exp; + frexpl(diff_sum,&exp); + eval -= exp; + } + } + + eval += 999 - 3*abs( e1->index - e2->index ); + + if ( e1->eval < eval || e1->eval == eval && e1->diff_sum > diff_sum ) + { + e1->eval = eval; + e1->diff_sum = diff_sum; + e1->hit = e2; + } + + if ( e2->eval < eval || e2->eval == eval && e2->diff_sum > diff_sum ) + { + e2->eval = eval; + e2->diff_sum = diff_sum; + e2->hit = e1; + } + + if ( max_eval < eval ) + max_eval = eval; + + } + } + + + //--- logging + + if ( logging > 1 ) + { + LogListXPF(l1,1,"patching list",loop,max_eval); + LogListXPF(l2,2,"reference list",loop,max_eval); + } + + + //--- update + + uint update_count = 0; + if ( max_eval > 0 ) + { + for ( i1 = 0, e1 = l1->list; i1 < l1->used; i1++, e1++ ) + { + if ( e1->eval == max_eval ) + { + e2 = e1->hit; + if ( e2->eval == max_eval && e2->hit == e1 ) + { + if (!e1->obj->ref_id) + e1->obj->ref_id = e2->obj->ref_id; + e1->eval = e2->eval = -1; + update_count++; + } + } + } + } + + return update_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError RepairXPF ( kmp_t *kmp ) +{ + if (!repair_kmp) + { + if (!repair_xpf_fname) + return ERR_NOTHING_TO_DO; + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,repair_xpf_fname,"course.kmp",false,0); + repair_xpf_fname = 0; + if (err) + { + ResetRawData(&raw); + return err; + } + + disable_patch_on_load++; + repair_kmp = MALLOC(sizeof(*repair_kmp)); + err = ScanRawDataKMP(repair_kmp,true,&raw,0); + disable_patch_on_load--; + if ( err > ERR_WARNING ) + { + ResetKMP(repair_kmp); + FREE(repair_kmp); + repair_kmp = 0; + return err; + } + } + + + //--- setup lists + + xpf_mode1_info_t *rinfo = CreateXpfModelInfo( repair_kmp->dlist + KMP_GOBJ ); + xpf_mode1_info_t *kinfo = CreateXpfModelInfo( kmp->dlist + KMP_GOBJ ); + + if ( !rinfo || !kinfo || !kinfo->broken ) + { + FREE(rinfo); + FREE(kinfo); + return ERR_NOTHING_TO_DO; + } + + PRINT0("REPAIR XPF! rep=%d/%d, kmp=%d/%d\n", + rinfo->broken, rinfo->used, kinfo->broken, kinfo->used ); + + int loop = 1; + while (DiffXPF(kinfo,rinfo,loop)) + loop++; + + if ( logging > 1 ) + fputc('\n',stdlog); + + FREE(rinfo); + FREE(kinfo); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// |
