aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-kmp-text.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--project/src/lib-kmp-text.c7192
1 files changed, 7192 insertions, 0 deletions
diff --git a/project/src/lib-kmp-text.c b/project/src/lib-kmp-text.c
new file mode 100644
index 0000000..c30c57b
--- /dev/null
+++ b/project/src/lib-kmp-text.c
@@ -0,0 +1,7192 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * 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-kcl.h"
+#include "lib-szs.h"
+#include "lib-bzip2.h"
+#include "kmp.inc"
+
+#include <math.h>
+#include <stddef.h>
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// cond_ref table ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const gobj_cond_ref_t cond_ref_tab[COR__N+1] =
+{
+ { COR_NEVER, "NEVER", "", 0,"never", 0x0000,0x00,0 },
+ { COR_LECODE, "LECODE", "P1234",2,"lecode", 0xffff,0xff,0 },
+ { COR_SINGLE, "SINGLE", "P1", 2,"1 player @Wii", 0x5551,0x00,0 },
+ { COR_MULTI, "MULTI", "P234", 2,"2-4 players @Wii", 0xaaae,0x00,0 },
+ { COR_OFFLINE, "OFFLINE", "OFF", 2,"offline", 0x400f,0x00,0 },
+ { COR_ONLINE, "ONLINE", "ON", 2,"online", 0xbff0,0x00,0 },
+
+ { COR_LE6, "LE6", "1_6", 3,"≤6 players", 0xc03f,0x00,0 },
+ { COR_GE7, "GE7", "7_99", 2,"≥7 players", 0x3fc0,0x00,0 },
+ { COR_LE9, "LE9", "1_9", 2,"≤9 players", 0xc0ff,0x00,0 },
+ { COR_GE10, "GE10", "10_99",2,"≥10 players", 0x3f00,0x00,0 },
+ { COR_LE12, "LE12", "1_12", 2,"≤12 players", 0xc3ff,0x00,0 },
+ { COR_GE13, "GE13", "13_99",2,"≥13 players", 0x3c00,0x00,0 },
+ { COR_LE18, "LE18", "1_18", 2,"≤18 players", 0xcfff,0x00,0 },
+ { COR_GE19, "GE19", "19_99",2,"≥19 players", 0x3000,0x00,0 },
+
+ { COR_7_9, "7_9", "", 3,"7-9 players", 0x00c0,0x00,0 },
+ { COR_NOT_7_9, "NOT_7_9", "", 2,"<7 or >9 players", 0xff3f,0x00,0 },
+ { COR_7_12, "7_12", "", 2,"7-12 players", 0x03c0,0x00,0 },
+ { COR_NOT_7_12, "NOT_7_12", "", 2,"<7 or >12 players", 0xfc3f,0x00,0 },
+ { COR_7_18, "7_18", "", 2,"7-18 players", 0x0fc0,0x00,0 },
+ { COR_NOT_7_18, "NOT_7_18", "", 2,"<7 or >18 players", 0xf03f,0x00,0 },
+ { COR_10_12, "10_12", "", 2,"10-12 players", 0x0300,0x00,0 },
+ { COR_NOT_10_12, "NOT_10_12", "", 2,"<10 or >12 players", 0xfcff,0x00,0 },
+ { COR_10_18, "10_18", "", 2,"10-18 players", 0x0f00,0x00,0 },
+ { COR_NOT_10_18, "NOT_10_18", "", 2,"<10 or >18 players", 0xf0ff,0x00,0 },
+ { COR_13_18, "13_18", "", 2,"13-18 players", 0x0c00,0x00,0 },
+ { COR_NOT_13_18, "NOT_13_18", "", 2,"<13 or >18 players", 0xf3ff,0x00,0 },
+
+ { COR_TIMETRIAL, "TIMETRIAL", "TT", 3,"time trial", 0x4000,0x04,1 },
+ { COR_NO_TIMETRIAL, "NOT_TIMETRIAL","NOTT", 2,"not time trial", 0xbfff,0xfb,0 },
+ { COR_BATTLE, "BATTLE", "", 2,"battle", 0x0000,0x03,0 },
+ { COR_RACE, "RACE", "", 2,"race", 0x0000,0xfc,0 },
+
+ { COR_BALLOON, "BALLOON", "", 3,"balloon", 0x0000,0x01,0 },
+ { COR_NO_BALLOON, "NOT_BALLOON", "", 2,"not balloon", 0x0000,0xfe,0 },
+ { COR_COIN, "COIN", "", 2,"coin", 0x0000,0x02,0 },
+ { COR_NO_COIN, "NOT_COIN", "", 2,"not coin", 0x0000,0xfd,0 },
+ { COR_VERSUS, "VERSUS", "", 2,"versus", 0x0000,0x04,0 },
+ { COR_NO_VERSUS, "NOT_VERSUS", "", 2,"not versus", 0x0000,0xfb,0 },
+ { COR_ITEMRAIN, "ITEMRAIN", "", 2,"item rain", 0x0000,0x08,0 },
+ { COR_NO_ITEMRAIN, "NOT_ITEMRAIN", "", 2,"not item rain", 0x0000,0xf7,0 },
+ { COR_SETTING5, "SETTING5", "", 2,"settings 5", 0x0000,0x10,0 },
+ { COR_NO_SETTING5, "NOT_SETTING5", "", 2,"not setting 5", 0x0000,0xef,0 },
+ { COR_SETTING6, "SETTING6", "", 2,"settings 6", 0x0000,0x20,0 },
+ { COR_NO_SETTING6, "NOT_SETTING6", "", 2,"not setting 6", 0x0000,0xdf,0 },
+ { COR_SETTING7, "SETTING7", "", 2,"settings 7", 0x0000,0x40,0 },
+ { COR_NO_SETTING7, "NOT_SETTING7", "", 2,"not setting 7", 0x0000,0xbf,0 },
+ { COR_SETTING8, "SETTING8", "", 2,"settings 8", 0x0000,0x80,0 },
+ { COR_NO_SETTING8, "NOT_SETTING8", "", 2,"not setting 8", 0x0000,0x7f,0 },
+
+ { COR_P2, "P2", "", 3,"2 players @Wii", 0xaaa2,0x00,0 },
+ { COR_P134, "P134", "", 2,"1|3|4 players @Wii", 0x555d,0x00,0 },
+ { COR_P3, "P3", "", 2,"3 players @Wii", 0x0004,0x00,0 },
+ { COR_P124, "P124", "", 2,"1|2|4 players @Wii", 0xfffb,0x00,0 },
+ { COR_P4, "P4", "", 2,"4 players @Wii", 0x0008,0x00,0 },
+ { COR_P123, "P123", "", 2,"1-3 players @Wii", 0xfff7,0x00,0 },
+ { COR_P12, "P12", "", 2,"1-2 players @Wii", 0xfff3,0x00,0 },
+ { COR_P34, "P34", "", 2,"3-4 players @Wii", 0x000c,0x00,0 },
+ { COR_P13, "P13", "", 2,"1|3 players @Wii", 0x5555,0x00,0 },
+ { COR_P24, "P24", "", 2,"2|4 players @Wii", 0xaaaa,0x00,0 },
+ { COR_P14, "P14", "", 2,"1|4 players @Wii", 0x5559,0x00,0 },
+ { COR_P23, "P23", "", 2,"2-3 players @Wii", 0xaaa6,0x00,0 },
+
+ { CORD_DEBUG, "DEBUG", "", 5,"if LE-CODE debug", 0x0001,0x00,2 },
+ { CORD_RELEASE, "RELEASE", "", 4,"if LE-CODE release", 0x0002,0x00,2 },
+ { CORD_TESTVAL, "TESTVAL", "", 4,"have test values", 0x0004,0x00,2 },
+ { CORD_NO_TESTVAL, "NOT_TESTVAL", "", 4,"haven't test values",0x0008,0x00,2 },
+
+ { 0,"","",0,"",0,0,0 },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+const gobj_cond_ref_t * FindConditionRef ( u16 ref_id )
+{
+ const gobj_cond_ref_t *rc = cond_ref_tab;
+ if ( ref_id < COR__N )
+ {
+ const gobj_cond_ref_t *rc2 = cond_ref_tab + ref_id - 0x1000;
+ if ( rc2->ref_id <= ref_id )
+ rc = rc2;
+ }
+
+ for ( ; rc->ref_id; rc++ )
+ if ( rc->ref_id == ref_id )
+ return rc;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool IsConditionRefEnabled
+ ( const gobj_cond_mask_t *cm, u16 ref_id, const gobj_cond_ref_t *cref )
+{
+ DASSERT(cm);
+
+ //--- check predefinied condition for ENGINE
+
+ if (IsConditionRefEngineId(ref_id))
+ return ref_id & cm->engine_mask;
+
+
+ //--- check predefinied condition for RANDOM
+
+ if (IsConditionRefRandomId(ref_id))
+ return ref_id & cm->random_mask;
+
+
+ //--- check hard coded condition => search list
+
+ if (IsConditionRefId(ref_id))
+ {
+ if (!cref)
+ cref = FindConditionRef(ref_id);
+ if (cref)
+ {
+ switch (cref->op)
+ {
+ case 0:
+ return cref->cond_mask & cm->bit_mask
+ || cref->set_mask & cm->game_mask;
+
+ case 1:
+ return cref->cond_mask & cm->bit_mask
+ && cref->set_mask & cm->game_mask;
+ }
+ }
+ return false;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool GetConditionRefInfo
+ ( cond_ref_info_t *cri, u16 ref_id, u16 obj_id, bool create_cond )
+{
+ DASSERT(cri);
+ memset(cri,0,sizeof(*cri));
+
+ if ( obj_id >= GOBJ_MIN_DEF || obj_id & GOBJ_M_LECODE )
+ cri->cmd_suffix = "ENABLE";
+ else
+ {
+ cri->cmd_suffix = "DISABLE";
+ cri->xor = 1;
+ }
+
+ if (IsConditionRefTestId(ref_id))
+ {
+ cri->is_condref = true;
+ if (cri->xor)
+ {
+ cri->xor = 1;
+ ref_id ^= 1;
+ }
+ if (create_cond)
+ {
+ snprintf( cri->cond, sizeof(cri->cond),
+ "IF$TEST(%u,%u)", ref_id >> 6 & 15, ref_id >> 1 & 31 );
+ StringCopyS(cri->info,sizeof(cri->info),
+ "Test condition for LE-CODE developers" );
+ }
+ }
+ else if (IsConditionRefEngineId(ref_id))
+ {
+ cri->is_condref = true;
+ if (cri->xor)
+ {
+ cri->xor = GOBJ_MASK_COND_ENGINE;
+ ref_id ^= GOBJ_MASK_COND_ENGINE;
+ }
+ if (create_cond)
+ {
+ char *end = cri->cond + sizeof(cri->cond) - 3;
+ char *dest = StringCopyE(cri->cond,end,"if$engine(");
+ static const char name[][7]
+ = { "battle", "50", "100", "150", "200", "150m", "200m", "" };
+
+ uint i, m = ref_id;
+ for ( i = 0; name[i][0]; i++, m >>= 1 )
+ if ( m&1 )
+ {
+ if (dest[-1] != '(' )
+ *dest++ = ',';
+ dest = StringCat2E(dest,end,"en$",name[i]);
+ }
+ *dest++ = ')';
+ *dest = 0;
+
+ StringCopyS(cri->info,sizeof(cri->info),
+ "if selected engines are active" );
+ }
+ }
+ else if (IsConditionRefRandomId(ref_id))
+ {
+ cri->is_condref = true;
+ if (cri->xor)
+ {
+ cri->xor = GOBJ_MASK_COND_RND;
+ ref_id ^= GOBJ_MASK_COND_RND;
+ }
+ if (create_cond)
+ {
+ char *end = cri->cond + sizeof(cri->cond) - 4;
+ char *dest = StringCopyE(cri->cond,end,"if$random(");
+ uint i, m;
+ for ( i = m = 1; i <= GOBJ_MAX_RANDOM; i++, m <<= 1 )
+ if ( ref_id & m && dest < end )
+ {
+ if (dest[-1] != '(' )
+ *dest++ = ',';
+ *dest++ = '0'+i;
+ }
+ *dest++ = ')';
+ *dest = 0;
+
+ StringCopyS(cri->info,sizeof(cri->info),
+ "if selected random scenario is active" );
+ }
+ }
+ else
+ {
+ cri->condref = FindConditionRef(ref_id);
+ if (cri->condref)
+ {
+ cri->is_condref = true;
+ if (create_cond)
+ {
+ const gobj_cond_ref_t *iref = cri->xor
+ ? FindConditionRef(ref_id^cri->xor)
+ : cri->condref;
+ if (iref)
+ {
+ if ( iref->option & 4 )
+ {
+ snprintf( cri->cond, sizeof(cri->cond), "IFDEV$%s",iref->name);
+ snprintf( cri->info, sizeof(cri->info),
+ "DEVELOPERS: %s", iref->info );
+ }
+ else
+ {
+ snprintf( cri->cond, sizeof(cri->cond), "IF$%s",iref->name);
+ snprintf( cri->info, sizeof(cri->info), "%s%s",
+ iref->option & 1 ? "if " : "",
+ iref->info );
+ }
+ }
+ }
+ }
+ else
+ cri->is_condref = IsConditionRefId(ref_id);
+
+ if ( cri->is_condref && !*cri->info )
+ snprintf( cri->cond, sizeof(cri->cond), "%#x",ref_id^cri->xor );
+ }
+
+ return cri->is_condref;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// link info ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetRouteLinksKMP ( const kmp_t *ckmp )
+{
+ DASSERT(ckmp);
+ kmp_t *kmp = (kmp_t*)ckmp;
+ if (kmp->linfo)
+ {
+ memset(kmp->linfo,0,sizeof(*kmp->linfo));
+ kmp->linfo_sect = ~0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+kmp_linfo_t * SetupRouteLinksKMP ( const kmp_t *ckmp, uint sect, bool force )
+{
+ DASSERT(ckmp);
+ kmp_t *kmp = (kmp_t*)ckmp;
+ if (!kmp->linfo)
+ {
+ kmp->linfo = MALLOC(sizeof(*kmp->linfo));
+ kmp->linfo_sect = ~0;
+ }
+
+ if ( force || kmp->linfo_sect != sect )
+ {
+ kmp->linfo_sect = sect;
+ AnalyseRouteKMP(kmp->linfo,kmp,sect);
+ noPRINT("ROUTE INFO for section #%d [%s] => %u\n",
+ sect, kmp_section_name[sect].name1, kmp->linfo->used );
+ }
+ return (kmp_linfo_t*) kmp->linfo;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+uint AnalyseRouteKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect )
+{
+ DASSERT(li);
+ DASSERT(kmp);
+
+ switch(sect)
+ {
+ case KMP_CKPH:
+ case KMP_ENPH:
+ case KMP_ITPH:
+ return AnalyseGroupLinksKMP(li,kmp,sect);
+
+ case KMP_CKPT:
+ case KMP_ENPT:
+ case KMP_ITPT:
+ return AnalyseRouteLinksKMP(li,kmp,sect);
+
+ default:
+ ASSERT_MSG(0,"AnalyseRouteKMP(): Unsupported KMP section: %u",sect);
+ memset(li,0,sizeof(*li));
+ li->sect_pt = li->sect_ph = KMP_NO_SECT;
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint AnalyseGroupLinksKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_ph )
+{
+ DASSERT(li);
+ DASSERT(kmp);
+ memset(li,0,sizeof(*li));
+
+
+ //--- section dependent setup
+
+ switch(sect_ph)
+ {
+ case KMP_CKPH:
+ case KMP_CKPT:
+ li->sect_pt = KMP_CKPT;
+ li->sect_ph = KMP_CKPH;
+ break;
+
+ case KMP_ENPH:
+ case KMP_ENPT:
+ li->sect_pt = KMP_ENPT;
+ li->sect_ph = KMP_ENPH;
+ break;
+
+ case KMP_ITPH:
+ case KMP_ITPT:
+ li->sect_pt = KMP_ITPT;
+ li->sect_ph = KMP_ITPH;
+ break;
+
+ default:
+ ASSERT_MSG(0,"AnalyseGroupLinksKMP(): Unsupported KMP section: %u",sect_ph);
+ li->sect_pt = li->sect_ph = KMP_NO_SECT;
+ return 0;
+ }
+
+
+ //--- setup
+
+ uint n = kmp->dlist[li->sect_ph].used;
+ if ( n > KMP_MAX_LINFO )
+ n = KMP_MAX_LINFO;
+
+
+ //--- iterate groups
+
+ uint i, max = 0;
+ const kmp_enph_entry_t * p = (kmp_enph_entry_t*)kmp->dlist[li->sect_ph].list;
+
+ for ( i = 0; i < n; i++, p++ )
+ {
+ li->n_point[i] = p->pt_len;
+ if (IsDispatchPointKMP(p))
+ li->gmode[i] |= LINFOG_DISPATCH;
+
+ uint l;
+ for ( l = 0; l < KMP_MAX_PH_LINK; l++ )
+ {
+ u8 j = p->prev[l];
+ if ( j != 0xff )
+ {
+ li->summary[i] |= LINFO_PREV;
+ li->list[i][j] |= LINFO_PREV;
+ li->summary[j] |= LINFO_BY_PREV;
+ li->list[j][i] |= LINFO_BY_PREV;
+ if ( max <= i ) max = i+1;
+ if ( max <= j ) max = j+1;
+ }
+
+ j = p->next[l];
+ if ( j != 0xff )
+ {
+ li->summary[i] |= LINFO_NEXT;
+ li->list[i][j] |= LINFO_NEXT;
+ li->summary[j] |= LINFO_BY_NEXT;
+ li->list[j][i] |= LINFO_BY_NEXT;
+ if ( max <= i ) max = i+1;
+ if ( max <= j ) max = j+1;
+ }
+ }
+ }
+
+ noPRINT(">>> GMODE/group\n");
+ //HEXDUMP16(0,0,li->gmode,max);
+ return li->used = max;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+uint AnalyseRouteLinksPH ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_ph )
+{
+ DASSERT(li);
+ DASSERT(kmp);
+ memset(li,0,sizeof(*li));
+
+
+ //--- section dependent setup
+
+ switch(sect_ph)
+ {
+ case KMP_CKPH:
+ case KMP_CKPT:
+ li->sect_pt = KMP_CKPT;
+ li->sect_ph = KMP_CKPH;
+ break;
+
+ case KMP_ENPH:
+ case KMP_ENPT:
+ li->sect_pt = KMP_ENPT;
+ li->sect_ph = KMP_ENPH;
+ break;
+
+ case KMP_ITPH:
+ case KMP_ITPT:
+ li->sect_pt = KMP_ITPT;
+ li->sect_ph = KMP_ITPH;
+ break;
+
+ default:
+ ASSERT_MSG(0,"AnalyseRouteLinksPH(): Unsupported KMP section: %u",sect_ph);
+ li->sect_pt = li->sect_ph = KMP_NO_SECT;
+ return 0;
+ }
+
+
+ //--- setup
+
+ uint n = kmp->dlist[li->sect_ph].used;
+ if ( n > KMP_MAX_LINFO )
+ n = KMP_MAX_LINFO;
+
+ const kmp_enph_entry_t *ph_list = (kmp_enph_entry_t*)kmp->dlist[li->sect_ph].list;
+
+
+ //--- iterate groups
+
+ uint i, max = 0;
+ const kmp_enph_entry_t *p = ph_list;
+
+ for ( i = 0; i < n; i++, p++ )
+ {
+ if (!p->pt_len)
+ continue;
+
+ const uint iprev = p->pt_start;
+ const uint inext = p->pt_start + p->pt_len - 1;
+
+ kmp_linfo_g_mode gmode = 0;
+ if (IsDispatchPointKMP(p))
+ gmode |= LINFOG_DISPATCH;
+ uint pi;
+ for ( pi = iprev; pi <= inext; pi++ )
+ {
+ li->n_point[pi] = p->pt_len;
+ li->gmode[pi] = gmode;
+ }
+
+ uint l;
+ for ( l = 0; l < KMP_MAX_PH_LINK; l++ )
+ {
+ u8 j = p->prev[l];
+ if ( j != 0xff && j < n )
+ {
+ const uint link = ph_list[j].pt_start + ph_list[j].pt_len - 1;
+ if ( link < KMP_MAX_LINFO )
+ {
+ li->summary[iprev] |= LINFO_G_PREV;
+ li->list[iprev][link] |= LINFO_G_PREV;
+ li->summary[link] |= LINFO_G_BY_PREV;
+ li->list[link][iprev] |= LINFO_G_BY_PREV;
+ if ( max <= iprev ) max = iprev+1;
+ if ( max <= link ) max = link+1;
+ }
+ }
+
+ if ( inext < KMP_MAX_LINFO )
+ {
+ j = p->next[l];
+ if ( j != 0xff && j < n )
+ {
+ const uint link = ph_list[j].pt_start;
+ if ( link < KMP_MAX_LINFO )
+ {
+ li->summary[inext] |= LINFO_G_NEXT;
+ li->list[inext][link] |= LINFO_G_NEXT;
+ li->summary[link] |= LINFO_G_BY_NEXT;
+ li->list[link][inext] |= LINFO_G_BY_NEXT;
+ if ( max <= inext ) max = inext+1;
+ if ( max <= link ) max = link+1;
+ }
+ }
+ }
+ }
+ }
+
+ noPRINT(">>> GMODE/points\n");
+ //HEXDUMP16(0,0,li->gmode,max);
+
+ return li->used = max;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint AnalyseRouteLinksKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_pt )
+{
+ DASSERT(li);
+ DASSERT(kmp);
+
+
+ //--- section dependent setup
+
+ switch(sect_pt)
+ {
+ case KMP_CKPT:
+ case KMP_CKPH:
+ return AnalyseRouteLinksCKPT(li,kmp);
+ break;
+
+ case KMP_ENPT:
+ case KMP_ENPH:
+ case KMP_ITPT:
+ case KMP_ITPH:
+ break;
+
+ default:
+ ASSERT_MSG(0,"AnalyseRouteLinksKMP(): Unsupported KMP section: %u",sect_pt);
+ memset(li,0,sizeof(*li));
+ li->sect_pt = li->sect_ph = KMP_NO_SECT;
+ return 0;
+ }
+
+
+ //--- setup
+
+ uint max = AnalyseRouteLinksPH(li,kmp,sect_pt);
+
+ uint n = kmp->dlist[li->sect_pt].used;
+ if ( n > KMP_MAX_LINFO )
+ n = KMP_MAX_LINFO;
+ if ( max < n )
+ max = n;
+
+ const kmp_enpt_entry_t *p = (kmp_enpt_entry_t*)kmp->dlist[li->sect_pt].list;
+
+
+ //--- iterate points
+
+ uint i;
+ for ( i = 0; i < n; i++, p++ )
+ {
+ if ( i > 0 && !( li->summary[i] & LINFO_G_PREV ) )
+ {
+ li->summary[i] |= LINFO_D_PREV;
+ li->list[i][i-1] |= LINFO_D_PREV;
+ li->summary[i-1] |= LINFO_D_BY_PREV;
+ li->list[i-1][i] |= LINFO_D_BY_PREV;
+ }
+
+ if ( i < KMP_MAX_LINFO-1 && !( li->summary[i] & LINFO_G_NEXT ) )
+ {
+ li->summary[i] |= LINFO_D_NEXT;
+ li->list[i][i+1] |= LINFO_D_NEXT;
+ li->summary[i+1] |= LINFO_D_BY_NEXT;
+ li->list[i+1][i] |= LINFO_D_BY_NEXT;
+ }
+ }
+
+ return li->used = max;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint AnalyseRouteLinksCKPT ( kmp_linfo_t *li, const kmp_t *kmp )
+{
+ DASSERT(li);
+ DASSERT(kmp);
+ memset(li,0,sizeof(*li));
+ li->sect_pt = KMP_CKPT;
+ li->sect_ph = KMP_CKPH;
+
+
+ //--- setup
+
+ uint max = AnalyseRouteLinksPH(li,kmp,KMP_CKPH);
+
+ uint n = kmp->dlist[KMP_CKPT].used;
+ if ( n > KMP_MAX_LINFO )
+ n = KMP_MAX_LINFO;
+ if ( max < n )
+ max = n;
+
+ const kmp_ckpt_entry_t *p = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list;
+
+
+ //--- iterate points
+
+ uint i;
+ for ( i = 0; i < n; i++, p++ )
+ {
+ uint j = p->prev;
+ if ( j != 0xff )
+ {
+ li->summary[i] |= LINFO_D_PREV;
+ li->list[i][j] |= LINFO_D_PREV;
+ li->summary[j] |= LINFO_D_BY_PREV;
+ li->list[j][i] |= LINFO_D_BY_PREV;
+ }
+
+ j = p->next;
+ if ( j != 0xff )
+ {
+ li->summary[i] |= LINFO_D_NEXT;
+ li->list[i][j] |= LINFO_D_NEXT;
+ li->summary[j] |= LINFO_D_BY_NEXT;
+ li->list[j][i] |= LINFO_D_BY_NEXT;
+ }
+ }
+
+ return li->used = max;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp PrintLinkInfoMode ( kmp_linfo_mode mode, int print_group, bool adjust )
+{
+ // print_group: -1=never, 0:auto, 1:always
+ char *buf = GetCircBuf(10);
+ char *dest = buf;
+
+ if (adjust)
+ {
+ *dest++ = mode & LINFO_D_PREV ? 'P' : '-';
+ *dest++ = mode & LINFO_D_NEXT ? 'N' : '-';
+ *dest++ = mode & LINFO_D_BY_PREV ? 'p' : '-';
+ *dest++ = mode & LINFO_D_BY_NEXT ? 'n' : '-';
+
+ if ( print_group > 0 || !print_group && mode&LINFO_M_G )
+ {
+ *dest++ = mode & LINFO_G_PREV ? 'P' : '-';
+ *dest++ = mode & LINFO_G_NEXT ? 'N' : '-';
+ *dest++ = mode & LINFO_G_BY_PREV ? 'p' : '-';
+ *dest++ = mode & LINFO_G_BY_NEXT ? 'n' : '-';
+ }
+ }
+ else
+ {
+ if ( mode & LINFO_D_PREV ) *dest++ = 'P';
+ if ( mode & LINFO_D_NEXT ) *dest++ = 'N';
+ if ( mode & LINFO_D_BY_PREV ) *dest++ = 'p';
+ if ( mode & LINFO_D_BY_NEXT ) *dest++ = 'n';
+
+ if ( print_group >= 0 && mode&LINFO_M_G )
+ {
+ *dest++ = ',';
+ if ( mode & LINFO_G_PREV ) *dest++ = 'P';
+ if ( mode & LINFO_G_NEXT ) *dest++ = 'N';
+ if ( mode & LINFO_G_BY_PREV ) *dest++ = 'p';
+ if ( mode & LINFO_G_BY_NEXT ) *dest++ = 'n';
+ }
+ }
+ *dest = 0;
+ return buf;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// kmp parser function get() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enum // get mode
+{
+ GTMD_N, // number of elements
+
+ GTMD_HD_U16, // head 'value' as u16
+ GTMD_HD_U8HI, // hight byte of head 'value'
+ GTMD_HD_U8LO, // low byte of head 'value'
+
+ GTMD__DATA, // separator of header info and data info
+
+ GTMD_U8, // unsigned 8 bit
+ GTMD_U8M1, // unsigned 8 bit, but 0xff is -1
+ GTMD_U16, // unsigned 16 bit
+ GTMD_U16M1, // unsigned 16 bit, but 0xffff is -1
+
+ GTMD_DBL, // double value
+ GTMD_DBL2, // 2d vector
+ GTMD_DBL3, // 3d double vector
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// [[kmp_func_t]]
+
+#define POTI_PT_OFF 0x10
+
+typedef struct kmp_func_t
+{
+ char name[18]; // variable name
+ u16 sect; // section
+ u16 off; // data offset, for POTI: off>POTI_PT_OFF: route point
+ u16 get_mode; // see enum above
+ u8 n_var; // number of variables
+ u8 n_param; // number of function parameters
+ ccp get_info; // NULL or function info for get function
+ ccp set_info; // NULL or function info for set function
+
+} kmp_func_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const kmp_func_t kmp_func_list[] =
+{
+ { "KTPT$N", KMP_KTPT, 0, GTMD_N, 0,0,
+ "Returns the number of start points.",
+ 0 },
+ { "KTPT$POS", KMP_KTPT, 0x00, GTMD_DBL3, 1,1,
+ "Returns a start point position.",
+ 0 },
+ { "KTPT$ROT", KMP_KTPT, 0x0c, GTMD_DBL3, 1,1,
+ "Returns a start point rotation.",
+ 0 },
+ { "KTPT$ID", KMP_KTPT, 0x18, GTMD_U16M1, 1,1,
+ "Returns a start point ID.",
+ 0 },
+ { "KTPT$UNKNOWN", KMP_KTPT, 0x1a, GTMD_U16, 1,1,
+ "Returns the unknown value of a start point.",
+ 0 },
+
+ //---------------
+
+ { "ENPT$N", KMP_ENPT, 0, GTMD_N, 0,0,
+ "Returns the number of enemy points.",
+ 0 },
+ { "ENPT$POS", KMP_ENPT, 0x00, GTMD_DBL3, 1,1,
+ "Returns the position of an enemy point.",
+ 0 },
+ { "ENPT$SCALE", KMP_ENPT, 0x0c, GTMD_DBL, 1,1,
+ "Returns the scale of an enemy point.",
+ 0 },
+ { "ENPT$PROP", KMP_ENPT, 0x10, GTMD_U16, 2,2,
+ "Returns property 0 or 1 of an enemy point.",
+ 0 },
+
+ //---------------
+
+ { "ENPH$N", KMP_ENPH, 0, GTMD_N, 0,0,
+ "Returns the number of enemy point groups.",
+ 0 },
+ { "ENPH$START", KMP_ENPH, 0x00, GTMD_U8, 1,1,
+ "Returns the enemy point start index of an enemy point group.",
+ 0 },
+ { "ENPH$LEN", KMP_ENPH, 0x01, GTMD_U8, 1,1,
+ "Returns the number of enemy points of an enemy point group.",
+ 0 },
+ { "ENPH$PREV", KMP_ENPH, 0x02, GTMD_U8M1, KMP_MAX_PH_LINK,2,
+ "Returns link 0..5 to the previous enemy point group.",
+ 0 },
+ { "ENPH$NEXT", KMP_ENPH, 0x08, GTMD_U8M1, KMP_MAX_PH_LINK,2,
+ "Returns link 0..5 to the next enemy point group.",
+ 0 },
+ { "ENPH$UNKNOWN", KMP_ENPH, 0x0e, GTMD_U16, 1,1,
+ "Returns the unknown value of an enemy point group.",
+ 0 },
+
+ //---------------
+
+ { "ITPT$N", KMP_ITPT, 0, GTMD_N, 0,0,
+ "Returns the number of item points.",
+ 0 },
+ { "ITPT$POS", KMP_ITPT, 0x00, GTMD_DBL3, 1,1,
+ "Returns the position of an item point.",
+ "Set the position of an item point"
+ " and returns 1 on success ans 0 , if failed." },
+ { "ITPT$SCALE", KMP_ITPT, 0x0c, GTMD_DBL, 1,1,
+ "Returns the scale of an item point.",
+ 0 },
+ { "ITPT$PROP", KMP_ITPT, 0x10, GTMD_U16, 2,2,
+ "Returns property 0 or 1 of an item point.",
+ 0 },
+
+ //---------------
+
+ { "ITPH$N", KMP_ITPH, 0, GTMD_N, 0,0,
+ "Returns the number of item point groups.",
+ 0 },
+ { "ITPH$START", KMP_ITPH, 0x00, GTMD_U8, 1,1,
+ "Returns the item point start index of an item point group.",
+ 0 },
+ { "ITPH$LEN", KMP_ITPH, 0x01, GTMD_U8, 1,1,
+ "Returns the number of item points of an item point group.",
+ 0 },
+ { "ITPH$PREV", KMP_ITPH, 0x02, GTMD_U8M1, KMP_MAX_PH_LINK,2,
+ "Returns link 0..5 to the previous item point group.",
+ 0 },
+ { "ITPH$NEXT", KMP_ITPH, 0x08, GTMD_U8M1, KMP_MAX_PH_LINK,2,
+ "Returns link 0..5 to the next item point group.",
+ 0 },
+ { "ITPH$UNKNOWN", KMP_ITPH, 0x0e, GTMD_U16, 1,1,
+ "Returns the unknown value of an item point group.",
+ 0 },
+
+ //---------------
+
+ { "CKPT$N", KMP_CKPT, 0, GTMD_N, 0,0,
+ "Returns the number of check points.",
+ 0 },
+ { "CKPT$LEFT", KMP_CKPT, 0x00, GTMD_DBL2, 1,1,
+ "Returns the left position of a check point.",
+ 0 },
+ { "CKPT$RIGHT", KMP_CKPT, 0x08, GTMD_DBL2, 1,1,
+ "Returns the right position of a check point.",
+ 0 },
+ { "CKPT$RESPAWN", KMP_CKPT, 0x10, GTMD_U8, 1,1,
+ "Returns the respawn index of a check point.",
+ 0 },
+ { "CKPT$MODE", KMP_CKPT, 0x11, GTMD_U8M1, 1,1,
+ "Returns the mode of check point.",
+ 0 },
+ { "CKPT$PREV", KMP_CKPT, 0x12, GTMD_U8M1, 1,1,
+ "Returns the index of the previous check point.",
+ 0 },
+ { "CKPT$NEXT", KMP_CKPT, 0x13, GTMD_U8M1, 1,1,
+ "Returns the index of the next check point.",
+ 0 },
+
+ //---------------
+
+ { "CKPH$N", KMP_CKPH, 0, GTMD_N, 0,0,
+ "Returns the number of check point groups.",
+ 0 },
+ { "CKPH$START", KMP_CKPH, 0x00, GTMD_U8, 1,1,
+ "Returns the check point start index of a check point group.",
+ 0 },
+ { "CKPH$LEN", KMP_CKPH, 0x01, GTMD_U8, 1,1,
+ "Returns the number of check points of a check point group.",
+ 0 },
+ { "CKPH$PREV", KMP_CKPH, 0x02, GTMD_U8M1, KMP_MAX_PH_LINK,2,
+ "Returns link 0..5 to the previous check point group.",
+ 0 },
+ { "CKPH$NEXT", KMP_CKPH, 0x08, GTMD_U8M1, KMP_MAX_PH_LINK,2,
+ "Returns link 0..5 to the next check point group.",
+ 0 },
+ { "CKPH$UNKNOWN", KMP_CKPH, 0x0e, GTMD_U16, 1,1,
+ "Returns the unknown value of a check point group.",
+ 0 },
+
+ //---------------
+
+ { "GOBJ$N", KMP_GOBJ, 0, GTMD_N, 0,0,
+ "Returns the number of global objects.",
+ 0 },
+ { "GOBJ$ID", KMP_GOBJ, 0x00, GTMD_U16, 1,1,
+ "Returns the object ID of a global object.",
+ 0 },
+ { "GOBJ$UNKNOWN", KMP_GOBJ, 0x02, GTMD_U16, 1,1,
+ "Returns the unknown value of a global object.",
+ 0 },
+ { "GOBJ$POS", KMP_GOBJ, 0x04, GTMD_DBL3, 1,1,
+ "Returns the position of a global object.",
+ 0 },
+ { "GOBJ$ROT", KMP_GOBJ, 0x10, GTMD_DBL3, 1,1,
+ "Returns the rotation of a global object.",
+ 0 },
+ { "GOBJ$SCALE", KMP_GOBJ, 0x1c, GTMD_DBL3, 1,1,
+ "Returns the scale of a global object.",
+ 0 },
+ { "GOBJ$ROUTE", KMP_GOBJ, 0x28, GTMD_U16M1, 1,1,
+ "Returns -1 or the index of a related route"
+ " of a global object.",
+ 0 },
+ { "GOBJ$SET", KMP_GOBJ, 0x2a, GTMD_U16, 8,2,
+ "Returns setting 0..7 of a global object.",
+ 0 },
+ { "GOBJ$PFLAGS", KMP_GOBJ, 0x3a, GTMD_U16, 1,1,
+ "Returns the presence flags of a global object.",
+ 0 },
+
+ //---------------
+
+ { "POTI$N", KMP_POTI, 0, GTMD_N, 0,0,
+ "Returns the number of routes.",
+ 0 },
+ { "POTI$NN", KMP_POTI, 0, GTMD_HD_U16, 0,0,
+ "Returns the total number of points of all routes.",
+ 0 },
+ { "POTI$NP", KMP_POTI, 0x00, GTMD_U8, 1,1,
+ "Returns the number of points of a route.",
+ 0 },
+ { "POTI$SMOOTH", KMP_POTI, 0x02, GTMD_U8, 1,1,
+ "Returns the smooth value of a route"
+ " (route header byte at offset 0x02).",
+ 0 },
+ { "POTI$BACK", KMP_POTI, 0x03, GTMD_U8, 1,1,
+ "Returns the forward+backward value of a route"
+ " (route header byte at offset 0x03).",
+ 0 },
+ { "POTI$POS", KMP_POTI, 0x10, GTMD_DBL3, 1,2, // off+POTI_PT_OFF
+ "Returns the position of a point of a route."
+ " The point index is relative to the route.",
+ 0 },
+ { "POTI$SET", KMP_POTI, 0x1c, GTMD_U16, 2,3, // off+POTI_PT_OFF
+ "Returns setting 0 or 1 of a point of a route."
+ " The point index is relative to the route.",
+ 0 },
+
+ //---------------
+
+ { "AREA$N", KMP_AREA, 0, GTMD_N, 0,0,
+ "Returns the number of areas.",
+ 0 },
+ { "AREA$MODE", KMP_AREA, 0x00, GTMD_U8M1, 1,1,
+ "Returns the mode of an area.",
+ 0 },
+ { "AREA$TYPE", KMP_AREA, 0x01, GTMD_U8M1, 1,1,
+ "Returns the type of an area. Cameras are type 0.",
+ 0 },
+ { "AREA$DEST", KMP_AREA, 0x02, GTMD_U8M1, 1,1,
+ "Returns the destination index (e.g. camera index) of an area.",
+ 0 },
+ { "AREA$UNKNOWN", KMP_AREA, 0x03, GTMD_U8, 1,1,
+ "Returns the unknown value of an area.",
+ 0 },
+ { "AREA$POS", KMP_AREA, 0x04, GTMD_DBL3, 1,1,
+ "Returns the position of an area.",
+ 0 },
+ { "AREA$ROT", KMP_AREA, 0x10, GTMD_DBL3, 1,1,
+ "Returns the rotation of an area.",
+ 0 },
+ { "AREA$SCALE", KMP_AREA, 0x1c, GTMD_DBL3, 1,1,
+ "Returns the scale of an area.",
+ 0 },
+ { "AREA$SET", KMP_AREA, 0x28, GTMD_U16, 4,2,
+ "Returns setting 0..3 of an area.",
+ 0 },
+
+ //---------------
+
+ { "CAME$N", KMP_CAME, 0, GTMD_N, 0,0,
+ "Returns the number of cameras.",
+ 0 },
+ { "CAME$OCAM", KMP_CAME, 0, GTMD_HD_U8HI, 0,0,
+ "Returns the index of the opening camera.",
+ 0 },
+ { "CAME$SCAM", KMP_CAME, 0, GTMD_HD_U8LO, 0,0,
+ "Returns the index of the selection camera.",
+ 0 },
+ { "CAME$TYPE", KMP_CAME, 0x00, GTMD_U8, 1,1,
+ "Returns the type of a camera.",
+ 0 },
+ { "CAME$NEXT", KMP_CAME, 0x01, GTMD_U8M1, 1,1,
+ "Returns -1 or the link to the next camera.",
+ 0 },
+ { "CAME$UNKNOWN_02", KMP_CAME, 0x02, GTMD_U8, 1,1,
+ "Returns the unknown value at offser 0x02 of a camera.",
+ 0 },
+ { "CAME$ROUTE", KMP_CAME, 0x03, GTMD_U8M1, 1,1,
+ "Returns -1 or the link to the related route.",
+ 0 },
+ { "CAME$UNKNOWN_04", KMP_CAME, 0x04, GTMD_U16, 1,1,
+ "Returns the unknown value at offser 0x04 of a camera.",
+ 0 },
+ { "CAME$ZOOM_SPEED", KMP_CAME, 0x06, GTMD_U16, 1,1,
+ "Returns the zoom speed in units per 100/60 sec.",
+ 0 },
+ { "CAME$VIEWPT_SPEED", KMP_CAME, 0x08, GTMD_U16, 1,1,
+ "Returns the view point speed in units per 100/60 sec.",
+ 0 },
+ { "CAME$UNKNOWN_0A", KMP_CAME, 0x0a, GTMD_U16, 1,1,
+ "Returns the unknown value at offser 0x0a of a camera.",
+ 0 },
+ { "CAME$POS", KMP_CAME, 0x0c, GTMD_DBL3, 1,1,
+ "Returns the position of a camera.",
+ 0 },
+ { "CAME$UNKNOWN_18", KMP_CAME, 0x18, GTMD_DBL3, 1,1,
+ "Returns the unknown value at offser 0x18 of a camera.",
+ 0 },
+ { "CAME$ZOOM_BEGIN", KMP_CAME, 0x24, GTMD_DBL, 1,1,
+ "Returns the zoom beginning angle of a camera.",
+ 0 },
+ { "CAME$ZOOM_END", KMP_CAME, 0x28, GTMD_DBL, 1,1,
+ "Returns the zoom ending angle of a camera.",
+ 0 },
+ { "CAME$VIEWPT_BEGIN", KMP_CAME, 0x2c, GTMD_DBL3, 1,1,
+ "Returns the start position of the view point.",
+ 0 },
+ { "CAME$VIEWPT_END", KMP_CAME, 0x38, GTMD_DBL3, 1,1,
+ "Returns the end position of the view point.",
+ 0 },
+ { "CAME$TIME", KMP_CAME, 0x44, GTMD_DBL, 1,1,
+ "Returns the run time of a camera in 1/60 sec.",
+ 0 },
+
+ //---------------
+
+ { "JGPT$N", KMP_JGPT, 0, GTMD_N, 0,0,
+ "Returns the number of respan points.",
+ 0 },
+ { "JGPT$POS", KMP_JGPT, 0x00, GTMD_DBL3, 1,1,
+ "Returns the position of a respawn point.",
+ 0 },
+ { "JGPT$ROT", KMP_JGPT, 0x0c, GTMD_DBL3, 1,1,
+ "Returns the rotation of a respawn point.",
+ 0 },
+ { "JGPT$ID", KMP_JGPT, 0x18, GTMD_U16, 1,1,
+ "Returns the ID of a respawn point.",
+ 0 },
+ { "JGPT$EFFECT", KMP_JGPT, 0x1a, GTMD_U16, 1,1,
+ "Returns the effect value of a respawn point.",
+ 0 },
+
+ //---------------
+
+ { "CNPT$N", KMP_CNPT, 0, GTMD_N, 0,0,
+ "Returns the number of canon point points.",
+ 0 },
+ { "CNPT$POS", KMP_CNPT, 0x00, GTMD_DBL3, 1,1,
+ "Returns the position of a canon point.",
+ 0 },
+ { "CNPT$ROT", KMP_CNPT, 0x0c, GTMD_DBL3, 1,1,
+ "Returns the rotation of a canon point.",
+ 0 },
+ { "CNPT$ID", KMP_CNPT, 0x18, GTMD_U16, 1,1,
+ "Returns the ID of a canon point.",
+ 0 },
+ { "CNPT$EFFECT", KMP_CNPT, 0x1a, GTMD_U16, 1,1,
+ "Returns the efffect value of a canon point.",
+ 0 },
+
+ //---------------
+
+ { "MSPT$N", KMP_MSPT, 0, GTMD_N, 0,0,
+ "Returns the number of battle points.",
+ 0 },
+ { "MSPT$POS", KMP_MSPT, 0x00, GTMD_DBL3, 1,1,
+ "Returns the position of a battle point.",
+ 0 },
+ { "MSPT$ROT", KMP_MSPT, 0x0c, GTMD_DBL3, 1,1,
+ "Returns the rotation of a battle point.",
+ 0 },
+ { "MSPT$ID", KMP_MSPT, 0x18, GTMD_U16, 1,1,
+ "Returns the ID of a battle point.",
+ 0 },
+ { "MSPT$EFFECT", KMP_MSPT, 0x1a, GTMD_U16, 1,1,
+ "Returns the efffect value of a battle point.",
+ 0 },
+
+ //---------------
+
+ { "STGI$N", KMP_STGI, 0, GTMD_N, 0,0,
+ "Returns the number of stage infos.",
+ 0 },
+ { "STGI$BYTE", KMP_STGI, 0x00, GTMD_U8, 12,2,
+ "Returns setting byte 0..11 of a stage info.",
+ 0 },
+};
+
+#define KMP_FUNC_LIST_N (sizeof(kmp_func_list)/sizeof(*kmp_func_list))
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError F_get
+(
+ 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);
+ DASSERT(fpar);
+ DASSERT( fpar->user_id >= 0 && fpar->user_id < KMP_FUNC_LIST_N );
+
+ if ( si && si->kmp )
+ {
+ const kmp_func_t *fp = kmp_func_list + fpar->user_id;
+ DASSERT( n_param >= fp->n_param );
+ DASSERT( fp->sect < KMP_N_SECT );
+ DASSERT( fp->get_info );
+
+ const List_t *dlist = si->kmp->dlist + fp->sect;
+ uint max_index1 = dlist->used;
+ int val = n_param > 0 ? GetIntV(param) : 0;
+ uint index1 = val < 0 ? val + max_index1 : val;
+ const u8 *data = dlist->list + dlist->elem_size * index1 + fp->off;
+
+ if ( fp->sect == KMP_POTI && fp->off >= POTI_PT_OFF && index1 < max_index1 )
+ {
+ param++;
+ n_param--;
+
+ const kmp_poti_group_t *pgrp = (kmp_poti_group_t*)dlist->list;
+ uint pt_start = 0;
+ while ( index1-- > 0 )
+ pt_start += (pgrp++)->n_point;
+
+ dlist = &si->kmp->poti_point;
+ max_index1 = pgrp->n_point;
+ val = n_param > 0 ? GetIntV(param) : 0;
+ index1 = val < 0 ? val + max_index1 : val;
+ PRINT("POTI/PT: index= %u/%u +%u\n",index1,max_index1,pt_start);
+ data = dlist->list
+ + dlist->elem_size * ( pt_start + index1 )
+ + fp->off - POTI_PT_OFF;
+ }
+
+ val = n_param > 1 ? GetIntV(param+1) : 0;
+ const uint index2 = val < 0 ? val + fp->n_var : val;
+
+ const bool valid_index12 = index1 < max_index1 && index2 < fp->n_var;
+ noPRINT("index1=%d/%d, index2=%d/%d, valid=%d\n",
+ index1, max_index1, index2, fp->n_var, valid_index12 );
+
+ switch(fp->get_mode)
+ {
+ case GTMD_N:
+ res->i = dlist->used;
+ res->mode = VAR_INT;
+ return ERR_OK;
+
+ case GTMD_HD_U16:
+ res->i = si->kmp->value[fp->sect];
+ res->mode = VAR_INT;
+ return ERR_OK;
+
+ case GTMD_HD_U8HI:
+ res->i = si->kmp->value[fp->sect] >> 8;
+ res->mode = VAR_INT;
+ return ERR_OK;
+
+ case GTMD_HD_U8LO:
+ res->i = si->kmp->value[fp->sect] & 0xff;
+ res->mode = VAR_INT;
+ return ERR_OK;
+
+ case GTMD_U8:
+ case GTMD_U8M1:
+ if (valid_index12)
+ {
+ const u8 val = data[index2];
+ res->i = val == 0xff && fp->get_mode == GTMD_U8M1 ? -1 : (int)val;
+ res->mode = VAR_INT;
+ return ERR_OK;
+ }
+ break;
+
+ case GTMD_U16:
+ case GTMD_U16M1:
+ if (valid_index12)
+ {
+ const u16 val = ((u16*)data)[index2];
+ res->i = val == 0xffff && fp->get_mode == GTMD_U16M1
+ ? -1 : (int)val;
+ res->mode = VAR_INT;
+ return ERR_OK;
+ }
+ break;
+
+ case GTMD_DBL:
+ if (valid_index12)
+ return ResultFloat( res, (float*)data + index2, 1 );
+ break;
+
+ case GTMD_DBL2:
+ if (valid_index12)
+ return ResultFloat( res, (float*)data + 2 * index2, 2 );
+ break;
+
+ case GTMD_DBL3:
+ if (valid_index12)
+ return ResultFloat( res, (float*)data + 3 * index2, 3 );
+ break;
+ }
+ }
+
+ res->i = 0;
+ res->mode = VAR_UNSET;
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef TEST
+
+static enumError F_set
+(
+ 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);
+ DASSERT(fpar);
+ DASSERT( fpar->user_id >= 0 && fpar->user_id < KMP_FUNC_LIST_N );
+
+ if ( si && si->kmp )
+ {
+ const kmp_func_t *fp = kmp_func_list + fpar->user_id;
+ DASSERT( n_param >= fp->n_param );
+ DASSERT( fp->sect < KMP_N_SECT );
+ DASSERT( fp->set_info );
+
+ PRINT(">>> F_SET <<<\n");
+ // ...
+ }
+
+ res->i = 0;
+ res->mode = VAR_UNSET;
+ return ERR_OK;
+}
+
+#endif
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// kmp_group_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void InitializeGL
+(
+ kmp_group_list_t *gl, // list to initialize
+ int selector,
+ kmp_t *kmp, // related KMP
+ kmp_ph_t *ph // related PH info
+)
+{
+ DASSERT(gl);
+ DASSERT(kmp);
+ DASSERT(ph);
+
+ memset(gl,0,sizeof(*gl));
+ gl->kmp = kmp;
+ gl->ph = ph;
+ gl->selector = toupper(selector);
+ InitializeLine(&gl->line);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint ScanGroupNames
+(
+ kmp_group_list_t * gl, // group list
+ kmp_group_elem_t * elist, // name list
+ ScanInfo_t * si // valid scan info
+)
+{
+ DASSERT(gl);
+ DASSERT(elist);
+ DASSERT(si);
+ ScanFile_t *sf = si->cur_file;
+ DASSERT(sf);
+
+ char name[KMP_MAX_NAME_LEN+3];
+
+ uint idx = 0;
+ while ( idx < KMP_MAX_PH_LINK )
+ {
+ const kmp_oneway_mode oneway
+ = NextCharSI(si,false) == '>' ? ONEWAY_BOTH : ONEWAY_NONE;
+ if ( oneway != ONEWAY_NONE )
+ sf->ptr++;
+
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+ if ( gl->selector && *sf->ptr == '!' )
+ {
+ int ch = *++sf->ptr;
+ if (ch)
+ {
+ sf->ptr++;
+ if ( toupper(ch) != gl->selector )
+ continue;
+ }
+ }
+
+ uint n = 1;
+ if ( *sf->ptr == '*' )
+ {
+ sf->ptr++;
+ const uint num = *sf->ptr - '0';
+ if ( num < 10 )
+ {
+ sf->ptr++;
+ if ( gl->selector == 'E' )
+ n = num;
+ }
+ }
+
+ while ( n-- > 0 && idx < KMP_MAX_PH_LINK )
+ {
+ memcpy(elist->name,name,sizeof(elist->name));
+ elist->oneway = oneway;
+ elist++;
+ idx++;
+ }
+ }
+
+ return idx;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError GroupPrevGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si // valid scan info
+)
+{
+ DASSERT(gl);
+ DASSERT(si);
+
+ if ( !gl->used)
+ {
+ GotoEolSI(si);
+ return ERR_OK;
+ }
+
+ SkipCharSI(si,':');
+ kmp_group_t *grp = gl->group + gl->used - 1;
+ ScanGroupNames(gl,grp->prev,si);
+ //HEXDUMP16(0,0,grp->prev,sizeof(grp->prev));
+ return CheckEolSI(si);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError DefaultClassGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si // valid scan info
+)
+{
+ DASSERT(gl);
+ DASSERT(gl->ph);
+ DASSERT(si);
+
+ char name[20];
+ SkipCharSI(si,':');
+ if (!ScanNameSI(si,name,sizeof(name),true,true,0))
+ {
+ GotoEolSI(si);
+ return ERR_WARNING;
+ }
+
+ int stat = InsertClassNamePH(gl->ph,name,false);
+ noPRINT("NEW DEFAULT CLASS: %d [%s]\n",stat,name);
+ if ( stat >= 0 )
+ gl->ph->default_class = stat;
+
+ return CheckEolSI(si);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ClassGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si, // valid scan info
+ kmp_gopt2_t * go2 // options to set
+)
+{
+ DASSERT(gl);
+ DASSERT(gl->ph);
+ DASSERT(si);
+
+ char name[20];
+ SkipCharSI(si,':');
+ if (!ScanNameSI(si,name,sizeof(name),true,true,0))
+ {
+ GotoEolSI(si);
+ return ERR_WARNING;
+ }
+
+ const int stat = InsertClassNamePH(gl->ph,name,false);
+ noPRINT("NEW CLASS: %d [%s]\n",stat,name);
+ if ( stat >= 0 )
+ {
+ go2->prev.rclass = go2->prev.lclass =
+ go2->next.rclass = go2->next.lclass = stat;
+
+ if ( SkipCharSI(si,',') && ScanNameSI(si,name,sizeof(name),true,true,0) )
+ {
+ const int stat = InsertClassNamePH(gl->ph,name,false);
+ noPRINT("NEW NEXT CLASS: %d [%s]\n",stat,name);
+ if ( stat >= 0 )
+ go2->next.rclass = go2->next.lclass = stat;
+ }
+ }
+
+ return CheckEolSI(si);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ACClassGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si, // valid scan info
+ kmp_gopt2_t * go2, // options to set
+ kmp_gopt_t * gopt // prev or next
+)
+{
+ DASSERT(gl);
+ DASSERT(gl->ph);
+ DASSERT(si);
+ DASSERT(gopt);
+
+ char name[20];
+ SkipCharSI(si,':');
+ if (!ScanNameSI(si,name,sizeof(name),true,true,0))
+ {
+ GotoEolSI(si);
+ return ERR_WARNING;
+ }
+
+ int stat = InsertClassNamePH(gl->ph,name,false);
+ noPRINT("NEW LINK CLASS: %d [%s]\n",stat,name);
+ if ( stat >= 0 )
+ gopt->lclass = stat;
+
+ return CheckEolSI(si);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError GroupSettingsGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si, // valid scan info
+ kmp_gopt2_t * go2 // options to set
+)
+{
+ DASSERT(gl);
+ DASSERT(si);
+ DASSERT(go2);
+
+ if (!gl->used)
+ return ERR_WARNING;
+
+ long set0 = 0, set1 = 0;
+ kmp_rtype_mode rtype = 0;
+
+ SkipCharSI(si,':');
+ enumError err = ScanUValueSI(si,&set0,0);
+ if ( !err && SkipCharSI(si,',') )
+ {
+ err = ScanUValueSI(si,&set1,0);
+ if ( !err && SkipCharSI(si,',') )
+ {
+ char name[20];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+ int stat;
+ const KeywordTab_t *key = ScanKeyword(&stat,name,kmp_rtype_name);
+ if (!key)
+ {
+ err = ERR_WARNING;
+ if (!si->no_warn)
+ PrintKeywordError(kmp_rtype_name,name,stat,0,"$SETTINGS");
+ }
+ else
+ rtype = key->id;
+ }
+ }
+
+ go2->rtype_user = rtype;
+ go2->setting[0] = set0;
+ go2->setting[1] = set1;
+
+ noPRINT("\e[36;1m rtype %d, g-settings: %u 0x%02x [%d]\e[0m\n",
+ go2->rtype_user, go2->setting[0], go2->setting[1], err );
+ if (err)
+ {
+ GotoEolSI(si);
+ return err;
+ }
+
+ return CheckEolSI(si);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError GroupOnewayGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si, // valid scan info
+ kmp_gopt2_t * go2 // options to set
+)
+{
+ DASSERT(gl);
+ DASSERT(si);
+ DASSERT(go2);
+
+ if (!gl->used)
+ return ERR_WARNING;
+
+ SkipCharSI(si,':');
+
+ char name[20];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+ int stat;
+ const KeywordTab_t *key = ScanKeyword(&stat,name,kmp_oneway_name);
+ if (!key)
+ {
+ if (!si->no_warn)
+ PrintKeywordError(kmp_oneway_name,name,stat,0,"$ONEWAY");
+ GotoEolSI(si);
+ return ERR_WARNING;
+ }
+
+ go2->ac_oneway = key->id;
+ return CheckEolSI(si);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError AddGroupGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si, // valid scan info
+ char var_prefix, // Prefix for var names
+ uint index, // index of first point
+ uint n_dim // Line_t support: number of dimensions
+)
+{
+ DASSERT(gl);
+ DASSERT(si);
+ ScanFile_t *sf = si->cur_file;
+ DASSERT(sf);
+
+ char name[KMP_MAX_NAME_LEN+3];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+
+ const bool split_group = !strcmp(name,"$SPLIT");
+ if (split_group)
+ {
+ if ( gl->ph && gl->ph->ac_mode != KMP_AC_PREV )
+ {
+ si->total_err++;
+ sf->line_err++;
+ CheckEolSI(si);
+ if (!si->no_warn)
+ ERROR0(ERR_WARNING,
+ "Command '$SPLIT' only possible if AUTO-CONNECT is set to AC$PREV"
+ " => ignored [%s @%u].\n", sf->name, sf->line );
+ return ERR_WARNING;
+ }
+
+ if ( !gl->used || index == gl->group[gl->used-1].first_index )
+ {
+ si->total_err++;
+ sf->line_err++;
+ CheckEolSI(si);
+ if (!si->no_warn)
+ ERROR0(ERR_WARNING,
+ "Command '$SPLIT' ignored [%s @%u].\n", sf->name, sf->line );
+ return ERR_WARNING;
+ }
+ }
+ else if ( strcmp(name,"$GROUP") )
+ {
+ if ( !strcmp(name,"$PREV") )
+ return GroupPrevGL(gl,si);
+ if (!strcmp(name,"$LINE"))
+ return ScanLine(&gl->line,si,n_dim,LM_LINE);
+ if (!strcmp(name,"$BORDER"))
+ return ScanLine(&gl->line,si,n_dim,LM_BORDER);
+ if (!strcmp(name,"$BEZIER"))
+ return ScanLine(&gl->line,si,n_dim,LM_BEZIER);
+
+ if ( gl->ph )
+ {
+ if ( !strcmp(name,"$DEF-CLASS") )
+ return DefaultClassGL(gl,si);
+
+ if ( gl->used > 0 && gl->used <= KMP_MAX_GROUP )
+ {
+ kmp_gopt2_t *go2 = gl->ph->gopt + gl->used - 1;
+ if ( !strcmp(name,"$SETTINGS") )
+ return GroupSettingsGL(gl,si,go2);
+ if ( !strcmp(name,"$ONEWAY") )
+ return GroupOnewayGL(gl,si,go2);
+ if ( !strcmp(name,"$CLASS") )
+ return ClassGL(gl,si,go2);
+ if ( !strcmp(name,"$AC-PREV") )
+ return ACClassGL(gl,si,go2,&go2->prev);
+ if ( !strcmp(name,"$AC-NEXT") )
+ return ACClassGL(gl,si,go2,&go2->next);
+ }
+ }
+
+ si->total_err++;
+ sf->line_err++;
+ CheckEolSI(si);
+ return ERROR0(ERR_WARNING,
+ "Line ignored [%s @%u]: %s ...\n",
+ sf->name, sf->line, name );
+ }
+
+ if ( gl->used >= KMP_MAX_GROUP )
+ {
+ si->total_err++;
+ sf->line_err++;
+ CheckEolSI(si);
+ return ERROR0(ERR_WARNING,
+ "To many groups, new group ignored: %s @%u\n",
+ sf->name, sf->line );
+ }
+
+
+ // before incrementing the group index
+ if (gl->ph)
+ {
+ noPRINT("NEW GROUP: %d, class %d\n",gl->used,gl->ph->default_class);
+ kmp_gopt2_t *go2 = gl->ph->gopt + gl->used;
+ go2->prev.rclass = go2->prev.lclass =
+ go2->next.rclass = go2->next.lclass = gl->ph->default_class;
+ }
+
+ // create new group by incrementing group index
+ kmp_group_t *grp = gl->group + gl->used++;
+ grp->first_index = grp->last_index = index;
+
+ if ( NextCharSI(si,false) == '-' )
+ {
+ sf->ptr++;
+ grp->no_rotate = true;
+ }
+
+ ScanNameSI(si,name+2,sizeof(name)-2,true,true,0);
+ memcpy(grp->name,name+2,sizeof(grp->name));
+ name[0] = var_prefix;
+ name[1] = '.';
+ DefineIntVar(&si->gvar,name,gl->used-1);
+
+ if (split_group)
+ {
+ kmp_group_t *prev = gl->group + gl->used - 2;
+ DASSERT( prev >= gl->group );
+ memcpy(grp->next,prev->next,sizeof(grp->next));
+ memset(prev->next,0,sizeof(prev->next));
+ memcpy(prev->next->name,name,sizeof(prev->next->name));
+
+ CheckEolSI(si);
+ return ERR_OK;
+ }
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,false);
+ noPRINT("--> %02x : %.6s\n",ch,sf->ptr);
+ if (!ch)
+ break;
+ sf->ptr++;
+ if ( ch == ':' )
+ break;
+ }
+
+ ScanGroupNames(gl,grp->next,si);
+ CheckEolSI(si);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint AddPointGL
+(
+ kmp_group_list_t * gl, // group list
+ uint index // index of first point
+)
+{
+ if (!gl->used)
+ gl->used++;
+
+ kmp_group_t *grp = gl->group + gl->used - 1;
+ grp->last_index = index;
+ return index - grp->first_index;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int SearchNearestDP
+(
+ kmp_group_list_t * gl, // group list
+ kmp_t * kmp, // KMP data structure
+ kmp_class_mode cls, // one of KCLS_*
+ const float * base // base position
+)
+{
+ DASSERT(gl);
+ DASSERT(kmp);
+ const kmp_ph_t *ph = gl->ph;
+ DASSERT(ph);
+ DASSERT( ph->sect_ph == KMP_ENPH || ph->sect_ph == KMP_ITPH );
+ DASSERT( ph->sect_pt == KMP_ENPT || ph->sect_pt == KMP_ITPT );
+
+ if ( cls == KCLS_OFF )
+ return -1;
+
+ int i, found = -1;
+ kmp_group_t *grp;
+ double found_distance = 0.0;
+
+ List_t *list = kmp->dlist + ph->sect_ph;
+ kmp_enph_entry_t *ph_list = (kmp_enph_entry_t*)(list->list);
+
+ for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ )
+ {
+ const kmp_gopt2_t *go2 = ph->gopt + i;
+ if ( go2->rtype_active != KMP_RT_DISPATCH )
+ continue;
+
+ kmp_class_mode dcls = go2->prev.rclass;
+ if ( dcls == KCLS_OFF || cls != dcls && cls != KCLS_ANY && dcls != KCLS_ANY )
+ continue;
+
+ noPRINT("--- CANDIDATE %2u: rtype=%d>%d, class=%d,%d|%d,%d\n",
+ i,
+ go2->rtype_user, go2->rtype_active,
+ go2->prev.rclass, go2->prev.lclass,
+ go2->next.rclass, go2->next.lclass );
+
+ const kmp_enpt_entry_t *pt
+ = (kmp_enpt_entry_t*)kmp->dlist[ph->sect_pt].list
+ + ph_list[i].pt_start;
+
+ const double dx = pt->position[0] - base[0];
+ const double dy = pt->position[1] - base[1];
+ const double dz = pt->position[2] - base[2];
+ const double distance = dx*dx + dy*dy + dz*dz;
+
+ if ( found < 0 || distance < found_distance )
+ {
+ found = i;
+ found_distance = distance;
+ }
+ }
+
+ noPRINT("SearchNearestDP - return %d\n",found);
+ return found;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void TermGL
+(
+ kmp_group_list_t * gl, // group list
+ ScanInfo_t * si, // not NULL: print error message
+ kmp_t * kmp, // KMP data structure
+ uint sect_ph, // PH section
+ int rotate_links // >0: rotate links um x positions
+)
+{
+ DASSERT(gl);
+ DASSERT(gl->ph);
+ DASSERT(kmp);
+ DASSERT( sect_ph == KMP_CKPH || sect_ph == KMP_ENPH || sect_ph == KMP_ITPH );
+
+ CheckLevelSI(si);
+ ResetRouteLinksKMP(kmp);
+ const bool auto_prev = gl->ph->ac_mode == KMP_AC_PREV;
+ memset(gl->ph->gname,0,sizeof(gl->ph->gname));
+
+ List_t *list = kmp->dlist + sect_ph;
+ list->used = 0;
+
+ uint i;
+ kmp_group_t *grp = gl->group;
+ for ( i = 0; i < gl->used; i++, grp++ )
+ {
+ kmp_enph_entry_t * entry = AppendList(list);
+ entry->pt_start = grp->first_index;
+ entry->pt_len = grp->last_index - grp->first_index + 1;
+
+ memset(entry->next,-1,sizeof(entry->next));
+ memset(entry->prev,-1,sizeof(entry->prev));
+ if ( i < KMP_MAX_GROUP )
+ memcpy(gl->ph->gname+i,grp->name,sizeof(*gl->ph->gname));
+
+ kmp_rtype_mode rtype = gl->ph->gopt[i].rtype_user;
+ if ( rtype == KMP_RT_AUTO )
+ rtype = IsDispatchPointKMP(entry) ? KMP_RT_DISPATCH : KMP_RT_ROUTE;
+ gl->ph->gopt[i].rtype_active = rtype;
+ }
+
+ rotate_links = rotate_links <= 0 ? 0 : rotate_links % KMP_MAX_PH_LINK;
+ PRINT_IF(rotate_links,"ROTATE-LINKS %d\n",rotate_links);
+
+ for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ )
+ {
+ //--- NEXT links
+
+ uint l;
+ for ( l = 0; l < KMP_MAX_PH_LINK; l++ )
+ {
+ const uint l2 = grp->no_rotate
+ ? l
+ : ( l + rotate_links ) % KMP_MAX_PH_LINK;
+ kmp_group_elem_t *elem = grp->next+l2;
+ ccp lname = elem->name;
+ if (!*lname)
+ continue;
+
+ noPRINT(">> '%s' links to '%s'\n",grp->name,lname);
+ kmp_group_t *grp2 = gl->group;
+ bool found = false;
+ uint j;
+ for ( j = 0; j < gl->used; j++, grp2++ )
+ {
+ if (!strcmp(lname,grp2->name))
+ {
+ found = true;
+
+ kmp_enph_entry_t * entry;
+ entry = (kmp_enph_entry_t*)(list->list) + i;
+ InsertLinkPH(entry->next,j,si,lname,true);
+ if (elem->oneway)
+ InsertOnewayPH(gl->ph,i,j);
+
+ if ( auto_prev && !FindOnewayPH(gl->ph,i,j) )
+ {
+ entry = (kmp_enph_entry_t*)(list->list) + j;
+ InsertLinkPH(entry->prev,i,si,lname,false);
+ }
+ break;
+ }
+ }
+
+ if ( !found && !si->no_warn )
+ ERROR0(ERR_WARNING,
+ "%s group %s: $GROUP name not found: %s",
+ kmp_section_name[gl->ph->sect_pt].name1, grp->name, lname);
+ }
+
+ //--- PREV links
+
+ if (!auto_prev)
+ {
+ for ( l = 0; l < KMP_MAX_PH_LINK; l++ )
+ {
+ kmp_group_elem_t *elem = grp->prev+l;
+ ccp lname = elem->name;
+ if (!*lname)
+ continue;
+
+ noPRINT(">> '%s' links to '%s'\n",grp->name,lname);
+ kmp_group_t *grp2 = gl->group;
+ bool found = false;
+ uint j;
+ for ( j = 0; j < gl->used; j++, grp2++ )
+ {
+ if (!strcmp(lname,grp2->name))
+ {
+ found = true;
+
+ kmp_enph_entry_t * entry;
+ entry = (kmp_enph_entry_t*)(list->list) + i;
+ InsertLinkPH(entry->prev,j,si,lname,true);
+ if (elem->oneway)
+ InsertOnewayPH(gl->ph,i,j);
+ break;
+ }
+ }
+
+ if ( !found && !si->no_warn )
+ ERROR0(ERR_WARNING,
+ "%s group %s: $PREV name not found: %s",
+ kmp_section_name[gl->ph->sect_pt].name1, grp->name, lname);
+ }
+ }
+ }
+
+
+ //--- @AUTO-CONNECT == AC$DISPATCH
+
+ kmp_ph_t *ph = gl->ph;
+ DASSERT(ph);
+
+ if ( ph && ph->ac_mode == KMP_AC_DISPATCH
+ && ( sect_ph == KMP_ENPH || sect_ph == KMP_ITPH ))
+ {
+
+ //--- standard routes
+
+ const kmp_linfo_t *li = SetupRouteLinksKMP(kmp,sect_ph,true);
+
+ for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ )
+ {
+ const kmp_gopt2_t *go2 = ph->gopt + i;
+ if ( go2->rtype_active == KMP_RT_DISPATCH )
+ continue;
+
+ PRINT(">>> STDROUTE %2u: %d %s, rtype=%d>%d, oneway=%d, class=%d,%d|%d,%d\n",
+ i, li->gmode[i] & LINFOG_DISPATCH,
+ PrintLinkInfoMode(li->summary[i],1,true),
+ go2->rtype_user, go2->rtype_active,
+ go2->ac_oneway,
+ go2->prev.rclass, go2->prev.lclass,
+ go2->next.rclass, go2->next.lclass );
+
+ kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i;
+ const kmp_linfo_mode mode = li->summary[i];
+
+ if ( !(mode & LINFO_G_PREV) && go2->prev.lclass != KCLS_OFF )
+ {
+ noPRINT("-> SEARCH PREV, CLASS %d\n",go2->prev.lclass);
+ const kmp_enpt_entry_t *pt
+ = (kmp_enpt_entry_t*)kmp->dlist[li->sect_pt].list
+ + entry->pt_start;
+ const int dp = SearchNearestDP(gl,kmp,go2->prev.lclass,pt->position);
+ if ( dp >= 0 )
+ {
+ kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i;
+ InsertLinkPH(entry->prev,dp,0,0,false);
+ if ( go2->ac_oneway & ONEWAY_PREV )
+ InsertOnewayPH(ph,i,dp);
+ }
+ }
+
+ if ( !(mode & LINFO_G_NEXT) && go2->next.lclass != KCLS_OFF )
+ {
+ noPRINT("-> SEARCH NEXT, CLASS %d\n",go2->prev.lclass);
+ const kmp_enpt_entry_t *pt
+ = (kmp_enpt_entry_t*)kmp->dlist[li->sect_pt].list
+ + entry->pt_start + entry->pt_len - 1;
+ const int dp = SearchNearestDP(gl,kmp,go2->prev.lclass,pt->position);
+ if ( dp >= 0 )
+ {
+ kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i;
+ InsertLinkPH(entry->next,dp,0,0,false);
+ if ( go2->ac_oneway & ONEWAY_NEXT )
+ InsertOnewayPH(ph,i,dp);
+ }
+ }
+ }
+
+
+ //--- dispatch points
+
+ li = SetupRouteLinksKMP(kmp,sect_ph,true);
+
+ for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ )
+ {
+ const kmp_gopt2_t *go2 = ph->gopt + i;
+ if ( go2->rtype_active != KMP_RT_DISPATCH || go2->next.lclass == KCLS_OFF )
+ continue;
+
+ PRINT(">>> DISPATCH %2u: %d %s, rtype=%d>%d, oneway=%d, class=%d,%d|%d,%d\n",
+ i, li->gmode[i] & LINFOG_DISPATCH,
+ PrintLinkInfoMode(li->summary[i],1,true),
+ go2->rtype_user, go2->rtype_active,
+ go2->ac_oneway,
+ go2->prev.rclass, go2->prev.lclass,
+ go2->next.rclass, go2->next.lclass );
+
+ const kmp_linfo_mode mode = li->summary[i];
+ if ( mode & (LINFO_G_PREV|LINFO_G_NEXT) )
+ continue; // mode already set
+
+ kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i;
+ const kmp_linfo_mode *l = li->list[i];
+
+ uint j;
+ for ( j = 0; j < li->used; j++, l++ )
+ if ( *l & (LINFO_G_BY_PREV|LINFO_G_BY_NEXT) && !FindOnewayPH(ph,j,i) )
+ InsertLinkPH(entry->next,j,0,0,false);
+ }
+ }
+
+
+ //--- fix empty link lists
+
+ if ( ph->ac_flags & KMP_ACF_FIX )
+ {
+ const int fix_prev = ph->ac_flags & KMP_ACF_FIX_PREV;
+ const int fix_next = ph->ac_flags & KMP_ACF_FIX_NEXT;
+ kmp_enph_entry_t *entry = (kmp_enph_entry_t*)list->list;
+
+ for ( i = 0; i < gl->used; i++, entry++ )
+ {
+ if ( fix_prev && entry->prev[0] == M1(entry->prev[0]) )
+ entry->prev[0] = i;
+ if ( fix_next && entry->next[0] == M1(entry->next[0]) )
+ entry->next[0] = i;
+ }
+ }
+
+ ResetRouteLinksKMP(kmp); // invalidate link-info because modified
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// more kmp functions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int get_next
+(
+ 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
+ int user_id, // user defined id
+ int * cur_idx // not NULL: store current index here
+)
+{
+ DASSERT(res);
+ DASSERT(param);
+ DASSERT(n_param>=1);
+ DASSERT( user_id == KMP_ENPT || user_id == KMP_ITPT );
+
+ // [[2do]] use GetNextPointKMP()
+
+ if (cur_idx)
+ *cur_idx = -1;
+
+ int idx = GetIntV(param);
+ res->i = 0;
+ res->mode = VAR_UNSET;
+
+ if ( si && si->kmp )
+ {
+ const kmp_t *kmp = si->kmp;
+
+ const List_t *ptlist, *phlist;
+ if ( user_id == KMP_ENPT )
+ {
+ ptlist = kmp->dlist + KMP_ENPT;
+ phlist = kmp->dlist + KMP_ENPH;
+ }
+ else
+ {
+ ptlist = kmp->dlist + KMP_ITPT;
+ phlist = kmp->dlist + KMP_ITPH;
+ }
+
+ if ( idx < 0 )
+ idx += ptlist->used;
+
+ if ( idx >= 0 && idx < ptlist->used )
+ {
+ if (cur_idx)
+ *cur_idx = idx;
+
+ const kmp_enph_entry_t *ph = (kmp_enph_entry_t*)phlist->list;
+ uint n = phlist->used;
+ while ( idx >= ph->pt_start + ph->pt_len && n-- > 0 )
+ ph++;
+
+ noPRINT(">> %u %zu[%u+%u]\n", idx,
+ ph-(kmp_enph_entry_t*)phlist->list,ph->pt_start,ph->pt_len);
+ if ( idx == ph->pt_start + ph->pt_len - 1 )
+ {
+ idx = -1; // invalidate
+ uint ph_idx = ~0;
+
+ if ( n_param > 1 )
+ {
+ uint idx2 = GetIntV(param+1);
+ if ( idx2 < KMP_MAX_PH_LINK )
+ ph_idx = ph->next[idx2];
+ }
+ else
+ {
+ uint idx2;
+ for ( idx2 = 0; idx2 < KMP_MAX_PH_LINK; idx2++ )
+ {
+ if (!IS_M1(ph->next[idx2]))
+ {
+ ph_idx = ph->next[idx2];
+ break;
+ }
+ }
+ }
+
+ if ( ph_idx < phlist->used )
+ {
+ ph = (kmp_enph_entry_t*)phlist->list + ph_idx;
+ idx = ph->pt_start;
+ }
+ }
+ else
+ idx++;
+
+ if ( idx >= 0 && idx < ptlist->used )
+ return idx;
+ }
+ }
+ return -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError F_get_next
+(
+ 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);
+ DASSERT(n_param>=1);
+ DASSERT(fpar);
+ DASSERT( fpar->user_id == KMP_ENPT || fpar->user_id == KMP_ITPT );
+
+ int next = get_next(res,param,n_param,si,fpar->user_id,0);
+ if ( next >= 0 )
+ {
+ res->i = next;
+ res->mode = VAR_INT;
+ }
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError F_get_hdir
+(
+ 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);
+ DASSERT(n_param>=1);
+ DASSERT(fpar);
+ DASSERT( fpar->user_id == KMP_ENPT || fpar->user_id == KMP_ITPT );
+
+ int current;
+ int next = get_next(res,param,n_param,si,fpar->user_id,&current);
+ if ( next >= 0 )
+ {
+ DASSERT(si);
+ const kmp_t *kmp = si->kmp;
+ DASSERT(kmp);
+
+ const kmp_enpt_entry_t *pt = (kmp_enpt_entry_t*)kmp->dlist[fpar->user_id].list;
+ res->d = atan2( pt[next].position[0] - pt[current].position[0],
+ pt[next].position[2] - pt[current].position[2] ) * (180/M_PI);
+ res->mode = VAR_DOUBLE;
+ }
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError F_isKMP
+(
+ 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->kmp )
+ {
+ result = 1;
+
+ if (si->kmp->fname)
+ {
+ ccp fname = strrchr(si->kmp->fname,'/');
+ if (fname)
+ fname++;
+ else
+ fname = si->kmp->fname;
+
+ if ( !strcasecmp(fname,"course.kmp")
+ || !strcasecmp(fname,"course.txt")
+ || !strcasecmp(fname,"course.txt.kmp") )
+ {
+ result = 2;
+ }
+ }
+ }
+
+ res->i = result;
+ res->mode = VAR_INT;
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const struct FuncTable_t kmp_func_tab[] =
+{
+ { 1, 2, "ENPT$NEXT", F_get_next, KMP_ENPT,
+ "int", "enpt$next(enpt_index[,next_index])",
+ "Returns the index of the next enemy point."
+ " The 'next_index' is only used, if multiple links are available." },
+
+ { 1, 2, "ITPT$NEXT", F_get_next, KMP_ITPT,
+ "int", "itpt$next(itpt_index[,next_index])",
+ "Returns the index of the next item point."
+ " The 'next_index' is only used, if multiple links are available." },
+
+ { 1, 2, "ENPT$HDIR", F_get_hdir, KMP_ENPT,
+ "float", "enpt$hDir(enpt_index[,next_index])",
+ "Calculate the horizontal direction in degree of an enemy point."
+ " The 'next_index' is only used, if multiple links are available." },
+
+ { 1, 2, "ITPT$HDIR", F_get_hdir, KMP_ITPT,
+ "float", "itpt$hDir(itpt_index[,next_index])",
+ "Calculate the horizontal direction in degree of an item point."
+ " The 'next_index' is only used, if multiple links are available." },
+
+ { 0, 0, "ISKMP", F_isKMP, 0, // replace only function call
+ "int", "isKMP()", 0 }, // but not info
+
+ {0,0,0,0,0,0,0,0}
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// setup kmp parser ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const VarMap_t * SetupVarsKMP()
+{
+ static VarMap_t vm = {0};
+ if (!vm.used)
+ {
+ //--- setup parser function get()
+
+ uint i;
+ const kmp_func_t *fp = kmp_func_list;
+ for ( i = 0; i < KMP_FUNC_LIST_N; i++, fp++ )
+ {
+ char syntax[80];
+ if ( fp->get_info)
+ {
+ DefineParserFunc(fp->name,fp->n_param,fp->n_param,FF_KMP,F_get,i);
+
+ ccp retval = fp->get_mode == GTMD_DBL
+ ? "float"
+ : fp->get_mode > GTMD_DBL
+ ? "vector"
+ : "int";
+ char *dest = syntax;
+ ccp src = fp->name;
+ while (*src)
+ *dest++ = tolower((int)*src++);
+ *dest++ = '(';
+ if ( fp->n_param > 0 )
+ {
+ src = fp->sect == KMP_POTI ? "route" : kmp_section_name[fp->sect].name1;
+ while (*src)
+ *dest++ = tolower((int)*src++);
+
+ switch(fp->n_param)
+ {
+ case 1:
+ src = "_idx";
+ break;
+
+ case 2:
+ src = fp->sect == KMP_POTI && fp->off >= POTI_PT_OFF
+ ? "_idx,pt_idx"
+ : "_idx,val_idx";
+ break;
+
+ case 3:
+ src = "_idx,pt_idx,val_idx";
+ break;
+
+ };
+ while (*src)
+ *dest++ = *src++;
+ }
+ *dest++ = ')';
+ *dest = 0;
+ DASSERT(dest < syntax + sizeof(syntax) );
+
+ DefineParserFuncInfo(retval,syntax,fp->get_info);
+ }
+
+ #ifdef TEST // [[2do]]
+ if (fp->set_info)
+ {
+ char namebuf[40];
+ StringCat2S(namebuf,sizeof(namebuf),"SET_",fp->name);
+ DefineParserFunc(namebuf,fp->n_param+1,fp->n_param+1,FF_KMP,F_set,i);
+
+ char *dest = syntax;
+ ccp src = namebuf;
+ while (*src)
+ *dest++ = tolower((int)*src++);
+ *dest++ = '(';
+ if ( fp->n_param > 0 )
+ {
+ src = fp->sect == KMP_POTI ? "route" : kmp_section_name[fp->sect].name1;
+ while (*src)
+ *dest++ = tolower((int)*src++);
+
+ switch(fp->n_param)
+ {
+ case 1:
+ src = "_idx";
+ break;
+
+ case 2:
+ src = fp->sect == KMP_POTI && fp->off >= POTI_PT_OFF
+ ? "_idx,pt_idx"
+ : "_idx,val_idx";
+ break;
+
+ case 3:
+ src = "_idx,pt_idx,val_idx";
+ break;
+
+ };
+ while (*src)
+ *dest++ = *src++;
+ }
+ DASSERT(dest < syntax + sizeof(syntax) );
+ StringCopyE(dest,syntax+sizeof(syntax),",value)");
+ DefineParserFuncInfo("int",syntax,fp->set_info);
+ }
+ #endif
+ }
+
+
+ //--- setup additional functions
+
+ DefineParserFuncTab(kmp_func_tab,FF_KMP);
+ SetupReferenceKCL(&vm);
+
+
+ //--- setup object names
+
+ DefineObjectNameVars(&vm);
+
+
+ //--- setup KMP section names
+
+ char varname[50];
+ const KeywordTab_t *key;
+ for ( key = kmp_section_name; key->name2; key++ )
+ {
+ snprintf(varname,sizeof(varname),"SECT$%s",key->name1);
+ DefineIntVar(&vm,varname,key->id+1);
+ }
+
+
+ //--- setup integer variables
+
+ struct inttab_t { ccp name; int val; };
+ static const struct inttab_t inttab[] =
+ {
+ { "MODE$SLOT", 0 },
+ { "MODE$D", 0 },
+
+ { "MODE$TEST", 0 },
+ { "MODE$OCAM", 0 },
+ { "MODE$LAPS", 3 },
+ { "MODE$SHOWRT", 0 },
+ { "MODE$SHOWCK", 0 },
+ { "MODE$AUTOMODE", 0 },
+
+ { "AC$OFF", KMP_AC_OFF },
+ { "AC$PREV", KMP_AC_PREV },
+ { "AC$DISPATCH", KMP_AC_DISPATCH },
+ { "ACF$FIX_PREV", KMP_ACF_FIX_PREV },
+ { "ACF$FIX_NEXT", KMP_ACF_FIX_NEXT },
+ { "ACF$FIX", KMP_ACF_FIX },
+
+ { "AM$OFF", KMP_AM_OFF },
+ { "AM$NORM", KMP_AM_NORM },
+ { "AM$RENUMBER", KMP_AM_RENUMBER },
+ { "AM$1LAP", KMP_AM_1_LAP },
+ { "AM$SHORT", KMP_AM_SHORT },
+ { "AM$UNLIMIT", KMP_AM_UNLIMIT },
+
+ { "SORT$OFF", KMP_SORT_OFF },
+ { "SORT$GROUPING", KMP_SORT_GROUPING },
+ { "SORT$ANGLE", KMP_SORT_ANGLE },
+ { "SORT$TINY", KMP_SORT_TINY },
+
+ { "DOB$OFF", KMP_DOB_OFF },
+ { "DOB$BEGIN", KMP_DOB_BEGIN },
+ { "DOB$END", KMP_DOB_END },
+ { "DOB$BEFORE", KMP_DOB_BEFORE },
+ { "DOB$BEHIND", KMP_DOB_BEHIND },
+
+ { "EN$BATTLE", ENGM_BATTLE },
+ { "EN$50", ENGM_50 },
+ { "EN$100", ENGM_100 },
+ { "EN$150", ENGM_150 },
+ { "EN$200", ENGM_200 },
+ { "EN$150M", ENGM_150M },
+ { "EN$200M", ENGM_200M },
+ { "EN$150X", ENGM_150M | ENGM_150 },
+ { "EN$200X", ENGM_200M | ENGM_200 },
+ { "EN$MIRROR", ENGM_150M | ENGM_200M },
+ { "EN$ALL", ENGM__ALL },
+
+ { "F$FALL", KPFL_FALL },
+ { "F$SNAP", KPFL_SNAP },
+ { "F$JGPT", KPFL_JGPT },
+
+ { "ITEM$RANDOM", 0x00 },
+ { "ITEM$BANANA", 0x01 },
+ { "ITEM$MUSHROOM", 0x02 },
+ { "ITEM$3MUSHROOMS", 0x03 },
+ { "ITEM$STAR", 0x04 },
+ { "ITEM$3GREEN", 0x05 },
+ { "ITEM$BANANA_MUSHROOM", 0x06 },
+ { "ITEM$GREEN", 0x07 },
+ { "ITEM$RED", 0x08 },
+ { "ITEM$MEGA", 0x0a },
+ { "ITEM$CLOUD", 0x0b },
+ { "ITEM$STAR_MUSHROOM", 0x0c },
+ { "ITEM$MUSHROOM_GREEN", 0x0d },
+ { "ITEM$3BANANAS_GREEN", 0x24 },
+
+ { "PF$OFFLINE1", 0x01 },
+ { "PF$OFFLINE2", 0x02 },
+ { "PF$OFFLINE3", 0x04 },
+ { "PF$OFFLINE", 0x07 },
+ { "PF$ONLINE1", 0x08 },
+ { "PF$ONLINE2", 0x10 },
+ { "PF$ONLINE3", 0x20 },
+ { "PF$ONLINE", 0x38 },
+ { "PF$ALL", 0x3f },
+
+ {0,0}
+ };
+
+ const struct inttab_t * ip;
+ for ( ip = inttab; ip->name; ip++ )
+ DefineIntVar(&vm,ip->name,ip->val);
+
+
+ //--- extended p-flags
+
+ char name[20];
+ const gobj_cond_ref_t *rc;
+ for ( rc = cond_ref_tab; rc->ref_id; rc++ )
+ {
+ ccp prefix = rc->option & 4 ? "IFDEV$" : "IF$";
+ StringCat2S(name,sizeof(name),prefix,rc->name);
+ DefineIntVar(&vm,name,rc->ref_id);
+
+ if (rc->alias[0])
+ {
+ StringCat2S(name,sizeof(name),prefix,rc->alias);
+ DefineIntVar(&vm,name,rc->ref_id);
+ }
+
+ if ( rc->cond_mask && !(rc->option & 4) )
+ {
+ StringCat2S(name,sizeof(name),"C$",rc->name);
+ DefineIntVar(&vm,name,rc->cond_mask);
+
+ if (rc->alias[0])
+ {
+ StringCat2S(name,sizeof(name),"C$",rc->alias);
+ DefineIntVar(&vm,name,rc->cond_mask);
+ }
+ }
+ }
+
+
+ //--- setup basic parser variables
+
+ DefineParserVars(&vm);
+ }
+
+ return &vm;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// kcl fall helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[kcl_fall_t]]
+
+typedef struct kcl_fall_t
+{
+ //--- input
+
+ kcl_t *fall_kcl; // KCL to use, never NULL if 'kf->valid'
+
+ int width; // input for "KCL-FALL-WIDTH"
+ Var_t pre_add; // input for "KCL-FALL-PRE-ADD"
+ Var_t post_add; // input for "KCL-FALL-POST-ADD"
+ u32 types; // input for "KCL-FALL-TYPES"
+
+ //---- status
+
+ bool valid; // true: calculation can be done
+
+} kcl_fall_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define KCL_FALL_VARS(k,v) \
+ { "KCL-FALL-ADD-ROAD", SPM_BOOL, &k->enable_fall_kcl }, \
+ { "KCL-FALL-WIDTH", SPM_INT, &v.width }, \
+ { "KCL-FALL-PRE-ADD", SPM_VAR, &v.pre_add }, \
+ { "KCL-FALL-POST-ADD", SPM_VAR, &v.post_add }, \
+ { "KCL-FALL-TYPES", SPM_U32, &v.types },
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetKclFall ( kcl_fall_t *kf )
+{
+ DASSERT(kf);
+ memset(kf,0,sizeof(*kf));
+ kf->types = M1(kf->types);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool CheckKclFall ( kcl_fall_t *kf )
+{
+ DASSERT(kf);
+
+ kf->fall_kcl = 0;
+ kf->valid = kf->width > 0 && kf->types;
+ if (kf->valid)
+ {
+ ToVectorYV(&kf->pre_add);
+ ToVectorYV(&kf->post_add);
+
+ kf->fall_kcl = GetReferenceKCL();
+ if (!kf->fall_kcl)
+ kf->valid = false;
+ }
+ return kf->valid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CalcKclFall ( kcl_fall_t *kf, float *vector )
+{
+ DASSERT(kf);
+ if (kf->valid)
+ {
+ DASSERT(kcl_ref);
+ DASSERT(kf->fall_kcl);
+
+ double3 pt;
+ pt.x = vector[0] + kf->pre_add.x;
+ pt.y = vector[1] + kf->pre_add.y;
+ pt.z = vector[2] + kf->pre_add.z;
+ double temp = FallKCL(kf->fall_kcl,0,pt,kf->width,0,kf->types,0);
+ if ( temp >= 0.0 && temp < pt.y )
+ {
+ vector[0] = kf->post_add.x + pt.x;
+ vector[1] = kf->post_add.y + temp;
+ vector[2] = kf->post_add.z + pt.z;
+ }
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// route object helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[rtobj_info_t]]
+
+typedef struct rtobj_info_t
+{
+ //--- input
+
+ int route_object; // input for "ROUTE-OBJECT"
+ Var_t route_shift; // input for "ROUTE-OBJECT-SHIFT"
+ Var_t route_scale; // input for "ROUTE-OBJECT-SCALE"
+ Var_t route_rotate; // input for "ROUTE-OBJECT-ROTATE"
+ int show_route; // input for "SHOW-ROUTE"
+ int route_itembox; // input for "ROUTE-ITEMBOX"
+
+ //--- data
+
+ kmp_rtobj_t *def_obj; // default object
+ kmp_rtobj_t rtobj; // resulting data
+ bool valid; // true: rtobj is set
+
+} rtobj_info_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ROUTE_OBJECT_VARS(v) \
+ { "ROUTE-OBJECT", SPM_INT, &v.route_object }, \
+ { "ROUTE-OBJECT-SHIFT", SPM_VAR, &v.route_shift }, \
+ { "ROUTE-OBJECT-SCALE", SPM_VAR, &v.route_scale }, \
+ { "ROUTE-OBJECT-ROTATE",SPM_VAR, &v.route_rotate }, \
+ { "SHOW-ROUTE", SPM_INT, &v.show_route }, \
+ { "ROUTE-ITEMBOX", SPM_INT, &v.route_itembox },
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetRouteInfo ( rtobj_info_t *ri, kmp_rtobj_t *def_obj )
+{
+ DASSERT(ri);
+ DASSERT(def_obj);
+
+ memset(ri,0,sizeof(*ri));
+ ri->def_obj = def_obj;
+ memcpy(&ri->rtobj,ri->def_obj,sizeof(ri->rtobj));
+ ri->route_object = N_KMP_GOBJ;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool CheckRouteInfo ( rtobj_info_t *ri )
+{
+ DASSERT(ri);
+ DASSERT(ri->def_obj);
+ noPRINT_IF( ri->route_object != N_KMP_GOBJ,
+ ">> CheckRouteInfo() obj=%d, shrt=%d\n",
+ ri->route_object, ri->show_route );
+
+ if (!ri->route_object)
+ ri->rtobj.obj = 0;
+ else if ( ri->route_object < 0 )
+ {
+ memcpy(&ri->rtobj,ri->def_obj,sizeof(ri->rtobj));
+ ri->show_route = 1;
+ }
+ else if ( ri->route_object < N_KMP_GOBJ )
+ {
+ ri->rtobj.obj = ri->route_object;
+ ri->show_route = 1;
+ }
+ ri->route_object = N_KMP_GOBJ;
+
+ if ( ri->route_shift.mode != VAR_UNSET )
+ {
+ ri->rtobj.shift = GetVectorFV(&ri->route_shift);
+ ri->route_shift.mode = VAR_UNSET;
+ }
+
+ if ( ri->route_scale.mode != VAR_UNSET )
+ {
+ ri->rtobj.scale = GetVectorFV(&ri->route_scale);
+ ri->route_scale.mode = VAR_UNSET;
+ }
+
+ if ( ri->route_rotate.mode != VAR_UNSET )
+ {
+ ri->rtobj.rotate = GetVectorFV(&ri->route_rotate);
+ ri->route_rotate.mode = VAR_UNSET;
+ }
+
+ if ( ri->show_route > 0 && !ri->rtobj.obj )
+ ri->rtobj.obj = ri->def_obj->obj;
+
+ if ( ri->route_itembox > 0 && !ri->valid )
+ {
+ ri->show_route = 1;
+ ri->rtobj.obj = GOBJ_ITEMBOX;
+ }
+
+ ri->valid = ri->show_route > 0 && ri->rtobj.obj;
+ return ri->valid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool AssignRouteInfo ( rtobj_info_t *ri, kmp_rtobj_t *dest )
+{
+ DASSERT(ri);
+ DASSERT(dest);
+ if (ri->valid)
+ memcpy(dest,&ri->rtobj,sizeof(*dest));
+ else
+ memset(dest,0,sizeof(*dest));
+
+ return ri->valid;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// transform pos helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[tform_info_t]]
+
+typedef struct tform_info_t
+{
+ //--- input
+
+ int reset; // input for "TFORM-RESET"
+ Var_t scale; // input for "TFORM-SCALE"
+ Var_t shift; // input for "TFORM-SHIFT"
+ Var_t rotate; // input for "TFORM-ROTATE"
+ Var_t translate; // input for "TFORM-TRANSLATE"
+
+
+ //--- data
+
+ bool active; // transformation is active
+ MatrixD_t matrix; // transformation matrix;
+
+} tform_info_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define TFORM_VARS(v) \
+ { "TFORM-RESET", SPM_INC, &v.reset }, \
+ { "TFORM-SCALE", SPM_VAR, &v.scale }, \
+ { "TFORM-SHIFT", SPM_VAR, &v.shift }, \
+ { "TFORM-ROTATE", SPM_VAR, &v.rotate }, \
+ { "TFORM-TRANSLATE", SPM_VAR, &v.translate }, \
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetTformInfo ( tform_info_t *ti )
+{
+ DASSERT(ti);
+
+ memset(ti,0,sizeof(*ti));
+ InitializeMatrixD(&ti->matrix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool CheckTformInfo ( tform_info_t *ti )
+{
+ DASSERT(ti);
+
+ if (ti->reset)
+ {
+ PRINT("#TFORM: RESET(%d)\n",ti->reset);
+ ti->reset = 0;
+ ti->active = false;
+ InitializeMatrixD(&ti->matrix);
+ }
+
+ if ( ti->scale.mode != VAR_UNSET )
+ {
+ double3 d = GetVectorV(&ti->scale);
+ PRINT("#TFORM: SCALE %g %g %g\n",d.x,d.y,d.z);
+ SetScaleMatrixD(&ti->matrix,&d,0);
+ ti->scale.mode = VAR_UNSET;
+ ti->active = true;
+ }
+
+ if ( ti->shift.mode != VAR_UNSET )
+ {
+ double3 d = GetVectorV(&ti->shift);
+ PRINT("#TFORM: SHIFT %g %g %g\n",d.x,d.y,d.z);
+ SetShiftMatrixD(&ti->matrix,&d);
+ ti->shift.mode = VAR_UNSET;
+ ti->active = true;
+ }
+
+ if ( ti->rotate.mode != VAR_UNSET )
+ {
+ double3 d = GetVectorV(&ti->rotate);
+ PRINT("#TFORM: ROTATE %g %g %g\n",d.x,d.y,d.z);
+ SetRotateMatrixD(&ti->matrix,0,d.x,0.0,0);
+ SetRotateMatrixD(&ti->matrix,1,d.y,0.0,0);
+ SetRotateMatrixD(&ti->matrix,2,d.z,0.0,0);
+ ti->rotate.mode = VAR_UNSET;
+ ti->active = true;
+ }
+
+ if ( ti->translate.mode != VAR_UNSET )
+ {
+ double3 d = GetVectorV(&ti->translate);
+ PRINT("#TFORM: TRANSLATE %g %g %g\n",d.x,d.y,d.z);
+ SetTranslateMatrixD(&ti->matrix,&d);
+ ti->translate.mode = VAR_UNSET;
+ ti->active = true;
+ }
+
+ return ti->active;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformPos ( tform_info_t *ti, float32 *pos )
+{
+ DASSERT(ti);
+ DASSERT(pos);
+
+ if (ti->active)
+ *(float3*)pos = TransformF3MatrixD(&ti->matrix,(float3*)pos);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextKMP() sub functions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static kmp_pt_flag_t ScanPtFlags ( ScanInfo_t *si )
+{
+ DASSERT(si);
+
+ if ( NextCharSI(si,false) == '(' )
+ {
+ si->cur_file->ptr++;
+ DEFINE_VAR(val);
+ CheckWarnSI(si,')',ScanExprSI(si,&val));
+ return GetIntV(&val);
+ }
+
+ char fbuf[20];
+ ScanNameSI(si,fbuf,sizeof(fbuf),true,true,0);
+
+ kmp_pt_flag_t flags = 0;
+ if ( strchr(fbuf,'F') ) flags |= KPFL_FALL;
+ if ( strchr(fbuf,'S') ) flags |= KPFL_SNAP;
+ if ( strchr(fbuf,'J') ) flags |= KPFL_JGPT;
+
+ #if 0
+ if ( si->cur_file->revision < xxxx && !( flags & KPFL_UPDATE_Rxxxx ) )
+ flags |= KPFL_UPDATE_Rxxxx;
+ #endif
+ return flags;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanText_PT_PH
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si, // valid data
+ uint sect_pt // KMP PT section
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ DASSERT( sect_pt == KMP_ENPT || sect_pt == KMP_ITPT );
+
+ uint sect_ph;
+ kmp_ph_t *ph;
+ kmp_flag_t *kf;
+
+ if ( sect_pt == KMP_ENPT )
+ {
+ sect_ph = KMP_ENPH;
+ ph = &kmp->enph;
+ kf = &kmp->enpt_flag;
+ }
+ else
+ {
+ sect_ph = KMP_ITPH;
+ ph = &kmp->itph;
+ kf = &kmp->itpt_flag;
+ }
+
+ ph->default_class = KCLS_DEFAULT;
+
+ ccp pt_name = kmp_section_name[sect_pt].name1;
+ ccp ph_name = kmp_section_name[sect_ph].name1;
+ TRACE("ScanText_PT_PH(%s,%s)\n",pt_name,ph_name);
+
+ char pt_hd_name[20], ph_hd_name[20];
+ snprintf( pt_hd_name, sizeof(pt_hd_name), "%s-HEAD-VALUE", pt_name );
+ snprintf( ph_hd_name, sizeof(ph_hd_name), "%s-HEAD-VALUE", ph_name );
+
+ rtobj_info_t rtinfo;
+ ResetRouteInfo(&rtinfo,&kmp->rtobj);
+
+ tform_info_t tform_pos;
+ ResetTformInfo(&tform_pos);
+
+ kcl_fall_t kfall;
+ ResetKclFall(&kfall);
+
+ int auto_group_name = 0, rotate_group_links = 0;
+ int show_options = 0, export_flags = 0;
+ double auto_fill = 0.0;
+ u8 auto_connect = ph->ac_mode | ph->ac_flags;
+ u16 mask1, mask2;
+ mask1 = mask2 = 0xffff;
+
+ ScanParam_t ptab[] =
+ {
+ { pt_hd_name, SPM_U16, kmp->value+sect_pt },
+ { ph_hd_name, SPM_U16, kmp->value+sect_ph },
+ { "AUTO-GROUP-NAME", SPM_INT, &auto_group_name },
+ { "ROTATE-GROUP-LINKS", SPM_INT, &rotate_group_links },
+ { "EXPORT-FLAGS", SPM_INT, &export_flags },
+ { "AUTO-FILL", SPM_DOUBLE, &auto_fill },
+ { "SHOW-OPTIONS", SPM_INT, &show_options },
+ { "AUTO-CONNECT", SPM_U8, &auto_connect },
+ { "MASK-1", SPM_U16, &mask1 },
+ { "MASK-2", SPM_U16, &mask2 },
+ ROUTE_OBJECT_VARS(rtinfo)
+ TFORM_VARS(tform_pos)
+ KCL_FALL_VARS(kmp,kfall)
+ {0}
+ };
+
+ kmp_group_list_t gl;
+ InitializeGL(&gl,*pt_name,kmp,ph);
+ List_t *ptlist = kmp->dlist + sect_pt;
+ ptlist->used = 0;
+
+ enumError err = ERR_OK;
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ CheckRouteInfo(&rtinfo);
+ CheckTformInfo(&tform_pos);
+ CheckKclFall(&kfall);
+ continue;
+ }
+
+ if ( ch == '$' )
+ {
+ AddGroupGL(&gl,si,ph_name[0],ptlist->used,3);
+ continue;
+ }
+
+ u16 idx;
+ if (ScanU16SI(si,&idx,1,0))
+ {
+ err = ERR_WARNING;
+ break;
+ }
+
+ kmp_enpt_entry_t temp;
+ ScanFloatV3SI(si,temp.position,1);
+ ScanFloatSI(si,&temp.scale,1);
+ if ( si->cur_file->szs_modifier > 0 )
+ ScanBE32SI_swap(si,(u32*)temp.prop,1,1);
+ else
+ ScanU16SI(si,temp.prop,2,0);
+ const u8 flags = NextCharSI(si,false) ? ScanPtFlags(si) : kf->mask;
+ CheckEolSI(si);
+
+ const kmp_enpt_entry_t * prev = GetListElem(ptlist,-1,&temp);
+ if (!ptlist->used) // no previous point available
+ gl.line.n = 1;
+ else if ( auto_fill >= 1.0
+ && gl.line.n == 1
+ && gl.used
+ && ptlist->used != gl.group[gl.used-1].first_index )
+ {
+ double3 dist3;
+ dist3.x = temp.position[0] - prev->position[0];
+ dist3.y = temp.position[1] - prev->position[1];
+ dist3.z = temp.position[2] - prev->position[2];
+ const double dist = Length3(dist3);
+ if ( dist > auto_fill )
+ {
+ gl.line.n = (int)trunc(dist/auto_fill) + 1;
+ PRINT("DIST=%8.2f, FILL=%8.2f => N=%2u\n",dist,auto_fill,gl.line.n);
+ }
+ }
+
+ uint p;
+ for ( p = 1; p <= gl.line.n; p++ )
+ {
+ AddPointGL(&gl,ptlist->used);
+ if ( ptlist->used < KMP_MAX_POINT )
+ kf->flags[ptlist->used] = flags;
+
+ kmp_enpt_entry_t * entry = AppendList(ptlist);
+ memcpy(entry,&temp,sizeof(*entry));
+ CalcLine(&gl.line,entry->position,prev->position,temp.position,4,p);
+ TransformPos(&tform_pos,entry->position);
+ if ( flags & KPFL_FALL )
+ CalcKclFall(&kfall,entry->position);
+ }
+ InitializeLine(&gl.line);
+ }
+
+ TermGL(&gl,si,kmp,sect_ph,rotate_group_links);
+
+ if (gl.have_options)
+ show_options = true;
+
+ #if HAVE_PRINT0
+ {
+ uint i;
+ for ( i = 0; i < gl.used; i++ )
+ PRINT("GROUP %3u -> %s\n",i,ph->gname[i]);
+ }
+ #endif
+
+ if (opt_export_flags)
+ export_flags = opt_export_flags > 0;
+
+ kf->export = export_flags;
+ ph->ac_mode = auto_connect & KMP_AC_MASK;
+ ph->ac_flags = auto_connect & KMP_ACF_MASK;
+ ph->mask[0] = mask1;
+ ph->mask[1] = mask2;
+ if ( opt_route_options == OFFON_AUTO )
+ ph->show_options = show_options;
+
+ if (auto_group_name)
+ RenameGroupKMP( kmp, sect_ph, ph->gname, ph->ac_mode == KMP_AC_DISPATCH);
+
+ if ( sect_pt == KMP_ENPT )
+ AssignRouteInfo(&rtinfo,&kmp->rtobj_enpt);
+ else
+ AssignRouteInfo(&rtinfo,&kmp->rtobj_itpt);
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextKTPT
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextKTPT()\n");
+
+ tform_info_t tform_pos;
+ ResetTformInfo(&tform_pos);
+
+ kcl_fall_t kfall;
+ ResetKclFall(&kfall);
+
+ ScanParam_t ptab[] =
+ {
+ { "KTPT-HEAD-VALUE", SPM_U16, kmp->value+KMP_KTPT },
+ TFORM_VARS(tform_pos)
+ KCL_FALL_VARS(kmp,kfall)
+ {0}
+ };
+
+ List_t *dlist = kmp->dlist + KMP_KTPT;
+ dlist->used = 0;
+
+ #if HAVE_PRINT0
+ si->debug++;
+ #endif
+
+ for(;;)
+ {
+ #if HAVE_PRINT0
+ {
+ ccp eol = FindNextLineFeedSI(si,false);
+ PRINT("##LINE %4u: %.*s\n",si->line,(int)(eol-si->ptr),si->ptr);
+ }
+ #endif
+
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ #if HAVE_PRINT0
+ {
+ ccp eol = FindNextLineFeedSI(si,false);
+ PRINT("##LINE %4u: %.*s\n",si->line,(int)(eol-si->ptr),si->ptr);
+ }
+ #endif
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ CheckTformInfo(&tform_pos);
+ CheckKclFall(&kfall);
+ continue;
+ }
+
+ u16 idx;
+ if (ScanU16SI(si,&idx,1,0))
+ break;
+
+ kmp_ktpt_entry_t * entry = AppendList(dlist);
+ ScanFloatV3SI(si,entry->position,2);
+ ScanU16SI(si,(u16*)&entry->player_index,1,0);
+ ScanU16SI(si,(u16*)&entry->unknown,1,si->cur_file->szs_modifier);
+
+ TransformPos(&tform_pos,entry->position);
+ CalcKclFall(&kfall,entry->position);
+ CheckEolSI(si);
+ }
+ CheckLevelSI(si);
+
+ #if HAVE_PRINT0
+ si->debug--;
+ #endif
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextCKPT
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextCKPT()\n");
+
+ int auto_mode = 0, auto_next = 0, auto_respawn = 0;
+ int auto_group_name = 0, rotate_group_links = 0;
+ memset(kmp->ckpt_auto_respawn,0,sizeof(kmp->ckpt_auto_respawn));
+
+ ScanParam_t ptab[] =
+ {
+ { "AUTO-MODE", SPM_INT, &auto_mode },
+ { "AUTO-NEXT", SPM_INT, &auto_next },
+ { "AUTO-GROUP-NAME", SPM_INT, &auto_group_name },
+ { "AUTO-RESPAWN", SPM_INT, &auto_respawn },
+ { "ROTATE-GROUP-LINKS", SPM_INT, &rotate_group_links },
+ { "ROUTE-OBJECT-MODE", SPM_INT, &kmp->ckpt_object_mode },
+ { "ROUTE-OBJECT1", SPM_INT, &kmp->ckpt_object1 },
+ { "ROUTE-OBJECT2", SPM_INT, &kmp->ckpt_object2 },
+ { "ROUTE-OBJECT1-SCALE", SPM_VAR, &kmp->ckpt_object1_scale },
+ { "ROUTE-OBJECT2-SCALE", SPM_VAR, &kmp->ckpt_object2_scale },
+ { "ROUTE-OBJECT1-BASE", SPM_FLOAT, &kmp->ckpt_object1_base },
+ { "ROUTE-OBJECT2-BASE", SPM_FLOAT, &kmp->ckpt_object2_base },
+ { "CKPT-HEAD-VALUE", SPM_U16, kmp->value+KMP_CKPT },
+ { "CKPH-HEAD-VALUE", SPM_U16, kmp->value+KMP_CKPH },
+ {0}
+ };
+
+ kmp_group_list_t gl;
+ InitializeGL(&gl,0,kmp,&kmp->ckph);
+ List_t *ptlist = kmp->dlist + KMP_CKPT;
+ ptlist->used = 0;
+ uint last_mode = 0;
+ float max_x = 0.0, max_y = 0.0;
+
+ const uint max_n = 0x100;
+ float ftemp[max_n+1][4]; // x,y,unused,width/2
+
+ enumError err = ERR_OK;
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ if ( ch == '$' )
+ {
+ AddGroupGL(&gl,si,'C',ptlist->used,2);
+ continue;
+ }
+
+ u16 idx;
+ if (ScanU16SI(si,&idx,1,0))
+ {
+ err = ERR_WARNING;
+ break;
+ }
+
+ kmp_ckpt_entry_t temp;
+ ScanFloatV2SI(si,temp.left,2);
+
+ if ( auto_respawn > 0 )
+ {
+ si->no_warn++;
+ ScanU8SI(si,&temp.respawn,1,0);
+ si->no_warn--;
+ }
+ else
+ ScanU8SI(si,&temp.respawn,1,0);
+
+ if ( si->cur_file->szs_modifier > 0 )
+ {
+ ScanU8SI(si,&temp.mode,1,1);
+ ScanU8SI(si,&temp.prev,2,0);
+ }
+ else
+ ScanU8SI(si,&temp.mode,3,0);
+ CheckEolSI(si);
+
+ kmp_ckpt_entry_t * prev = GetListElem(ptlist,-1,&temp);
+ if (!ptlist->used) // no previous point available
+ gl.line.n = 1;
+ else if ( gl.line.n > max_n )
+ gl.line.n = max_n;
+
+ float *p1 = 0, *p2 = 0;
+ if ( gl.line.n > 1 )
+ {
+ if ( gl.line.line_mode == LM_BORDER )
+ {
+ p1 = ftemp[0];
+ memcpy(p1,prev->left,4*sizeof(float));
+ p2 = ftemp[gl.line.n];
+ memcpy(p2,temp.left,4*sizeof(float));
+ }
+ else
+ {
+ p1 = ftemp[0];
+ p1[0] = ( prev->left[0] + prev->right[0] ) / 2;
+ p1[1] = ( prev->left[1] + prev->right[1] ) / 2;
+ p1[2] = 0.0;
+ double dx = prev->left[0] - prev->right[0];
+ double dy = prev->left[1] - prev->right[1];
+ p1[3] = sqrt ( dx*dx + dy*dy ) / 2;
+
+ p2 = ftemp[gl.line.n];
+ p2[0] = ( temp.left[0] + temp.right[0] ) / 2;
+ p2[1] = ( temp.left[1] + temp.right[1] ) / 2;
+ p2[2] = 0.0;
+ dx = temp.left[0] - temp.right[0];
+ dy = temp.left[1] - temp.right[1];
+ p2[3] = sqrt ( dx*dx + dy*dy ) / 2;
+
+ if ( gl.line.line_mode == LM_BEZIER && !gl.line.n_pt )
+ {
+ double angle1 = CalcDirection2F(prev->left,prev->right);
+ double angle2 = CalcDirection2F(temp.left,temp.right);
+ PRINT("BINGO 2: %7.2f %7.2f -> %7.2f %7.2f\n",
+ angle1, angle2,
+ fmod(angle1+M_PI,M_PI), fmod(angle2+M_PI,M_PI) );
+
+ double dx = p1[0] - p2[0];
+ double dy = p1[1] - p2[1];
+ double range = sqrt( dx*dx + dy*dy ) * 0.707; // sin(pi/4)
+
+ gl.line.pt[0][0] = p1[0] + cos(angle1) * range;
+ gl.line.pt[0][1] = p1[1] - sin(angle1) * range;
+ gl.line.pt[1][0] = p2[0] - cos(angle2) * range;
+ gl.line.pt[1][1] = p2[1] + sin(angle2) * range;
+ gl.line.n_pt = 2;
+ PRINT("\n-> %11.3f %11.3f\n"
+ " %11.3f %11.3f\n"
+ " %11.3f %11.3f\n"
+ " %11.3f %11.3f\n",
+ p1[0], p1[1],
+ gl.line.pt[0][0], gl.line.pt[0][1],
+ gl.line.pt[1][0], gl.line.pt[1][1],
+ p2[0], p2[1] );
+ }
+ }
+ }
+
+ uint p;
+ for ( p = 1; p <= gl.line.n; p++ )
+ {
+ const uint grp_idx = AddPointGL(&gl,ptlist->used);
+ kmp_ckpt_entry_t * entry = AppendList(ptlist);
+ memcpy(entry,&temp,sizeof(*entry));
+ if ( p < gl.line.n )
+ CalcLine(&gl.line,ftemp[p],p1,p2,4,p);
+
+ switch (auto_mode)
+ {
+ //case KMP_AM_NORM: // normalize modes
+ // --> delayed
+ // break;
+
+ case KMP_AM_RENUMBER: // renumber modes
+ if (!entry->mode)
+ last_mode = 0;
+ else if (!IS_M1(entry->mode))
+ entry->mode = ++last_mode;
+ break;
+
+ case KMP_AM_1_LAP: // define modes as 1 lap race
+ entry->mode = ptlist->used < 7 ? ptlist->used % 2 - 1 : -1;
+ break;
+
+ case KMP_AM_SHORT: // define modes to get a very short race
+ entry->mode = ptlist->used % 2 - 1;
+ break;
+
+ case KMP_AM_UNLIMIT: // remove lap counter for unlimited race
+ {
+ static const u8 tab[] = { 0, 3,0,1,2 };
+ entry->mode = ptlist->used < sizeof(tab)/sizeof(*tab)
+ ? tab[ptlist->used] : -1;
+
+ if ( max_x < entry->left[0] )
+ max_x = entry->left[0];
+ if ( max_x < entry->right[0] )
+ max_x = entry->right[0];
+
+ if ( max_y < entry->left[1] )
+ max_y = entry->left[1];
+ if ( max_y < entry->right[1] )
+ max_y = entry->right[1];
+ }
+ break;
+
+ default:
+ if ( !IS_M1(entry->mode) )
+ last_mode = entry->mode;
+ break;
+ }
+
+ if (auto_next)
+ {
+ entry->next = ~0;
+ entry->prev = grp_idx ? ptlist->used - 2 : ~0;
+ if ( grp_idx > 0 && IS_M1(entry[-1].next) )
+ entry[-1].next = ptlist->used - 1;
+ }
+
+ if ( auto_respawn > 0 )
+ {
+ const uint idx = ptlist->used - 1;
+ if ( idx < KMP_MAX_POINT )
+ kmp->ckpt_auto_respawn[idx] = 1;
+ }
+ }
+
+ if ( gl.line.line_mode == LM_BORDER )
+ {
+ // now copy the calculated values
+ for ( p = 1; p < gl.line.n; p++ )
+ {
+ kmp_ckpt_entry_t * entry = prev + p;
+ memcpy(entry,ftemp[p],4*sizeof(float));
+ }
+ }
+ else
+ {
+ // now calculate angle and real positions
+ for ( p = 1; p < gl.line.n; p++ )
+ {
+ kmp_ckpt_entry_t * entry = prev + p;
+ float (*f)[4] = ftemp + p-1;
+ double angle1 = CalcDirection2F(f[0],f[1]);
+ double angle2 = CalcDirection2F(f[1],f[2]);
+ noPRINT("%2u: %6.0f %6.0f %6.0f | %6.0f %6.0f %6.0f"
+ " | %6.0f %6.0f %6.0f | %7.2f %7.2f\n",
+ p,
+ f[0][0],f[0][1],f[0][3], f[1][0],f[1][1],f[1][3], f[2][0],f[2][1],f[2][3],
+ angle1 * 180.0 / M_PI, angle2 * 180.0 / M_PI );
+
+ angle1 = ( angle1 + angle2 ) / 2;
+ double d = cos(angle1) * f[1][3];
+ entry->left [0] = f[1][0] + d;
+ entry->right[0] = f[1][0] - d;
+ d = sin(angle1) * f[1][3];
+ entry->left [1] = f[1][1] - d;
+ entry->right[1] = f[1][1] + d;
+ }
+ }
+ InitializeLine(&gl.line);
+ }
+
+ if ( auto_mode == KMP_AM_NORM )
+ {
+ uint min_search = 1, next_val = 1;
+ kmp_ckpt_entry_t *pt_end = (kmp_ckpt_entry_t*)ptlist->list + ptlist->used;
+ for(;;)
+ {
+ uint min_found = 0xff;
+ kmp_ckpt_entry_t *pt;
+ for ( pt = (kmp_ckpt_entry_t*)ptlist->list; pt < pt_end; pt++ )
+ if ( pt->mode >= min_search && pt->mode < min_found )
+ min_found = pt->mode;
+
+ if ( min_found == 0xff )
+ break;
+
+ for ( pt = (kmp_ckpt_entry_t*)ptlist->list; pt < pt_end; pt++ )
+ if ( pt->mode == min_found )
+ pt->mode = next_val;
+
+ next_val++;
+ min_search = min_found + 1;
+ }
+ }
+ else if ( auto_mode == KMP_AM_UNLIMIT && ptlist->used > 3 )
+ {
+ kmp_ckpt_entry_t *pt = ((kmp_ckpt_entry_t*)ptlist->list) + 3;
+ pt->left[0] = max_x + 20000.0;
+ pt->left[1] = max_y + 20000.0;
+ pt->right[0] = max_x + 22000.0;
+ pt->right[1] = max_y + 22000.0;
+ }
+ else if ( auto_mode == KMP_AM_SHORT || auto_mode == KMP_AM_1_LAP )
+ kmp->multi_ckpt_mode_0_ok = true;
+
+ TermGL(&gl,si,kmp,KMP_CKPH,rotate_group_links);
+ if (auto_group_name)
+ RenameGroupKMP(kmp,KMP_CKPH,kmp->ckph.gname,false);
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int get_defobj_id ( ScanInfo_t * si, u16 min_id, ccp cmd )
+{
+ u16 obj_id;
+ const enumError err = ScanU16SI(si,&obj_id,1,0);
+ if (err)
+ return -1;
+
+ ScanFile_t *sf = si->cur_file;
+ if (min_id)
+ {
+ if ( obj_id >= min_id && obj_id <= min_id+0xfff )
+ return obj_id;
+ }
+ else if (IsDefinitionObjectId(obj_id))
+ return obj_id;
+
+ sf->ptr = sf->prev_ptr;
+ WarnIgnoreSI(si,"Invalid id for %s (value %#x). ",cmd,obj_id);
+ GotoEolSI(si);
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+
+static u16 negate_ref_id ( u16 ref_id )
+{
+ if (IsConditionRefEngineId(ref_id))
+ return ref_id ^ GOBJ_MASK_COND_ENGINE;
+
+ if (IsConditionRefRandomId(ref_id))
+ return ref_id ^ GOBJ_MASK_COND_RND;
+
+// if (IsConditionRefTestId(ref_id))
+// return ref_id ^ GOBJ_MASK_COND_RND;
+
+ return ref_id ^ 1;
+}
+
+//-----------------------------------------------------------------------------
+
+static void set_ref_id ( List_t *dlist, u16 ref_id, int mode )
+{
+ // mode: <0: do nothing, =0:disable GOBJ_M_LECODE, >0:enable GOBJ_M_LECODE
+
+ kmp_gobj_entry_t *entry = GetListElem(dlist,-1,0);
+ if (entry)
+ {
+ if ( !mode || IsDefinitionObjectId(entry->obj_id) )
+ entry->obj_id &= ~GOBJ_M_LECODE;
+ else if ( mode > 0 )
+ entry->obj_id |= GOBJ_M_LECODE;
+
+ entry->ref_id = ref_id;
+ entry->pflags = entry->pflags & ~GOBJ_M_PF_MODE | 1 << GOBJ_S_PF_MODE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static void disable_enable ( ScanInfo_t *si, List_t *dlist, bool enable )
+{
+ DEFINE_VAR(var);
+ if (!ScanExprSI(si,&var))
+ {
+ int ref_id = GetIntV(&var);
+ if ( ref_id < 0 )
+ {
+ ref_id = -ref_id;
+ enable = !enable;
+ }
+
+ if ( IsConditionRefId(ref_id) || IsConditionRefTestId(ref_id) )
+ {
+ if (!enable)
+ ref_id = negate_ref_id(ref_id);
+ set_ref_id(dlist,ref_id,enable);
+ }
+ else if ( si->no_warn <= 0 )
+ {
+ ccp eol = FindNextLineFeedSI(si,true);
+ ScanFile_t *sf = si->cur_file;
+ ERROR0(ERR_WARNING,
+ "Invalid ID %#x for predefinied condition [%s @%u]:\n%.*s\n",
+ ref_id, sf->name, sf->line, (int)(eol-sf->prev_ptr), sf->prev_ptr );
+ }
+ }
+ FreeV(&var);
+ CheckEolSI(si);
+}
+
+//-----------------------------------------------------------------------------
+
+static void disable_enable_by
+ ( ScanInfo_t *si, List_t *dlist, bool enable, bool not )
+{
+ DEFINE_VAR(var);
+ if (!ScanExprSI(si,&var))
+ {
+ int ref_id = GetIntV(&var);
+ if ( ref_id < 0 )
+ {
+ ref_id = -ref_id;
+ enable = !enable;
+ }
+
+ if (IsDefinitionObjectId(ref_id))
+ {
+ if (not)
+ ref_id |= GOBJ_DEF_F_NOT;
+ else
+ ref_id &= ~GOBJ_DEF_F_NOT;
+ set_ref_id(dlist,ref_id,enable);
+ }
+ else if ( si->no_warn <= 0 )
+ {
+ ccp eol = FindNextLineFeedSI(si,true);
+ ScanFile_t *sf = si->cur_file;
+ ERROR0(ERR_WARNING,
+ "Invalid ID %#x for a reference to a definition-object [%s @%u]:\n%.*s\n",
+ ref_id, sf->name, sf->line, (int)(eol-sf->prev_ptr), sf->prev_ptr );
+ }
+ }
+ FreeV(&var);
+ CheckEolSI(si);
+}
+
+//-----------------------------------------------------------------------------
+
+static enumError ScanTextGOBJ
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextGOBJ()\n");
+
+ tform_info_t tform_pos;
+ ResetTformInfo(&tform_pos);
+
+ kcl_fall_t kfall;
+ ResetKclFall(&kfall);
+
+ int auto_object_name = 0, sort_objects = 0;
+ int fix_objdef = 0, sort_objdef = 0;
+ int fix_reference = 0, sort_reference = 0;
+ uint auto_enemy_item = 0, auto_enemy_index = 32;
+ ScanParam_t ptab[] =
+ {
+ { "GOBJ-HEAD-VALUE", SPM_U16, kmp->value+KMP_GOBJ },
+ { "AUTO-ENEMY-ITEM", SPM_UINT, &auto_enemy_item },
+ { "AUTO-OBJECT-NAME", SPM_INT, &auto_object_name },
+ { "SORT-OBJECTS", SPM_INT, &sort_objects },
+
+ { "FIX-OBJDEF", SPM_INT, &fix_objdef },
+ { "SORT-OBJDEF", SPM_INT, &sort_objdef },
+ { "FIX-REFERENCE", SPM_INT, &fix_reference },
+ { "SORT-REFERENCE", SPM_INT, &sort_reference },
+
+ TFORM_VARS(tform_pos)
+ KCL_FALL_VARS(kmp,kfall)
+ {0}
+ };
+
+ List_t *dlist = kmp->dlist + KMP_GOBJ;
+ dlist->used = 0;
+
+ bool ignore_itembox
+ = kmp->rtobj_enpt.obj == GOBJ_ITEMBOX
+ || kmp->rtobj_itpt.obj == GOBJ_ITEMBOX
+ || kmp->rtobj_jgpt.obj == GOBJ_ITEMBOX
+ || kmp->rtobj_cnpt.obj == GOBJ_ITEMBOX
+ || kmp->rtobj_mspt.obj == GOBJ_ITEMBOX;
+ if (!ignore_itembox)
+ {
+ uint pi;
+ for ( pi = 0; pi < kmp->n_rtobj_poti; pi++ )
+ if ( kmp->rtobj_poti[pi].rtobj.obj == GOBJ_ITEMBOX )
+ {
+ ignore_itembox = true;
+ break;
+ }
+ }
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ CheckTformInfo(&tform_pos);
+ CheckKclFall(&kfall);
+ continue;
+ }
+
+ if ( ch == '$' )
+ {
+ ScanFile_t *sf = si->cur_file;
+ DASSERT(sf);
+
+ char name[KMP_MAX_NAME_LEN+1];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+ int start_index = -1;
+ u16 min_id = 0x2000;
+
+ if (!strcmp(name,"$DEFOBJ-RACE"))
+ start_index = KMP_GMODE_VERSUS;
+ else if (!strcmp(name,"$DEFOBJ-BATTLE"))
+ start_index = KMP_GMODE_BALLOON;
+ else if (!strcmp(name,"$DEFOBJ-OR"))
+ {
+ start_index = KMP_GMODE_BALLOON;
+ min_id = 0x4000;
+ }
+ else if (!strcmp(name,"$DEFOBJ-AND"))
+ {
+ start_index = KMP_GMODE_BALLOON;
+ min_id = 0x6000;
+ }
+ else if (!strcmp(name,"$DISABLE"))
+ {
+ disable_enable(si,dlist,false);
+ continue;
+ }
+ else if (!strcmp(name,"$ENABLE"))
+ {
+ disable_enable(si,dlist,true);
+ continue;
+ }
+ else if (!strcmp(name,"$DISABLE-BY"))
+ {
+ disable_enable_by(si,dlist,false,true);
+ continue;
+ }
+ else if (!strcmp(name,"$ENABLE-BY"))
+ {
+ disable_enable_by(si,dlist,true,false);
+ continue;
+ }
+ else if (!strcmp(name,"$DISABLE-BY-NOT"))
+ {
+ disable_enable_by(si,dlist,false,false);
+ continue;
+ }
+ else if (!strcmp(name,"$ENABLE-BY-NOT"))
+ {
+ disable_enable_by(si,dlist,true,true);
+ continue;
+ }
+ else if (!strcmp(name,"$REFERENCE"))
+ {
+ int ref_id = get_defobj_id(si,0,name);
+ if ( ref_id >= 0 )
+ set_ref_id(dlist,ref_id,0);
+ continue;
+ }
+ else
+ {
+ sf->ptr = sf->prev_ptr;
+ WarnIgnoreSI(si,"Unknown keyword »%s«.",name);
+ GotoEolSI(si);
+ continue;
+ }
+
+ //-- $DEFOBJ-RACE | $DEFOBJ-BATTLE | $DEFOBJ-OR | $DEFOBJ-AND
+
+ int obj_id = get_defobj_id(si,min_id,name);
+ if ( obj_id < 0 )
+ continue;
+
+ kmp_gobj_entry_t * entry = AppendList(dlist);
+ entry->obj_id = obj_id;
+ entry->route_id = ~0;
+ entry->pflags = GOBJ_PF_MODE1;
+
+ u16 val = 0;
+ int index = 0;
+ DEFINE_VAR(var);
+ while ( start_index < 8 )
+ {
+ if (!ScanExprSI(si,&var))
+ val = GetIntV(&var);
+ while ( index <= start_index )
+ entry->setting[index++] = val;
+ start_index++;
+
+ if (!SkipCharSI(si,','))
+ break;
+ }
+
+ if ( min_id == 0x2000 )
+ while ( index < 8 )
+ entry->setting[index++] = val;
+
+ CheckEolSI(si);
+ FreeV(&var);
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_GOBJ,0);
+ if (err)
+ break;
+
+ kmp_gobj_entry_t * entry = AppendList(dlist);
+
+ if ( si->cur_file->szs_modifier > 0 )
+ {
+ ScanU16SI(si,&entry->obj_id,1,1);
+ ScanU16SI(si,&entry->ref_id,1,0);
+ ScanFloatV3SI(si,entry->position,3);
+ ScanBE32SI_swap(si,(u32*)&entry->route_id,5,1);
+ }
+ else
+ {
+ ScanU16SI(si,&entry->obj_id,1,0);
+ ScanFloatV3SI(si,entry->position,1);
+ ScanU16SI(si,entry->setting,4,0);
+ ScanU16SI(si,&entry->route_id,1,0);
+ ScanU16SI(si,&entry->ref_id,1,0);
+ ScanFloatV3SI(si,entry->rotation,1);
+ ScanU16SI(si,entry->setting+4,4,0);
+ ScanU16SI(si,&entry->pflags,1,0);
+ ScanFloatV3SI(si,entry->scale,1);
+ }
+
+ TransformPos(&tform_pos,entry->position);
+ CalcKclFall(&kfall,entry->position);
+ CheckEolSI(si);
+
+ if ( ignore_itembox
+ && entry->obj_id < N_KMP_GOBJ
+ && ObjectInfo[entry->obj_id].flags & OBF_ITEMBOX )
+ {
+ dlist->used--;
+ }
+ else if ( auto_enemy_item
+ && entry->obj_id == GOBJ_ITEMBOX
+ && !entry->setting[2] )
+ {
+ for(;;)
+ {
+ if ( auto_enemy_index > 32 )
+ auto_enemy_index = 0;
+ if ( auto_enemy_item & ( 1 << auto_enemy_index++ ) )
+ break;
+ }
+ entry->setting[2] = auto_enemy_index - 1;
+ }
+ }
+ CheckLevelSI(si);
+
+ FixDefObj(kmp,fix_objdef);
+ FixReference(kmp,fix_reference);
+
+ if (SortGOBJ(kmp,sort_objects))
+ auto_object_name = 1;
+
+ if (SortDefObj(kmp,sort_objdef))
+ auto_object_name = 1;
+
+ if (SortReference(kmp,sort_reference))
+ auto_object_name = 1;
+
+ if ( auto_object_name > 0 )
+ {
+ ResetIL(kmp->index+KMP_GOBJ);
+ FillIL(kmp->index+KMP_GOBJ,dlist->used);
+ }
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextAREA
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextAREA()\n");
+
+ ScanParam_t ptab[] =
+ {
+ { "AREA-HEAD-VALUE", SPM_U16, kmp->value+KMP_AREA },
+ {0}
+ };
+
+ List_t *dlist = kmp->dlist + KMP_AREA;
+ dlist->used = 0;
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_AREA,2);
+ if (err)
+ break;
+
+ kmp_area_entry_t * entry = AppendList(dlist);
+
+ if ( si->cur_file->szs_modifier > 0 )
+ {
+ ScanBE32SI(si,(u32*)&entry->mode,1,1);
+ ScanFloatV3SI(si,entry->position,3);
+ ScanBE32SI_swap(si,(u32*)entry->setting,2,1);
+ }
+ else if ( si->cur_file->revision < 2956 )
+ {
+ // 2011-08-26, v0.17a, r2956 -> new table layout
+
+ ScanBE16SI(si,(u16*)&entry->mode,2,0);
+ ScanFloatV3SI(si,entry->position,1);
+ ScanU16SI(si,entry->setting,4,0);
+ ScanFloatV3SI(si,entry->rotation,1);
+ ScanFloatV3SI(si,entry->scale,1);
+ }
+ else
+ {
+ ScanU8SI(si,&entry->mode,2,0);
+ ScanFloatV3SI(si,entry->position,1);
+ ScanU16SI(si,entry->setting,4,0);
+ ScanU8SI(si,&entry->dest_id,2,0);
+ ScanFloatV3SI(si,entry->rotation,1);
+ ScanFloatV3SI(si,entry->scale,1);
+ }
+ CheckEolSI(si);
+ }
+ CheckLevelSI(si);
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextCAME
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextCAME()\n");
+
+ u8 *value = (u8*)(kmp->value + KMP_CAME);
+ ScanParam_t ptab[] =
+ {
+ { "CAME-HEAD-VALUE", SPM_U16, (u16*)value },
+ #ifdef LITTLE_ENDIAN
+ { "OPENING-INDEX", SPM_U8, value+1 },
+ { "SELECTION-INDEX", SPM_U8, value+0 },
+ #else
+ { "OPENING-INDEX", SPM_U8, value+0 },
+ { "SELECTION-INDEX", SPM_U8, value+1 },
+ #endif
+ {0}
+ };
+
+ List_t *dlist = kmp->dlist + KMP_CAME;
+ dlist->used = 0;
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_CAME,2);
+ if (err)
+ break;
+
+ kmp_came_entry_t * entry = AppendList(dlist);
+
+ uint zoom_speed = 0, viewpt_speed = 0;
+
+ if ( si->cur_file->szs_modifier > 0 )
+ {
+ ScanBE32SI(si,(u32*)&entry->type,1,1);
+ ScanBE32SI_swap(si,(u32*)&entry->came_speed,2,1);
+ ScanFloatV3SI(si,entry->position,2);
+ ScanFloatSI(si,&entry->zoom_begin,2);
+ ScanFloatV3SI(si,entry->viewpt_begin,2);
+ ScanFloatSI(si,&entry->time,1);
+ zoom_speed = entry->zoom_speed;
+ viewpt_speed = entry->viewpt_speed;
+ }
+ else if ( si->cur_file->revision < 2955 )
+ {
+ // 2011-08-26, v0.17a, r2956 -> new table layout
+
+ ScanU8SI(si,&entry->type,4,0);
+ ScanU16SI(si,&entry->came_speed,1,0);
+ ScanFloatV3SI(si,entry->position,1);
+ ScanFloatSI(si,&entry->time,1);
+ ScanU32SI(si,&zoom_speed,1,0);
+ ScanFloatV3SI(si,entry->rotation,1);
+ ScanU32SI(si,&viewpt_speed,1,0);
+ ScanFloatSI(si,&entry->zoom_begin,2);
+ ScanU16SI(si,&entry->unknown_0x0a,1,0);
+ ScanFloatV3SI(si,entry->viewpt_begin,2);
+ }
+ else
+ {
+ ScanU8SI(si,&entry->type,1,0);
+ ScanU16SI(si,&entry->came_speed,1,0);
+ ScanFloatV3SI(si,entry->position,1);
+ ScanFloatSI(si,&entry->zoom_begin,1);
+ ScanU8SI(si,&entry->next,1,0);
+ ScanU32SI(si,&zoom_speed,1,0);
+ ScanFloatV3SI(si,entry->rotation,1);
+ ScanFloatSI(si,&entry->zoom_end,1);
+ ScanU8SI(si,&entry->unknown_0x02,1,0);
+ ScanU32SI(si,&viewpt_speed,1,0);
+ ScanFloatV3SI(si,entry->viewpt_begin,1);
+ ScanU8SI(si,&entry->route,1,0);
+ ScanU16SI(si,&entry->unknown_0x0a,1,0);
+ ScanFloatV3SI(si,entry->viewpt_end,1);
+ ScanFloatSI(si,&entry->time,1);
+ }
+ CheckEolSI(si);
+
+ const double zoom_len = fabs(entry->zoom_begin-entry->zoom_end);
+ entry->zoom_speed = zoom_speed;
+ if ( zoom_speed == ~(u32)0 )
+ entry->zoom_speed = entry->time >= 0.001
+ ? (uint)floor( 100.0 * zoom_len / entry->time )
+ : 1;
+
+ const double viewpt_len = LengthF(entry->viewpt_begin,entry->viewpt_end);
+ entry->viewpt_speed = viewpt_speed;
+ if ( viewpt_speed == ~(u32)0 )
+ entry->viewpt_speed = entry->time >= 0.001
+ ? (uint)floor( 100.0 * viewpt_len / entry->time )
+ : 1;
+
+ DefineIndexDouble(si,"CAME.TIME",entry->time);
+ DefineIndexDouble(si,"ZOOM.LEN",zoom_len);
+ DefineIndexDouble(si,"VIEWPT.LEN",viewpt_len);
+ }
+ CheckLevelSI(si);
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextPT2
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si, // valid data
+ uint sect // KMP section index
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ DASSERT(sect<KMP_N_SECT);
+ TRACE("ScanTextPT2(%s)\n",kmp_section_name[sect].name1);
+
+ Var_t *snap2_to_enpt, *snap_to_enpt, *hsnap_to_enpt;
+ kmp_rtobj_t *rtobj = 0;
+
+ Var_t var_dummy, *auto_adjust = &var_dummy;
+ int int_dummy, *auto_define = &int_dummy, *auto_remove = &int_dummy;
+ float float_dummy, *auto_distance = &float_dummy;
+ kmp_flag_t *kf = 0;
+ int export_flags = 0;
+
+ switch (sect)
+ {
+ case KMP_JGPT:
+ snap2_to_enpt = &kmp->jgpt_snap2;
+ snap_to_enpt = &kmp->jgpt_snap;
+ hsnap_to_enpt = &kmp->jgpt_hsnap;
+ rtobj = &kmp->rtobj_jgpt;
+ auto_define = &kmp->jgpt_auto_define;
+ auto_distance = &kmp->jgpt_auto_distance;
+ auto_adjust = &kmp->jgpt_auto_adjust;
+ auto_remove = &kmp->jgpt_auto_remove;
+ kf = &kmp->jgpt_flag;
+ break;
+
+ case KMP_CNPT:
+ snap2_to_enpt = &kmp->cnpt_snap2;
+ snap_to_enpt = &kmp->cnpt_snap;
+ hsnap_to_enpt = &kmp->cnpt_hsnap;
+ rtobj = &kmp->rtobj_cnpt;
+ break;
+
+ case KMP_MSPT:
+ snap2_to_enpt = &kmp->mspt_snap2;
+ snap_to_enpt = &kmp->mspt_snap;
+ hsnap_to_enpt = &kmp->mspt_hsnap;
+ rtobj = &kmp->rtobj_mspt;
+ break;
+
+ default:
+ var_dummy.mode = VAR_UNSET;
+ snap2_to_enpt = &var_dummy;
+ snap_to_enpt = &var_dummy;
+ hsnap_to_enpt = &var_dummy;
+ break;
+ }
+
+ rtobj_info_t rtinfo;
+ ResetRouteInfo(&rtinfo,&kmp->rtobj);
+
+ tform_info_t tform_pos;
+ ResetTformInfo(&tform_pos);
+
+ kcl_fall_t kfall;
+ ResetKclFall(&kfall);
+
+ int auto_id = 0;
+ char hd_name[20];
+ snprintf( hd_name, sizeof(hd_name),
+ "%s-HEAD-VALUE", kmp_section_name[sect].name1 );
+ ScanParam_t ptab[] =
+ {
+ { hd_name, SPM_U16, kmp->value+sect },
+ { "AUTO-ID", SPM_INT, &auto_id },
+ { "SNAP2-TO-ENPT", SPM_VAR, snap2_to_enpt },
+ { "SNAP-TO-ENPT", SPM_VAR, snap_to_enpt },
+ { "HSNAP-TO-ENPT", SPM_VAR, hsnap_to_enpt },
+ { "AUTO-DEFINE", SPM_INT, auto_define },
+ { "AUTO-DISTANCE", SPM_FLOAT, auto_distance },
+ { "AUTO-ADJUST", SPM_VAR, auto_adjust },
+ { "AUTO-REMOVE", SPM_INT, auto_remove },
+ { "EXPORT-FLAGS", SPM_INT, &export_flags },
+ ROUTE_OBJECT_VARS(rtinfo)
+ TFORM_VARS(tform_pos)
+ KCL_FALL_VARS(kmp,kfall)
+ {0}
+ };
+
+ List_t *dlist = kmp->dlist + sect;
+ dlist->used = 0;
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ CheckRouteInfo(&rtinfo);
+ CheckTformInfo(&tform_pos);
+ CheckKclFall(&kfall);
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,dlist->used,kmp->index+sect,2);
+ if (err)
+ break;
+
+ kmp_jgpt_entry_t * entry = AppendList(dlist);
+ ScanFloatV3SI(si,entry->position,2);
+ ScanU16SI(si,(u16*)&entry->id,1,0);
+ ScanU16SI(si,(u16*)&entry->effect,1,si->cur_file->szs_modifier);
+
+ const u8 flags = !kf ? KPFL_M_ALL
+ : NextCharSI(si,false) ? ScanPtFlags(si) : kf->mask;
+ if ( kf && (uint)(dlist->used-1) < KMP_MAX_POINT )
+ kf->flags[dlist->used-1] = flags;
+
+ TransformPos(&tform_pos,entry->position);
+ if ( flags & KPFL_FALL )
+ CalcKclFall(&kfall,entry->position);
+ CheckEolSI(si);
+
+ if (auto_id)
+ entry->id = dlist->used - 1;
+ }
+ CheckLevelSI(si);
+
+ PRINT_IF( snap2_to_enpt && snap2_to_enpt->mode,
+ "SNAP-TO-ENPT: %d -> jgpt=%d, cnpt=%d, mspt=%d\n",
+ snap2_to_enpt->mode, kmp->jgpt_snap2.mode,
+ kmp->cnpt_snap2.mode, kmp->mspt_snap2.mode );
+ PRINT_IF( snap_to_enpt && snap_to_enpt->mode,
+ "SNAP-TO-ENPT: %d -> jgpt=%d, cnpt=%d, mspt=%d\n",
+ snap_to_enpt->mode, kmp->jgpt_snap.mode,
+ kmp->cnpt_snap.mode, kmp->mspt_snap.mode );
+
+ if (kf)
+ kf->export = opt_export_flags ? opt_export_flags>0 : export_flags;
+
+ if (rtobj)
+ AssignRouteInfo(&rtinfo,rtobj);
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextSTGI
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextSTGI()\n");
+
+ ScanParam_t ptab[] =
+ {
+ { "STGI-HEAD-VALUE", SPM_U16, kmp->value+KMP_STGI },
+ {0}
+ };
+
+ List_t *dlist = kmp->dlist + KMP_STGI;
+ dlist->used = 0;
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_STGI,2);
+ if (err)
+ break;
+
+ kmp_stgi_entry_t * entry = AppendList(dlist);
+ if ( si->cur_file->revision < 3379 )
+ {
+ // 2012-01-28, v0.26a, r3379
+ ScanBE16SI(si,(u16*)&entry->lap_count,6,0);
+ entry->flare_color = be32(&entry->flare_color);
+ }
+ else
+ {
+ ScanU8SI(si,&entry->lap_count,4,0);
+ ScanU32SI(si,&entry->flare_color,1,0);
+ ScanU8SI(si,&entry->flare_alpha,2,0);
+
+ DEFINE_VAR(var);
+ ScanExprSI(si,&var);
+ if ( var.mode == VAR_DOUBLE || var.mode == VAR_VECTOR )
+ entry->speed_mod = SpeedMod2u16(GetDoubleV(&var));
+ else
+ {
+ u8 v2;
+ ScanU8SI(si,&v2,1,0);
+ entry->speed_mod = GetIntV(&var) << 8 | v2;
+ }
+ }
+ CheckEolSI(si);
+ }
+ CheckLevelSI(si);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextSETUP
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextSETUP()\n");
+
+ rtobj_info_t rtinfo;
+ ResetRouteInfo(&rtinfo,&kmp->rtobj);
+
+ ScanParam_t ptab[] =
+ {
+ ROUTE_OBJECT_VARS(rtinfo)
+ {0}
+ };
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ CheckRouteInfo(&rtinfo);
+ continue;
+ }
+
+ char name[50];
+ if ( !ScanNameSI(si,name,sizeof(name),true,true,0)
+ || NextCharSI(si,true) != '=' )
+ {
+ CheckEolSI(si);
+ continue;
+ }
+ DASSERT(si->cur_file);
+ si->cur_file->ptr++;
+ noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr);
+
+ if (!strcmp(name,"REVISION"))
+ {
+ ScanU32SI(si,(u32*)&kmp->revision,1,0);
+ si->init_revision = si->cur_file->revision = kmp->revision;
+ DefineIntVar(&si->gvar,"REVISION$SETUP",kmp->revision);
+ DefineIntVar(&si->gvar,"REVISION$ACTIVE",kmp->revision);
+ }
+ else if (!strcmp(name,"KCL-FALL-ADD-ROAD"))
+ {
+ DEFINE_VAR(val);
+ if ( ScanExprSI(si,&val) <= ERR_WARNING )
+ kmp->enable_fall_kcl = GetBoolV(&val);
+ }
+ else if (!strcmp(name,"KMP-WIM0"))
+ {
+ DEFINE_VAR(val);
+ if ( ScanExprSI(si,&val) <= ERR_WARNING )
+ kmp->wim0_export = GetBoolV(&val) || opt_wim0 > 0;
+ }
+ // "UNKNOWN-0X0C" replaced by "KMP-VERSION" in 2020-01
+ else if ( !strcmp(name,"KMP-VERSION") || !strcmp(name,"UNKNOWN-0X0C") )
+ ScanU32SI(si,&kmp->kmp_version,1,0);
+ else if (!strcmp(name,"INFO"))
+ ConcatStringsSI(si,&kmp->info,0);
+ else
+ GotoEolSI(si);
+ CheckEolSI(si);
+ }
+ CheckLevelSI(si);
+ if (rtinfo.valid)
+ AssignRouteInfo(&rtinfo,&kmp->rtobj);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextKMP() route functions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+typedef struct route_info_t
+{
+ int index; // index of route
+
+ int f_xss;
+ int f_yss;
+ int f_zss;
+ int f_scale;
+ int f_shift;
+ int f_hrotate; // [[[hrot]] [[2do]]
+
+ rtobj_info_t rtinfo;
+ kcl_fall_t kfall;
+
+ double xss[4];
+ double yss[4];
+ double zss[4];
+ Var_t scale[2];
+ Var_t shift;
+ Var_t hrotate[2]; // [[[hrot]] [[2do]]
+ int reverse_order;
+
+ double route_time; // >0.0: recalc setting 1
+
+ int show_route; // >0: enable itemboxes for this route
+
+} route_info_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void CloseRoute
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si, // valid data
+ const kmp_poti_group_t
+ * grp, // group to close
+ route_info_t * ri // route info data
+)
+{
+ DASSERT(kmp);
+ DASSERT(grp);
+ DASSERT(ri);
+
+ const uint np = grp->n_point;
+ DASSERT( np <= kmp->poti_point.used );
+
+ kmp_poti_point_t *pp0 = (kmp_poti_point_t*)kmp->poti_point.list
+ + kmp->poti_point.used - np;
+
+ //--- xss/yss/zss
+
+ if ( ri->f_xss && fabs(ri->xss[0]-ri->xss[2]) < 1e-6 )
+ ri->f_xss = 0;
+
+ if ( ri->f_yss && fabs(ri->yss[0]-ri->yss[2]) < 1e-6 )
+ ri->f_yss = 0;
+
+ if ( ri->f_zss && fabs(ri->zss[0]-ri->zss[2]) < 1e-6 )
+ ri->f_zss = 0;
+
+ if ( ri->f_xss || ri->f_yss || ri->f_zss )
+ {
+ ri->f_shift = 1;
+ ri->f_scale = 1;
+ AssignVectorV3(&ri->shift,0.0,0.0,0.0);
+ AssignVectorV3(ri->scale,1.0,1.0,1.0);
+
+ if (ri->f_xss)
+ {
+ double *d = ri->xss;
+ PRINT("XSS[%d]: %11.3f %11.3f | %11.3f %11.3f\n",
+ ri->f_xss, d[0], d[1], d[2], d[3] );
+ const double scale = ( d[3] - d[1] ) / ( d[2] - d[0] );
+ ri->scale[0].x = scale;
+ ri->shift.x = d[1] - d[0] * scale;
+ }
+
+ if (ri->f_yss)
+ {
+ double *d = ri->yss;
+ PRINT("YSS[%d]: %11.3f %11.3f | %11.3f %11.3f\n",
+ ri->f_yss, d[0], d[1], d[2], d[3] );
+ const double scale = ( d[3] - d[1] ) / ( d[2] - d[0] );
+ ri->scale[0].y = scale;
+ ri->shift.y = d[1] - d[0] * scale;
+ }
+
+ if (ri->f_zss)
+ {
+ double *d = ri->zss;
+ PRINT("ZSS[%d]: %11.3f %11.3f | %11.3f %11.3f\n",
+ ri->f_zss, d[0], d[1], d[2], d[3] );
+ const double scale = ( d[3] - d[1] ) / ( d[2] - d[0] );
+ ri->scale[0].z = scale;
+ ri->shift.z = d[1] - d[0] * scale;
+ }
+ }
+
+
+ //--- scale
+
+ if ( ri->f_scale > 1 )
+ {
+ Var_t *d = ri->scale;
+ ToVectorV(d+0);
+ ToVectorV(d+1);
+ PRINT("SCALE[%d]: %11.3f %11.3f %11.3f | %11.3f %11.3f %11.3f\n",
+ ri->f_scale, d[0].x, d[0].y, d[0].z, d[1].x, d[1].y, d[1].z );
+
+ kmp_poti_point_t *pp = pp0;
+ uint i;
+ for ( i = 0; i < np; i++, pp++ )
+ {
+ pp->position[0] = ( pp->position[0] - d[1].x ) * d[0].x + d[1].x;
+ pp->position[1] = ( pp->position[1] - d[1].y ) * d[0].y + d[1].y;
+ pp->position[2] = ( pp->position[2] - d[1].z ) * d[0].z + d[1].z;
+ }
+ }
+ else if ( ri->f_scale > 0 )
+ {
+ Var_t *d = ri->scale;
+ ToVectorV(d+0);
+ PRINT("SCALE[%d]: %11.3f %11.3f %11.3f\n",
+ ri->f_scale, d->x, d->y, d->z );
+
+ kmp_poti_point_t *pp = pp0;
+ uint i;
+ for ( i = 0; i < np; i++, pp++ )
+ {
+ pp->position[0] *= d->x;
+ pp->position[1] *= d->y;
+ pp->position[2] *= d->z;
+ }
+ }
+
+
+ //--- shift
+
+ if ( ri->f_shift )
+ {
+ Var_t *d = &ri->shift;
+ ToVectorV(d);
+ PRINT("SHIFT[%d]: %11.3f %11.3f %11.3f\n",
+ ri->f_shift, d->x, d->y, d->z );
+
+ kmp_poti_point_t *pp = pp0;
+ uint i;
+ for ( i = 0; i < np; i++, pp++ )
+ {
+ pp->position[0] += d->x;
+ pp->position[1] += d->y;
+ pp->position[2] += d->z;
+ }
+ }
+
+
+ //--- hrotate // [[[hrot]] [[2do]]
+
+ if ( ri->f_hrotate )
+ {
+ Var_t *d = ri->hrotate;
+ ToVectorV(d+1);
+ double rot = GetDoubleV(d++) * (M_PI/180.0);
+ PRINT("HROTATE[%d]: %11.3f %11.3f %11.3f -> %6.3f\n",
+ ri->f_hrotate, d->x, d->y, d->z, rot );
+
+ kmp_poti_point_t *pp = pp0;
+ uint i;
+ for ( i = 0; i < np; i++, pp++ )
+ {
+ const double dx = pp->position[0] - d->x;
+ const double dz = pp->position[2] - d->z;
+ const double width = sqrt ( dx*dx + dz*dz );
+ if ( width >= 1e-6 )
+ {
+ const double new_rot = atan2(dx,dz) + rot;
+ pp->position[0] = d->x + sin(new_rot) * width;
+ pp->position[2] = d->y + cos(new_rot) * width;
+ }
+ }
+ }
+
+
+ //--- reverse order
+
+ if (ri->reverse_order)
+ {
+ kmp_poti_point_t *pp1 = pp0;
+ kmp_poti_point_t *pp2 = pp0 + np - 1;
+ while ( pp1 < pp2 )
+ {
+ noPRINT("P: %p %p / %zd %zd\n",pp1,pp2,pp1-pp0,pp2-pp0);
+ kmp_poti_point_t temp;
+ memcpy(&temp,pp1, sizeof(temp));
+ memcpy(pp1++,pp2, sizeof(*pp1));
+ memcpy(pp2--,&temp,sizeof(*pp2));
+ }
+ }
+
+
+ //--- define variables
+
+ uint i;
+ double total_len = 0.0, total_tim = 0.0;
+ kmp_poti_point_t *pp = pp0;
+ for ( i = 0; i < np-1; i++, pp++ )
+ {
+ const double len = LengthF(pp->position,pp[1].position);
+ total_len += len;
+ if ( pp->speed > 0 )
+ total_tim += len / pp->speed;
+ }
+
+ DefineIndexDouble(si,"LEN",total_len);
+ DefineIndexDouble(si,"TIME",total_tim);
+ DefineIndexDouble(si,"TIME.SET",total_tim);
+
+
+ //--- set route time
+
+ if ( ri->route_time > 0.0 && total_tim > 0.0 )
+ {
+ PRINT("SET ROUTE TIME %s, N=%d+%u, time=%4.2f, len=%5.3f, set=%4.2f\n",
+ last_index_name, kmp->poti_point.used - np, np,
+ total_tim, total_len, ri->route_time );
+
+
+ const double factor = ri->route_time / total_tim;
+ double sum_src = 0.0, sum_dest = 0.0;
+ pp = pp0;
+ for ( i = 0; i < np-1; i++, pp++ )
+ {
+ if ( pp->speed > 0 )
+ {
+ const double len = LengthF(pp->position,pp[1].position);
+ const double tim = len / pp->speed;
+ sum_src += tim;
+ const double wanted = sum_src * factor - sum_dest;
+ DASSERT(wanted>=0.0);
+ pp->speed = wanted > 0.0 ? floor( len / wanted + 0.5 ) : 0.0;
+ if (!pp->speed)
+ pp->speed = 1;
+ sum_dest += len / pp->speed;
+ noPRINT("%6.2f/%4.2f - %6.2f/%4.2f - %6.2f -> %5u\n",
+ sum_src/60.0, total_tim,
+ sum_dest/60.0, total_tim*factor,
+ wanted, pp->speed );
+ }
+ }
+ DefineIndexDouble(si,"TIME.SET",sum_dest);
+ }
+
+
+ //--- enable ruote itemboxes
+
+ kmp_rtobj_t rtobj;
+ if (AssignRouteInfo(&ri->rtinfo,&rtobj))
+ {
+ const uint max = sizeof(kmp->rtobj_poti)/sizeof(*kmp->rtobj_poti);
+ uint i;
+ for ( i = 0; i <= kmp->n_rtobj_poti; i++ )
+ {
+ if ( i >= max )
+ {
+ ERROR0(ERR_WARNING,
+ "Route list full -> @ROUTE-OBJECT ignored for route #%u",ri->index);
+ break;
+ }
+
+ kmp_rtobj_list_t *rptr = kmp->rtobj_poti + i;
+ if ( i == kmp->n_rtobj_poti )
+ {
+ rptr->index = ri->index;
+ kmp->n_rtobj_poti++;
+
+ }
+
+ if ( rptr->index == ri->index )
+ {
+ AssignRouteInfo(&ri->rtinfo,&rptr->rtobj);
+ PRINT("@ROUTE-OBJECT[%u] = 0x%x -> %u\n",
+ i, ri->index, rptr->rtobj.obj );
+ break;
+ }
+ }
+ }
+
+ memset(ri,0,sizeof(*ri));
+ ResetRouteInfo(&ri->rtinfo,&kmp->rtobj);
+ ResetKclFall(&ri->kfall);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTextPOTI
+(
+ kmp_t * kmp, // KMP data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(kmp);
+ DASSERT(si);
+ TRACE("ScanTextPOTI()\n");
+
+ List_t *glist = kmp->dlist + KMP_POTI;
+ List_t *plist = &kmp->poti_point;
+ glist->used = plist->used = 0;
+
+ Line_t line;
+ InitializeLine(&line);
+ kmp_poti_group_t *grp = 0;
+ u8 g_smooth = 0, g_back = 0;
+
+ int auto_route_name = 0;
+ route_info_t ri = {0};
+ ResetRouteInfo(&ri.rtinfo,&kmp->rtobj);
+ ResetKclFall(&ri.kfall);
+
+ ScanParam_t ptab[] =
+ {
+ { "AUTO-ROUTE-NAME", SPM_INT, &auto_route_name },
+ { "XSS", SPM_DOUBLE, ri.xss, 4,4, &ri.f_xss },
+ { "YSS", SPM_DOUBLE, ri.yss, 4,4, &ri.f_yss },
+ { "ZSS", SPM_DOUBLE, ri.zss, 4,4, &ri.f_zss },
+ { "SCALE", SPM_VAR, ri.scale, 1,2, &ri.f_scale },
+ { "SHIFT", SPM_VAR, &ri.shift, 1,1, &ri.f_shift },
+// [[[hrot]] [[2do]]
+ { "HROTATE", SPM_VAR, ri.hrotate, 1,2, &ri.f_hrotate },
+ { "REVERSE-ORDER", SPM_INT, &ri.reverse_order },
+ { "SET-ROUTE-TIME", SPM_DOUBLE, &ri.route_time },
+ ROUTE_OBJECT_VARS(ri.rtinfo)
+ KCL_FALL_VARS(kmp,ri.kfall)
+ {0}
+ };
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ CheckRouteInfo(&ri.rtinfo);
+ CheckKclFall(&ri.kfall);
+ continue;
+ }
+
+ if ( ch == '$' )
+ {
+ char name[KMP_MAX_NAME_LEN+1];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+
+ const int is_bezier = !strcmp(name,"$BEZIER");
+ if ( is_bezier || !strcmp(name,"$LINE") )
+ {
+ enumError err = ScanLine(&line,si,3,is_bezier);
+ if (err)
+ return err;
+ continue;
+ }
+
+ if ( !strcmp(name,"$COPY") )
+ {
+ uint r_idx;
+ if (ScanU32SI(si,&r_idx,1,0))
+ continue;
+
+ if ( r_idx >= glist->used )
+ {
+ ScanFile_t *sf = si->cur_file;
+ DASSERT(sf);
+ si->total_err++;
+ sf->line_err++;
+ ERROR0(ERR_WARNING,
+ "Can't copy unknown route #%u [%s @%u]\n",
+ r_idx, sf->name, sf->line );
+ GotoEolSI(si);
+ }
+
+ uint r_beg = 0, r_end = UINT_MAX;
+ if (NextCharSI(si,false))
+ {
+ if (ScanU32SI(si,&r_beg,1,0))
+ continue;
+
+ if (NextCharSI(si,false))
+ {
+ if (ScanU32SI(si,&r_end,1,0))
+ continue;
+ r_end++;
+ }
+ }
+
+ const kmp_poti_group_t *srcgrp = GetListElem(glist,r_idx,0);
+ DASSERT(srcgrp);
+ if ( r_end > srcgrp->n_point )
+ r_end = srcgrp->n_point;
+ PRINT("COPY ROUTE: %u,%u..%u\n",r_idx,r_beg,r_end);
+ if ( r_beg >= r_end )
+ continue;
+
+ const kmp_poti_point_t * psrc = GetListElem(plist,0,0);
+ psrc += r_beg;
+ while ( r_idx > 0 )
+ {
+ r_idx--;
+ const kmp_poti_group_t *g = GetListElem(glist,r_idx,0);
+ DASSERT(g);
+ psrc += g->n_point;
+ }
+ DASSERT( psrc < (kmp_poti_point_t*)GetListElem(plist,0,0) + plist->used );
+
+ if (!grp)
+ {
+ grp = AppendList(glist);
+ grp->smooth = g_smooth;
+ grp->back = g_back;
+ }
+ DASSERT(grp);
+ grp->n_point += r_end - r_beg;
+
+ for ( ; r_beg < r_end; r_beg++, psrc++ )
+ {
+ kmp_poti_point_t * pdest = AppendList(plist);
+ memcpy(pdest,psrc,sizeof(*pdest));
+ }
+ kmp->value[KMP_POTI] = plist->used;
+ continue;
+ }
+
+ const bool cmd_close = !strcmp(name,"$CLOSE");
+ if ( !cmd_close && strcmp(name,"$ROUTE") && strcmp(name,"$GROUP") )
+ {
+ ScanFile_t *sf = si->cur_file;
+ DASSERT(sf);
+ si->total_err++;
+ sf->line_err++;
+ CheckEolSI(si);
+ ERROR0(ERR_WARNING,
+ "Line ignored [%s @%u]: %s ...\n",
+ sf->name, sf->line, name );
+ continue;
+ }
+
+
+ //--- close group
+
+ if (grp)
+ CloseRoute(kmp,si,grp,&ri);
+ grp = 0;
+
+ if (cmd_close)
+ continue;
+
+ ScanIndexSI(si,0,glist->used,kmp->index+KMP_POTI,2);
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,false);
+ noPRINT("--> %02x : %.6s\n",ch,si->cur_file->ptr);
+ if (!ch)
+ break;
+ si->cur_file->ptr++;
+ if ( ch == ':' )
+ break;
+ }
+ u16 g_temp;
+ ScanU16SI(si,&g_temp,1,0);
+ if (NextCharSI(si,false))
+ {
+ g_smooth = g_temp;
+ ScanU8SI(si,&g_back,1,0);
+ }
+ else
+ {
+ g_smooth = g_temp >> 8;
+ g_back = (u8)g_temp;
+ }
+ noPRINT("ROUTE: smooth=%u, back=%u\n",g_smooth,g_back);
+ CheckEolSI(si);
+ continue;
+ }
+
+ if ( !grp || !grp->n_point ) // no previous point available
+ line.n = 1;
+ enumError err;
+ ScanIndexSI(si,&err, grp ? grp->n_point + line.n -1 : 0, 0,2);
+ if (err)
+ break;
+
+ if (!grp)
+ {
+ ri.index = glist->used;
+ grp = AppendList(glist);
+ grp->smooth = g_smooth;
+ grp->back = g_back;
+ }
+ DASSERT(grp);
+ grp->n_point += line.n;
+
+ kmp_poti_point_t temp;
+ ScanFloatV3SI(si,temp.position,1);
+ if ( si->cur_file->szs_modifier > 0 )
+ ScanBE32SI_swap(si,(u32*)&temp.speed,1,1);
+ else
+ ScanU16SI(si,&temp.speed,2,0);
+ CheckEolSI(si);
+
+ const kmp_poti_point_t * prev = GetListElem(plist,-1,&temp);
+
+ uint p;
+ for ( p = 1; p <= line.n; p++ )
+ {
+ kmp_poti_point_t * ppos = AppendList(plist);
+ memcpy(ppos,&temp,sizeof(*ppos));
+ CalcLine(&line,ppos->position,prev->position,temp.position,3,p);
+ CalcKclFall(&ri.kfall,ppos->position);
+ }
+ kmp->value[KMP_POTI] = plist->used;
+ InitializeLine(&line);
+ }
+ if (grp)
+ CloseRoute(kmp,si,grp,&ri);
+ CheckLevelSI(si);
+
+ if ( auto_route_name > 0 )
+ {
+ ResetIL(kmp->index+KMP_POTI);
+ FillIL(kmp->index+KMP_POTI,glist->used);
+ }
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static kmp_gobj_entry_t * AddObject
+(
+ kmp_t * kmp, // KMP data structure
+ const float * pos, // 3D vector with position
+ const float * rot, // NULL or a rotation 3D vector
+ kmp_rtobj_t * rtobj // object to insert
+)
+{
+ DASSERT(kmp);
+ DASSERT(pos);
+ DASSERT(rtobj);
+ DASSERT( rtobj->obj > 0 && rtobj->obj < N_KMP_GOBJ );
+
+ List_t *glist = kmp->dlist + KMP_GOBJ;
+ kmp_gobj_entry_t *gobj = AppendList(glist);
+ gobj->obj_id = rtobj->obj;
+ gobj->position[0] = pos[0] + rtobj->shift.x;
+ gobj->position[1] = pos[1] + rtobj->shift.y;
+ gobj->position[2] = pos[2] + rtobj->shift.z;
+ memcpy(gobj->scale,rtobj->scale.v,sizeof(gobj->scale));
+ memcpy(gobj->rotation,rtobj->rotate.v,sizeof(gobj->rotation));
+ gobj->route_id = (typeof(gobj->route_id))~0;
+ gobj->pflags = 0x3f;
+ if (rot)
+ {
+ gobj->rotation[0] = NormDegreeF( gobj->rotation[0] + rot[0] );
+ gobj->rotation[1] = NormDegreeF( gobj->rotation[1] + rot[1] );
+ gobj->rotation[2] = NormDegreeF( gobj->rotation[2] + rot[2] );
+ }
+ return gobj;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AddEnemyRouteObjects
+(
+ kmp_t * kmp, // KMP data structure
+ uint sect, // KMP section index
+ kmp_rtobj_t *rtobj // object to insert
+)
+{
+ DASSERT(kmp);
+ DASSERT(rtobj);
+ DASSERT( sect == KMP_ENPT || sect == KMP_ITPT );
+
+ if (rtobj->obj)
+ {
+ PRINT("AddEnemyRouteObjects(%s)\n",kmp_section_name[sect].name1);
+ const uint n = kmp->dlist[sect].used;
+ const kmp_enpt_entry_t * pt = (kmp_enpt_entry_t*)kmp->dlist[sect].list;
+
+ uint i;
+ for ( i = 0; i < n; i++ )
+ {
+ int last = -1, link;
+ for ( link = 0; link < KMP_MAX_PH_LINK; link++ )
+ {
+ int next = GetNextPointKMP(kmp,sect,i,link,false,-1,0,0,0,0);
+ if ( next >= 0 && next != last )
+ {
+ last = next;
+ float3 rot = CalcDirection3F(pt[i].position,pt[next].position);
+ AddObject(kmp,pt[i].position,rot.v,rtobj);
+ }
+ }
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AddJugemRouteObjects
+(
+ kmp_t * kmp, // KMP data structure
+ uint sect, // KMP section index
+ kmp_rtobj_t *rtobj // object to insert
+)
+{
+ DASSERT(kmp);
+ DASSERT(rtobj);
+ DASSERT( sect == KMP_JGPT || sect == KMP_CNPT || sect == KMP_MSPT );
+
+ if (rtobj->obj)
+ {
+ PRINT("AddJugemRouteObjects(%s)\n",kmp_section_name[sect].name1);
+ const uint n = kmp->dlist[sect].used;
+ const kmp_jgpt_entry_t * pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list;
+
+ uint i;
+ for ( i = 0; i < n; i++, pt++ )
+ AddObject(kmp,pt->position,pt->rotation,rtobj);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AddRouteObjects
+(
+ kmp_t * kmp // KMP data structure
+)
+{
+ DASSERT(kmp);
+ PRINT("AddRouteObjects()\n");
+
+ const uint ng = kmp->dlist[KMP_POTI].used;
+ uint ilist;
+ kmp_rtobj_list_t *rptr = kmp->rtobj_poti;
+ for ( ilist = 0; ilist < kmp->n_rtobj_poti; ilist++, rptr++ )
+ {
+ int route_idx = rptr->index;
+ if ( route_idx < 0 || route_idx >= ng || !rptr->rtobj.obj )
+ continue;
+
+ PRINT("ROUTE-ITEMBOX %u/%u -> %d/%u\n",
+ ilist, kmp->n_rtobj_poti, route_idx, ng );
+ const kmp_poti_group_t *grp = (kmp_poti_group_t*)kmp->dlist[KMP_POTI].list;
+ kmp_poti_point_t *pt = (kmp_poti_point_t*)kmp->poti_point.list;
+ while ( route_idx-- > 0 )
+ {
+ pt += grp->n_point;
+ grp++;
+ }
+
+ uint i;
+ for ( i = 0; i < grp->n_point; i++ )
+ {
+ float3 rot = CalcDirection3F( pt[i].position,
+ pt[ (i+1) % grp->n_point ].position );
+ AddObject( kmp, pt[i].position, rot.v, &rptr->rtobj );
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setup_scale ( Var_t *dest, const Var_t *src )
+{
+ DASSERT(dest);
+ DASSERT(src);
+
+ if ( src->mode == VAR_VECTOR )
+ AssignVar(dest,src);
+ else
+ AssignVectorV3(dest,0.0,GetYDoubleV(src),0.0);
+ DASSERT( dest->mode == VAR_VECTOR );
+
+ if ( dest->x <= 0.0 )
+ dest->x = RTOBJ_SCALE_X;
+ else if ( dest->x < RTOBJ_SCALE_MIN )
+ dest->y = RTOBJ_SCALE_MIN;
+
+ if ( dest->y <= 0.0 )
+ dest->y = RTOBJ_SCALE_Y;
+ else if ( dest->y < RTOBJ_SCALE_MIN )
+ dest->y = RTOBJ_SCALE_MIN;
+
+ if ( dest->z <= 0.0 )
+ dest->z = RTOBJ_SCALE_Z;
+ else if ( dest->z < RTOBJ_SCALE_MIN )
+ dest->y = RTOBJ_SCALE_MIN;
+}
+
+//-----------------------------------------------------------------------------
+
+static void AddCheckpointObject
+(
+ kmp_t * kmp // KMP data structure
+)
+{
+ DASSERT(kmp);
+ PRINT("AddCheckpointObject()\n");
+
+ struct obj_t
+ {
+ int object;
+ Var_t scale;
+ float base;
+
+ } obj1, obj2;
+
+ obj1.object = kmp->ckpt_object1 > 0 && kmp->ckpt_object1 < N_KMP_GOBJ
+ ? kmp->ckpt_object1 : 0;
+ setup_scale(&obj1.scale,&kmp->ckpt_object1_scale);
+ obj1.base = kmp->ckpt_object1_base;
+
+ obj2.object = kmp->ckpt_object2 > 0 && kmp->ckpt_object2 < N_KMP_GOBJ
+ ? kmp->ckpt_object2 : 0;
+ setup_scale(&obj2.scale,&kmp->ckpt_object2_scale);
+ obj2.base = kmp->ckpt_object2_base;
+
+ const struct obj_t *obj_lap_lt = 0, *obj_lap_rt = 0;
+ const struct obj_t *obj_man_lt = 0, *obj_man_rt = 0;
+ const struct obj_t *obj_std_lt = 0, *obj_std_rt = 0;
+
+ switch (kmp->ckpt_object_mode)
+ {
+ case 1:
+ obj_lap_lt = &obj2;
+ obj_lap_rt = &obj2;
+ obj_man_lt = &obj1;
+ obj_man_rt = &obj1;
+ break;
+
+ case 2:
+ obj_lap_lt = &obj2;
+ obj_lap_rt = &obj2;
+ obj_man_lt = &obj2;
+ obj_man_rt = &obj2;
+ obj_std_lt = &obj1;
+ obj_std_rt = &obj1;
+ break;
+
+ case 3:
+ obj_lap_lt = &obj1;
+ obj_lap_rt = &obj2;
+ obj_man_lt = &obj1;
+ obj_man_rt = &obj2;
+ obj_std_lt = &obj1;
+ obj_std_rt = &obj2;
+ break;
+
+ default:
+ return;
+ }
+
+ opt_auto_add = true;
+
+ List_t *glist = kmp->dlist + KMP_GOBJ;
+ const kmp_ckpt_entry_t *pt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list;
+ uint i, n = kmp->dlist[KMP_CKPT].used;
+ for ( i = 0; i < n; i++, pt++ )
+ {
+ double direction, factor = IS_M1(pt->mode) ? 1.0 : 3.0;
+ WidthAndDirection(0,&direction,true,pt->left,pt->right);
+
+ const struct obj_t *obj;
+ obj = IS_M1(pt->mode) ? obj_std_lt : pt->mode ? obj_man_lt : obj_lap_lt;
+ if (obj)
+ {
+ kmp_gobj_entry_t *go = AppendList(glist);
+ go->obj_id = obj->object;
+ go->position[0] = pt->left[0];
+ go->position[1] = obj->base;
+ go->position[2] = pt->left[1];
+ go->rotation[1] = direction;
+ go->scale[0] = obj->scale.x * factor;
+ go->scale[1] = obj->scale.y;
+ go->scale[2] = obj->scale.z * factor;
+ go->route_id = (typeof(go->route_id))~0;
+ go->pflags = 0x3f;
+ }
+
+ obj = IS_M1(pt->mode) ? obj_std_rt : pt->mode ? obj_man_rt : obj_lap_rt;
+ if (obj)
+ {
+ kmp_gobj_entry_t *go = AppendList(glist);
+ go->obj_id = obj->object;
+ go->position[0] = pt->right[0];
+ go->position[1] = obj->base;
+ go->position[2] = pt->right[1];
+ go->rotation[1] = direction;
+ go->scale[0] = obj->scale.x * factor;
+ go->scale[1] = obj->scale.y;
+ go->scale[2] = obj->scale.z * factor;
+ go->route_id = (typeof(go->route_id))~0;
+ go->pflags = 0x3f;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void SnapToENPT
+(
+ kmp_t * kmp, // KMP data structure
+ uint sect // KMP section index
+)
+{
+ const Var_t * add2; // not NULL: enter 2D-full mode && value to add
+ const Var_t * add1; // not NULL: enter 1D-full mode && value to add
+ const Var_t * hadd; // not NULL: enter horiz-mode && value to add
+ const kmp_flag_t *kf = 0;
+
+ switch(sect)
+ {
+ case KMP_JGPT:
+ add2 = &kmp->jgpt_snap2;
+ add1 = &kmp->jgpt_snap;
+ hadd = &kmp->jgpt_hsnap;
+ kf = &kmp->jgpt_flag;
+ break;
+
+ case KMP_CNPT:
+ add2 = &kmp->cnpt_snap2;
+ add1 = &kmp->cnpt_snap;
+ hadd = &kmp->cnpt_hsnap;
+ break;
+
+ case KMP_MSPT:
+ add2 = &kmp->mspt_snap2;
+ add1 = &kmp->mspt_snap;
+ hadd = &kmp->mspt_hsnap;
+ break;
+
+ default:
+ ASSERT(0);
+ return;
+ }
+
+ const Var_t *add;
+ enum { MODE2, MODE1, HMODE } mode;
+
+ if ( add2 && add2->mode != VAR_UNSET )
+ {
+ mode = MODE2;
+ add = add2;
+ }
+ else if ( add1 && add1->mode != VAR_UNSET )
+ {
+ mode = MODE1;
+ add = add1;
+ }
+ else if ( hadd && hadd->mode != VAR_UNSET )
+ {
+ mode = HMODE;
+ add = hadd;
+ }
+ else
+ return;
+
+ PRINT("SnapToENPT(%s) mode=%d\n",kmp_section_name[sect].name1,mode);
+
+ const uint n_enpt = kmp->dlist[KMP_ENPT].used;
+ if (!n_enpt)
+ return;
+ const kmp_enpt_entry_t *enpt1 = (kmp_enpt_entry_t*)kmp->dlist[KMP_ENPT].list;
+
+ kmp_jgpt_entry_t *pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list;
+ kmp_jgpt_entry_t *pt_end = pt + kmp->dlist[sect].used;
+
+ uint j;
+ for ( j = 0; pt < pt_end; j++, pt++ )
+ {
+ if ( kf && j < KMP_MAX_POINT && !( kf->flags[j] & KPFL_SNAP ) )
+ continue;
+
+ int i, enpt_idx = -1;
+ double min_dist = 0.0;
+ const kmp_enpt_entry_t *enpt;
+ for ( i = 0, enpt = enpt1; i < n_enpt; i++, enpt++ )
+ {
+ if ( i < KMP_MAX_POINT && !( kmp->enpt_flag.flags[i] & KPFL_SNAP ))
+ continue;
+
+ const double dx = pt->position[0] - enpt->position[0];
+ const double dy = pt->position[1] - enpt->position[1];
+ const double dz = pt->position[2] - enpt->position[2];
+ const double dist = dx*dx + dy*dy + dz*dz;
+ if ( enpt_idx < 0 || min_dist > dist )
+ {
+ min_dist = dist;
+ enpt_idx = i;
+ }
+ }
+ if ( enpt_idx < 0 )
+ break; // no usable ENPT found -> abort
+
+ uint next_idx = GetNextPointKMP(kmp,KMP_ENPT,enpt_idx,-1,false,0,0,0,0,0);
+ DASSERT( next_idx < n_enpt );
+
+ enpt = enpt1 + enpt_idx;
+
+ DEFINE_VAR(var);
+ var.mode = VAR_VECTOR;
+ var.x = enpt->position[0];
+ var.y = mode == HMODE ? pt->position[1] : enpt->position[1];
+ var.z = enpt->position[2];
+ DASSERT(add);
+ CalcExpr(&var,add,OPI_ADD,0);
+ pt->position[0] = var.x;
+ pt->position[1] = var.y;
+ pt->position[2] = var.z;
+
+ const kmp_enpt_entry_t *next = enpt1 + next_idx;
+ if ( mode == MODE2 )
+ {
+ double3 p1, p2;
+ p1.x = enpt->position[0];
+ p1.y = enpt->position[1];
+ p1.z = enpt->position[2];
+ p2.x = next->position[0];
+ p2.y = next->position[1];
+ p2.z = next->position[2];
+ double3 dir = CalcDirection3D(&p1,&p2);
+ pt->rotation[0] = dir.x;
+ pt->rotation[1] = dir.y;
+ pt->rotation[2] = dir.z;
+ }
+ else
+ {
+ pt->rotation[0] = pt->rotation[2] = 0.0;
+ pt->rotation[1] = atan2( next->position[0] - enpt->position[0],
+ next->position[2] - enpt->position[2] ) * (180/M_PI);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int FindNearestJGPT2
+(
+ kmp_t *kmp, // valid KMP
+ double x, // x-coordinate
+ double z, // z-coordinate
+ double *res_dist2 // not NULL: store distance^2 here
+)
+{
+ DASSERT(kmp);
+
+ int found = -1;
+ double max_dist2 = 1e30;
+
+ const uint n_jgpt = kmp->dlist[KMP_JGPT].used;
+ kmp_jgpt_entry_t *jgpt = (kmp_jgpt_entry_t*)kmp->dlist[KMP_JGPT].list;
+
+ uint i;
+ for ( i = 0; i < n_jgpt; i++, jgpt++ )
+ {
+ const double dx = x - jgpt->position[0];
+ const double dz = z - jgpt->position[2];
+ const double dist2 = dx*dx + dz*dz;
+ if ( max_dist2 > dist2 )
+ {
+ max_dist2 = dist2;
+ found = i;
+ }
+ }
+
+ if ( res_dist2 )
+ *res_dist2 = max_dist2;
+ return found;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int FindNearestJGPT3
+(
+ kmp_t *kmp, // valid KMP
+ const float32 *pos, // 3d coordinates
+ double *res_dist2 // not NULL: store distance^2 here
+)
+{
+ DASSERT(kmp);
+ DASSERT(pos);
+
+ int found = -1;
+ double max_dist2 = 1e30;
+
+ const uint n_jgpt = kmp->dlist[KMP_JGPT].used;
+ kmp_jgpt_entry_t *jgpt = (kmp_jgpt_entry_t*)kmp->dlist[KMP_JGPT].list;
+
+ uint i;
+ for ( i = 0; i < n_jgpt; i++, jgpt++ )
+ {
+ double3 d;
+ d.x = pos[0] - jgpt->position[0];
+ d.y = pos[1] - jgpt->position[1];
+ d.z = pos[2] - jgpt->position[2];
+ const double dist2 = LengthSqare3(d);
+ if ( max_dist2 > dist2 )
+ {
+ max_dist2 = dist2;
+ found = i;
+ }
+ }
+
+ if ( res_dist2 )
+ *res_dist2 = max_dist2;
+ return found;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AutoRespawn ( kmp_t * kmp )
+{
+ DASSERT(kmp);
+
+
+ //--- auto add respawn points
+
+ const uint min_rm = kmp->jgpt_auto_remove > 1 ? 0 : kmp->dlist[KMP_JGPT].used;
+ const int sect = kmp->jgpt_auto_define - 1;
+ if ( sect == KMP_ENPT || sect == KMP_ITPT )
+ {
+ const uint n = kmp->dlist[sect].used;
+ const kmp_enpt_entry_t *pt0 = (kmp_enpt_entry_t*)kmp->dlist[sect].list;
+ const kmp_enpt_entry_t *pt = pt0;
+ const kmp_flag_t *kf = sect == KMP_ENPT ? &kmp->enpt_flag : &kmp->itpt_flag;
+
+ uint i;
+ for ( i = 0; i < n && kmp->dlist[KMP_JGPT].used < KMP_MAX_POINT; i++, pt++ )
+ {
+ if ( i < KMP_MAX_POINT && !( kf->flags[i] & KPFL_JGPT ) )
+ continue;
+
+ double dist2;
+ FindNearestJGPT3(kmp,pt->position,&dist2);
+ if ( dist2 > kmp->jgpt_auto_distance )
+ {
+ kmp_jgpt_entry_t * entry = AppendList(kmp->dlist+KMP_JGPT);
+ entry->position[0] = pt->position[0] + kmp->jgpt_auto_adjust.x;
+ entry->position[1] = pt->position[1] + kmp->jgpt_auto_adjust.y;
+ entry->position[2] = pt->position[2] + kmp->jgpt_auto_adjust.z;
+ entry->id = kmp->dlist[KMP_JGPT].used - 1;
+ entry->effect = M1(entry->effect);
+
+ uint next_idx = GetNextPointKMP(kmp,KMP_ENPT,i,-1,false,0,0,0,0,0);
+ DASSERT( next_idx < n );
+ const kmp_enpt_entry_t *next = pt0 + next_idx;
+
+ double3 p1, p2;
+ p1.x = pt->position[0];
+ p1.y = pt->position[1];
+ p1.z = pt->position[2];
+ p2.x = next->position[0];
+ p2.y = next->position[1];
+ p2.z = next->position[2];
+ double3 dir = CalcDirection3D(&p1,&p2);
+ entry->rotation[0] = dir.x;
+ entry->rotation[1] = dir.y;
+ entry->rotation[2] = dir.z;
+ }
+ }
+ }
+
+
+ //--- auto connect check points to respawn points
+
+ u8 jgpt_used[KMP_MAX_POINT];
+ memset(jgpt_used,0,sizeof(jgpt_used));
+
+ const uint n_ckpt = kmp->dlist[KMP_CKPT].used < KMP_MAX_POINT
+ ? kmp->dlist[KMP_CKPT].used : KMP_MAX_POINT;
+ kmp_ckpt_entry_t *ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list;
+ uint i;
+ for ( i = 0; i < n_ckpt; i++, ckpt++ )
+ {
+ if ( kmp->ckpt_auto_respawn[i] )
+ {
+ const double x = ( ckpt->left[0] + ckpt->right[0] ) / 2;
+ const double z = ( ckpt->left[1] + ckpt->right[1] ) / 2;
+ ckpt->respawn = FindNearestJGPT2(kmp,x,z,0);
+ }
+ if ( (uint)ckpt->respawn < KMP_MAX_POINT )
+ jgpt_used[ckpt->respawn] = 1;
+ }
+
+
+ //--- remove unused respawn points
+
+ const uint n_jgpt = kmp->dlist[KMP_JGPT].used;
+ if ( kmp->jgpt_auto_remove > 0 && n_jgpt > min_rm )
+ {
+ PRINT("--> JGPT: auto remove: %u..%u\n",min_rm,n_jgpt-1);
+
+ u8 map[KMP_MAX_POINT];
+ memset(map,0,sizeof(map));
+
+ kmp_jgpt_entry_t *base = (kmp_jgpt_entry_t*)kmp->dlist[KMP_JGPT].list;
+ kmp_jgpt_entry_t *src = base, *dest = base;
+
+ uint i, new_idx = 0;
+ for ( i = 0; i < n_jgpt; i++, src++ )
+ {
+ if (jgpt_used[i])
+ {
+ new_idx = dest - base;
+ memcpy(dest,src,sizeof(*dest));
+ dest++;
+ }
+ map[i] = new_idx;
+ }
+ kmp->dlist[KMP_JGPT].used = dest - base;
+ //HEXDUMP16(0,0,map,sizeof(map));
+
+ kmp_ckpt_entry_t *ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list;
+ for ( i = 0; i < n_ckpt; i++, ckpt++ )
+ ckpt->respawn = map[ckpt->respawn];
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextKMP(): reference KCL ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void DefineReferenceKCLbyKMP ( kmp_t *kmp )
+{
+ kcl_ref_prio = 0;
+
+ if ( kmp && kmp->enable_fall_kcl )
+ {
+ if (kmp->fall_kcl)
+ {
+ ResetKCL(kmp->fall_kcl);
+ FREE(kmp->fall_kcl);
+ kmp->fall_kcl = 0;
+ }
+
+ DrawKCL_t dk;
+ memset(&dk,0,sizeof(dk));
+
+ const draw_mode_t pflags = ( KMP_DRAW & DMD_M_PFLAGS ) >> DMD_SHIFT_PFLAGS;
+
+ uint n = kmp->dlist[KMP_GOBJ].used;
+ PRINT("\e[36;1m### DefineReferenceKCLbyKMP(), N(GOBJ) = %u\e[0m\n",n);
+ const kmp_gobj_entry_t *op = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list;
+ for ( ; n > 0; n--, op++ )
+ {
+ if ( op->obj_id < N_KMP_GOBJ
+ && ObjectInfo[op->obj_id].flags & OBF_ROAD
+ && ( !pflags || pflags & op->pflags )
+ )
+ {
+ if (!kmp->fall_kcl)
+ {
+ kcl_t *ref = LoadReferenceKCL();
+ PRINT("\e[36;1m### CREATE KMP, kcl_ref=%d\e[0m\n",ref!=0);
+ if (!ref)
+ break;
+
+ kmp->fall_kcl = MALLOC(sizeof(kcl_t));
+ CopyKCL(kmp->fall_kcl,true,ref);
+ kmp->fall_kcl->silent_octree = true;
+ kmp->fall_kcl->fast = true;
+ dk.kcl = dk.draw = kcl_ref_prio = kmp->fall_kcl;
+ dk.kmp = kmp;
+ dk.detailed = dk.as_road = true;
+ }
+ AddObject2KCL(&dk,op);
+ }
+ }
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextKMP() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanTextKMP
+(
+ kmp_t * kmp, // KMP data structure
+ bool init_kmp, // true: initialize 'kmp' first
+ const void * data, // data to scan
+ uint data_size // size of 'data'
+)
+{
+ PRINT("ScanTextKMP(init=%d)\n",init_kmp);
+ KMP_ACTION_LOG(false,"ScanTextKMP() %s\n",kmp->fname);
+
+ DASSERT(kmp);
+ if (init_kmp)
+ InitializeKMP(kmp);
+ else if (kmp->fname)
+ DefineAutoLoadReferenceKCL(kmp->fname,".kmp.txt");
+
+ ScanInfo_t si;
+ InitializeSI(&si,data,data_size,kmp->fname,kmp->revision);
+ si.predef = SetupVarsKMP();
+
+ enumError max_err = ERR_OK;
+ kmp->is_pass2 = false;
+
+ for(;;)
+ {
+ PRINT("----- SCAN KMP SECTIONS, PASS%u ...\n",kmp->is_pass2+1);
+
+ max_err = ERR_OK;
+ si.kmp = kmp;
+ si.no_warn = !kmp->is_pass2;
+ si.total_err = 0;
+ DefineIntVar(&si.gvar, "$PASS", kmp->is_pass2+1 );
+
+ for(;;)
+ {
+ char ch = NextCharSI(&si,true);
+ if (!ch)
+ break;
+
+ if ( ch != '[' )
+ {
+ NextLineSI(&si,true,false);
+ continue;
+ }
+ ResetLocalVarsSI(&si,kmp->revision);
+
+ si.cur_file->ptr++;
+ char name[20];
+ ScanNameSI(&si,name,sizeof(name),true,true,0);
+ noPRINT("--> pass=%u: #%04u: %s\n",kmp->is_pass2+1,si.cur_file->line,name);
+
+ int abbrev_count;
+ const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,kmp_section_name);
+ if ( !cmd || abbrev_count )
+ continue;
+ NextLineSI(&si,false,false);
+ noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr);
+
+ enumError err = ERR_OK;
+ switch (cmd->id)
+ {
+ case KMP_KTPT:
+ err = ScanTextKTPT(kmp,&si);
+ break;
+
+ case KMP_ENPT:
+ err = ScanText_PT_PH( kmp, &si, KMP_ENPT );
+ break;
+
+ case KMP_ITPT:
+ err = ScanText_PT_PH( kmp, &si, KMP_ITPT );
+ break;
+
+ case KMP_CKPT:
+ err = ScanTextCKPT(kmp,&si);
+ break;
+
+ case KMP_GOBJ:
+ err = ScanTextGOBJ(kmp,&si);
+ break;
+
+ case KMP_POTI:
+ err = ScanTextPOTI(kmp,&si);
+ break;
+
+ case KMP_AREA:
+ err = ScanTextAREA(kmp,&si);
+ break;
+
+ case KMP_CAME:
+ err = ScanTextCAME(kmp,&si);
+ break;
+
+ case KMP_JGPT:
+ err = ScanTextPT2(kmp,&si,KMP_JGPT);
+ break;
+
+ case KMP_CNPT:
+ err = ScanTextPT2(kmp,&si,KMP_CNPT);
+ break;
+
+ case KMP_MSPT:
+ err = ScanTextPT2(kmp,&si,KMP_MSPT);
+ break;
+
+ case KMP_STGI:
+ err = ScanTextSTGI(kmp,&si);
+ break;
+
+ case KMP_N_SECT: // alias for [SETUP]
+ err = ScanTextSETUP(kmp,&si);
+ break;
+
+ default:
+ // ignore all other section without any warnings
+ break;
+ }
+
+ if ( max_err < err )
+ max_err = err;
+ }
+
+ if (kmp->is_pass2)
+ break;
+
+ kmp->is_pass2 = true;
+
+ DefineReferenceKCLbyKMP(kmp);
+ RestartSI(&si);
+ ClearSectionsKMP(kmp);
+ }
+
+ #if HAVE_PRINT0
+ printf("VAR DUMP/GLOBAL:\n");
+ DumpVarMap(stdout,3,&si.gvar,false);
+ #endif
+
+ CheckLevelSI(&si);
+ if ( max_err < ERR_WARNING && si.total_err )
+ max_err = ERR_WARNING;
+ PRINT("ERR(ScanTextKMP) = %u (errcount=%u)\n", max_err, si.total_err );
+ ResetSI(&si);
+
+ SnapToENPT(kmp,KMP_JGPT);
+ SnapToENPT(kmp,KMP_CNPT);
+ SnapToENPT(kmp,KMP_MSPT);
+
+ AutoRespawn(kmp);
+
+ AddEnemyRouteObjects(kmp,KMP_ENPT,&kmp->rtobj_enpt);
+ AddEnemyRouteObjects(kmp,KMP_ITPT,&kmp->rtobj_itpt);
+ AddJugemRouteObjects(kmp,KMP_JGPT,&kmp->rtobj_jgpt);
+ AddJugemRouteObjects(kmp,KMP_CNPT,&kmp->rtobj_cnpt);
+ AddJugemRouteObjects(kmp,KMP_MSPT,&kmp->rtobj_mspt);
+ AddRouteObjects(kmp);
+ AddCheckpointObject(kmp);
+
+ if (opt_generic)
+ {
+ uint sect;
+ for ( sect = 0; sect < KMP_N_SECT; sect++ )
+ {
+ ResetIL(kmp->index+sect);
+ FillIL(kmp->index+sect,kmp->dlist[sect].used);
+ }
+ }
+
+ kmp->battle_mode = CheckBattleModeKMP(kmp);
+ DefineReferenceKCLbyKMP(0);
+ return max_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SaveTextKMP() helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[print_kmp_group_t]]
+
+typedef struct print_kmp_group_t
+{
+ int index; // current group index
+ int max; // max group index
+ const kmp_enph_entry_t *ph; // pointer to next ph element
+ const kmp_group_name_t *gname; // list with names
+
+} print_kmp_group_t;
+
+//-----------------------------------------------------------------------------
+
+static void init_print_kmp_group
+(
+ print_kmp_group_t * grp, // group element, will be initialized
+ const kmp_t * kmp, // KMP data structure
+ uint sect_ph, // related ph section index, may be invalid
+ const kmp_group_name_t * gname // list with names
+)
+{
+ DASSERT(grp);
+ DASSERT(kmp);
+
+ memset(grp,0,sizeof(*grp));
+
+ if ( sect_ph < KMP_N_SECT )
+ {
+ grp->max = kmp->dlist[sect_ph].used;
+ grp->ph = (kmp_enph_entry_t*)kmp->dlist[sect_ph].list;
+ grp->gname = gname;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static ccp print_kmp_group
+(
+ File_t * F, // output file
+ const kmp_t * kmp, // KMP data structure
+ const kmp_ph_t * ph, // related PH info
+ print_kmp_group_t * grp, // group element, will be initialized
+ uint index, // index of next element
+ ccp eol_text, // text, printed to close previous line
+ bool check_eline // true: print cross ref of GOBJ/ELINE_CONTROL
+)
+{
+ DASSERT(F);
+ DASSERT(kmp);
+ DASSERT(ph);
+ DASSERT(grp);
+
+ if ( grp->index < grp->max && index == grp->ph->pt_start )
+ {
+ if (eol_text)
+ {
+ fputs(eol_text,F->f);
+ eol_text = 0;
+ }
+
+ const uint idx = grp->index++;
+ const u8 *ptr, *end;
+
+ fprintf(F->f,"\r\n # zero based index: %u, prev:", idx );
+ for ( ptr = grp->ph->prev, end = ptr + KMP_MAX_PH_LINK; ptr < end; ptr++ )
+ if ( *ptr != 0xff )
+ fprintf(F->f," %u",*ptr);
+
+ fprintf(F->f,", next:");
+ for ( ptr = grp->ph->next, end = ptr + KMP_MAX_PH_LINK; ptr < end; ptr++ )
+ if ( *ptr != 0xff )
+ fprintf(F->f," %u",*ptr);
+
+
+ //--- $GROUP
+
+ fprintf(F->f,"\r\n $GROUP %s, next:", grp->gname[idx] );
+
+ for ( ptr = grp->ph->next, end = ptr + KMP_MAX_PH_LINK; ptr < end; )
+ {
+ const u8 *start = ptr;
+ const u8 code = *ptr++;
+ while ( ptr < end && *ptr == code )
+ ptr++;
+ if ( code != 0xff )
+ {
+ const uint n = ptr - start;
+ if ( n > 1 )
+ fprintf(F->f," %s*%u",grp->gname[code],n);
+ else
+ fprintf(F->f," %s",grp->gname[code]);
+ }
+ }
+
+
+ //--- $PREV
+
+ bool print_prev = ph->ac_mode != KMP_AC_PREV;
+ if ( !print_prev && idx < KMP_MAX_LINFO )
+ {
+ SetupRouteLinksKMP(kmp,ph->sect_ph,false);
+ DASSERT(kmp->linfo);
+ DASSERT(kmp->linfo_sect==ph->sect_ph);
+
+ uint i;
+ kmp_linfo_mode *ptr;
+ for ( i = 0, ptr = kmp->linfo->list[idx]; i < kmp->linfo->used; i++ )
+ {
+ const kmp_linfo_mode md = *ptr++;
+ if ( !!(md & LINFO_PREV) == !(md & LINFO_BY_NEXT) )
+ {
+ print_prev = true;
+ break;
+ }
+ }
+ }
+
+ if (print_prev)
+ {
+ fprintf(F->f,"\r\n $PREV:");
+
+ for ( ptr = grp->ph->prev, end = ptr + KMP_MAX_PH_LINK; ptr < end; )
+ {
+ const u8 *start = ptr;
+ const u8 code = *ptr++;
+ while ( ptr < end && *ptr == code )
+ ptr++;
+ if ( code != 0xff )
+ {
+ const uint n = ptr - start;
+ if ( n > 1 )
+ fprintf(F->f," %s*%u",grp->gname[code],n);
+ else
+ fprintf(F->f," %s",grp->gname[code]);
+ }
+ }
+ }
+
+
+
+ //--- $SETTINGS
+
+ if ( idx < KMP_MAX_GROUP && grp->ph )
+ {
+ const kmp_gopt2_t *go2 = ph->gopt + idx;
+ if ( ph->show_options || go2->setting[0] || go2->setting[1]
+ || go2->rtype_user || go2->rtype_active == KMP_RT_DISPATCH )
+ {
+ const KeywordTab_t *key = GetKewordById(kmp_rtype_name,go2->rtype_user);
+ fprintf(F->f, "\r\n $SETTINGS: %u 0x%02x %s",
+ go2->setting[0], go2->setting[1],
+ key ? key->name1 : "AUTO" );
+ if ( go2->rtype_user != go2->rtype_active )
+ {
+ const KeywordTab_t *key = GetKewordById(kmp_rtype_name,go2->rtype_active);
+ if (key)
+ fprintf(F->f, " # >%s",key->name1);
+ }
+ }
+
+ if ( ph->show_options )
+ {
+#if 0 // [[ac]]
+ fprintf(F->f,
+ "\r\n $CONNECT-PREV: %3d %3d %3d # class prio min"
+ "\r\n $CONNECT-NEXT: %3d %3d %3d",
+ go2->prev.cls, go2->prev.prio, go2->prev.min,
+ go2->next.cls, go2->next.prio, go2->next.min );
+#endif
+ }
+ }
+
+
+ //--- ELINE_CONTROL
+
+ if (check_eline)
+ {
+ uint gidx;
+ for ( gidx = 0; gidx < kmp->dlist[KMP_GOBJ].used; gidx++ )
+ {
+ const kmp_gobj_entry_t *op = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list + gidx;
+ if ( op->obj_id == GOBJ_ELINE_CONTROL && op->setting[7] == idx )
+ fprintf(F->f,
+ "\r\n # GOBJ/ELINE_CONTROL #%u links to this group:"
+ " enter=%d, sec=%d, id=%d, next=%d",
+ gidx, op->setting[0], op->setting[1], op->setting[2], op->setting[3] );
+ }
+ }
+
+ fputs("\r\n\r\n",F->f);
+ grp->ph++;
+ }
+
+ return eol_text;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp print_kmp_flags ( kmp_pt_flag_t flags, kmp_pt_flag_t pmask )
+{
+ if (!opt_kmp_flags)
+ return EmptyString;
+
+ static char buf[4];
+
+ char *d = buf;
+ if ( pmask & KPFL_FALL ) *d++ = flags & KPFL_FALL ? 'F' : '-';
+ if ( pmask & KPFL_SNAP ) *d++ = flags & KPFL_SNAP ? 'S' : '-';
+ if ( pmask & KPFL_JGPT ) *d++ = flags & KPFL_JGPT ? 'J' : '-';
+
+ DASSERT( d < buf + sizeof(buf) );
+ *d = 0;
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void print_kmp_pt
+(
+ File_t * F, // output file
+ const kmp_t * kmp, // KMP data structure
+ uint sect_pt // KMP PT section index
+)
+{
+ DASSERT(F);
+ DASSERT(kmp);
+ DASSERT( sect_pt == KMP_ENPT || sect_pt == KMP_ITPT );
+
+ //--- section setup
+
+ char buf[30];
+ uint sect_ph;
+ const kmp_ph_t *ph;
+ const kmp_flag_t *kf;
+ kmp_pt_flag_t flag_mask;
+ uint show_route;
+
+ if ( sect_pt == KMP_ENPT )
+ {
+ sect_ph = KMP_ENPH;
+ ph = &kmp->enph;
+ kf = &kmp->enpt_flag;
+ flag_mask = KPFL_M_ENPT;
+ show_route = 1 << SHRT_ENPT;
+ }
+ else
+ {
+ sect_ph = KMP_ITPH;
+ ph = &kmp->itph;
+ kf = &kmp->itpt_flag;
+ flag_mask = KPFL_M_ITPT;
+ show_route = 1 << SHRT_ITPT;
+ }
+
+ ccp pt_name = kmp_section_name[sect_pt].name1;
+ ccp ph_name = kmp_section_name[sect_ph].name1;
+
+
+ //--- print data
+
+ fprintf(F->f,text_kmp_pt_info_cr,
+ REVISION_NUM,
+ pt_name, kmp->value[sect_pt], // @%s-HEAD-VALUE = %#x
+ ph_name, kmp->value[sect_ph], // @%s-HEAD-VALUE = %#x
+ show_route,
+ opt_export_flags ? opt_export_flags>0 : kf->export );
+
+ if ( opt_route_options > OFFON_OFF )
+ {
+ ccp ac_mode, ac_fix;
+
+ switch ( ph->ac_flags & KMP_ACF_FIX )
+ {
+ case KMP_ACF_FIX_PREV: ac_fix = " | ACF$FIX_PREV"; break;
+ case KMP_ACF_FIX_NEXT: ac_fix = " | ACF$FIX_NEXT"; break;
+ case KMP_ACF_FIX: ac_fix = " | ACF$FIX"; break;
+ default: ac_fix = ""; break;
+ }
+
+ switch( ph->ac_mode )
+ {
+ case KMP_AC_OFF: ac_mode = "AC$OFF"; break;
+ case KMP_AC_PREV: ac_mode = "AC$PREV"; break;
+ case KMP_AC_DISPATCH: ac_mode = "AC$DISPATCH"; break;
+
+ default: // should never happen
+ snprintf(buf,sizeof(buf),"0x%0x",ph->ac_mode|ph->ac_flags);
+ ac_mode = buf;
+ ac_fix = "";
+ }
+
+ if ( sect_pt == KMP_ENPT )
+ fprintf(F->f,text_kmp_enpt_ropt_cr, ph->show_options, ac_mode, ac_fix );
+ else
+ fprintf(F->f,text_kmp_itpt_ropt_cr, ph->show_options, ac_mode, ac_fix );
+ }
+
+ if ( ph->mask[0] != 0xffff || ph->mask[1] != 0xffff )
+ fprintf(F->f,text_kmp_pt_mask_cr, ph->mask[0], ph->mask[1] );
+
+ fprintf(F->f, text_kmp_pt_head_cr, pt_name );
+
+ const uint n = kmp->dlist[sect_pt].used;
+ const kmp_enpt_entry_t * pt = (kmp_enpt_entry_t*)kmp->dlist[sect_pt].list;
+
+ print_kmp_group_t grp;
+ init_print_kmp_group(&grp,kmp,sect_ph,ph->gname);
+ ccp eol = "";
+
+ uint i, gi = 0;
+ for ( i = 0; i < n; i++, gi++, pt++ )
+ {
+ eol = print_kmp_group(F,kmp,ph,&grp,i,eol,sect_pt==KMP_ENPT);
+ if (!eol)
+ {
+ gi = 0;
+ eol = "";
+ }
+ else if ( gi > 1 )
+ {
+ fprintf(F->f," %7.2f",
+ AngleVector(pt[-2].position,pt[-1].position,pt->position,true) );
+ }
+
+ fprintf(F->f,"%s%4u %11.3f %11.3f %11.3f %7.3f %#6x %#6x",
+ eol, i,
+ pt->position[0], pt->position[1], pt->position[2],
+ pt->scale,
+ pt->prop[0], pt->prop[1] );
+ eol = "\r\n";
+
+ const kmp_pt_flag_t flags = i < KMP_MAX_POINT ? kf->flags[i] : kf->mask;
+ fprintf(F->f," %-3s",print_kmp_flags(flags,flag_mask));
+
+ if ( gi > 0 )
+ fprintf(F->f," # %9.3f", LengthF(pt[-1].position,pt->position) );
+ }
+ fputs(eol,F->f);
+
+
+ //----- list class names -----
+
+ if ( KMP_MODE & KMPMD_DUMP_CLASS && ph->class_name.used > KCLS_USER )
+ {
+ fprintf(F->f,"\r\n#----- List of %u classes -----\r\n#\r\n",
+ ph->class_name.used);
+
+ ParamFieldItem_t *ptr = ph->class_name.field, *end;
+ for ( end = ptr + ph->class_name.used; ptr < end; ptr++ )
+ fprintf(F->f,"#%4d %s\r\n",ptr->num,ptr->key);
+ }
+
+
+ //----- list oneway links -----
+
+ if ( KMP_MODE & KMPMD_DUMP_ONEWAY && ph->oneway_used )
+ {
+ fprintf(F->f,"\r\n#----- List of %u oneway link%s -----\r\n#",
+ ph->oneway_used, ph->oneway_used==1 ? "" : "s" );
+
+ SortOnewayPH(ph);
+
+ uint max_fw = 1;
+ u16 *ptr, *end = ph->oneway_list + ph->oneway_used;
+ for ( ptr = ph->oneway_list; ptr < end; ptr++ )
+ {
+ const uint fw = strlen(grp.gname[*ptr>>8]);
+ if ( max_fw < fw )
+ max_fw = fw;
+ }
+
+ uint prev = ~0;
+ for ( ptr = ph->oneway_list; ptr < end; ptr++ )
+ {
+ uint from = *ptr>>8;
+ if ( prev != from )
+ {
+ prev = from;
+ fprintf(F->f,"\r\n# %*s :",max_fw,grp.gname[from]);
+ }
+ fprintf(F->f," %s",grp.gname[*ptr&0xff]);
+ }
+ fputs("\r\n",F->f);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void print_kmp_ph
+(
+ File_t * F, // output file
+ const kmp_t * kmp, // KMP data structure
+ uint sect_ph // KMP section index
+)
+{
+ DASSERT(F);
+ DASSERT(kmp);
+ DASSERT( sect_ph == KMP_CKPH || sect_ph == KMP_ENPH || sect_ph == KMP_ITPH );
+
+ //--- section setup
+
+ uint sect_pt;
+ const kmp_ph_t *ph;
+
+ if ( sect_ph == KMP_CKPH )
+ {
+ sect_pt = KMP_CKPT;
+ ph = &kmp->ckph;
+ }
+ else if ( sect_ph == KMP_ENPH )
+ {
+ sect_pt = KMP_ENPT;
+ ph = &kmp->enph;
+ }
+ else
+ {
+ sect_pt = KMP_ITPT;
+ ph = &kmp->itph;
+ }
+
+ ccp pt_name = kmp_section_name[sect_pt].name1;
+ ccp ph_name = kmp_section_name[sect_ph].name1;
+
+ //--- print data
+
+ fprintf(F->f,text_kmp_ph_cr,
+ pt_name,
+ REVISION_NUM,
+ ph_name, kmp->value[sect_ph],
+ ph_name );
+
+ const uint n = kmp->dlist[sect_ph].used;
+ const kmp_enph_entry_t * p = (kmp_enph_entry_t*)kmp->dlist[sect_ph].list;
+ uint i;
+ for ( i = 0; i < n; i++, p++ )
+ {
+ fprintf(F->f,"%4u %3u %3u %3u ",
+ i, p->pt_start, p->pt_start+p->pt_len-1, p->pt_len );
+ DASSERT( p->prev + KMP_MAX_PH_LINK == p->next ); // assume 'prev' before 'next'
+ const u8 * d = p->prev;
+ uint j;
+ for ( j = 0; j < 12; j++, d++ )
+ {
+ if ( *d == 0xff )
+ fprintf(F->f,"%s -", j==6 ? " " : "" );
+ else
+ fprintf(F->f,"%s%4u", j==6 ? " " : "", *d );
+ }
+
+ if ( i < KMP_MAX_GROUP )
+ {
+ const kmp_gopt2_t *go2 = ph->gopt + i;
+ #ifdef TEST
+ fprintf(F->f," %3u 0x%02x # %-6s %s\r\n",
+ go2->setting[0], go2->setting[1], ph->gname[i],
+ i < kmp->index[sect_ph].used ? kmp->index[sect_ph].list[i] : "-" );
+ #else
+ fprintf(F->f," %3u 0x%02x # %s\r\n",
+ go2->setting[0], go2->setting[1], ph->gname[i] );
+ #endif
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void print_kmp_pt2
+(
+ File_t * F, // output file
+ const kmp_t * kmp, // KMP data structure
+ uint sect, // KMP section index
+ uint * ref_count // NULL or reference counter array (len=0x100)
+)
+{
+ DASSERT(F);
+ DASSERT(kmp);
+ DASSERT( sect < KMP_N_SECT );
+
+ uint i;
+ const uint n = kmp->dlist[sect].used;
+ const kmp_jgpt_entry_t * pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list;
+ IndexList_t *il = (IndexList_t*)kmp->index + sect; // cast because remove const
+ FillIL(il,n);
+
+ bool auto_id = true;
+ for ( i = 0; i < n; i++, pt++ )
+ if ( pt->id != i )
+ {
+ auto_id = false;
+ break;
+ }
+
+ ccp pt_name = kmp_section_name[sect].name1;
+ uint show_route = sect == KMP_JGPT
+ ? (1<<SHRT_JGPT)
+ : sect == KMP_CNPT
+ ? (1<<SHRT_CNPT)
+ : (1<<SHRT_MSPT);
+ const u8 *pt_flags = sect == KMP_JGPT ? kmp->jgpt_flag.flags : 0;
+
+ fprintf( F->f, text_kmp_pt2_cr,
+ REVISION_NUM,
+ pt_name, kmp->value[sect],
+ auto_id,
+ show_route );
+
+ if ( sect == KMP_JGPT )
+ fprintf( F->f, text_kmp_jgpt_auto_cr, kmp->jgpt_flag.export );
+ else
+ fprintf( F->f, text_kmp_pt2_head_cr, pt_name );
+
+ pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list;
+ for ( i = 0; i < n; i++, pt++ )
+ {
+ PrintNameIL(F->f,il,i,5,5,0);
+ fprintf(F->f," %11.3f %11.3f %11.3f %7.2f %7.2f %7.2f %6d %6s",
+ pt->position[0], pt->position[1], pt->position[2],
+ pt->rotation[0], pt->rotation[1], pt->rotation[2],
+ pt->id, PrintU16M1(pt->effect) );
+
+ if (pt_flags)
+ {
+ const kmp_pt_flag_t flags
+ = i < KMP_MAX_POINT ? pt_flags[i] : kmp->jgpt_flag.mask;
+ fprintf(F->f," %s",print_kmp_flags(flags,KPFL_M_JGPT));
+ }
+
+ fprintf(F->f,"%s\r\n", ref_count && !ref_count[i] ? " # unused" : "" );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp GetObjectName ( uint obj_id )
+{
+ const uint bufsize = OBJ_FW_NAME + 3;
+ char *buf = GetCircBuf(bufsize);
+
+ if ( obj_id < N_KMP_GOBJ && ObjectInfo[obj_id].name )
+ snprintf(buf,bufsize,"o$%s",ObjectInfo[obj_id].name);
+ else
+ snprintf(buf,bufsize,"0x%x",obj_id);
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PrintDefObjInfoHelper ( FILE *f, ccp prefix, int mode, u16 mask )
+{
+ DASSERT(f);
+
+ gobj_condition_set_t gcs;
+ const uint n = GetGobjConditions(&gcs,mode,mask);
+
+ uint i;
+ for ( i = 0; i < n; i++ )
+ {
+ const gobj_condition_t *c = gcs.cond+i;
+ fprintf(f,"# %s/0x%04x: %s\n",prefix,c->mask,c->info );
+ }
+
+ return n;
+}
+
+static void PrintDefObjInfo ( FILE *f, const kmp_gobj_entry_t * gobj )
+{
+ DASSERT(f);
+ DASSERT(gobj);
+
+ const u16 balloon = gobj->setting[KMP_GMODE_BALLOON];
+ const u16 coin = gobj->setting[KMP_GMODE_COIN];
+ const u16 versus = gobj->setting[KMP_GMODE_VERSUS];
+ const u16 itemrain = gobj->setting[KMP_GMODE_ITEMRAIN];
+
+ uint count = 0;
+ if ( balloon != versus || coin != versus )
+ {
+ if ( balloon == coin )
+ count += PrintDefObjInfoHelper(f,"Battle",KMP_GMODE_BALLOON,balloon);
+ else
+ {
+ count += PrintDefObjInfoHelper(f,"Balloon",KMP_GMODE_BALLOON,balloon);
+ count += PrintDefObjInfoHelper(f,"Coin",KMP_GMODE_COIN,coin);
+ }
+ count += PrintDefObjInfoHelper(f,"Versus",KMP_GMODE_VERSUS,versus);
+ }
+ else
+ count += PrintDefObjInfoHelper(f,"*",-1,versus);
+
+ if ( versus != itemrain )
+ count += PrintDefObjInfoHelper(f,"Itemrain",KMP_GMODE_ITEMRAIN,itemrain);
+
+ if (!count)
+ fputs("# Always disabled!\n",f);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SaveTextKMP() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SaveTextKMP
+(
+ const kmp_t * kmp, // pointer to valid KMP
+ ccp fname, // filename of destination
+ bool set_time // true: set time stamps
+)
+{
+ // use DOS/Windows line format -> unix can handle it ;)
+
+ DASSERT(kmp);
+ DASSERT(fname);
+ PRINT("SaveTextKMP(%s,%d)\n",fname,set_time);
+ KMP_ACTION_LOG(false,"SaveTextKMP() %s\n",fname);
+
+ char temp_buf[100];
+
+
+ //--- open file
+
+ File_t F;
+ enumError err = CreateFileOpt(&F,true,fname,testmode,kmp->fname);
+ if ( err > ERR_WARNING || !F.f )
+ return err;
+ SetFileAttrib(&F.fatt,&kmp->fatt,0);
+
+
+ //--- print header + syntax info
+
+ if ( print_header && !brief_count && !export_count )
+ fprintf(F.f,text_kmp_head_cr);
+ else
+ fprintf(F.f,
+ "%s\r\n"
+ "# KMP syntax & semantics: https://szs.wiimm.de/info/kmp-syntax.html\r\n"
+ ,KMP_TEXT_MAGIC);
+
+
+ //--- var defs
+
+ uint i, n, gi, ng, np;
+ char buf1[0x100], buf2[0x20];
+
+
+ //--- print info section & section counter alphabetically
+
+ if ( brief_count || export_count )
+ {
+ fprintf(F.f,
+ "%s[SETUP]\r\n\r\n"
+ "TOOL = %s\r\n"
+ "SYSTEM = %s\r\n"
+ "VERSION = %s\r\n"
+ "REVISION = %u\r\n"
+ "DATE = %s\r\n"
+ "INFO = \"%s\"\r\n"
+ "\r\n"
+ "UNKNOWN-0x0c = %#x\r\n"
+ "KCL-FALL-ADD-ROAD = 0\r\n"
+ "BATTLE-MODE = %d\r\n"
+ "KMP-WIM0 = %d\r\n"
+ "\r\n",
+ section_sep,
+ tool_name, SYSTEM, VERSION, REVISION_NUM, DATE,
+ kmp->info ? kmp->info : "",
+ kmp->kmp_version, kmp->battle_mode, kmp->wim0_export );
+ }
+ else
+ {
+ fprintf(F.f, text_kmp_setup_cr,
+ tool_name, SYSTEM, VERSION, REVISION_NUM, DATE,
+ kmp->info ? kmp->info : "",
+ kmp->kmp_version, kmp->battle_mode, kmp->wim0_export );
+
+ ccp last_name = "";
+ for(;;)
+ {
+ ccp found_name = 0;
+ uint sect, found_sect = 0;
+ for ( sect = 0; sect < KMP_N_SECT; sect++ )
+ {
+ ccp name = kmp_section_name[sect].name1;
+ if ( strcmp(name,last_name) > 0
+ && ( !found_name || strcmp(name,found_name) < 0 ) )
+ {
+ found_name = name;
+ found_sect = sect;
+ }
+ }
+
+ if (!found_name)
+ break;
+
+ if ( found_sect == KMP_POTI )
+ fprintf(F.f, " # N-%s = %5u,%5u\r\n",
+ found_name,
+ kmp->dlist[found_sect].used, kmp->poti_point.used );
+ else
+ fprintf(F.f, " # N-%s = %5u\r\n",
+ found_name,
+ kmp->dlist[found_sect].used );
+ last_name = found_name;
+ }
+ }
+
+
+ //--- print default settings
+
+ fprintf(F.f, text_kmp_defaults_cr,
+ GetObjectName(kmp->rtobj.obj),
+ kmp->rtobj.shift.x, kmp->rtobj.shift.y, kmp->rtobj.shift.z,
+ kmp->rtobj.scale.x, kmp->rtobj.scale.y, kmp->rtobj.scale.z );
+
+
+ //--- print parameter support
+
+ if (print_param)
+ {
+ if ( !brief_count && !export_count )
+ fwrite(text_kmp_param1_cr,1,sizeof(text_kmp_param1_cr)-1,F.f);
+ else
+ fprintf(F.f,"#%.78s\r\n# Parameter support\r\n",Minus300);
+
+ fwrite(text_kmp_param2_cr,1,sizeof(text_kmp_param2_cr)-1,F.f);
+
+ if ( !brief_count && !export_count )
+ fwrite(text_kmp_param3_cr,1,sizeof(text_kmp_param3_cr)-1,F.f);
+ }
+
+
+ //--- fill index lists
+
+ IndexList_t *ill = (IndexList_t*)kmp->index; // cast because remove const
+ for ( i = 0; i < KMP_N_SECT; i++ )
+ FillIL(ill+i,kmp->dlist[i].used);
+
+
+ //--- print macros
+
+ fwrite(text_kmp_macros_cr,1,sizeof(text_kmp_macros_cr)-1,F.f);
+
+
+ //--- print AREA entries
+
+ fprintf(F.f,text_kmp_area_cr,REVISION_NUM,kmp->value[KMP_AREA]);
+
+ n = kmp->dlist[KMP_AREA].used;
+ const kmp_area_entry_t * area = (kmp_area_entry_t*)kmp->dlist[KMP_AREA].list;
+ for ( i = 0; i < n; i++, area++ )
+ {
+ *buf1 = 0;
+ PrintNameIL(F.f,ill+KMP_AREA,i,6,8,0);
+ fprintf(F.f,
+ "%#4x %#4x %12.3f %11.3f %11.3f %#6x %#6x %#6x %#6x\r\n >",
+ area->mode, area->type,
+ area->position[0], area->position[1], area->position[2],
+ area->setting[0], area->setting[1], area->setting[2], area->setting[3] );
+
+ if ( area->type == 0x00 )
+ PrintNameIL(F.f,ill+KMP_CAME,area->dest_id,10,10,2);
+ else
+ fprintf(F.f,"%#10x",area->dest_id);
+
+ fprintf(F.f,
+ " %#4x %12.3f %11.3f %11.3f\r\n"
+ " > %28.3f %11.3f %11.3f\r\n"
+ "#%.84s\r\n",
+ area->unknown,
+ area->rotation[0], area->rotation[1], area->rotation[2],
+ area->scale[0], area->scale[1], area->scale[2],
+ Minus300 );
+ }
+
+
+ //--- print CAME entries
+
+ uint ocam = kmp->value[KMP_CAME] >> 8;
+ uint scam = kmp->value[KMP_CAME] & 0xff;
+
+ fprintf(F.f,text_kmp_came_cr,REVISION_NUM,
+ GetNameIL(ill+KMP_CAME,ocam),
+ GetNameIL(ill+KMP_CAME,scam) );
+
+ // find opening cameras
+
+ n = kmp->dlist[KMP_CAME].used;
+ const kmp_came_entry_t * came = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list;
+
+ uint ocount = 0, oidx = ocam;
+ memset(buf1,0,sizeof(buf1));
+ if (print_param)
+ while ( oidx < n && ocount < 0xff && oidx < sizeof(buf1) && !buf1[oidx] )
+ {
+ buf1[oidx] = ++ocount;
+ oidx = came[oidx].next;
+ }
+
+ for ( i = 0; i < n; i++, came++ )
+ {
+ //--- AREA reference
+
+ const kmp_area_entry_t * area = (kmp_area_entry_t*)kmp->dlist[KMP_AREA].list;
+ uint count, idx, nn = kmp->dlist[KMP_AREA].used;
+ for ( count = idx = 0; idx < nn; idx++, area++ )
+ if ( area->type == 0x00 && area->dest_id == i )
+ fprintf(F.f,"%s %s",
+ count++ ? "" : "\t# AREA reference:",
+ GetNameIL(ill+KMP_AREA,idx) );
+ if (count)
+ fputs("\r\n",F.f);
+
+
+ //--- CAME reference
+
+ count = 0;
+ if ( i == ocam )
+ fprintf(F.f,"%s @OPENING-INDEX",
+ count++ ? "" : "\t# CAME reference:" );
+ if ( i == scam )
+ fprintf(F.f,"%s @SELECTION-INDEX",
+ count++ ? "" : "\t# CAME reference:" );
+
+ const kmp_came_entry_t * cref = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list;
+ nn = kmp->dlist[KMP_CAME].used;
+ for ( idx = 0; idx < nn; idx++, cref++ )
+ if ( cref->next == i )
+ fprintf(F.f,"%s %s",
+ count++ ? "" : "\t# CAME reference:",
+ GetNameIL(ill+KMP_CAME,idx) );
+ if (count)
+ fputs("\r\n",F.f);
+
+
+ //--- CAME object
+
+ PrintNameIL(F.f,ill+KMP_CAME,i,6,8,0);
+ fprintf(F.f," %3u %7u %11.3f %11.3f %11.3f %10.3f",
+ came->type, came->came_speed,
+ came->position[0], came->position[1], came->position[2],
+ came->zoom_begin );
+ const double came_time = came->time/60.0;
+ if ( came_time >= 0.01 )
+ fprintf(F.f," # came time:%8.2fs",came_time);
+ fputs("\r\n >",F.f);
+
+ if (buf1[i])
+ {
+ uint idx = (uchar)buf1[i];
+ ccp term = "\r\n > ";
+ if ( idx == ocount )
+ fprintf(F.f,
+ " ( mode$ocam == %u ? %s : mode$test ? %s : %s )%s",
+ idx, GetNameIL(ill+KMP_CAME,i),
+ GetNameIL(ill+KMP_CAME,kmp->value[KMP_CAME]>>8),
+ GetNameIL(ill+KMP_CAME,came->next), term );
+ else
+ fprintf(F.f,
+ " ( mode$ocam == %u ? %s : %s )%s",
+ idx, GetNameIL(ill+KMP_CAME,i),
+ GetNameIL(ill+KMP_CAME,came->next), term );
+ }
+ else
+ PrintNameIL(F.f,ill+KMP_CAME,came->next,10,12,2);
+
+ fprintf(F.f," %5u %11.3f %11.3f %11.3f %10.3f",
+ came->zoom_speed,
+ came->rotation[0], came->rotation[1], came->rotation[2],
+ came->zoom_end );
+ const double zoom_length = fabs(came->zoom_begin-came->zoom_end);
+ if ( zoom_length >= 0.01 && came->zoom_speed )
+ fprintf(F.f," # zoom time:%8.2fs",
+ zoom_length / came->zoom_speed * (10.0/6.0) );
+
+ fprintf(F.f,"\r\n > %#9x %7u %11.3f %11.3f %11.3f",
+ came->unknown_0x02, came->viewpt_speed,
+ came->viewpt_begin[0], came->viewpt_begin[1], came->viewpt_begin[2] );
+ const double viewpt_len = LengthF(came->viewpt_begin,came->viewpt_end);
+ if ( viewpt_len >= 0.01 && came->viewpt_speed )
+ fprintf(F.f," # v.pt time:%8.2fs",
+ viewpt_len / came->viewpt_speed * (10.0/6.0) );
+ fputs("\r\n >",F.f);
+
+ PrintNameIL(F.f,ill+KMP_POTI,came->route,10,11,2);
+ fprintf(F.f,
+ " %#6x %11.3f %11.3f %11.3f %10.3f",
+ came->unknown_0x0a,
+ came->viewpt_end[0], came->viewpt_end[1], came->viewpt_end[2],
+ came->time );
+ if ( viewpt_len >= 0.01 )
+ fprintf(F.f," # v.pt len:%9.2f",viewpt_len);
+ fprintf(F.f,"\r\n#%.91s\r\n",Minus300);
+ }
+
+
+ //--- print CKPH entries
+
+ if ( brief_count < 2 && !export_count )
+ {
+ fwrite(text_kmp_ckph_cr,1,sizeof(text_kmp_ckph_cr)-1,F.f);
+ print_kmp_ph(&F,kmp,KMP_CKPH);
+ }
+
+
+ //--- print CKPT entries
+
+ bool auto_next = true;
+ n = kmp->dlist[KMP_CKPT].used;
+ ng = kmp->dlist[KMP_CKPH].used;
+ const kmp_ckph_entry_t * ckph = (kmp_ckph_entry_t*)kmp->dlist[KMP_CKPH].list;
+ for ( gi = 0; gi < ng && auto_next; gi++, ckph++ )
+ {
+ uint start = ckph->pt_start;
+ uint end = start + ckph->pt_len - 1;
+ if ( !ckph->pt_len || end >= n )
+ {
+ auto_next = false;
+ break;
+ }
+
+ const kmp_ckpt_entry_t * ckpt
+ = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list + start;
+ uint j;
+ for ( j = start; j <= end; j++, ckpt++ )
+ {
+ if ( ckpt->prev != ( j == start ? -1 : j-1 )
+ || ckpt->next != ( j == end ? -1 : j+1 ) )
+ {
+ auto_next = false;
+ break;
+ }
+ }
+ }
+
+ fprintf(F.f,text_kmp_ckpt_cr,
+ REVISION_NUM,
+ kmp->value[KMP_CKPT], kmp->value[KMP_CKPH],
+ auto_next,
+ ObjectInfo[RTOBJ_1].name, ObjectInfo[RTOBJ_2].name,
+ RTOBJ_SCALE_X, RTOBJ_SCALE_Y, RTOBJ_SCALE_Z,
+ RTOBJ_SCALE_X, RTOBJ_SCALE_Y, RTOBJ_SCALE_Z,
+ RTOBJ_BASE, RTOBJ_BASE );
+
+ const kmp_ckpt_entry_t * ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list;
+
+ print_kmp_group_t grp;
+ init_print_kmp_group(&grp,kmp,KMP_CKPH,kmp->ckph.gname);
+ uint jgpt_count[0x100];
+ memset(jgpt_count,0,sizeof(jgpt_count));
+
+ u8 prev_mode = M1(prev_mode);
+ for ( i = 0; i < n; i++, ckpt++ )
+ {
+ if ( print_kmp_group(&F,kmp,&kmp->ckph,&grp,i,"",false)
+ && IS_M1(ckpt->mode) && !IS_M1(prev_mode) )
+ {
+ fputs("\r\n",F.f);
+ }
+ prev_mode = ckpt->mode;
+ jgpt_count[(uint)ckpt->respawn]++;
+
+ double width, direction;
+ WidthAndDirection(&width,&direction,true,ckpt->left,ckpt->right);
+
+ const uint indent
+ = fprintf(F.f,"%4u %11.3f %11.3f %11.3f %11.3f ",
+ i,
+ ckpt->left[0], ckpt->left[1],
+ ckpt->right[0], ckpt->right[1] );
+ PrintNameIL(F.f,ill+KMP_JGPT,ckpt->respawn,5,5,indent);
+ fprintf(F.f," %3d %3d %3d # %9.3f %7.2f\r\n",
+ IS_M1(ckpt->mode) ? -1 : (uint)ckpt->mode,
+ IS_M1(ckpt->prev) ? -1 : (uint)ckpt->prev,
+ IS_M1(ckpt->next) ? -1 : (uint)ckpt->next,
+ width, direction );
+ }
+
+
+ //--- print CNPT entries
+
+ fwrite(text_kmp_cnpt_cr,1,sizeof(text_kmp_cnpt_cr)-1,F.f);
+ print_kmp_pt2(&F,kmp,KMP_CNPT,0);
+
+
+ //--- print ENPH entries
+
+ if ( brief_count < 2 && !export_count )
+ {
+ fwrite(text_kmp_enph_cr,1,sizeof(text_kmp_enph_cr)-1,F.f);
+ print_kmp_ph(&F,kmp,KMP_ENPH);
+ }
+
+
+ //--- print ENPT entries
+
+ fwrite(text_kmp_enpt_cr,1,sizeof(text_kmp_enpt_cr)-1,F.f);
+ print_kmp_pt(&F,kmp,KMP_ENPT);
+
+
+ //--- print GOBJ statistics
+
+ n = kmp->dlist[KMP_GOBJ].used;
+ fprintf(F.f,text_kmp_gobj_cr,REVISION_NUM,kmp->value[KMP_GOBJ]);
+
+ uint min_id = 0, n_types = 0, n_defobj = 0;
+ for(;;)
+ {
+ uint current_id = UINT_MAX, count = 0;
+
+ const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list;
+ for ( i = 0; i < n; i++, gobj++ )
+ {
+ if ( gobj->obj_id >= min_id )
+ {
+ if ( gobj->obj_id == current_id )
+ count++;
+ else if ( gobj->obj_id < current_id )
+ {
+ count = 1;
+ current_id = gobj->obj_id;
+ }
+ }
+ }
+
+ if (!count)
+ break;
+
+ if (IsDefinitionObjectId(current_id))
+ n_defobj += count;
+ else
+ {
+ const u16 relevant_id = current_id & GOBJ_M_OBJECT;
+ if ( relevant_id < N_KMP_GOBJ && ObjectInfo[relevant_id].name )
+ {
+ const ObjectInfo_t *oi = ObjectInfo + relevant_id;
+ fprintf(F.f,
+ "##%4u %#6x %c%-*s %s\r\n",
+ count, current_id, current_id==relevant_id ? ' ' : '|',
+ OBJ_FW_NAME, oi->name, oi->charact );
+ }
+ else
+ fprintf(F.f,"##%4u %#6x\r\n", count, current_id );
+ n_types++;
+ }
+ min_id = current_id + 1;
+ }
+
+ const int nstd = n - n_defobj;
+ fprintf(F.f,"##%.83s\r\n## GOBJ total: %u object%s, %u type%s",
+ Minus300,
+ nstd, nstd == 1 ? "" : "s",
+ n_types, n_types == 1 ? "" : "s" );
+ if (n_defobj)
+ fprintf(F.f," + %u definition object%s",
+ n_defobj, n_defobj == 1 ? "" : "s" );
+ fprintf(F.f,"\r\n##%.83s\r\n\r\n",Minus300);
+
+
+ //--- print GOBJ entries
+
+ fwrite(text_kmp_gobj_tabhead_cr,1,sizeof(text_kmp_gobj_tabhead_cr)-1,F.f);
+
+ u8 done[N_KMP_GOBJ];
+ memset(done,0,sizeof(done));
+
+ const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list;
+ for ( i = 0; i < n; i++, gobj++ )
+ {
+ ccp form1, form2;
+ form1 = form2 = settings_format_xxxx.std;
+
+ const u16 relevant_id = gobj->obj_id & (GOBJ_M_OBJECT|GOBJ_M_DEFINITION);
+ const ObjectInfo_t *oi = GetObjectInfo(relevant_id);
+ DASSERT(oi);
+
+ if (oi->name)
+ {
+ form1 = form2 = settings_format_uuuu.std;
+ ccp char_sep = *oi->charact ? ", " : "";
+
+ if (!done[relevant_id] )
+ {
+ done[relevant_id] = 1;
+
+ fprintf(F.f,
+ "## o$%s = %#x = %u%s%s\r\n",
+ oi->name, relevant_id, relevant_id,
+ char_sep, oi->charact );
+
+ if (oi->info)
+ {
+ fputs("## Description: ",F.f);
+ PutLines(F.f,0,80,13,"## ",oi->info,"\r\n");
+ }
+
+ if (oi->fname)
+ fprintf(F.f,"## File names: %s\r\n",oi->fname);
+
+ uint p;
+ for ( p = 0; p < 8; p++ )
+ if ( oi->param[p] )
+ {
+ fprintf(F.f,"## Setting #%u: ",p+1);
+ PutLines(F.f,13,80,13,"## ",oi->param[p],"\r\n");
+ }
+ fprintf(F.f,"#%.84s\r\n",Minus300);
+ }
+
+ if ( relevant_id == gobj->obj_id )
+ fprintf(F.f,"# o$%s, S",oi->name);
+ else
+ fprintf(F.f,"# o$%s|%#06x, S",oi->name,gobj->obj_id&GOBJ_M_FLAGS);
+
+ uint p;
+ for ( p = 0; p < 8; p++ )
+ fputc( oi->param[p] ? '1'+p : '-', F.f );
+ fprintf(F.f,"%s%s\r\n",char_sep, oi->charact);
+
+ if (oi->format1)
+ form1 = oi->format1->std;
+ if (oi->format2)
+ form2 = oi->format2->std;
+ }
+
+ //--- print special features
+
+ bool print_condition = false;
+ bool print_reference = false;
+
+ kmp_ana_defobj_t ad;
+ AnalyseDefObject(&ad,gobj);
+ if ( ad.is_defobj )
+ {
+ char usage_info_buf[40], *usage_info;
+ const uint usage = CountDefObjUsage(kmp,gobj->obj_id);
+ if (usage)
+ {
+ usage_info = usage_info_buf;
+ snprintf(usage_info_buf,sizeof(usage_info_buf),
+ "It is used by %u regular object%s.",
+ usage, usage == 1 ? "" : "s" );
+ }
+ else
+ usage_info = "It is not used by any regular object.";
+
+ if ( ad.is_pure_defobj && !(KMP_MODE & KMPMD_FULL_DEFOBJ) )
+ {
+ fprintf(F.f,
+ "# Definition object type %s. Param: %s\r\n",
+ ad.cmd_name, ad.par_info );
+ if (!ad.is_logical)
+ {
+ fputs("# Meaning of the conditions:\r\n",F.f);
+ PrintDefObjInfo(F.f,gobj);
+ }
+ fprintf(F.f,"# %s\r\n $DEFOBJ-%-7s %#06x",
+ usage_info,ad.cmd_name,gobj->obj_id);
+ uint i;
+ for ( i = ad.index_beg; i < ad.index_end; i++ )
+ {
+ if (ad.is_logical)
+ {
+ if (!gobj->setting[i])
+ continue;
+ cond_ref_info_t cri;
+ GetConditionRefInfo(&cri,gobj->setting[i],~0,true);
+ fprintf(F.f,", %s",cri.cond);
+ continue;
+ }
+ fprintf(F.f,", %s",GetDefObjConditionName(gobj->setting[i]));
+ }
+ fputs("\r\n",F.f);
+
+ if ( IsConditionRefId(gobj->ref_id) )
+ {
+ cond_ref_info_t cri;
+ GetConditionRefInfo(&cri,gobj->ref_id,gobj->obj_id,true);
+ fprintf(F.f," $%s %s%s%s\r\n",
+ cri.cmd_suffix, cri.cond,
+ cri.info ? " # " : "", cri.info ? cri.info : "" );
+ }
+
+ fprintf(F.f,"#%.84s\r\n",Minus300);
+ continue;
+ }
+ fprintf(F.f,"# Type %#06x: Definition object!\r\n# %s\r\n",
+ GOBJ_M_DEFINITION, usage_info );
+ }
+ else
+ {
+ if ( gobj->obj_id & GOBJ_M_LECODE )
+ fprintf(F.f,"# |%#x => Disabled object."
+ " Conditional enabled by LE-CODE.\r\n",
+ GOBJ_M_LECODE);
+
+ const u16 mode = GetPFlagsMode(gobj->pflags);
+ if (mode)
+ fprintf(F.f,"# Extended presence flags, mode %u\r\n",mode);
+
+ kmp_ana_ref_t ar;
+ AnalyseRefObject(&ar,gobj);
+ if ( ar.is_condref || ar.is_condrnd || ar.is_condtest )
+ print_condition = true;
+ else if (ar.is_objref)
+ print_reference = true;
+ else if (ar.condref_valid)
+ fprintf(F.f,
+ "# Warning: Valid condition, but P-Flags mode is %u\r\n",
+ ar.mode );
+ else if (ar.objref_valid)
+ fprintf(F.f,
+ "# Warning: Valid reference id, but P-Flags mode is %u\r\n",
+ ar.mode );
+ else if ( ar.mode_valid && gobj->ref_id )
+ fprintf(F.f,
+ "# Warning: Invalid reference id: %#06x\r\n",
+ gobj->ref_id);
+ }
+
+ //--- print object information
+
+ snprintf(buf1,sizeof(buf1),form1,
+ gobj->setting[0], gobj->setting[1],
+ gobj->setting[2], gobj->setting[3] );
+ switch (relevant_id)
+ {
+ case GOBJ_ELINE_CONTROL:
+ {
+ const int idx = IS_M1(gobj->setting[7]) ? -1 : gobj->setting[7];
+ if ( (uint)idx < KMP_MAX_GROUP && kmp->enph.gname[idx] )
+ snprintf(temp_buf,sizeof(temp_buf),"E.%s",kmp->enph.gname[idx]);
+ else
+ snprintf(temp_buf,sizeof(temp_buf),"%6d",idx);
+ snprintf(buf2,sizeof(buf2),"%6u %6u %6u %6s",
+ gobj->setting[4], gobj->setting[5],
+ gobj->setting[6], temp_buf );
+ }
+ break;
+
+ default:
+ snprintf(buf2,sizeof(buf2),form2,
+ gobj->setting[4], gobj->setting[5],
+ gobj->setting[6], gobj->setting[7] );
+ }
+
+ PrintNameIL(F.f,ill+KMP_GOBJ,i,5,5,0);
+ fprintf(F.f,
+ " %#6x %11.3f %11.3f %11.3f %s %6s\r\n"
+ " > %#6x %11.3f %11.3f %11.3f %s %#6x\r\n"
+ " > %11.3f %11.3f %11.3f\r\n",
+ gobj->obj_id,
+ gobj->position[0], gobj->position[1], gobj->position[2],
+ buf1, GetNameIL(ill+KMP_POTI,gobj->route_id),
+ gobj->ref_id,
+ gobj->rotation[0], gobj->rotation[1], gobj->rotation[2],
+ buf2, gobj->pflags,
+ gobj->scale[0], gobj->scale[1], gobj->scale[2] );
+
+
+ //--- print special definitions
+
+ if (print_condition)
+ {
+ cond_ref_info_t cri;
+ GetConditionRefInfo(&cri,gobj->ref_id,gobj->obj_id,true);
+ if (cri.is_condref)
+ fprintf(F.f," $%s %s%s%s\r\n",
+ cri.cmd_suffix, cri.cond,
+ cri.info ? " # " : "", cri.info ? cri.info : "" );
+ PRINT0("# CR[%#x,%#x]: is=%d, found=%d, cmd=%s, xor:%x->%#x, c=%s, i=%s\n",
+ gobj->ref_id, gobj->obj_id,
+ cri.is_condref, cri.condref!=0, cri.cmd_suffix,
+ cri.xor, gobj->ref_id^cri.xor, cri.cond, cri.info );
+ }
+ else if (print_reference)
+ {
+ fprintf(F.f," $%sABLE-BY%s %#06x\r\n",
+ gobj->obj_id & GOBJ_M_LECODE ? "EN" : "DIS",
+ (gobj->obj_id ^ gobj->ref_id) & GOBJ_DEF_F_NOT ? "" : "-NOT",
+ gobj->ref_id & ~GOBJ_DEF_F_NOT );
+ }
+ fprintf(F.f,"#%.84s\r\n",Minus300);
+ }
+
+
+ //--- print ITPH entries
+
+ if ( brief_count < 2 && !export_count )
+ {
+ fwrite(text_kmp_itph_cr,1,sizeof(text_kmp_itph_cr)-1,F.f);
+ print_kmp_ph(&F,kmp,KMP_ITPH);
+ }
+
+
+ //--- print ITPT entries
+
+ fwrite(text_kmp_itpt_cr,1,sizeof(text_kmp_itpt_cr)-1,F.f);
+ print_kmp_pt(&F,kmp,KMP_ITPT);
+
+
+ //--- print JGPT entries
+
+ fwrite(text_kmp_jgpt_cr,1,sizeof(text_kmp_jgpt_cr)-1,F.f);
+ print_kmp_pt2(&F,kmp,KMP_JGPT,jgpt_count);
+
+
+ //--- print KTPT entries
+
+ fprintf(F.f,text_kmp_ktpt_cr,REVISION_NUM,kmp->value[KMP_KTPT]);
+
+ n = kmp->dlist[KMP_KTPT].used;
+ const kmp_ktpt_entry_t * ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list;
+ for ( i = 0; i < n; i++, ktpt++ )
+ {
+ fprintf(F.f,"%4u %11.3f %11.3f %11.3f %7.2f %7.2f %7.2f %5d %#6x\r\n",
+ i,
+ ktpt->position[0], ktpt->position[1], ktpt->position[2],
+ ktpt->rotation[0], ktpt->rotation[1], ktpt->rotation[2],
+ ktpt->player_index, ktpt->unknown );
+ }
+
+
+ //--- print MSPT entries
+
+ fwrite(text_kmp_mspt_cr,1,sizeof(text_kmp_mspt_cr)-1,F.f);
+ print_kmp_pt2(&F,kmp,KMP_MSPT,0);
+
+
+ //--- print POTI entries
+
+ fprintf(F.f,text_kmp_poti_cr,REVISION_NUM,1<<SHRT_POTI);
+
+ ng = kmp->dlist[KMP_POTI].used;
+ const kmp_poti_group_t * pg = (kmp_poti_group_t*)kmp->dlist[KMP_POTI].list;
+ np = kmp->poti_point.used;
+ const kmp_poti_point_t * pp = (kmp_poti_point_t*)kmp->poti_point.list;
+
+ for ( gi = i = 0; gi < ng && i < np; gi++, pg++ )
+ {
+ uint en = pg->n_point;
+ DASSERT( en <= np-i );
+ fprintf(F.f,"\r\n $ROUTE %s, settings: %u %u # section %u\r\n",
+ GetNameIL(ill+KMP_POTI,gi),
+ pg->smooth, pg->back, gi+1 );
+ const bool is_cyclic = !pg->back;
+
+ //--- GOBJ reference
+
+ const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list;
+ uint count, idx, nn = kmp->dlist[KMP_GOBJ].used;
+ for ( count = idx = 0; idx < nn; idx++, gobj++ )
+ if ( gobj->route_id == (uint)gi )
+ {
+ const u16 relevant_id = gobj->obj_id & GOBJ_M_OBJECT;
+ fprintf(F.f,"\t# GOBJ reference: %-8s type %#6x",
+ GetNameIL(ill+KMP_GOBJ,idx), gobj->obj_id );
+ if ( relevant_id < N_KMP_GOBJ )
+ {
+ const ObjectInfo_t *oi = ObjectInfo + relevant_id;
+ if (oi->name)
+ {
+ if ( relevant_id == gobj->obj_id )
+ fprintf(F.f," = %s",oi->name);
+ else
+ fprintf(F.f," = %#06x | %s",gobj->obj_id&GOBJ_M_FLAGS,oi->name);
+ //DEL fprintf(F.f," = %s | %#06x",oi->name,gobj->obj_id&GOBJ_M_FLAGS);
+ }
+ }
+ fputs("\r\n",F.f);
+ }
+
+ //--- CAME reference
+
+ ccp came_ref = 0;
+ const kmp_came_entry_t * came = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list;
+ nn = kmp->dlist[KMP_CAME].used;
+ for ( count = idx = 0; idx < nn; idx++, came++ )
+ if ( came->route == gi )
+ {
+ ccp came_name = GetNameIL(ill+KMP_CAME,idx);
+ if (!came_ref)
+ came_ref = came_name;
+ fprintf(F.f,"%s %s",
+ count++ ? "" : "\t# CAME reference:",
+ came_name );
+ }
+
+ if (came_ref)
+ fprintf(F.f,"\r\n\r\n"
+ " # @set-route-time = %s.came.time # in 1/60 sec\r\n",came_ref);
+
+
+ //--- print points
+
+ fputs("\r\n",F.f);
+ double total_len = 0.0, total_tim = 0.0;
+ uint ei;
+ for ( ei = 0; ei < en; ei++, i++, pp++ )
+ {
+ fprintf(F.f, "%4u %11.3f %11.3f %11.3f %5u %5u #",
+ ei,
+ pp->position[0], pp->position[1], pp->position[2],
+ pp->speed, pp->unknown );
+
+ if ( ei < en-1 || is_cyclic )
+ {
+ const kmp_poti_point_t * pnext = pp + 1;
+ if ( ei >= en-1 )
+ pnext -= en;
+ double len = LengthF(pp->position,pnext->position);
+ total_len += len;
+ double tim = len > 0.0 && pp->speed > 0
+ ? len / (pp->speed*60) : 0.0;
+ if ( tim >= 0.01 && tim <= 999.99 )
+ {
+ total_tim += tim;
+ fprintf(F.f," %6.2f %9.3f", tim, len );
+ }
+ else
+ fprintf(F.f," - %9.3f", len );
+
+ if ( ei || is_cyclic )
+ {
+ const kmp_poti_point_t * pprev = pp - 1;
+ if (!ei)
+ pprev += en;
+ fprintf(F.f," %7.2f",
+ AngleVector(pprev->position,pp->position,pnext->position,true) );
+ }
+ }
+ fputs("\r\n",F.f);
+ }
+
+ if ( en > 1 )
+ fprintf(F.f,"%56s %6.2f %9.3f\r\n","#TOTAL#",total_tim,total_len);
+ }
+
+
+ //--- print STGI entries
+
+ fprintf(F.f,text_kmp_stgi_cr,REVISION_NUM,kmp->value[KMP_STGI]);
+
+ n = kmp->dlist[KMP_STGI].used;
+ const kmp_stgi_entry_t * stgi = (kmp_stgi_entry_t*)kmp->dlist[KMP_STGI].list;
+
+ for ( i = 0; i < n; i++, stgi++ )
+ {
+ PrintNameIL(F.f,ill+KMP_STGI,i,4,5,0);
+ fprintf(F.f,"%5u %4u %5u %5u %#10x 0x%02x %#5x %#6x %#4x # ",
+ stgi->lap_count, stgi->pole_pos,
+ stgi->narrow_start, stgi->enable_lens_flare,
+ stgi->flare_color,
+ stgi->flare_alpha, stgi->unknown_09,
+ stgi->speed_mod >> 8, stgi->speed_mod & 0xff );
+ if (stgi->speed_mod)
+ fprintf(F.f,"%7.3f\r\n",SpeedMod2float(stgi->speed_mod));
+ else
+ fputs(" -\r\n",F.f);
+ }
+
+
+ //--- terminate
+
+ fputs(section_end,F.f);
+ ResetFile(&F,set_time);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+