aboutsummaryrefslogtreecommitdiff
path: root/project/src/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--project/src/analyze.c1361
1 files changed, 1361 insertions, 0 deletions
diff --git a/project/src/analyze.c b/project/src/analyze.c
new file mode 100644
index 0000000..0c294b0
--- /dev/null
+++ b/project/src/analyze.c
@@ -0,0 +1,1361 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * 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-brres.h"
+#include "lib-breff.h"
+#include "lib-image.h"
+#include "db-kcl.h"
+#include "lib-kcl.h"
+#include "lib-kmp.h"
+#include "lib-mdl.h"
+#include "lib-pat.h"
+#include "db-object.h"
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// option support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enum
+{
+ ANA_MD_DEFAULT = 0x0001,
+ ANA_MD_BRRES = 0x0002,
+ ANA_MD_MDL = 0x0004,
+ ANA_MD_PAT = 0x0008,
+ ANA_MD_TEX = 0x0010,
+
+ ANA_MD_BREFF = 0x0100,
+ ANA_MD_BREFT = 0x0200,
+ ANA_MD_KCL = 0x0400,
+ ANA_MD_TPL = 0x0800,
+
+ ANA_KMP_GOBJ = 0x1000,
+ ANA_KMP_KTPT = 0x2000,
+ ANA_MKWANA = 0x4000,
+
+ ANA_M_BRRES = ANA_MD_BRRES | ANA_MD_MDL | ANA_MD_PAT | ANA_MD_TEX,
+ ANA_M_KMP = ANA_KMP_GOBJ | ANA_KMP_KTPT,
+
+ ANA_M_ALL = 0xffff,
+};
+
+uint opt_analyze_mode = ANA_MD_DEFAULT;
+
+//-----------------------------------------------------------------------------
+
+int ScanOptAnalyzeMode ( ccp arg )
+{
+ static const KeywordTab_t tab[] =
+ {
+ { 0, "NONE", "-", ANA_M_ALL },
+ { ANA_M_ALL, "ALL", 0, 0 },
+
+ { ANA_MD_BRRES, "BRRES", "BRES", 0 },
+ { ANA_MD_MDL, "MDL0", 0, 0 },
+ { ANA_MD_PAT, "PAT0", 0, 0 },
+ { ANA_MD_TEX, "TEX0", 0, 0 },
+
+ { ANA_MD_BREFF, "BREFF", "REFF", 0 },
+ { ANA_MD_BREFT, "BREFT", "REFT", 0 },
+ { ANA_MD_TPL, "TPL", 0, 0 },
+
+ { ANA_MD_KCL, "KCL", 0, 0 },
+
+ { ANA_M_KMP, "KMP", 0, 0 },
+ { ANA_KMP_GOBJ, "GOBJ", 0, 0 },
+ { ANA_KMP_KTPT, "KTPT", 0, 0 },
+
+ { ANA_MKWANA, "MKW_ANA", "MKWANA", 0 },
+
+ { 0,0,0,0 }
+ };
+
+ int stat = ScanKeywordList(arg,tab,0,true,0,0,
+ "Option --analyze-mode",ERR_SYNTAX);
+ if ( stat != -1 )
+ {
+ opt_analyze_mode = stat;
+ return 0;
+ }
+ return 1;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// defines ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define DUMP_NULL_OFFSET 0 // dump null values
+#define DUMP_INVALID_OFFSET 0 // dump offset, that point before string pool
+#define DUMP_GROUP_POINTER 0 // dump group relative offets
+#define DUMP_ENTRY_POINTER 0 // dump entry relative offets
+#define DUMP_VERSION 1 // dump version info of entries
+
+#define ANA_SPEC_STRINGS 0 // enable special strings analysis
+#define ANA_SPLT_SECT 0 // try to find split sections
+
+//--- enable special tests
+#define ANA_MDL_11_0 1 // MDL vers 11 sect 0
+#define ANA_MDL_11_8 1 // MDL vers 11 sect 8
+#define ANA_MDL_11_8_0_VAL 1 // MDL vers 11 sect 8.0
+#define ANA_MDL_11_8_3_VAL 1 // MDL vers 11 sect 8.3
+#define ANA_TEX_HD 1 // TEX header
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// structs ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+typedef struct analyze_item_t
+{
+ u32 offset; // data offset
+ brres_group_t * grp; // pointer to group
+ brres_entry_t * entry; // pointer to entry
+ ccp entry_name; // pointer to entry string
+ ccp info; // short item info
+
+ int index1; // index level 1 (grp/section index)
+ int index2; // index level 2 (entry index)
+ int index3; // index level 3
+ uint brsub_mode; // brsub mode
+
+} analyze_item_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define MAX_ANA_ITEM 10000
+
+typedef struct analyze_t
+{
+ szs_file_t * szs;
+ u8 * data;
+ uint data_size;
+
+ file_format_t fform;
+ ccp ff_name;
+ int version;
+
+ analyze_item_t list[MAX_ANA_ITEM];
+ uint used;
+
+} analyze_t;
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// AnalyzeBRSUB(), helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int cmp_analyze_item ( const void * va, const void * vb )
+{
+ const analyze_item_t * a = va;
+ const analyze_item_t * b = vb;
+
+ if ( a->offset < b->offset ) return -1;
+ if ( a->offset > b->offset ) return 1;
+ if ( a->index1 < b->index1 ) return -1;
+ if ( a->index1 > b->index1 ) return 1;
+ if ( a->index2 < b->index2 ) return -1;
+ if ( a->index2 > b->index2 ) return 1;
+ if ( a->index3 < b->index3 ) return -1;
+ if ( a->index3 > b->index3 ) return 1;
+ if ( a < b ) return -1;
+ if ( a > b ) return 1;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static analyze_item_t * add_item
+(
+ analyze_t * ana, // analyse
+ ccp info, // short item info
+ brres_group_t * grp, // pointer to group
+ uint grp_index, // index of group/section
+ uint index3, // free second index
+ brres_entry_t * entry, // pointer to entry
+ u8 * data // pointer to entry related data
+)
+{
+ DASSERT(ana);
+ if ( ana->used < MAX_ANA_ITEM )
+ {
+ analyze_item_t * item = ana->list + ana->used++;
+
+ item->offset = data - ana->data;
+ item->grp = grp;
+ item->entry = entry;
+ item->entry_name = "-";
+ item->index1 = grp_index;
+ item->index2 = grp && entry ? entry - grp->entry : 0;
+ item->index3 = index3;
+ item->brsub_mode = BRSUB_MODE3(ana->fform,ana->version,grp_index);
+ item->info = info;
+ return item;
+ }
+
+ ERROR0(ERR_FATAL,"Analyze list full (max=%u): %s\n",MAX_ANA_ITEM,ana->szs->fname);
+ exit(ERR_FATAL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int collect_raw_data
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ u8 * data, // data pointer
+ u32 data_size // size of 'data'
+)
+{
+ DASSERT(it);
+ analyze_t * ana = it->param;
+ DASSERT(ana);
+
+ // additional data
+ add_item(ana,"raw data",0,-1,1,0,data);
+
+ // ignore other
+ add_item(ana,"ignore",0,0xffff,0,0,data+data_size);
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int collect_group_data
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ brres_group_t * grp, // pointer to group
+ uint grp_index // index of group
+)
+{
+ DASSERT(it);
+ analyze_t * ana = it->param;
+ DASSERT(ana);
+ add_item(ana,"group",grp,0xffff,0,grp->entry,(u8*)grp);
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// collect_entry_data() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int collect_entry_data
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ brres_group_t * grp, // pointer to group
+ uint grp_index, // index of group
+ brres_entry_t * entry, // pointer to entry
+ u8 * data // pointer to entry related data
+)
+{
+ DASSERT(it);
+ analyze_t * ana = it->param;
+ DASSERT(ana);
+
+ analyze_item_t * item = add_item(ana,"section",grp,grp_index,0,entry,data);
+ item->entry_name = GetStringBRRES(it->szs,grp,
+ it->endian->rd32(&entry->name_off),"?");
+
+ #if ANA_SPLT_SECT
+
+ switch (BRSUB_MODE3(ana->fform,ana->version,grp_index))
+ {
+ case BRSUB_MODE3(FF_MDL,11, 1):
+ case BRSUB_MODE3(FF_MDL,11, 2):
+ case BRSUB_MODE3(FF_MDL,11, 3):
+ case BRSUB_MODE3(FF_MDL,11, 4):
+ case BRSUB_MODE3(FF_MDL,11, 5):
+ case BRSUB_MODE3(FF_MDL,11, 9):
+ case BRSUB_MODE3(FF_MDL,11,10):
+ {
+ noPRINT(">>> %s.%u.%02u: %5x,%5x,%5x\n",
+ ana->ff_name, grp_index, (int)( entry - grp->entry ),
+ it->endian->rd32(data),
+ it->endian->rd32(data+0x08),
+ it->endian->rd32(data+0x0c));
+ add_item(ana,"end of section",grp,grp_index,99,entry,
+ data + it->endian->rd32(data));
+ }
+ break;
+
+ case BRSUB_MODE3(FF_MDL,11,8):
+ {
+ u32 off = it->endian->rd32(data);
+ item = add_item(ana,"end of section",grp,grp_index,99,entry,data+off);
+
+ uint n_lay = it->endian->rd32(data+0x2c);
+ off = it->endian->rd32(data+0x30);
+ if ( n_lay && off )
+ {
+ uint i;
+ for ( i = 0; i < n_lay; i++, off += 0x34 )
+ item = add_item(ana,"mdl 8 layer",grp,grp_index,1,entry,data+off);
+ }
+ else
+ off = 0x418;
+ item = add_item(ana,"end of mdl 8 layer",grp,grp_index,2,entry,data+off);
+
+ off = it->endian->rd32(data+0x3c);
+ if (off)
+ item = add_item(ana,"mdl 8 setup code",grp,grp_index,3,entry,data+off);
+
+ off = it->endian->rd32(data+0x28);
+ if (off)
+ item = add_item(ana,"mdl 8 shader @28",grp,grp_index,4,entry,data+off);
+ }
+ break;
+
+ case BRSUB_MODE3(FF_MDL,11,11):
+ {
+ item = add_item(ana,"end of section",grp,grp_index,99,entry,
+ data + it->endian->rd32(data) * 8 );
+ }
+ break;
+
+ case BRSUB_MODE3(FF_PAT,4,0):
+ {
+ uint n = it->endian->rd16(data+0x0c);
+ u8 * d = data + 0x14 + 8*n;
+ item = add_item(ana,"pat",grp,grp_index,1,entry,d);
+
+ while ( it->endian->rd32(d) )
+ d += 4;
+ item = add_item(ana,"end of section",grp,grp_index,99,entry,d+4);
+ }
+ break;
+ }
+ #endif // ANA_SPLT_SECT
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// AnalyzeBRSUB() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// use [[subfile]]
+
+void AnalyzeBRSUB ( szs_file_t * szs, u8 * data, uint data_size, ccp name )
+{
+ if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_M_BRRES) )
+ return;
+
+ if ( IsValidBRSUB(data,data_size,data_size,0,FF_UNKNOWN,0,0,0) >= VALID_ERROR )
+ return;
+
+// [[analyse-magic]]
+ file_format_t fform = GetByMagicFF(data,data_size,data_size);
+ ccp ff_name = GetNameFF(0,fform);
+ PRINT("AnalyzeBRSUB() %s %s [size=%x]\n",ff_name,szs->fname,data_size);
+
+ if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_MD_BRRES) )
+ {
+ switch (fform)
+ {
+ case FF_MDL:
+ if (!(opt_analyze_mode&ANA_MD_MDL))
+ return;
+ break;
+
+ case FF_PAT:
+ if (!(opt_analyze_mode&ANA_MD_PAT))
+ return;
+ break;
+
+ case FF_TEX:
+ case FF_TEX_CT:
+ if (!(opt_analyze_mode&ANA_MD_TEX))
+ return;
+ fform = FF_TEX;
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ FILE * f = CreateAnalyzeFile();
+ if (!f)
+ return;
+
+ static bool init_done = false;
+ if (!init_done)
+ {
+ init_done = true;
+
+ #if ANA_MDL_11_0
+ fprintf(f,"#HAVE# HAVE-MDL.11.0\n");
+ #endif
+
+ #if ANA_MDL_11_8
+ fprintf(f,"#HAVE# HAVE-MDL.11.8\n");
+ #if ANA_MDL_11_8_0_VAL
+ fprintf(f,"#HAVE# HAVE-MDL.11.8.0-VAL HAVE-MDL-VAL0.11.8\n");
+ #endif
+ #if ANA_MDL_11_8_3_VAL
+ fprintf(f,"#HAVE# HAVE-MDL.11.8.3-VAL HAVE-MDL-VAL3.11.8\n");
+ #endif
+ #endif
+
+ #if ANA_TEX_HD
+ fprintf(f,"#HAVE# HAVE-TEX-HD\n");
+ #endif
+ }
+
+ //printf("ANALYZE %s: %s\n",ff_name,szs->fname);
+ fprintf(f,"\n\f\nANALYZE %s: %s\n\n",ff_name,szs->fname);
+
+ const endian_func_t * endian = szs->endian;
+ DASSERT(endian);
+ const u32 version = endian->rd32(data+8);
+ const int n_sect = GetSectionNumBRSUB(data,data_size,endian);
+ //const uint brsub_mode2 = BRSUB_MODE2(fform,version);
+
+
+ //--- special PAT dump
+
+ if ( fform == FF_PAT && version == 4 )
+ {
+ u32 off0 = endian->rd32(data+16+0*4);
+ u32 off1 = endian->rd32(data+16+1*4); if (!off1) off1 = off0;
+ u32 off2 = endian->rd32(data+16+2*4); if (!off2) off2 = off1;
+ u32 off3 = endian->rd32(data+16+3*4); if (!off3) off3 = off2;
+ u32 off4 = endian->rd32(data+16+4*4); if (!off4) off4 = off3;
+ u32 off5 = endian->rd32(data+16+5*4); if (!off5) off5 = off4;
+
+ ccp fname = strstr(szs->fname,"/object/");
+ if (fname)
+ fname += 8;
+ else
+ {
+ fname = strstr(szs->fname,"/track/");
+ if (fname)
+ fname += 7;
+ else
+ fname = name;
+ }
+
+ pat_analyse_t ana;
+ IsValidPAT(data,data_size,0,szs,fname,0,&ana);
+ fprintf(f,"#PAT-N# %4d %4d %4d %4d %4d %4d %4d %4d"
+ " | %4d %4d %4d %4d %4d %4d | v=%d,%d %s\n",
+ endian->rd16(data+0x2c+0*2),
+ endian->rd16(data+0x2c+1*2),
+ endian->rd16(data+0x2c+2*2),
+ endian->rd16(data+0x2c+3*2),
+ endian->rd16(data+0x2c+4*2),
+ endian->rd16(data+0x2c+5*2),
+ endian->rd16(data+0x2c+6*2),
+ endian->rd16(data+0x2c+7*2),
+ off1 - off0,
+ off2 - off1,
+ off3 - off2,
+ off4 - off3,
+ off5 - off4,
+ data_size - off5,
+ ana.valid, ana.data_complete, fname );
+
+ if (ana.data_complete)
+ {
+ const pat_s0_bhead_t *bh = ana.s0_base;
+ fprintf(f,
+ "#PAT-BH# %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x | %s\n",
+ be32(&bh->size),
+ be16(&bh->unknown_04),
+ be16(&bh->n_elem),
+ be16(&bh->unknown_08),
+ be16(&bh->unknown_0a),
+ be16(&bh->n_unknown),
+ be16(&bh->unknown_0e),
+ be16(&bh->unknown_10),
+ be16(&bh->unknown_12),
+ be16(&bh->unknown_14),
+ be16(&bh->unknown_16),
+ fname );
+
+ const pat_s0_belem_t *be = bh->elem;
+ uint i;
+ for ( i = 0; i < ana.n_sect0; i++, be++ )
+ fprintf(f,
+ "#PAT-BE.%u# %4x %4x %4x %4x %4x %4x | %s\n",
+ i,
+ be16(&be->unknown_00),
+ be16(&be->unknown_02),
+ be16(&be->unknown_04),
+ be16(&be->unknown_06),
+ be32(&be->offset_name),
+ be32(&be->offset_strref),
+ fname );
+
+ for ( i = 0; i < PAT_MAX_ELEM; i++ )
+ {
+ const pat_s0_sref_t *sr = ana.s0_sref[i];
+ if (!sr)
+ continue;
+ fprintf(f,
+ "#PAT-SR.%u# %4x %4x %4x %4x | %s\n",
+ i,
+ be32(&sr->offset_name),
+ be16(&sr->unknown_04),
+ be16(&sr->type),
+ be32(&sr->offset_strlist),
+ fname );
+ }
+
+ for ( i = 0; i < PAT_MAX_ELEM; i++ )
+ {
+ const pat_s0_shead_t *sh = ana.s0_shead[i];
+ if (!sh)
+ continue;
+ const float fm = bef4(&sh->factor);
+ const float f1 = bef4(&sh->elem[0].time);
+ const float f2 = bef4(&sh->elem[ana.n_s0_list[i]-1].time);
+ fprintf(f,
+ "#PAT-SH.%u# %4x %4x %8.5f [%8.5f,%8.5f] | %s\n"
+ "#PAT-SE#\n",
+ i,
+ be16(&sh->n_elem),
+ be16(&sh->unknown_02),
+ fm, fm * f2, fm * (f2-f1),
+ fname );
+
+ float last = 0.0;
+ uint j;
+ for ( j = 0; j < ana.n_s0_list[i]; j++ )
+ {
+ const pat_s0_selem_t *se = sh->elem + j;
+ float time = bef4(&se->time);
+ fprintf(f,
+ "#PAT-SE.%u.%02u# %11.5f [%8.5f] %4x %4x | %s\n",
+ i, j,
+ time, time-last,
+ be16(&se->index),
+ be16(&se->unknown_06),
+ fname );
+ last = time;
+ }
+ }
+ }
+
+ if ( ana.s3_list && ana.n_sect1 )
+ {
+ fprintf(f,"#PAT-S3#");
+
+ uint i;
+ for ( i = 0; i < ana.n_sect1; i++ )
+ fprintf(f," %8x",be32(ana.s3_list+i));
+ fprintf(f," | %s\n",fname);
+ }
+ }
+
+
+ //--- version and section number test
+
+ #if DUMP_VERSION
+ {
+ //--- find version and section count
+
+ const u32 size = endian->rd32(data+4);
+ const int n1 = GetKnownSectionNumBRSUB(fform,version);
+ const int n2 = GetGenericSectionNumBRSUB(data,data_size,endian);
+ const int n3 = GetSectionNumBRSUB(data,data_size,endian);
+
+ //printf("#VERSION# %s v=%02u n%c%2d,%2d,%2d : size=%05x : %s\n",
+ // ff_name, version, n1==n2 ? '=' :'?', n1, n2, n3, size, szs->fname );
+
+ fprintf(f,"#VERSION# %s v=%02u n%c%2d,%2d,%2d : size=%05x : %s\n",
+ ff_name, version, n1==n2 ? '=' :'?', n1, n2, n3, size, szs->fname );
+ }
+ #endif
+
+
+ //--- ffname with version
+
+ char ff_buf[20];
+ snprintf(ff_buf,sizeof(ff_buf),"%s-%x",ff_name,version);
+ ff_name = ff_buf;
+
+ {
+ fprintf(f,"#OFFSET# %s :",ff_name);
+ uint i;
+ for ( i = 0; i < n_sect; i++ )
+ fprintf(f," %5x",endian->rd32(data+0x10+4*i));
+ //fprintf(f," : %6x\n",endian->rd32(data+0x10+4*n_sect));
+ fprintf(f," : %5x\n",data_size);
+ }
+
+
+ //--- setup analyze_t
+
+ analyze_t ana;
+ memset(&ana,0,sizeof(ana));
+ ana.szs = szs;
+ ana.data = data;
+ ana.data_size = data_size;
+ ana.fform = fform;
+ ana.ff_name = ff_name;
+ ana.version = version;
+
+ analyze_item_t * item = ana.list;
+ item->offset = data_size; // end marker
+ item->index1 = -1;
+ item->index3 = 99;
+ item->info = "END OF DATA";
+ item++;
+ item->index1 = -1; // header = data begin
+ item->entry_name = name;
+ item->info = "BEGIN OF DATA";
+ item++;
+ ana.used = item - ana.list;
+
+ brsub_iterator_t it;
+ memset(&it,0,sizeof(it));
+ it.szs = szs;
+ it.brsub = (void*)data;
+ it.file_size = szs->data + szs->size - (u8*)data;
+ it.raw_func = collect_raw_data;
+ it.grp_func = collect_group_data;
+ it.entry_func = collect_entry_data;
+ it.param = &ana;
+ it.adjust = true; // suppress error messages
+
+ IterateStringsBRSUB(&it);
+
+
+ //--- special string
+
+ #if ANA_SPEC_STRINGS // find special strings
+
+ enum { MAX_SPECIAL_STR = 100 };
+ ccp special_str[MAX_SPECIAL_STR];
+ uint n_special_str = 0;
+
+ {
+ ccp base = (ccp)szs->data + szs->max_data_off;
+ u32 off = 4;
+ while ( off < szs->size )
+ {
+ ccp sptr = (ccp)base + off;
+ if (!*sptr)
+ break;
+
+ #define ADD_STR(s) if ( n_special_str < MAX_SPECIAL_STR && !strcmp(sptr,s) ) \
+ special_str[n_special_str++] = sptr;
+
+ ADD_STR("bds_eye.0");
+ ADD_STR("bds_eye.1");
+ ADD_STR("bds_eye.2");
+ ADD_STR("catherine_eye.0");
+ ADD_STR("catherine_eye.1");
+ ADD_STR("catherine_eye.2");
+ ADD_STR("diddy_eye.0");
+ ADD_STR("diddy_eye.1");
+ ADD_STR("diddy_eye.2");
+ ADD_STR("luigi_eye.0");
+ ADD_STR("luigi_eye.1");
+ ADD_STR("luigi_eye.2");
+ ADD_STR("waluigi_eye.0");
+ ADD_STR("waluigi_eye.1");
+ ADD_STR("waluigi_eye.2");
+
+ #undef ADD_STR
+
+ off += ALIGN32(strlen(sptr)+5,4);
+ ASSERT( n_special_str < MAX_SPECIAL_STR );
+ }
+ }
+ #endif
+
+ //--- sort & iterate data
+
+ #if ANA_TEX_HD0
+ analyze_item_t * texhd = 0;
+ #endif
+
+ qsort(ana.list,ana.used,sizeof(*ana.list),cmp_analyze_item);
+ uint i;
+ for ( i = 0; i < ana.used - 1; i++ )
+ {
+ analyze_item_t * item = ana.list + i;
+
+ char idxbuf1[20], idxbuf2[30];
+ if ( item->index1 < 0 )
+ snprintf(idxbuf1,sizeof(idxbuf1),"%s.++.%02u",
+ ff_name, item->index3 );
+ else if ( item->index3 == 99 )
+ snprintf(idxbuf1,sizeof(idxbuf1),"%s.%02u.~~",
+ ff_name, item->index1 );
+ else
+ {
+ snprintf(idxbuf1,sizeof(idxbuf1),"%s.%02u.%02u",
+ ff_name, item->index1, item->index3 );
+ }
+ snprintf(idxbuf2,sizeof(idxbuf2),"%s.%02d",
+ idxbuf1, item->index2 );
+
+ u8 *base = ana.data + item[0].offset;
+ const uint sect_size = item[1].offset - item[0].offset;
+ u32 val = endian->rd32(base);
+ fprintf(f,"\n#SECT# %3u. %6x..%5x, size=%x, u32=%x->%x : %s >>> %s <<<\n",
+ i, item->offset, item[1].offset,
+ sect_size, val, item->offset + val,
+ idxbuf2, item->info );
+
+ if ( item->index2 > 0 ) // ( && item->index3 != 99 )
+ fprintf(f,"#SECT-SIZE# %s : %2u %2u %2u : %4u = 0x%04x : %s\n",
+ ff_name,
+ item->index1, item->index2, item->index3,
+ sect_size, sect_size, item->info );
+
+ //HexDump(f,0,0,4,16,base,16);
+
+ if ( item[1].offset == item[0].offset )
+ continue;
+ if ( item->index1 == 0xffff )
+ continue;
+
+ fprintf(f,"#SIZE# %s size=%5x : %s tot-off=%6x/%x : >>> %s <<<\n",
+ idxbuf1, sect_size,
+ idxbuf2, item[0].offset, data_size, item->info );
+
+ fprintf(f,"### %s.-----.cnt : %s off=%5x\n",
+ idxbuf1, idxbuf2, item[0].offset );
+
+ bool found = false;
+ u8 *end = ana.data + item[1].offset, *ptr;
+ for ( ptr = base; ptr < end; ptr += 4 )
+ {
+ ccp string;
+ u32 offset = endian->rd32(ptr);
+
+ if (!offset)
+ {
+ #if DUMP_NULL_OFFSET // value == null
+ fprintf(f,"### %s.%05x._0_ : %s val=%8x\n",
+ idxbuf1,
+ (int)( ptr - base ),
+ idxbuf2,
+ offset );
+ #endif
+ continue;
+ }
+
+ #if DUMP_INVALID_OFFSET // value == other
+ if ( item->offset + offset < data_size )
+ {
+ fprintf(f,"### %s.%05x._<_ : %s val=%8x\n",
+ idxbuf1,
+ (int)( ptr - base ),
+ idxbuf2,
+ offset );
+ }
+ #endif
+
+ #if DUMP_GROUP_POINTER // group pointer
+ if ( item->grp && ptr >= (u8*)item->grp )
+ {
+ string = GetStringBRRES(szs,item->grp,offset,0);
+ if ( string && *string > ' ' && *string < 0x7f )
+ {
+ fprintf(f,"### %s.%05x.GRP : %s val=%8x : %s\n",
+ idxbuf1,
+ (int)( ptr - base ),
+ idxbuf2,
+ offset, string );
+ found = true;
+ }
+ }
+ #endif
+
+ #if DUMP_ENTRY_POINTER // entry pointer
+ if ( item->entry && ptr >= (u8*)item->entry )
+ {
+ string = GetStringBRRES(szs,item->entry,offset,0);
+ if ( string && *string > ' ' && *string < 0x7f )
+ {
+ fprintf(f,"### %s.%05x.ENT : %s val=%8x : %s\n",
+ idxbuf1,
+ (int)( ptr - base ),
+ idxbuf2,
+ offset, string );
+ found = true;
+ }
+ }
+ #endif
+ string = GetStringBRRES(szs,ana.data+item->offset,offset,0);
+ if ( string && *string > ' ' && *string < 0x7f )
+ {
+ fprintf(f,"### %s.%05x.DAT : %s val=%8x : %s\n",
+ idxbuf1,
+ (int)( ptr - base ),
+ idxbuf2,
+ offset, string );
+
+ found = true;
+
+ if ( string == item->entry_name )
+ fprintf(f,"### %s.%05x.=== : %s val=%8x : %s\n",
+ idxbuf1,
+ (int)( ptr - base ),
+ idxbuf2,
+ offset, string );
+
+ if ( item->index1 == -1 && ptr - base >= 4*n_sect )
+ fprintf(f,"### %s.%05x.NNN : %s val=%8x : %s\n",
+ idxbuf1,
+ (int)( ptr - base - 4*n_sect ),
+ idxbuf2,
+ offset, string );
+ }
+
+ #if ANA_SPEC_STRINGS // find special strings
+ if (n_special_str)
+ {
+ u8 * p2;
+ for ( p2 = ptr; p2 < end; p2 += 4 )
+ {
+ u32 offset = endian->rd32(p2);
+ if (!offset)
+ continue;
+ string = GetStringBRRES(szs,ptr,offset,0);
+ int i;
+ for ( i = 0; i < n_special_str; i++ )
+ if ( string == special_str[i] )
+ fprintf(f,"#SPEC# %s.%05x+%05x=%05x : off=%6x : %s\n",
+ idxbuf1,
+ (int)( ptr - base ),
+ (int)( p2 - ptr ),
+ (int)( p2 - base ),
+ offset, string );
+ }
+ }
+ #endif
+ }
+ if (!found)
+ fprintf(f,"### %s.xxxxx.non : %s\n", idxbuf1, idxbuf2 );
+
+ #if 0
+ if ( fform == FF_MDL && item->old_index == 0xc0 )
+ {
+ u32 *p = (u32*)base;
+ uint n = endian->rd32(p++), i;
+ for ( i = 0; i < n; i++ )
+ {
+ const u32 v1 = endian->rd32(p++);
+ const u32 v2 = endian->rd32(p++);
+
+ fprintf(f,"### MDL-b.%02u: %6x %6x -> %6x %6x [d=%4x]\n",
+ i, v1, v2,
+ item[0].offset + v1, item[0].offset + v2,
+ v2 - v1 );
+ }
+ }
+ #endif
+
+ #if ANA_MDL_11_0
+ if ( item->brsub_mode == BRSUB_MODE3(FF_MDL,11,0) )
+ {
+ #ifdef TEST0
+ FILE *f = stdout;
+ #endif
+ ccp prefix = "#MDL.s0# ";
+ fprintf(f,"%s-------------\n%s%s -> %s\n",
+ prefix,
+ prefix, idxbuf2, szs->fname );
+
+ mdl_sect0_t ms0;
+ InitializeMDLs0(&ms0,base,sect_size);
+ fprintf(f,"%sSTAT: ok=%d, n=%d, size=%u/%u\n",
+ prefix, ms0.ok, ms0.n, ms0.data_size, sect_size);
+ while (NextMDLs0(&ms0))
+ {
+ fprintf(f,"%sDUMP[%02x,%2u]:",prefix,*ms0.cur,ms0.cur_size);
+ HexDump(f, 1, ms0.cur-ms0.data_beg, 3, -50, ms0.cur, ms0.cur_size );
+ }
+ if (ms0.fail)
+ {
+ fprintf(f,"%sFAIL[%02x,*?]:",prefix,*ms0.fail);
+ HexDump(f, 1, ms0.fail-ms0.data_beg, 3, -50,
+ ms0.fail, ms0.data_end - ms0.fail );
+ }
+ }
+ #endif
+
+ #if ANA_MDL_11_8
+ if ( item->brsub_mode == BRSUB_MODE3(FF_MDL,11,8) )
+ {
+ ccp prefix = "#MDL.s8# ";
+ fprintf(f,"%s-------------\n%s%s -> %s -> %s\n",
+ prefix,
+ prefix, idxbuf2, szs->fname, name );
+ //u8 * off_base = base;
+ //hexdump_prefix = prefix;
+ //HexDump(f,0,base-off_base,4,16,base,sect_size);
+ if ( item->index3 == 0 )
+ {
+ u32 shader_off = endian->rd32(base+0x28);
+ u32 layer_cnt = endian->rd32(base+0x2c);
+ u32 layer_off = endian->rd32(base+0x30);
+ u32 layer_size = 0x34;
+ u32 grcode_off = endian->rd32(base+0x3c);
+ u32 end_layer = layer_off + layer_cnt * layer_size;
+ fprintf(f,"%sMEM #=%05x %u*l=%04x-%04x free=%03x>%02x u=%04x[%04x,s=%03x]"
+ " : s=%04x[%08x]\n",
+ prefix,
+ endian->rd32(base),
+ layer_cnt, layer_off, end_layer,
+ grcode_off - end_layer, (int)(base+grcode_off-data)&0x1f,
+ grcode_off, endian->rd16(base+grcode_off),
+ endian->rd32(base) - grcode_off,
+ shader_off, endian->rd32(base+shader_off) );
+
+ #if ANA_MDL_11_8_0_VAL
+ for ( ptr = base; ptr < end; ptr += 4 )
+ fprintf(f,"%sVAL0 %4x : %8x\n",
+ prefix, (int)(ptr-base), endian->rd32(ptr) );
+ #endif
+ }
+
+ if ( item->index3 == 3 )
+ {
+ #if ANA_MDL_11_8_3_VAL
+ for ( ptr = base; ptr < end; ptr += 4 )
+ fprintf(f,"%sVAL3 %4x : %8x\n",
+ prefix, (int)(ptr-base), endian->rd32(ptr) );
+ #endif
+ }
+ }
+ #endif
+
+ #if ANA_TEX_HD0
+ if ( ana.fform == FF_TEX && item->index1 < 0 && item->index3 == 1 )
+ texhd = item;
+
+ PRINT_IF(ana.fform == FF_TEX,"TEX %d %d %d %u\n",
+ item->index1,item->index2,item->index3,sect_size);
+ if ( ana.fform == FF_TEX && item->index1 == 0 && texhd )
+ {
+ texhd = 0;
+ }
+ #endif
+
+ #if ANA_TEX_HD
+ if ( ana.fform == FF_TEX && item->index1 < 0 && item->index3 == 1 )
+ {
+ tex_info_t * ti = (tex_info_t*)( ana.data + item->offset - 4 );
+ const uint wd = endian->rd16(&ti->width);
+ const uint ht = endian->rd16(&ti->height);
+ const uint iform = endian->rd32(&ti->iform);
+ fprintf(f,"#TEX-HD# %-6s %4u*%-4u",GetImageFormatName(iform,"?"),wd,ht);
+ noPRINT("OFF: %x %x %x\n",item[0].offset,item[1].offset,item[2].offset);
+ const uint raw_size = item[2].offset - item[1].offset;
+
+ uint xwd, xht, hb, vb, img_size;
+ const ImageGeometry_t *geo
+ = CalcImageGeometry(iform,wd,ht,&xwd,&xht,&hb,&vb,&img_size);
+ if (geo)
+ {
+ const uint n_blk = img_size / geo->block_size;
+ const uint m_blk = raw_size / geo->block_size;
+ const uint xbytes = raw_size - m_blk * geo->block_size;
+ fprintf(f," %6u %+6d b=%5u %4u.%u:",
+ img_size, raw_size - img_size,
+ n_blk, m_blk - n_blk, xbytes );
+ }
+ else
+ fprintf(f," %6u:",raw_size);
+
+ u32 * ptr = (u32*)( ana.data + item->offset );
+ u32 * end = (u32*)( ana.data + item->offset + sect_size );
+ fprintf(f," %x ..",endian->rd32(ptr)); ptr += 3;
+ while ( ptr < end )
+ fprintf(f," %8x",endian->rd32(ptr++));
+ fprintf(f,"\n");
+ }
+ #endif
+ }
+
+ fprintf(f,"--\n");
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// BREFF+BREFT sub files ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int ana_breff_subfile
+(
+ struct szs_iterator_t *it, // iterator struct with all infos
+ bool term // true: termination hint
+)
+{
+ if ( term || it->is_dir || memcmp(it->path,"files/",6) )
+ return 0;
+
+ noPRINT("BINGO BREFF %s\n",it->path);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int ana_breft_subfile
+(
+ struct szs_iterator_t *it, // iterator struct with all infos
+ bool term // true: termination hint
+)
+{
+ DASSERT(it);
+ DASSERT(it->szs);
+ if ( term || it->is_dir || memcmp(it->path,"files/",6) )
+ return 0;
+ noPRINT("BINGO BREFT %s\n",it->path);
+
+ FILE * f = it->param;
+ DASSERT(f);
+
+ fprintf(f,"#BREFF# file=%s\n",it->path);
+
+ u32 *d = (u32*)( it->szs->data + it->off);
+ fprintf(f,"#BREFF# HEAD: %8x %8x %8x %8x %8x %8x %8x %8x [%x]\n",
+ it->endian->rd32(d),
+ it->endian->rd32(d+1),
+ it->endian->rd32(d+2),
+ it->endian->rd32(d+3),
+ it->endian->rd32(d+4),
+ it->endian->rd32(d+5),
+ it->endian->rd32(d+6),
+ it->endian->rd32(d+7),
+ it->size );
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// AnalyzeBREFF() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void AnalyzeBREFF ( szs_file_t * szs, u8 * data, uint data_size, ccp name )
+{
+// [[analyse-magic]]
+ file_format_t fform = GetByMagicFF(data,data_size,data_size);
+ switch (fform)
+ {
+ case FF_BREFF:
+ if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_MD_BREFF) )
+ return;
+ break;
+
+ case FF_BREFT:
+ if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_MD_BREFT) )
+ return;
+ break;
+
+ default:
+ return;
+ }
+
+ ccp ff_name = GetNameFF(0,fform);
+ noPRINT("AnalyzeBREFF() %s %s\n",ff_name,szs->fname);
+
+ FILE * f = CreateAnalyzeFile();
+ if (!f)
+ return;
+
+ //printf("ANALYZE %s: %s\n",ff_name,szs->fname);
+ fprintf(f,"\n\f\nANALYZE %s: %s\n\n",ff_name,szs->fname);
+
+ brres_header_t *bh = (brres_header_t*)data;
+ breff_root_head_t *rh = (breff_root_head_t*)(bh+1);
+ breff_root_t *root = (breff_root_t*)(&rh->data);
+
+ const endian_func_t * endian = szs->endian;
+ DASSERT(endian);
+
+ fprintf(f,"#%s#N-SECT: %u\n#%s#VERSION: %u\n",
+ ff_name, endian->rd16(&bh->n_sections),
+ ff_name, endian->rd16(&bh->version));
+
+ fprintf(f,"#%s#UNKNOWN: %8x %8x %4x\n",
+ ff_name,
+ endian->rd32(&root->unknown1),
+ endian->rd32(&root->unknown2),
+ endian->rd16(&root->unknown3) );
+
+
+ szs_file_t szs2;
+// [[fname-]]
+ InitializeSubSZS(&szs2,szs,data-szs->data,szs->size,fform,0,false);
+ IterateFilesSZS(&szs2,
+ fform == FF_BREFF ? ana_breff_subfile : ana_breft_subfile,
+ f, true, false, -1, SORT_NONE );
+ ResetSZS(&szs2);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// AnalyzeTPL() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void AnalyzeTPL ( szs_file_t * szs, u8 * data, uint data_size, ccp name )
+{
+// [[analyse-magic]]
+ file_format_t fform = GetByMagicFF(data,data_size,data_size);
+ if ( fform != FF_TPL
+ || opt_analyze_mode != ANA_MD_DEFAULT
+ && !(opt_analyze_mode&ANA_MD_TPL) )
+ {
+ return;
+ }
+
+ ccp ff_name = GetNameFF(0,fform);
+ noPRINT("AnalyzeTPL() %s %s / %s\n",ff_name,szs->fname,name);
+
+ FILE * f = CreateAnalyzeFile();
+ if (!f)
+ return;
+
+ //printf("ANALYZE %s: %s / %s\n",ff_name,szs->fname,name);
+ fprintf(f,"\n\f\nANALYZE %s: %s / %s\n\n",ff_name,szs->fname,name);
+
+ const endian_func_t * endian = szs->endian;
+ DASSERT(endian);
+
+ const uint n_img = GetNImagesTPL(data,data_size,endian);
+ uint ni;
+ for ( ni = 0; ni < n_img; ni++ )
+ {
+ const tpl_header_t * head;
+ const tpl_imgtab_t * tab;
+ const tpl_pal_header_t * pal;
+ const tpl_img_header_t * img;
+ const u8 *pal_data, *img_data;
+
+ if (SetupPointerTPL( data, data_size, ni,
+ &head, &tab, &pal, &img,
+ &pal_data, &img_data, endian ))
+ {
+ noPRINT("TPL #%u: %6x %6x %6x %6x : %6x %6x : %6x\n",
+ ni,
+ (int)((u8*)head-data),
+ (int)((u8*)tab-data),
+ pal ? (int)((u8*)pal-data) : 0,
+ (int)((u8*)img-data),
+ pal_data ? (int)(pal_data-data) : 0,
+ (int)(img_data-data),
+ data_size );
+ fprintf(f,"#TPL-IMG# %u: %6x %6x %6x %6x : %6x %6x\n",
+ ni,
+ (int)((u8*)head-data),
+ (int)((u8*)tab-data),
+ pal ? (int)((u8*)pal-data) : 0,
+ (int)((u8*)img-data),
+ pal_data ? (int)(pal_data-data) : 0,
+ (int)(img_data-data) );
+ }
+ else
+ {
+ PRINT("TPL #%u: FAILED\n",ni);
+ fprintf(f,"#TPL-IMG# %u: FAILED\n",ni);
+ }
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// AnalyzeKMP() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void AnalyzeKMP ( const struct kmp_t * kmp, ccp base_fname )
+{
+ if ( opt_analyze_mode != ANA_MD_DEFAULT
+ && !(opt_analyze_mode&(ANA_M_KMP|ANA_MKWANA)) )
+ {
+ return;
+ }
+
+ PRINT("AnalyzeKMP(%s) %s\n",base_fname,kmp->fname);
+
+ FILE * f = CreateAnalyzeFile();
+ if (!f)
+ return;
+
+ if ( verbose >= 0 )
+ fprintf(f,"\n\f\nANALYZE KMP: %s\n\n",kmp->fname);
+
+ if ( opt_analyze_mode == ANA_MD_DEFAULT
+ || (opt_analyze_mode&ANA_KMP_GOBJ) )
+ {
+ const uint n = kmp->dlist[KMP_GOBJ].used;
+ const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list;
+ uint i;
+ for ( i = 0; i < n; i++, gobj++ )
+ {
+ ccp name = "?";
+ if ( gobj->obj_id < N_KMP_GOBJ )
+ {
+ const ObjectInfo_t *oi = ObjectInfo + gobj->obj_id;
+ if (oi->name)
+ name = oi->name;
+ }
+ fprintf(f,"#KMP-GOBJ# %04x %s\n", gobj->obj_id, name );
+ const u16 *s = gobj->setting;
+ fprintf(f,
+ "#KMP-GOBJ-ALL# %4x %4x %s %4x %4x %4x %4x %4x %4x %4x %4x %4x %s\n",
+ gobj->obj_id, gobj->ref_id,
+ gobj->route_id == 0xffff ? "-" : "R",
+ s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],
+ gobj->pflags,
+ name );
+
+ uint p;
+ for ( p = 0; p < 8; p++ )
+ fprintf(f,"#KMP-GOBJ-SET# %04x.%u.%04x\n",
+ gobj->obj_id, p+1, gobj->setting[p] );
+
+ fprintf(f,"#KMP-GOBJ-SET# %04x.u.%04x\n",
+ gobj->obj_id, gobj->ref_id );
+ fprintf(f,"#KMP-GOBJ-SET# %04x.r.%04x\n",
+ gobj->obj_id, gobj->route_id );
+ fprintf(f,"#KMP-GOBJ-SET# %04x.p.%04x\n",
+ gobj->obj_id, gobj->pflags );
+ }
+ }
+
+ if ( opt_analyze_mode == ANA_MD_DEFAULT
+ || (opt_analyze_mode&ANA_KMP_KTPT) )
+ {
+ kmp_finish_t kf;
+ CheckFinishLine(kmp,&kf);
+ fprintf(f,"#KTPT-DIST# %11.3f %7.2f *%u,%u %s : %s\n",
+ kf.distance, kf.dir_delta, kf.n_ckpt_0, kf.n_ktpt_m,
+ kf.warn ? "WARN" : kf.hint ? "HINT" : "- ", base_fname );
+ PRINT0("#FINISH# %s\n",LogFinishLine(&kf));
+ }
+
+ if ( opt_analyze_mode & ANA_MKWANA )
+ {
+ if (base_fname)
+ {
+ ccp slash = strrchr(base_fname,'/');
+ if (slash)
+ base_fname = slash;
+ }
+ else
+ base_fname = kmp->fname;
+ fprintf(f,"#MKWANA: ---\n#MKWANA: FILE = %s\n",base_fname);
+
+ uint n = kmp->dlist[KMP_KTPT].used;
+ if (n>0)
+ {
+ const kmp_ktpt_entry_t *ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list;
+ fprintf(f,"#MKWANA: START-POS = %11.3f %11.3f %11.3f\n",
+ ktpt->position[0], ktpt->position[1], ktpt->position[2] );
+ fprintf(f,"#MKWANA: START-DIR = %11.3f %11.3f %11.3f\n",
+ ktpt->rotation[0], ktpt->rotation[1], ktpt->rotation[2] );
+ }
+
+ n = kmp->dlist[KMP_STGI].used;
+ if (n>0)
+ {
+ const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp->dlist[KMP_STGI].list;
+ fprintf(f,"#MKWANA: POLE-POS = 0x%02x\n",stgi->pole_pos);
+ fprintf(f,"#MKWANA: NARROW = 0x%02x\n",stgi->narrow_start);
+ }
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// AnalyzeKCL() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void AnalyzeKCL ( const struct kcl_t * kcl )
+{
+ if ( opt_analyze_mode != ANA_MD_DEFAULT
+ && !(opt_analyze_mode&ANA_MD_KCL) )
+ {
+ return;
+ }
+
+ ccp fname = kcl->fname ? kcl->fname : "?";
+ PRINT("AnalyzeKCL() %s\n",fname);
+
+ FILE * f = CreateAnalyzeFile();
+ if (!f)
+ return;
+
+ ccp ptr, slash = strrchr(fname,'/');
+ slash = slash ? slash+1 : fname;
+ uint index = 0;
+ for ( ptr = fname + strlen(fname); ptr > fname; ptr-- )
+ if ( *ptr == '/' || *ptr == '\\' )
+ {
+ uint num = strtoul(ptr+1,0,10);
+ if ( num > 0 )
+ {
+ index = num;
+ break;
+ }
+ }
+ fprintf(f,"\n\f\nANALYZE KCL %02u: %s\n\n",index,fname);
+
+ u32 *count = CALLOC(N_KCL_FLAG,sizeof(*count));
+
+ const uint n = kcl->tridata.used;
+ const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list;
+ const kcl_tridata_t *end = td + n;
+ for ( ; td < end; td++ )
+ count[td->in_flag]++;
+
+ uint i;
+ for ( i = 0; i < N_KCL_FLAG; i++ )
+ if (count[i])
+ fprintf(f,"KCL-TYPE 0x%02x 0x%03x 0x%04x %6.2f%% T%02u %s\n",
+ i & 0x1f, i >> 5, i & ~0x1f, 100.0 * count[i] / n, index, slash );
+
+ FREE(count);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////