aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-analyze.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--project/src/lib-analyze.c737
1 files changed, 737 insertions, 0 deletions
diff --git a/project/src/lib-analyze.c b/project/src/lib-analyze.c
new file mode 100644
index 0000000..9d0c0ca
--- /dev/null
+++ b/project/src/lib-analyze.c
@@ -0,0 +1,737 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * 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-2022 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-analyze.h"
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// analyze_param_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void PrintHeaderAP
+(
+ analyze_param_t *ap, // valid structure
+ ccp analyze // analyzed object
+)
+{
+ DASSERT(ap);
+ DASSERT(analyze);
+
+ char buf[PATH_MAX+100];
+ PrintEscapedString(buf,sizeof(buf),ap->fname,-1,CHMD_UTF8,'"',0);
+
+ PrintScriptVars(&ap->ps,1,
+ "file=\"%s\"\n"
+ "file_type=\"%s\"\n"
+ "analyze=\"%s\"\n"
+ ,buf
+ ,GetNameFF(ap->szs.fform_file,ap->szs.fform_arch)
+ ,analyze
+ );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void PrintFooterAP
+(
+ analyze_param_t *ap, // valid structure
+ bool valid, // true if source is valid
+ u_usec_t duration_usec, // 0 or duration of analysis
+ ccp warn_format, // NULL or format for warn message
+ ... // arguments for 'warn_format'
+)
+{
+ DASSERT(ap);
+
+ char buf[1000];
+
+ PrintScriptVars(&ap->ps,0,"valid=%d\n",valid);
+ if ( warn_format && *warn_format )
+ {
+ va_list arg;
+ va_start(arg,warn_format);
+ vsnprintf(buf,sizeof(buf),warn_format,arg);
+ va_end(arg);
+ PrintScriptVars(&ap->ps,0,"warning=\"%s\"\n",buf);
+ }
+ else
+ PrintScriptVars(&ap->ps,0,"warning=\"\"\n");
+
+ if (duration_usec)
+ PrintScriptVars(&ap->ps,0,"duration_usec=%llu\n",duration_usec);
+ PutScriptVars(&ap->ps,2,0);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// analyze_szs_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeAnalyzeSZS ( analyze_szs_t * as )
+{
+ #ifdef DEBUG
+ static bool done = false;
+ if (!done)
+ {
+ done = true;
+ TRACE_SIZEOF(analyze_szs_t);
+ TRACE_SIZEOF(lex_info_t);
+ TRACE_SIZEOF(slot_ana_t);
+ TRACE_SIZEOF(slot_info_t);
+ TRACE_SIZEOF(kmp_finish_t);
+ TRACE_SIZEOF(kmp_usedpos_t);
+ TRACE_SIZEOF(szs_have_t);
+ TRACE_SIZEOF(szs_have_t);
+ TRACE_SIZEOF(have_file_mode_t);
+ TRACE_SIZEOF(szs_special_t);
+ TRACE_SIZEOF(kmp_special_t);
+ TRACE("-\n");
+ }
+ #endif
+
+ DASSERT(as);
+ memset(as,0,sizeof(*as));
+
+ InitializeLexInfo(&as->lexinfo);
+ InitializeFinishLine(&as->kmp_finish);
+ InitializeUsedPos(&as->used_pos);
+
+ as->ckpt0_count = -1; // number of LC in CKPT
+ as->lap_count = 3; // STGI lap counter
+ as->speed_factor = 1.0; // STGI speed factor
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetAnalyzeSZS ( analyze_szs_t * as )
+{
+ if (as)
+ {
+ ResetLexInfo(&as->lexinfo);
+ InitializeAnalyzeSZS(as);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AnalyzeSZS
+(
+ analyze_szs_t *as, // result
+ bool init_sa, // true: init 'as', false: reset 'as'
+ szs_file_t *szs, // SZS file to analysze
+ ccp fname // NULL or fname for slot analysis
+)
+{
+ //--- setup
+
+ const u_usec_t start_usec = GetTimerUSec();
+
+ analyze_szs_t as0;
+ if (!as)
+ {
+ as = &as0;
+ init_sa = true;
+ }
+
+ DASSERT(as);
+ if (init_sa)
+ InitializeAnalyzeSZS(as);
+ else
+ ResetAnalyzeSZS(as);
+
+ if (!szs)
+ return;
+
+ char *ct_dest = as->ct_attrib;
+ char *ct_end = as->ct_attrib + sizeof(as->ct_attrib);
+
+
+ //--- checksums
+
+ sha1_size_t sha1_data;
+ SHA1(szs->data,szs->size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_szs,sha1_data.hash);
+ memcpy(as->sha1_szs_norm,as->sha1_szs,sizeof(as->sha1_szs_norm));
+
+ sha1_data.size = htonl(szs->size);
+ CreateSSChecksumDB(as->db64,sizeof(as->db64),&sha1_data);
+ FindSpecialFilesSZS(szs,false);
+
+
+ //--- scan LEX
+
+ InitializeLexInfo(&as->lexinfo);
+
+ if (szs->course_lex_data)
+ {
+ lex_t lex;
+ InitializeLEX(&lex);
+ lex.check_only = true;
+ ScanLEX(&lex,false,szs->course_lex_data,szs->course_lex_size);
+ szs->have.lex_sect = lex.have_sect;
+ szs->have.lex_feat = lex.have_feat;
+ SetupLexInfo(&as->lexinfo,&lex);
+ ResetLEX(&lex);
+ }
+
+
+ //--- scan slots
+
+ AnalyzeSlot(&as->slotana,szs);
+ if (fname)
+ AnalyzeSlotByName(&as->slotinfo,true,MemByString(fname));
+ if (as->slotana.mandatory_slot[0])
+ AnalyzeSlotAttrib(&as->slotinfo,false,MemByString(as->slotana.mandatory_slot));
+ FinalizeSlotInfo(&as->slotinfo,true);
+ if (*as->slotinfo.slot_attrib)
+ ct_dest = StringCat2E(ct_dest,ct_end,",",as->slotinfo.slot_attrib);
+
+
+ //--- scan KMP
+
+ bool valid_track = true; // use temp var because of early 'return'
+
+ if (szs->course_kmp_data)
+ {
+ SHA1(szs->course_kmp_data,szs->course_kmp_size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_kmp,sha1_data.hash);
+ memcpy(as->sha1_kmp_norm,as->sha1_kmp,sizeof(as->sha1_kmp_norm));
+ as->sha1_kmp_slot = as->sha1_kmp_norm_slot
+ = GetSha1Slot(SHA1T_KMP,sha1_data.hash);
+
+ kmp_t kmp;
+ InitializeKMP(&kmp);
+ kmp.check_only = true;
+ kmp.lexinfo = &as->lexinfo;
+ const enumError err
+ = ScanKMP(&kmp,false,szs->course_kmp_data,szs->course_kmp_size,0);
+ if ( err <= ERR_WARNING )
+ {
+ if (kmp.stgi)
+ {
+ kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.stgi;
+ as->lap_count = stgi->lap_count;
+ as->speed_mod = stgi->speed_mod;
+ stgi->lap_count = 3;
+ stgi->speed_mod = 0;
+
+ SHA1(szs->data,szs->size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_szs_norm,sha1_data.hash);
+
+ SHA1(szs->course_kmp_data,szs->course_kmp_size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_kmp_norm,sha1_data.hash);
+ as->sha1_kmp_norm_slot = GetSha1Slot(SHA1T_KMP,sha1_data.hash);
+
+ stgi->lap_count = as->lap_count;
+ stgi->speed_mod = as->speed_mod;
+ }
+
+ CheckFinishLine(&kmp,&as->kmp_finish);
+ CheckWarnKMP(&kmp,&as->used_pos);
+ szs->warn_bits |= kmp.warn_bits;
+
+ const uint n_ckpt = kmp.dlist[KMP_CKPT].used;
+ if (n_ckpt)
+ {
+ const kmp_ckpt_entry_t *ckpt
+ = (kmp_ckpt_entry_t*)kmp.dlist[KMP_CKPT].list;
+ uint i;
+ as->ckpt0_count = 0;
+ for ( i = 0; i < n_ckpt; i++, ckpt++ )
+ if (!ckpt->mode)
+ as->ckpt0_count++;
+ }
+
+ const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.dlist[KMP_STGI].list;
+ if ( kmp.dlist[KMP_STGI].used > 0 )
+ {
+ as->lap_count = stgi->lap_count;
+ if (!szs->is_arena)
+ {
+ if ( as->ckpt0_count > 1 )
+ {
+ if ( as->lap_count == 1 )
+ ct_dest = StringCopyE(ct_dest,ct_end,",1lap");
+ ct_dest = snprintfE(ct_dest,ct_end,",%ulc",as->ckpt0_count);
+ }
+ else if ( as->lap_count != 0 && as->lap_count != 3 )
+ ct_dest = snprintfE(ct_dest,ct_end,",%ulap%s",
+ as->lap_count, as->lap_count == 1 ? "" : "s" );
+ }
+
+ if (stgi->speed_mod)
+ {
+ as->speed_mod = stgi->speed_mod;
+ as->speed_factor = SpeedMod2float(stgi->speed_mod);
+ char buf[20];
+ snprintf(buf,sizeof(buf),",x%4.2f",as->speed_factor);
+ char *ptr = buf + strlen(buf) - 1;
+ while ( *ptr == '0' )
+ *ptr-- = 0;
+ if ( *ptr == '.' )
+ *ptr = 0;
+ if ( strcmp(buf,",x0") && strcmp(buf,",x1") )
+ ct_dest = StringCopyE(ct_dest,ct_end,buf);
+ }
+ }
+ else if ( !szs->is_arena && as->ckpt0_count > 1 )
+ ct_dest = snprintfE(ct_dest,ct_end,",%ulc",as->ckpt0_count);
+
+ if ( as->kmp_finish.n_ktpt_m > 1 )
+ ct_dest = StringCopyE(ct_dest,ct_end,",2ktpt");
+
+ kmp_ana_pflag_t ap;
+ AnalysePFlagScenarios(&ap,&kmp,GMD_M_DEFAULT);
+ snprintf(as->gobj_info,sizeof(as->gobj_info),"%d %d %d %d",
+ ap.ag.n_gobj, ap.n_version, ap.n_res_std, ap.n_res_ext );
+ ResetPFlagScenarios(&ap);
+ }
+
+ DetectSpecialKMP(&kmp,szs->have.kmp);
+ if ( szs->have.kmp[HAVEKMP_WOODBOX_HT]
+ || szs->have.kmp[HAVEKMP_MUSHROOM_CAR]
+ || szs->have.kmp[HAVEKMP_PENGUIN_POS]
+ || szs->have.kmp[HAVEKMP_EPROP_SPEED]
+ || szs->have.kmp[HAVEKMP_GOOMBA_SIZE]
+ )
+ {
+ ct_dest = StringCopyE(ct_dest,ct_end,",gobj");
+ }
+
+ if ( szs->have.kmp[HAVEKMP_X_PFLAGS]
+ || szs->have.kmp[HAVEKMP_X_COND]
+ || szs->have.kmp[HAVEKMP_X_DEFOBJ]
+ || szs->have.kmp[HAVEKMP_X_RANDOM]
+ )
+ {
+ ct_dest = StringCopyE(ct_dest,ct_end,",xpf");
+ }
+
+ if (szs->have.kmp[HAVEKMP_COOB_R])
+ ct_dest = StringCopyE(ct_dest,ct_end,",coob-r");
+ if (szs->have.kmp[HAVEKMP_COOB_K])
+ ct_dest = StringCopyE(ct_dest,ct_end,",coob-k");
+ if (szs->have.kmp[HAVEKMP_UOOB])
+ ct_dest = StringCopyE(ct_dest,ct_end,",uoob");
+
+ ResetKMP(&kmp);
+ }
+ else
+ valid_track = false;
+
+
+ if ( szs->have.szs[HAVESZS_ITEM_SLOT_TABLE] >= HFM_MODIFIED )
+ ct_dest = StringCopyE(ct_dest,ct_end,",itemslot");
+
+ if ( szs->have.szs[HAVESZS_OBJFLOW] >= HFM_MODIFIED )
+ ct_dest = StringCopyE(ct_dest,ct_end,",objflow");
+
+ if ( szs->have.szs[HAVESZS_GHT_ITEM] >= HFM_MODIFIED
+ || szs->have.szs[HAVESZS_GHT_ITEM_OBJ] >= HFM_MODIFIED
+ || szs->have.szs[HAVESZS_GHT_KART] >= HFM_MODIFIED
+ || szs->have.szs[HAVESZS_GHT_KART_OBJ] >= HFM_MODIFIED
+ )
+ {
+ ct_dest = StringCopyE(ct_dest,ct_end,",geohit");
+ }
+
+ if ( szs->have.szs[HAVESZS_MINIGAME] >= HFM_MODIFIED )
+ ct_dest = StringCopyE(ct_dest,ct_end,",minigame");
+
+ if ( szs->have.szs[HAVESZS_AIPARAM_BAA] >= HFM_ORIGINAL
+ || szs->have.szs[HAVESZS_AIPARAM_BAS] >= HFM_ORIGINAL
+ )
+ {
+ ct_dest = StringCopyE(ct_dest,ct_end,",aiparam");
+ }
+
+
+ //--- scan KCL
+
+ if (szs->course_kcl_data)
+ {
+ SHA1(szs->course_kcl_data,szs->course_kcl_size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_kcl,sha1_data.hash);
+ as->sha1_kcl_slot = GetSha1Slot(SHA1T_KCL,sha1_data.hash);
+ }
+ else
+ valid_track = false;
+
+
+ //--- course model (course_model.brres, course_d_model.brres)
+
+ if (szs->course_model_data)
+ {
+ SHA1(szs->course_model_data,szs->course_model_size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_course,sha1_data.hash);
+ }
+ else if (szs->course_d_model_data)
+ {
+ SHA1(szs->course_d_model_data,szs->course_d_model_size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_course,sha1_data.hash);
+ }
+ else
+ valid_track = false;
+
+
+ //--- vrcorn (vrcorn_model.brres)
+
+ if (szs->vrcorn_model_data)
+ {
+ SHA1(szs->vrcorn_model_data,szs->vrcorn_model_size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_vrcorn,sha1_data.hash);
+ }
+// else
+// valid_track = false;
+
+
+ //--- minimap (map_model.brres)
+
+ if (szs->map_model_data)
+ {
+ SHA1(szs->map_model_data,szs->map_model_size,sha1_data.hash);
+ Sha1Bin2Hex(as->sha1_minimap,sha1_data.hash);
+ as->sha1_minimap_slot = GetSha1Slot(SHA1T_MAP,sha1_data.hash);
+ }
+ else
+ szs->warn_bits |= 1 << WARNSZS_NO_MINIMAP;
+
+
+ //--- missing and modified files
+
+ ccp miss = GetStatusMissedFile(szs->missed_file);
+ if ( miss && *miss )
+ ct_dest = StringCat2E(ct_dest,ct_end,",miss=",miss);
+
+ miss = GetStatusMissedFile(szs->modified_file);
+ if ( miss && *miss )
+ ct_dest = StringCat2E(ct_dest,ct_end,",mod=",miss);
+
+
+ //--- finalize ct_attrib by "lex" and "warn"
+
+ if (szs->course_lex_data)
+ ct_dest = StringCopyE(ct_dest,ct_end,",lex");
+
+ if (szs->warn_bits)
+ ct_dest = StringCat2E(ct_dest,ct_end,",warn=",GetWarnSZSNames(szs->warn_bits,'+'));
+
+ szs->have.valid = true;
+ as->have = szs->have;
+ as->valid_track = valid_track;
+ as->duration_usec = GetTimerUSec() - start_usec;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ExecAnalyzeSZS ( analyze_param_t *ap )
+{
+ DASSERT(ap);
+
+ const u_usec_t start_usec = GetTimerUSec();
+
+
+ //--- analyse szs
+
+ szs_file_t *szs = &ap->szs;
+ DASSERT(szs);
+
+ analyze_szs_t as;
+ InitializeAnalyzeSZS(&as);
+ AnalyzeSZS(&as,false,szs,ap->fname);
+
+ uint name_attrib_len = 0;
+ ccp name_attrib = strrchr(ap->fname,'[');
+ if (name_attrib)
+ {
+ name_attrib++;
+ ccp end = strrchr(name_attrib,']');
+ if (end)
+ {
+ name_attrib_len = end - name_attrib;
+ for ( ccp ptr = name_attrib; ptr < end; )
+ {
+ ccp sep = strchr(ptr,',');
+ if (!sep)
+ sep = end;
+ switch ( sep-ptr )
+ {
+ case 4:
+ if (!memcmp(ptr,"edit",4)) szs->have.attrib |= 1 << HAVEATT_EDIT;
+ break;
+
+ case 5:
+ if (!memcmp(ptr,"cheat",5)) szs->have.attrib |= 1 << HAVEATT_CHEAT;
+ break;
+
+ case 7:
+ if (!memcmp(ptr,"reverse",7)) szs->have.attrib |= 1 << HAVEATT_REVERSE;
+ break;
+ }
+ ptr = sep + 1;
+ }
+ }
+ }
+ else
+ name_attrib = EmptyString;
+
+
+ ccp texture_info = 0;
+ PrepareCheckTextureSZS(szs);
+ CheckTextureRefSZS(szs,&texture_info);
+
+ const u_usec_t duration_usec = GetTimerUSec() - start_usec;
+
+
+ //--- print result
+
+ PrintHeaderAP(ap,"track");
+ PrintScriptVars(&ap->ps,0,
+ "size=%zd\n"
+ "db64=\"%s\"\n"
+ "sha1=\"%s\"\n"
+ "sha1_norm=\"%s\"\n"
+ "sha1_kcl=\"%s\"\n"
+ "sha1_kmp=\"%s\"\n"
+ "sha1_kmp_norm=\"%s\"\n"
+ "sha1_course=\"%s\"\n"
+ "sha1_vrcorn=\"%s\"\n"
+ "sha1_minimap=\"%s\"\n"
+ "sha1_kcl_slot=%u\n"
+ "sha1_kmp_slot=%u\n"
+ "sha1_kmp_norm_slot=%u\n"
+ "sha1_minimap_slot=%u\n"
+ "valid_track=%u\n"
+ "is_arena=\"%u %s\"\n"
+ "n_ckpt0=%d\n"
+ "lap_count=%d\n"
+ "speed_factor=%5.3f\n"
+ "slot_info=\"%s\"\n"
+ "slot_attributes=\"%s\"\n"
+ "race_slot=\"%u %s\"\n"
+ "arena_slot=\"%u %s\"\n"
+ "edit_slot=\"%u %s\"\n"
+ "music_index=\"%u %s\"\n"
+ "itempos_factors=\"%5.3f %5.3f %5.3f\"\n"
+ "used_x_pos=\"%u=%s %3.2f %3.2f %3.2f %3.2f\"\n"
+ "used_y_pos=\"%u=%s %3.2f %3.2f %3.2f %3.2f\"\n"
+ "used_z_pos=\"%u=%s %3.2f %3.2f %3.2f %3.2f\"\n"
+ "used_pos_suggest=\"%s\"\n"
+ "ktpt2=\"%s %4.2f %4.2f\"\n"
+ "gobj=\"%s\"\n"
+ "missed_subfiles=\"%s\"\n"
+ "modified_subfiles=\"%s\"\n"
+ "special_files=\"%s\"\n"
+ "original_files=\"%s\"\n"
+ "modified_files=\"%s\"\n"
+ "special_attrib=\"%s\"\n"
+ "special_kmp=\"%s\"\n"
+ "lex_sections=\"%s\"\n"
+ "lex_features=\"%s\"\n"
+ "warn=\"%u=%s\"\n"
+ "ct_attributes=\"%s\"\n"
+ "name_attributes=\"%.*s\"\n"
+
+ ,szs->size
+ ,as.db64
+ ,as.sha1_szs
+ ,as.sha1_szs_norm
+ ,as.sha1_kcl
+ ,as.sha1_kmp
+ ,as.sha1_kmp_norm
+ ,as.sha1_course
+ ,as.sha1_vrcorn
+ ,as.sha1_minimap
+ ,as.sha1_kcl_slot
+ ,as.sha1_kmp_slot
+ ,as.sha1_kmp_norm_slot
+ ,as.sha1_minimap_slot
+ ,as.valid_track
+ ,szs->is_arena
+ ,is_arena_name[szs->is_arena]
+ ,as.ckpt0_count
+ ,as.lap_count
+ ,as.speed_factor
+ ,as.slotana.slot_info
+ ,as.slotinfo.slot_attrib
+ ,as.slotinfo.race_slot,as.slotinfo.race_info
+ ,as.slotinfo.arena_slot,as.slotinfo.arena_info
+ ,as.slotinfo.edit_slot,as.slotinfo.edit_info
+ ,as.slotinfo.music_index,as.slotinfo.music_info
+ ,as.lexinfo.item_factor.x,as.lexinfo.item_factor.y,as.lexinfo.item_factor.z
+ ,as.used_pos.orig.rating[0]
+ ,WarnLevelNameLo[as.used_pos.orig.rating[0]]
+ ,as.used_pos.orig.min.x,as.used_pos.orig.max.x
+ ,fabsf(as.used_pos.orig.max.x-as.used_pos.orig.min.x)
+ ,(as.used_pos.orig.min.x+as.used_pos.orig.max.x)/2
+ ,as.used_pos.orig.rating[1],WarnLevelNameLo[as.used_pos.orig.rating[1]]
+ ,as.used_pos.orig.min.y,as.used_pos.orig.max.y
+ ,fabsf(as.used_pos.orig.max.y-as.used_pos.orig.min.y)
+ ,(as.used_pos.orig.min.y+as.used_pos.orig.max.y)/2
+ ,as.used_pos.orig.rating[2],WarnLevelNameLo[as.used_pos.orig.rating[2]]
+ ,as.used_pos.orig.min.z,as.used_pos.orig.max.z
+ ,fabsf(as.used_pos.orig.max.z-as.used_pos.orig.min.z)
+ ,(as.used_pos.orig.min.z+as.used_pos.orig.max.z)/2
+ ,GetUsedPosObjSuggestion(as.used_pos.suggest,true,"")
+ ,as.kmp_finish.warn ? "warn" : as.kmp_finish.hint ? "hint" : "ok"
+ ,as.kmp_finish.distance
+ ,as.kmp_finish.dir_delta
+ ,as.gobj_info
+ ,GetStatusMissedFile(szs->missed_file)
+ ,GetStatusMissedFile(szs->modified_file)
+ ,CreateSpecialFileInfo(szs,~(1<<HFM_NONE),true,"")
+ ,CreateSpecialFileInfo(szs,1<<HFM_ORIGINAL,true,"")
+ ,CreateSpecialFileInfo(szs,1<<HFM_MODIFIED,true,"")
+ ,CreateSpecialInfoAttrib(szs->have.attrib,true,"")
+ ,CreateSpecialInfoKMP(szs->have.kmp,true,"")
+ ,CreateSectionInfoLEX(szs->have.lex_sect,true,"")
+ ,CreateFeatureInfoLEX(szs->have.lex_feat,true,"")
+ ,szs->warn_bits,GetWarnSZSNames(szs->warn_bits,' ')
+ ,as.ct_attrib+1
+ ,name_attrib_len,name_attrib
+ );
+
+ if (texture_info)
+ PrintScriptVars(&ap->ps,0,"texture_hack=\"%s\"\n",texture_info);
+
+ if ( long_count > 0 )
+ {
+ const uint n = szs->special_file ? szs->special_file->used : 0;
+ PrintScriptVars(&ap->ps,0,"subfile_n=%u\n",n);
+
+ uint i;
+ ParamFieldItem_t *ptr = szs->special_file->field;
+ for (i = 0; i < n; i++, ptr++ )
+ {
+ sha1_hex_t hex;
+ Sha1Bin2Hex(hex,(u8*)ptr->data);
+ PrintScriptVars(&ap->ps,0,"subfile_%u=\"%u %s %s\"\n",
+ i, ptr->num, hex, ptr->key );
+ }
+ }
+
+ PrintFooterAP(ap,true,duration_usec,0);
+ ResetAnalyzeSZS(&as);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ExecAnalyzeLECODE() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ExecAnalyzeLECODE ( analyze_param_t *ap )
+{
+ DASSERT(ap);
+
+ const u_usec_t start_usec = GetTimerUSec();
+
+ le_analyze_t ana;
+ AnalyzeLEBinary(&ana,ap->szs.data,ap->szs.size);
+
+ const u_usec_t duration_usec = GetTimerUSec() - start_usec;
+
+
+ //--- print result
+
+ PrintHeaderAP(ap,"lecode");
+ PrintScriptVars(&ap->ps,0,
+ "phase=%d\n"
+ "version=%d\n"
+ "build=%d\n"
+ "region=\"%s\"\n"
+ "debug=%d\n"
+ "header_size=%d\n"
+ "file_size=%d\n"
+ "creation_time=\"%u, %s\"\n"
+ "edit_time=\"%u, %s\"\n"
+ "required_szs_tool=\"%u, %s\"\n"
+ "edit_by_szs_tool=\"%u, %s\"\n"
+
+ ,ana.head->phase
+ ,ana.header_vers
+ ,ntohl(ana.head->build_number)
+ ,ana.head->region == 'P' ? "PAL"
+ :ana.head->region == 'E' ? "USA"
+ :ana.head->region == 'J' ? "Japan"
+ :ana.head->region == 'K' ? "Korea"
+ : "?"
+ ,ana.head->debug == 'D'
+ ,ana.header_size
+ ,ntohl(ana.head->file_size)
+ ,ana.creation_time
+ ,ana.creation_time ? PrintTimeByFormat("%F %T %Z",ana.creation_time) : "-"
+ ,ana.edit_time
+ ,ana.edit_time ? PrintTimeByFormat("%F %T %Z",ana.edit_time) : "-"
+ ,ana.szs_version
+ ,ana.szs_version ? DecodeVersion(ana.szs_version) : "-"
+ ,ana.edit_version
+ ,ana.edit_version ? DecodeVersion(ana.edit_version) : "-"
+ );
+
+ PrintFooterAP(ap,ana.valid>0,duration_usec,0);
+ ResetLEAnalyze(&ana);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// misc ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void CalcHaveSZS ( szs_file_t * szs )
+{
+ DASSERT(szs);
+ if ( szs->data && !szs->have.valid && CanBeATrackSZS(szs) )
+ {
+ szs_file_t temp;
+ CopySZS(&temp,true,szs);
+ temp.check_only = true;
+ analyze_szs_t as;
+ AnalyzeSZS(&as,true,&temp,temp.fname);
+ szs->have = temp.have;
+ ResetAnalyzeSZS(&as);
+ ResetSZS(&temp);
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+