aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-pat.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--project/src/lib-pat.c2166
1 files changed, 2166 insertions, 0 deletions
diff --git a/project/src/lib-pat.c b/project/src/lib-pat.c
new file mode 100644
index 0000000..131d0bc
--- /dev/null
+++ b/project/src/lib-pat.c
@@ -0,0 +1,2166 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * 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-pat.h"
+
+#include "pat.inc"
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// PAT action log ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool PAT_ACTION_LOG ( const char * format, ...)
+ __attribute__ ((__format__(__printf__,1,2)));
+
+static bool PAT_ACTION_LOG ( const char * format, ...)
+{
+ #if HAVE_PRINT
+ {
+ char buf[200];
+ snprintf(buf,sizeof(buf),">>>[PAT]<<< %s",format);
+ va_list arg;
+ va_start(arg,format);
+ PRINT_ARG_FUNC(buf,arg);
+ va_end(arg);
+ }
+ #endif
+
+ if ( verbose > 2 || PAT_MODE & PATMD_LOG )
+ {
+ fflush(stdout);
+ fprintf(stdlog," %s>[PAT]%s ",colset->heading,colset->info);
+ va_list arg;
+ va_start(arg,format);
+ vfprintf(stdlog,format,arg);
+ va_end(arg);
+ fputs(colset->reset,stdlog);
+ fflush(stdlog);
+ return true;
+ }
+
+ return false;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enum pat_entry_t
+{
+ PAT_SECT_SETUP,
+ PAT_SECT_SIMPLE,
+ PAT_SECT_HEADER,
+ PAT_SECT_S0_BASE,
+ PAT_SECT_S0_LIST,
+ PAT_SECT_STRINGS,
+
+ PAT_N_SECT // number of PAT sections
+};
+
+const KeywordTab_t pat_section_name[] =
+{
+ { PAT_SECT_SETUP, "SETUP", 0, 0 },
+ { PAT_SECT_SIMPLE, "SIMPLE", 0, 0 },
+ { PAT_SECT_HEADER, "HEADER", 0, 0 },
+ { PAT_SECT_S0_BASE, "S0_BASE", 0, 0 },
+ { PAT_SECT_S0_LIST, "S0_LIST", 0, 0 },
+ { PAT_SECT_STRINGS, "STRINGS", 0, 0 },
+ { 0,0,0,0 }
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// pat_mode_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+pat_mode_t PAT_MODE = PATMD_M_DEFAULT;
+int have_pat_patch_count = 0;
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t opt_pat_tab[] =
+{
+ { 0, "CLEAR", "RESET", PATMD_M_ALL | PATMD_F_HIDE },
+ { PATMD_M_DEFAULT, "DEFAULT", 0, PATMD_M_ALL | PATMD_F_HIDE },
+
+ { PATMD_LOG, "LOG", 0, 0 },
+ { PATMD_SILENT, "SILENT", 0, 0 },
+
+ { 0, "AUTO", 0, PATMD_M_BOTH | PATMD_F_HIDE },
+ { PATMD_M_BOTH, "BOTH", 0, PATMD_M_BOTH },
+ { PATMD_SIMPLE, "SIMPLE", 0, PATMD_M_BOTH },
+ { PATMD_COMPLEX, "COMPLEX", 0, PATMD_M_BOTH },
+
+ { PATMD_TEST0, "TEST0", "T0", PATMD_M_TEST | PATMD_F_HIDE },
+ { PATMD_TEST1, "TEST1", "T1", PATMD_M_TEST },
+ { PATMD_TEST2, "TEST2", "T2", PATMD_M_TEST },
+ { PATMD_TEST3, "TEST3", "T3", PATMD_M_TEST },
+
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LoadParametersPAT
+(
+ ccp log_prefix // not NULL:
+ // print log message with prefix
+)
+{
+ static bool done = false;
+ if (done)
+ return;
+ done = true;
+
+ SetPatMode(PAT_MODE);
+
+
+ //--- logging
+
+ if ( !PAT_ACTION_LOG("Global PAT Modes: %s\n",GetPatMode())
+ && log_prefix
+ && PAT_MODE != PATMD_M_DEFAULT
+ && !(PAT_MODE & PATMD_SILENT) )
+ {
+ fprintf(stdlog,"%sglobal pat modes: %s [%x]\n",log_prefix,GetPatMode(),PAT_MODE);
+ fflush(stdlog);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+pat_mode_t GetRelevantPatMode ( pat_mode_t mode )
+{
+ mode = mode & PATMD_M_ALL;
+
+ if ( PATCH_FILE_MODE & PFILE_F_LOG_ALL )
+ mode |= PATMD_LOG;
+
+ return mode;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SetPatMode ( uint new_mode )
+{
+ noPRINT("SET-PAT/IN: PC=%d, MPC=%d\n",
+ have_patch_count, have_pat_patch_count );
+
+// if (have_pat_patch_count)
+// have_patch_count--, have_pat_patch_count--;
+
+ PAT_MODE = GetRelevantPatMode(new_mode);
+
+// if ( PAT_MODE & PATMD_M_PATCH )
+// have_patch_count++, have_pat_patch_count++;
+
+ noPRINT("SET-PAT/OUT: PC=%d, MPC=%d\n",
+ have_patch_count, have_pat_patch_count );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptPat ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ static bool done = false;
+ if (!done)
+ {
+ done = true;
+ if ( *arg != '+' && *arg != '-' )
+ PAT_MODE = 0;
+ }
+
+ s64 stat = ScanKeywordList(arg,opt_pat_tab,0,true,0,PAT_MODE,
+ "Option --pat",ERR_SYNTAX);
+ if ( stat != -1 )
+ {
+ SetPatMode(stat);
+ return 0;
+ }
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint PrintPatMode ( char *buf, uint bufsize, pat_mode_t mode )
+{
+ DASSERT(buf);
+ DASSERT(bufsize>10);
+ char *dest = buf;
+ char *end = buf + bufsize - 1;
+
+ mode = GetRelevantPatMode(mode) | PATMD_F_HIDE;
+ pat_mode_t mode1 = mode;
+
+ const KeywordTab_t *ct;
+ for ( ct = opt_pat_tab; ct->name1 && dest < end; ct++ )
+ {
+ if ( ct->opt & PATMD_F_HIDE )
+ continue;
+
+ if ( ct->opt ? (mode & ct->opt) == ct->id : mode & ct->id )
+ {
+ if ( dest > buf )
+ *dest++ = ',';
+ dest = StringCopyE(dest,end,ct->name1);
+ mode &= ~(ct->id|ct->opt);
+ }
+ }
+
+ if ( mode1 == (PATMD_M_DEFAULT|PATMD_F_HIDE) )
+ dest = StringCopyE(dest,end," (default)");
+ else if (!mode1)
+ dest = StringCopyE(dest,end,"(none)");
+
+ *dest = 0;
+ return dest-buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetPatMode()
+{
+ static char buf[100] = {0};
+ if (!*buf)
+ PrintPatMode(buf,sizeof(buf),PAT_MODE);
+ return buf;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// PAT0 iterator ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int pat_iter_func
+(
+ brsub_cut_t *bcut, // pointer to data structure
+ int grp, // index of group, <0: grp_entry_t
+ int entry // index of entry, -1: no entry
+)
+{
+ DASSERT(bcut);
+ szs_iterator_t *it = bcut->it;
+ DASSERT(it);
+ DASSERT(it->func_it);
+
+ it->group = grp;
+ it->entry = entry;
+
+ noPRINT("PAT( %2d, %2d, %s )\n",grp,entry,it->path);
+
+ switch (grp)
+ {
+ case GROUP_IDX_SUB_HEADER:
+ {
+ const pat_head_t *head = (pat_head_t*)( bcut->data + it->off );
+ it->size = sizeof(*head);
+ it->entry = 0;
+ const int stat = it->func_it(it,false);
+ it->index++;
+ return stat;
+ }
+ }
+
+ const int stat = it->func_it(it,false);
+ it->index++;
+ return stat;
+}
+
+//-----------------------------------------------------------------------------
+
+static ccp pat_names[] =
+{
+ ".base", // 0
+ ".name", // 1
+ "", // 2
+ ".attrib", // 3
+ 0
+};
+
+static ccp GetSectNamePAT ( uint index )
+{
+ ccp res = index < sizeof(pat_names)/sizeof(*pat_names)
+ ? pat_names[index]
+ : 0;
+ if ( !res || !*res )
+ {
+ const int bufsize = 12;
+ char *buf = GetCircBuf(bufsize);
+ snprintf(buf,bufsize,".%u",index);
+ res = buf;
+ }
+ return res;
+}
+
+//-----------------------------------------------------------------------------
+
+int IterateFilesPAT
+(
+ struct szs_iterator_t *it, // iterator struct with all infos
+ bool term // true: termination hint
+)
+{
+ DASSERT(it);
+ return CutFilesBRSUB( it, pat_iter_func, 0,
+ pat_names, sizeof(pat_names) / sizeof(*pat_names) -1 );
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// PAT0 base interface ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializePAT ( pat_t * pat )
+{
+ DASSERT(pat);
+ memset(pat,0,sizeof(*pat));
+
+ pat->version = 4;
+ pat->n_sect = 6;
+ pat->revision = REVISION_NUM;
+
+ pat->fname = EmptyString;
+ InitializeParamField(&pat->s1_str);
+
+ LoadParametersPAT( verbose > 0 ? " - " : 0 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void ClearElementsPAT ( pat_t * pat )
+{
+ DASSERT(pat);
+
+ uint i;
+ pat_element_t *pe = pat->pelem;
+ for ( i = 0; i < pat->n_pelem; i++, pe++ )
+ {
+ FREE(pe->selem);
+ FreeString(pe->bname);
+ FreeString(pe->sname);
+ memset(pe,0,sizeof(*pe));
+ }
+ pat->n_pelem = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void ClearPAT ( pat_t * pat )
+{
+ DASSERT(pat);
+
+ ClearElementsPAT(pat);
+ if ( pat->outmode != PATMD_SIMPLE )
+ ResetParamField(&pat->s1_str);
+
+ FREE(pat->raw_data);
+ pat->raw_data = 0;
+ pat->raw_data_size = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetPAT ( pat_t * pat )
+{
+ DASSERT(pat);
+
+ ClearPAT(pat);
+ ResetParamField(&pat->s1_str);
+ FreeString(pat->fname);
+ FreeString(pat->name);
+ #if USE_NEW_CONTAINER_PAT
+ ResetContainer(&pat->container);
+ #else
+ FreeContainer(pat->old_container);
+ #endif
+ InitializePAT(pat);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool IsSimplePAT ( pat_t *pat )
+{
+ DASSERT(pat);
+ const pat_element_t *pe = pat->pelem;
+
+ if
+ (
+ pat->n_pelem != 1
+
+ || pat->head.unknown_00
+ || pat->head.unknown_02
+ || pat->head.unknown_0a
+ || pat->head.unknown_0c
+
+ || pat->base.unknown_04
+ || pat->base.unknown_08 != htons(0xffff)
+ || pat->base.unknown_0a
+ || pat->base.n_unknown != htons(1)
+ || pat->base.unknown_0e
+ || pat->base.unknown_10
+ || pat->base.unknown_12
+ || pat->base.unknown_14
+ || pat->base.unknown_16
+
+ || pe->belem.unknown_02
+ || pe->belem.unknown_04
+ || pe->belem.unknown_06 != htons(1)
+
+ || pe->sref.unknown_04
+ || pe->sref.type != htons(5)
+
+ || pe->shead.unknown_02
+
+ || !pe->bname
+ || !pe->sname
+ || strcmp(pe->bname,pe->sname)
+ )
+ return false;
+
+ uint i;
+ ParamFieldItem_t *it = pat->s1_str.field;
+ for ( i = 0; i < pat->s1_str.used; i++, it++ )
+ if (it->num)
+ return false;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp GetNamePAT ( pat_t *pat )
+{
+ DASSERT(pat);
+ if (!pat->name)
+ {
+ if (pat->fname)
+ {
+ ccp begin = strrchr(pat->fname,'/');
+ begin = begin ? begin + 1 : pat->fname;
+ ccp end = strchr(begin,'.');
+ const uint len = end ? end-begin : strlen(begin);
+ pat->name = MEMDUP(begin,len);
+ }
+ else
+ pat->name = STRDUP("pat");
+ }
+ return pat->name;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ParamFieldItem_t * AddS1String ( pat_t *pat, ccp string, u32 num )
+{
+ DASSERT(pat);
+ DASSERT(string);
+
+ ParamFieldItem_t *it = AppendParamField(&pat->s1_str,string,false,num,0);
+ DASSERT(it);
+ return it;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetStringPAT
+(
+ pat_t * pat, // PAT data structure
+ const void * base, // base for offset
+ u32 offset, // offset of string
+ ccp if_invalid // result if invalid
+)
+{
+ DASSERT(pat);
+ DASSERT(base);
+
+ if (offset)
+ {
+ ccp string = (ccp)base + offset;
+ #if USE_NEW_CONTAINER_PAT
+ if (InContainerP(&pat->container,string))
+ #else
+ if (InDataContainer(pat->old_container,string))
+ #endif
+ return string;
+ }
+
+ return if_invalid;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static float CalcFactorSHEAD
+(
+ pat_s0_selem_t *se, // element list, big endian
+ uint n_se // number of elements in 'se'
+)
+{
+ if ( !se || n_se < 2 )
+ return 1.0;
+
+ double min, max;
+ min = max = bef4(&se->time);
+ while ( --n_se > 0 )
+ {
+ se++;
+ const double time = bef4(&se->time);
+ if ( min > time ) min = time;
+ if ( max < time ) max = time;
+ }
+
+ // special rounding to get little smaller values like Nintendo
+ const float delta = max - min;
+ return delta < 1e-6 ? 1.0 : 0.9999999/delta;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+typedef struct slist_param_t
+{
+ float factor; // factor for list header
+ u16 unknown_02; // value for list header padding
+}
+slist_param_t;
+
+//-----------------------------------------------------------------------------
+
+typedef struct slist_list_t
+{
+ float factor; // factor for list header
+ u16 unknown_02; // value for list header padding
+
+ pat_s0_selem_t *selem; // list with string elements
+ uint selem_used; // number of used elements of 'selem'
+ uint selem_size; // number of alloced elements of 'selem'
+}
+slist_list_t;
+
+//-----------------------------------------------------------------------------
+
+static void Close_SList
+(
+ slist_param_t *param, // parameter, never NULL
+ slist_list_t *list // NULL or list element
+)
+{
+ DASSERT(param);
+
+ if (list)
+ {
+ list->unknown_02 = param->unknown_02;
+ list->factor = IsNormalF(param->factor)
+ ? param->factor
+ : CalcFactorSHEAD(list->selem,list->selem_used);
+ }
+
+ //--- reset param
+
+ param->factor = NAN;
+ param->unknown_02 = 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// CreateRawPAT() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError CreateRawPAT
+(
+ pat_t * pat // pointer to valid PAT
+)
+{
+ DASSERT(pat);
+ TRACE("CreateRawPAT()\n");
+
+ //--- setup string pool
+
+ string_pool_t sp;
+ InitializeStringPool(&sp);
+ InsertStringPool(&sp,GetNamePAT(pat),false,0);
+
+ uint i;
+ for ( i = 0; i < pat->s1_str.used; i++ )
+ InsertStringPool(&sp,pat->s1_str.field[i].key,false,0);
+
+
+ //--- calculate sizes
+
+ const uint s0_offset = PAT_HEAD_OFFSET + sizeof(pat_head_t);
+ uint s0_size = sizeof(pat_s0_bhead_t)
+ + pat->n_pelem
+ * ( sizeof(pat_s0_belem_t)
+ + sizeof(pat_s0_sref_t)
+ + sizeof(pat_s0_shead_t) );
+
+ pat_element_t *pe = pat->pelem;
+ for ( i = 0; i < pat->n_pelem; i++, pe++ )
+ {
+ s0_size += pe->selem_used * sizeof(pat_s0_selem_t);
+ InsertStringPool(&sp,pe->bname,false,0);
+ InsertStringPool(&sp,pe->sname,false,0);
+ }
+
+ const uint s1_size = sizeof(u32) * pat->s1_str.used;
+ const uint pat_size = s0_offset + s0_size + 2 * s1_size;
+
+ CalcStringPool(&sp,pat_size,&be_func);
+ uint total_size = pat_size + sp.size;
+
+ PRINT("\e[31;1m SIZE: s0=%x, s1=%x, pat=%x, tot=%x\e[0m\n",
+ s0_size, s1_size, pat_size, total_size );
+
+
+ //--- setup data
+
+ FREE(pat->raw_data);
+ pat->raw_data = CALLOC(1,total_size);
+ pat->raw_data_size = total_size;
+
+ memcpy( pat->raw_data + pat_size, sp.data, sp.size );
+
+
+ //--- setup brres sub header
+
+ brsub_header_t *brh = (brsub_header_t*)pat->raw_data;
+ memcpy(brh->magic,PAT_MAGIC,sizeof(brh->magic));
+
+ brh->size = htonl(pat_size);
+ brh->version = htonl(pat->version);
+ brh->grp_offset[0] = htonl(s0_offset);
+
+ const u32 s1_offset = s0_offset + s0_size;
+ brh->grp_offset[1] = htonl(s1_offset);
+
+ const u32 s3_offset = s1_offset + s1_size;
+ brh->grp_offset[2] = brh->grp_offset[3] = htonl(s3_offset);
+
+ brh->grp_offset[4] = htonl(s3_offset+s1_size);
+ brh->grp_offset[6] = htonl(FindStringPool(&sp,GetNamePAT(pat),0));
+
+
+ //--- setup header data
+
+ pat_head_t *ph = &pat->head;
+ ph->n_sect0 = htons(pat->n_pelem);
+ ph->n_sect1 = htons(pat->s1_str.used);
+ memcpy( pat->raw_data + PAT_HEAD_OFFSET, ph, sizeof(*ph) );
+
+
+ //--- setup section 0
+
+ pat_s0_bhead_t *bh = &pat->base;
+ const uint base_size = sizeof(pat_s0_bhead_t) + pat->n_pelem * sizeof(pat_s0_belem_t);
+ bh->size = htonl(base_size);
+ bh->n_elem = htons(pat->n_pelem);
+
+ pat_s0_bhead_t *dest_bh = (pat_s0_bhead_t*)(pat->raw_data + s0_offset);
+ memcpy( dest_bh, bh, sizeof(*bh) );
+
+
+ //--- setup section 0: string lists
+
+ u32 sref_offset = s0_offset + base_size;
+ u32 shead_offset = sref_offset + pat->n_pelem * sizeof(pat_s0_sref_t);
+
+ for ( i = 0; i < pat->n_pelem; i++ )
+ {
+ const int idx = pat->shead_order[i];
+ pat_element_t *pe = pat->pelem + idx;
+ pat_s0_shead_t *sh = &pe->shead;
+ sh->n_elem = htons(pe->selem_used);
+
+ //--- copy data
+
+ memcpy( pat->raw_data + shead_offset, sh, sizeof(*sh) );
+ pe->sref.offset_strlist
+ = htonl(shead_offset - sref_offset - sizeof(pat_s0_sref_t) * idx);
+ shead_offset += sizeof(pat_s0_shead_t);
+
+ const uint copy_size = sizeof(pat_s0_selem_t) * pe->selem_used;
+ memcpy( pat->raw_data + shead_offset, pe->selem, copy_size );
+ shead_offset += copy_size;
+ }
+
+
+ //--- setup section 0: string reference
+
+ for ( i = 0; i < pat->n_pelem; i++ )
+ {
+ const int idx = pat->sref_order[i];
+ pat_element_t *pe = pat->pelem + idx;
+ pat_s0_sref_t *sr = &pe->sref;
+ sr->offset_name = htonl(FindStringPool(&sp,pe->sname,0)-sref_offset);
+
+ memcpy( pat->raw_data + sref_offset, sr, sizeof(*sr) );
+ pe->belem.offset_strref = htonl(sref_offset - s0_offset);
+ sref_offset += sizeof(pat_s0_sref_t);
+ }
+
+
+ //--- setup section 0: base elements
+
+ for ( i = 0, pe = pat->pelem; i < pat->n_pelem; i++, pe++ )
+ {
+ pat_s0_belem_t *be = &pe->belem;
+ be->offset_name = htonl(FindStringPool(&sp,pe->bname,0)-s0_offset);
+
+ memcpy( dest_bh->elem+i, be, sizeof(*be) );
+ }
+
+
+ //--- setup section 1+3
+
+ ParamFieldItem_t *it = pat->s1_str.field;
+ for ( i = 0; i < pat->s1_str.used; i++, it++ )
+ {
+ u32 offset = FindStringPool(&sp,it->key,0);
+ if (offset)
+ offset -= s1_offset;
+ write_be32( pat->raw_data + s1_offset + 4*i, offset );
+ write_be32( pat->raw_data + s3_offset + 4*i, it->num );
+ }
+
+
+ //--- terminate
+
+ ResetStringPool(&sp);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// setup pat parser ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const VarMap_t * SetupVarsPAT()
+{
+ static VarMap_t vm = {0};
+ if (!vm.used)
+ {
+ #if 0
+ //--- setup integer variables
+
+ struct inttab_t { ccp name; int val; };
+ static const struct inttab_t inttab[] =
+ {
+ {0,0}
+ };
+
+ const struct inttab_t * ip;
+ for ( ip = inttab; ip->name; ip++ )
+ DefineIntVar(&vm,ip->name,ip->val);
+ #endif
+
+ //--- setup basic parser variables
+
+ DefineParserVars(&vm);
+ }
+
+ return &vm;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextPAT: helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanText_SETUP
+(
+ pat_t * pat, // PAT data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(pat);
+ DASSERT(si);
+ TRACE("ScanText_SETUP()\n");
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ 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*)&pat->revision,1,0);
+ si->init_revision = si->cur_file->revision = pat->revision;
+ DefineIntVar(&si->gvar,"REVISION$SETUP",pat->revision);
+ DefineIntVar(&si->gvar,"REVISION$ACTIVE",pat->revision);
+ }
+ else if (!strcmp(name,"NAME"))
+ ConcatStringsSI(si,(char**)&pat->name,0);
+ else
+ GotoEolSI(si);
+ CheckEolSI(si);
+ }
+ CheckLevelSI(si);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanText_SIMPLE
+(
+ pat_t * pat, // PAT data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(pat);
+ DASSERT(si);
+ TRACE("ScanText_SIMPLE()\n");
+
+ memset(&pat->head,0,sizeof(pat->head));
+ memset(&pat->base,0,sizeof(pat->base));
+ pat->base.unknown_08 = htons(0xffff);
+ pat->base.n_unknown = 1;
+
+ ClearElementsPAT(pat);
+ pat_element_t *pe = pat->pelem;
+ pe->belem.unknown_06 = htons(1);
+ pe->sref.type = htons(5);
+ pat->n_pelem = 1;
+
+ slist_param_t parm;
+ Close_SList(&parm,0);
+ slist_list_t elem;
+ memset(&elem,0,sizeof(elem));
+ double last_time = 0.0;
+
+ int enabled = 1;
+ ScanParam_t ptab[] =
+ {
+ { "ENABLED", SPM_INT, &enabled },
+ { "BRRES-NAME", SPM_STRING, &pat->name },
+ { "PAT-NAME", SPM_STRING, &pe->bname },
+ { "N-FRAMES", SPM_U16, &pat->head.n_frames },
+ { "CYCLIC", SPM_U16, &pat->head.cyclic },
+ { "UNKNOWN-00", SPM_U16, &pe->belem.unknown_00 },
+ { "FACTOR", SPM_FLOAT, &parm.factor },
+ { "PADDING", SPM_U16, &parm.unknown_02 },
+ {0}
+ };
+
+ enumError err = ERR_OK;
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ if (!enabled)
+ {
+ ClearPAT(pat);
+ pat->outmode = PATMD_COMPLEX;
+ return err;
+ }
+ continue;
+ }
+ pat->outmode = PATMD_SIMPLE;
+
+ double time;
+ char *str = 0;
+ if ( ScanDoubleSI(si,&time,1) || ConcatStringsSI(si,&str,0) )
+ continue;
+
+ ParamFieldItem_t *it
+ = FindInsertParamField( &pat->s1_str, str, true, 0 );
+ const uint sidx = it ? it - pat->s1_str.field : 0;
+
+ if ( elem.selem_used == elem.selem_size )
+ {
+ elem.selem_size += elem.selem_size + 20;
+ elem.selem = REALLOC(elem.selem,elem.selem_size*sizeof(*elem.selem));
+ }
+ DASSERT(elem.selem);
+ DASSERT(elem.selem_used < elem.selem_size);
+ pat_s0_selem_t *se = elem.selem + elem.selem_used++;
+ last_time += time;
+ write_bef4(&se->time,last_time);
+ se->index = htons(sidx);
+ se->unknown_06 = htons(0);
+
+ CheckEolSI(si);
+ }
+ Close_SList(&parm,&elem);
+
+ pe->sname = STRDUP(pe->bname);
+ be16n((u16*)&pat->head,(u16*)&pat->head,sizeof(pat->head)/2);
+ be16n((u16*)&pat->base,(u16*)&pat->base,sizeof(pat->base)/2);
+ be16n((u16*)&pe->belem.unknown_00,(u16*)&pe->belem.unknown_00,1);
+ pe->shead.unknown_02 = htons(elem.unknown_02);
+ write_bef4(&pe->shead.factor,elem.factor);
+ pe->selem = elem.selem;
+ pe->selem_used = elem.selem_used;
+ pe->selem_size = elem.selem_size;
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanText_HEADER
+(
+ pat_t * pat, // PAT data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(pat);
+ DASSERT(si);
+ TRACE("ScanText_HEADER()\n");
+
+ memset(&pat->head,0,sizeof(pat->head));
+
+ ScanParam_t ptab[] =
+ {
+ { "NAME", SPM_STRING, &pat->name },
+ { "UNKNOWN-00", SPM_U16, &pat->head.unknown_00 },
+ { "UNKNOWN-02", SPM_U16, &pat->head.unknown_02 },
+ { "N-FRAMES", SPM_U16, &pat->head.n_frames },
+ // { "N-SECT0", SPM_U16, &pat->head.n_sect0 },
+ // { "N-SECT1", SPM_U16, &pat->head.n_sect1 },
+ { "UNKNOWN-0A", SPM_U16, &pat->head.unknown_0a },
+ { "UNKNOWN-0C", SPM_U16, &pat->head.unknown_0c },
+ { "CYCLIC", SPM_U16, &pat->head.cyclic },
+ {0}
+ };
+
+ enumError err = ERR_OK;
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ WarnIgnoreSI(si,0);
+ }
+
+ be16n((u16*)&pat->head,(u16*)&pat->head,sizeof(pat->head)/2);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanText_S0_BASE
+(
+ pat_t * pat, // PAT data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(pat);
+ DASSERT(si);
+ TRACE("ScanText_S0_BASE()\n");
+
+ memset(&pat->base,0,sizeof(pat->base));
+ pat->base.unknown_08 = 0xffff;
+
+ ScanParam_t ptab[] =
+ {
+ // { "SIZE", SPM_U32, &pat->base.size },
+ { "UNKNOWN-04", SPM_U16, &pat->base.unknown_04 },
+ // { "N-ELEM", SPM_U16, &pat->base.n_elem },
+ { "UNKNOWN-08", SPM_U16, &pat->base.unknown_08 },
+ { "UNKNOWN-0A", SPM_U16, &pat->base.unknown_0a },
+ { "N-UNKNOWN", SPM_U16, &pat->base.n_unknown },
+ { "UNKNOWN-0E", SPM_U16, &pat->base.unknown_0e },
+ { "UNKNOWN-10", SPM_U16, &pat->base.unknown_10 },
+ { "UNKNOWN-12", SPM_U16, &pat->base.unknown_12 },
+ { "UNKNOWN-14", SPM_U16, &pat->base.unknown_14 },
+ { "UNKNOWN-16", SPM_U16, &pat->base.unknown_16 },
+ {0}
+ };
+
+ ClearElementsPAT(pat);
+ uint n_elem = 0;
+ uint n_sref = 0;
+ u8 elem_to_sref[PAT_MAX_ELEM];
+ u8 sref_to_shead[PAT_MAX_ELEM];
+ pat_s0_sref_t sref_list[PAT_MAX_ELEM];
+ memset(sref_list,0,sizeof(sref_list));
+ char *sname[PAT_MAX_ELEM] = {0};
+
+ enumError err = ERR_OK;
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ if ( ch == '$' )
+ {
+ char name[PAT_MAX_NAME_LEN+1];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+
+ if ( !strcmp(name,"$BASE") )
+ {
+ if ( n_elem == PAT_MAX_ELEM )
+ {
+ WarnIgnoreSI(si,
+ "Maximum number of base elements (%u) already defined.\n",
+ PAT_MAX_ELEM );
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,n_elem,0,2);
+ if (err)
+ continue;
+
+ pat_element_t *pe = pat->pelem + n_elem;
+ pat_s0_belem_t *be = &pe->belem;
+ memset(be,0,sizeof(*be));
+ ScanU16SI(si,(u16*)be,4,0);
+ be16n((u16*)be,(u16*)be,4);
+
+ ScanU8SI(si,elem_to_sref+n_elem,1,0);
+ ConcatStringsSI(si,(char**)&pe->bname,0);
+
+ n_elem++;
+ CheckEolSI(si);
+ continue;
+ }
+
+ if ( !strcmp(name,"$SREF") )
+ {
+ if ( n_sref == n_elem )
+ {
+ WarnIgnoreSI(si,
+ "You can't define more string references as base elements (%u).\n",
+ n_elem );
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,n_sref,0,2);
+ if (err)
+ continue;
+
+ pat_s0_sref_t *sref = sref_list + n_sref;
+ ScanU16SI(si,&sref->unknown_04,2,0);
+ be16n(&sref->unknown_04,&sref->unknown_04,2);
+
+ ScanU8SI(si,sref_to_shead+n_sref,1,0);
+ ConcatStringsSI(si,sname+n_sref,0);
+
+ n_sref++;
+ CheckEolSI(si);
+ continue;
+ }
+
+ si->cur_file->ptr = si->cur_file->prev_ptr;
+ }
+
+ WarnIgnoreSI(si,0);
+ }
+
+ be16n((u16*)&pat->base,(u16*)&pat->base,sizeof(pat->base)/2);
+ pat->n_pelem = n_elem;
+
+ //--- copy 'sref' and 'sname'
+
+ if (!si->no_warn)
+ {
+ uint i;
+ pat_element_t *pe = pat->pelem;
+ for ( i = 0; i < n_elem; i++, pe++ )
+ {
+ const uint s_idx = elem_to_sref[i];
+ noPRINT(">>> %u -> %u\n",i,s_idx);
+ if ( s_idx >= n_sref )
+ {
+ ERROR0(ERR_WARNING,
+ "No 'string reference' for base element %u (sref=%u).\n[%s @%u]\n",
+ i, s_idx, si->cur_file->name, si->cur_file->line );
+ continue;
+ }
+
+ memcpy( &pe->sref, sref_list+s_idx, sizeof(pe->sref) );
+ if (sname[s_idx])
+ pe->sname = STRDUP(sname[s_idx]);
+
+ pat->sref_order[s_idx] = i;
+ pat->elem_to_shead[i] = sref_to_shead[s_idx];
+ }
+ }
+
+ uint i;
+ for ( i = 0; i < n_sref; i++ )
+ FreeString(sname[i]);
+
+ //PRINT("\e[33;1m elem_to_shead:\e[0m\n");
+ //HEXDUMP16(15,0,pat->elem_to_shead,sizeof(pat->elem_to_shead));
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanText_S0_LIST
+(
+ pat_t * pat, // PAT data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(pat);
+ DASSERT(si);
+ TRACE("ScanText_S0_LIST()\n");
+
+ slist_param_t parm;
+ Close_SList(&parm,0);
+
+ ScanParam_t ptab[] =
+ {
+ { "FACTOR", SPM_FLOAT, &parm.factor },
+ { "PADDING", SPM_U16, &parm.unknown_02 },
+ {0}
+ };
+
+ slist_list_t elist[PAT_MAX_ELEM], *elem = 0;
+ memset(elist,0,sizeof(elist));
+ uint n_list = 0;
+ bool no_elem_warned = false;
+
+ double last_time = 0.0;
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ if ( ch == '$' )
+ {
+ char name[PAT_MAX_NAME_LEN+1];
+ ScanNameSI(si,name,sizeof(name),true,true,0);
+ if (!strcmp(name,"$LIST"))
+ {
+ Close_SList(&parm,elem);
+
+ if ( n_list == PAT_MAX_ELEM )
+ {
+ WarnIgnoreSI(si,
+ "Maximum number of string list (%u) already defined.\n",
+ PAT_MAX_ELEM );
+ elem = 0;
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,n_list,0,2);
+ if (err)
+ continue;
+
+ elem = elist + n_list++;
+ last_time = 0.0;
+ CheckEolSI(si);
+ continue;
+ }
+
+ WarnIgnoreSI(si,0);
+ continue;
+ }
+
+ if (!elem)
+ {
+ if (!no_elem_warned)
+ {
+ no_elem_warned = true;
+ WarnIgnoreSI(si,"Define a list with '$LIST <var_name>' first.\n");
+ }
+ else
+ GotoEolSI(si);
+ continue;
+ }
+
+ double time;
+ u16 param[2];
+ if ( ScanDoubleSI(si,&time,1) || ScanU16SI(si,param,2,0) )
+ continue;
+
+ if ( elem->selem_used == elem->selem_size )
+ {
+ elem->selem_size += elem->selem_size + 20;
+ elem->selem = REALLOC(elem->selem,elem->selem_size*sizeof(*elem->selem));
+ }
+ DASSERT(elem->selem);
+ DASSERT(elem->selem_used < elem->selem_size);
+ pat_s0_selem_t *se = elem->selem + elem->selem_used++;
+ last_time += time;
+ write_bef4(&se->time,last_time);
+ se->index = htons(param[0]);
+ se->unknown_06 = htons(param[1]);
+
+ CheckEolSI(si);
+ }
+ Close_SList(&parm,elem);
+
+ if (!si->no_warn)
+ {
+ uint i;
+ pat_element_t *pe = pat->pelem;
+ for ( i = 0; i < pat->n_pelem; i++, pe++ )
+ {
+ const uint h_idx = pat->elem_to_shead[i];
+ noPRINT(">>> %u -> %u\n",i,h_idx);
+ if ( h_idx >= n_list )
+ {
+ ERROR0(ERR_WARNING,
+ "No 'string list' for base element %u (index=%u).\n[%s @%u]\n",
+ i, h_idx, si->cur_file->name, si->cur_file->line );
+ continue;
+ }
+ pat->shead_order[h_idx] = i;
+
+ slist_list_t *elem = elist + h_idx;
+ FREE(pe->selem);
+ const uint copy_size = elem->selem_used * sizeof(*elem->selem);
+ pe->selem = MALLOC(copy_size);
+ memcpy(pe->selem,elem->selem,copy_size);
+ pe->selem_used = pe->selem_size = elem->selem_used;
+
+ write_bef4(&pe->shead.factor, elem->factor );
+ pe->shead.unknown_02 = htons(elem->unknown_02);
+ }
+ }
+
+ uint i;
+ for ( i = 0; i < n_list; i++ )
+ FREE(elist[i].selem);
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanText_STRINGS
+(
+ pat_t * pat, // PAT data structure
+ ScanInfo_t * si // valid data
+)
+{
+ DASSERT(pat);
+ DASSERT(si);
+ TRACE("ScanText_STRINGS()\n");
+
+ ResetParamField(&pat->s1_str);
+
+ ScanParam_t ptab[] =
+ {
+ {0}
+ };
+
+ for(;;)
+ {
+ char ch = NextCharSI(si,true);
+ if ( !ch || ch == '[' )
+ break;
+
+ if ( ch == '@' )
+ {
+ ScanParamSI(si,ptab);
+ continue;
+ }
+
+ enumError err;
+ ScanIndexSI(si,&err,pat->s1_str.used,0,2);
+ if (err)
+ continue;
+
+ uint attrib = 0;
+ if (ScanU32SI(si,&attrib,1,0))
+ continue;
+
+ char *str = 0;
+ if (ConcatStringsSI(si,&str,0))
+ continue;
+
+ AppendParamField(&pat->s1_str,str,true,attrib,0);
+ CheckEolSI(si);
+ }
+
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanTextPAT() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanTextPAT
+(
+ pat_t * pat, // PAT data structure
+ bool init_pat, // true: initialize 'pat' first
+ const void * data, // data to scan
+ uint data_size // size of 'data'
+)
+{
+ PRINT("ScanTextPAT(init=%d)\n",init_pat);
+
+ DASSERT(pat);
+ if (init_pat)
+ InitializePAT(pat);
+
+ ScanInfo_t si;
+ InitializeSI(&si,data,data_size,pat->fname,pat->revision);
+ si.predef = SetupVarsPAT();
+
+ enumError max_err = ERR_OK;
+ bool is_pass2 = false;
+
+ for(;;)
+ {
+ PRINT("----- SCAN PAT SECTIONS, PASS%u ...\n",is_pass2);
+
+ ClearPAT(pat);
+
+ max_err = ERR_OK;
+ si.no_warn = !is_pass2;
+ si.total_err = 0;
+ DefineIntVar(&si.gvar, "$PASS", is_pass2+1 );
+
+ for(;;)
+ {
+ char ch = NextCharSI(&si,true);
+ if (!ch)
+ break;
+
+ if ( ch != '[' )
+ {
+ NextLineSI(&si,true,false);
+ continue;
+ }
+ ResetLocalVarsSI(&si,pat->revision);
+
+ si.cur_file->ptr++;
+ char name[20];
+ ScanNameSI(&si,name,sizeof(name),true,true,0);
+ noPRINT("--> #%04u: %s\n",si.line,name);
+
+ int abbrev_count;
+ const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,pat_section_name);
+ if ( !cmd || abbrev_count )
+ continue;
+ NextLineSI(&si,false,false);
+
+ noPRINT("\e[44;37;1m pass %u [%s] line %u \e[0m\n",
+ is_pass2+1, cmd->name1, si.cur_file->line );
+
+ if ( cmd->id < 0 || cmd->id >= PAT_N_SECT )
+ continue;
+
+ enumError err = ERR_OK;
+ switch (cmd->id)
+ {
+ case PAT_SECT_SETUP:
+ err = ScanText_SETUP(pat,&si);
+ break;
+
+ case PAT_SECT_SIMPLE:
+ if ( pat->outmode != PATMD_COMPLEX )
+ err = ScanText_SIMPLE(pat,&si);
+ break;
+
+ case PAT_SECT_HEADER:
+ if ( pat->outmode != PATMD_SIMPLE )
+ {
+ pat->outmode = PATMD_COMPLEX;
+ err = ScanText_HEADER(pat,&si);
+ }
+ break;
+
+ case PAT_SECT_S0_BASE:
+ if ( pat->outmode != PATMD_SIMPLE )
+ {
+ pat->outmode = PATMD_COMPLEX;
+ err = ScanText_S0_BASE(pat,&si);
+ }
+ break;
+
+ case PAT_SECT_S0_LIST:
+ if ( pat->outmode != PATMD_SIMPLE )
+ {
+ pat->outmode = PATMD_COMPLEX;
+ err = ScanText_S0_LIST(pat,&si);
+ }
+ break;
+
+ case PAT_SECT_STRINGS:
+ if ( pat->outmode != PATMD_SIMPLE )
+ {
+ pat->outmode = PATMD_COMPLEX;
+ err = ScanText_STRINGS(pat,&si);
+ }
+ break;
+
+ default:
+ // ignore all other section without any warnings
+ break;
+ }
+
+ if ( max_err < err )
+ max_err = err;
+ }
+
+ if (is_pass2)
+ break;
+ is_pass2 = true;
+ RestartSI(&si);
+ }
+
+ #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(ScanTextPAT) = %u (errcount=%u)\n", max_err, si.total_err );
+ ResetSI(&si);
+
+ CreateRawPAT(pat);
+ return max_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ScanRawPAT() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanRawPAT
+(
+ pat_t * pat, // PAT data structure
+ bool init_pat, // true: initialize 'pat' first
+ const void * data, // data to scan
+ uint data_size, // size of 'data'
+ ContainerPAT_t * cdata // NULL or container-data
+)
+{
+ DASSERT(pat);
+
+ #if USE_NEW_CONTAINER_PAT // ---------------------------------------
+
+ if (init_pat)
+ InitializePAT(pat);
+
+ CatchContainerData(&pat->container,0,cdata);
+ AssignContainer(&pat->container,0,data,data_size,CPM_COPY);
+ #ifdef TEST
+ DumpInfoContainer(stdlog,collog,2,"PAT: ",&pat->container,0);
+ #endif
+
+ #else // !USE_NEW_CONTAINER_PAT // ---------------------------------
+
+ if (init_pat)
+ InitializePAT(pat);
+ else
+ FreeContainer(pat->old_container);
+ pat->old_container = cdata ? cdata : NewContainer(0,data,data_size,CPM_COPY);
+
+ #endif // !USE_NEW_CONTAINER_PAT // --------------------------------
+
+ const endian_func_t * endian = &be_func;
+ pat_analyse_t ana;
+ if ( IsValidPAT(data,data_size,data_size,0,pat->fname,endian,&ana) >= VALID_ERROR )
+ {
+ return ERROR0(ERR_INVALID_DATA,
+ "Invalid PAT file: %s\n", pat->fname ? pat->fname : "?");
+ }
+
+
+ //--- scan PAT base infos
+
+ pat->version = endian->rd32(data+8);
+ pat->n_sect = GetSectionNumBRSUB(data,data_size,endian);
+
+ u32 offset = pat->n_sect * 4 + sizeof(brsub_header_t);
+ ccp name = GetStringPAT(pat, data, be32(data+offset), 0 );
+ if (name)
+ pat->name = STRDUP(name);
+ PRINT("PAT/NAME: %s\n",pat->name);
+
+
+ //--- copy header and section 0
+
+ memcpy( &pat->head, ana.head, sizeof(pat->head) );
+ memcpy( &pat->base, ana.s0_base, sizeof(pat->base) );
+
+ memcpy( &pat->sref_order, ana.s0_sref_order, sizeof(pat->sref_order) );
+ memcpy( &pat->shead_order, ana.s0_shead_order, sizeof(pat->shead_order) );
+
+ uint i;
+ pat->n_pelem = ana.n_sect0 < PAT_MAX_ELEM ? ana.n_sect0 : PAT_MAX_ELEM;
+ pat_element_t *pe = pat->pelem;
+ for ( i = 0; i < pat->n_pelem; i++, pe++ )
+ {
+ memcpy( &pe->belem, ana.s0_base->elem+i, sizeof(pe->belem) );
+ memcpy( &pe->sref, ana.s0_sref[i], sizeof(pe->sref) );
+ memcpy( &pe->shead, ana.s0_shead[i], sizeof(pe->shead) );
+
+ pe->bname = STRDUP(GetStringPAT( pat, ana.s0_base,
+ be32(&ana.s0_base->elem[i].offset_name), "?" ));
+ pe->sname = STRDUP(GetStringPAT( pat, ana.s0_sref[i],
+ be32(&ana.s0_sref[i]->offset_name), "?" ));
+
+ const uint n = ana.n_s0_list[i];
+ if (n)
+ {
+ pe->selem_used = pe->selem_size = n;
+ pe->selem = REALLOC(pe->selem,n*sizeof(*pe->selem));
+ memcpy( pe->selem, ana.s0_shead[i]->elem, n*sizeof(*pe->selem) );
+ }
+ }
+
+
+ //--- analyse sections 1 and 3
+
+ if (ana.s1_list)
+ {
+ uint i;
+ for ( i = 0; i < ana.n_sect1; i++ )
+ {
+ ccp str = GetStringPAT(pat,ana.s1_list,be32(ana.s1_list+i),"?");
+ AddS1String( pat, str, ana.s3_list ? be32(ana.s3_list+i) : 0 );
+ }
+ }
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// PAT0 scan + load ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanPAT
+(
+ pat_t * pat, // PAT data structure
+ bool init_pat, // true: initialize 'pat' first
+ const void * data, // data to scan
+ uint data_size, // size of 'data'
+ ContainerPAT_t * cdata, // NULL or container-data
+ CheckMode_t mode // not NULL: call CheckPAT(mode)
+)
+{
+ DASSERT(pat);
+ PRINT("ScanPAT(mode=%x) size=%u\n",mode,data_size);
+
+ enumError err;
+// [[analyse-magic]]
+ switch (GetByMagicFF(data,data_size,data_size))
+ {
+ case FF_PAT:
+ pat->fform = FF_PAT;
+ err = ScanRawPAT(pat,init_pat,data,data_size,cdata);
+ break;
+
+ case FF_PAT_TXT:
+ #if USE_NEW_CONTAINER_PAT
+ FreeContainerData(cdata);
+ #else
+ FreeContainer(cdata);
+ #endif
+ pat->fform = FF_PAT_TXT;
+ err = ScanTextPAT(pat,init_pat,data,data_size);
+ break;
+
+ default:
+ #if USE_NEW_CONTAINER_PAT
+ FreeContainerData(cdata);
+ #else
+ FreeContainer(cdata);
+ #endif
+ if (init_pat)
+ InitializePAT(pat);
+ return ERROR0(ERR_INVALID_DATA,
+ "No PAT file: %s\n", pat->fname ? pat->fname : "?");
+ }
+
+ if ( err <= ERR_WARNING && mode )
+ CheckPAT(pat,mode);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ScanRawDataPAT
+(
+ pat_t * pat, // PAT data structure
+ bool init_pat, // true: initialize 'pat' first
+ struct raw_data_t * raw, // valid raw data
+ CheckMode_t mode // not NULL: call CheckPAT(mode)
+)
+{
+ DASSERT(pat);
+ DASSERT(raw);
+ if (init_pat)
+ InitializePAT(pat);
+ else
+ ResetPAT(pat);
+
+ pat->fname = raw->fname;
+ raw->fname = 0;
+ #if USE_NEW_CONTAINER_PAT
+ return ScanPAT( pat, false, raw->data, raw->data_size,
+ LinkContainerRawData(raw), mode );
+ #else
+ return ScanPAT(pat,false,raw->data,raw->data_size,ContainerRawData(raw),mode);
+ #endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError LoadPAT
+(
+ pat_t * pat, // PAT data structure
+ bool init_pat, // true: initialize 'pat' first
+ ccp fname, // valid pointer to filenname
+ bool ignore_no_file, // ignore if file does not exists
+ // and return warning ERR_NOT_EXISTS
+ CheckMode_t mode // not NULL: call CheckPAT(mode)
+)
+{
+ DASSERT(pat);
+ DASSERT(fname);
+ if (init_pat)
+ InitializePAT(pat);
+ else
+ ResetPAT(pat);
+
+ //--- load and scan data
+
+ raw_data_t raw;
+ enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,false);
+ if (!err)
+ {
+ pat->fname = raw.fname;
+ raw.fname = 0;
+ #if USE_NEW_CONTAINER_PAT
+ err = ScanPAT( pat, false, raw.data, raw.data_size,
+ LinkContainerRawData(&raw), mode );
+ #else
+ err = ScanPAT(pat,false,raw.data,raw.data_size,ContainerRawData(&raw),mode);
+ #endif
+ }
+
+ ResetRawData(&raw);
+ return err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SaveRawPAT() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SaveRawPAT
+(
+ pat_t * pat, // pointer to valid PAT
+ ccp fname, // filename of destination
+ bool set_time // true: set time stamps
+)
+{
+ DASSERT(pat);
+ DASSERT(fname);
+ PRINT("SaveRawPAT(%s,%d)\n",fname,set_time);
+
+ //--- create raw data
+
+ enumError err = CreateRawPAT(pat);
+ if (err)
+ return err;
+ DASSERT(pat->raw_data);
+ DASSERT(pat->raw_data_size);
+
+ //--- write to file
+
+ File_t F;
+ err = CreateFileOpt(&F,true,fname,testmode,pat->fname);
+ if ( err > ERR_WARNING || !F.f )
+ return err;
+ SetFileAttrib(&F.fatt,&pat->fatt,0);
+
+ if ( fwrite(pat->raw_data,1,pat->raw_data_size,F.f) != pat->raw_data_size )
+ err = FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname);
+ return ResetFile(&F,set_time);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SaveTextPAT() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SaveSimplePAT
+(
+ pat_t * pat, // pointer to valid PAT
+ FILE *f, // open FILE
+ pat_mode_t outmode // output mode
+)
+{
+ if (brief_count)
+ fprintf(f,
+ "%s[SIMPLE]\r\n"
+ "@REVISION = %u\r\n",
+ section_sep,
+ REVISION_NUM );
+ else
+ fprintf(f,text_pat_simple_cr,REVISION_NUM);
+
+ if ( outmode & PATMD_COMPLEX )
+ {
+ const bool is_simple = IsSimplePAT(pat);
+ fprintf(f,
+ "\r\n"
+ "# 0: Ignore this section and enable COMPLEX support%s.\r\n"
+ "# 1: Enable this section and disable all others%s.\r\n"
+ "@ENABLED = %u\r\n",
+ is_simple ? ""
+ : " (default because of complex PAT)",
+ is_simple ? " (default for this simple PAT)"
+ : " (data will be lost if enabled)",
+ is_simple );
+ }
+
+ const pat_element_t *pe = pat->pelem;
+ fprintf(f,
+ "\r\n"
+ "@BRRES-NAME = \"%s\"\r\n"
+ "@PAT-NAME = \"%s\"\r\n"
+ "@N-FRAMES = %5u\r\n"
+ "@CYCLIC = %5u\r\n"
+ "@UNKNOWN-00 = %5u\r\n"
+ ,GetNamePAT(pat)
+ ,pe->bname
+ ,ntohs(pat->head.n_frames)
+ ,ntohs(pat->head.cyclic)
+ ,ntohs(pe->belem.unknown_00)
+ );
+
+ float factor = bef4(&pe->shead.factor);
+ if (!IsSameF(factor,CalcFactorSHEAD(pe->selem,pe->selem_used),2))
+ fprintf(f,
+ "@FACTOR = %8.6f # if defined, automatic calculation is disabled\r\n",
+ factor );
+
+ uint l, fw = 4;
+ const pat_s0_selem_t *se;
+ for ( l = 0, se = pe->selem; l < pe->selem_used; l++, se++ )
+ {
+ uint sidx = ntohs(se->index);
+ if ( sidx < pat->s1_str.used )
+ {
+ const uint slen = strlen(pat->s1_str.field[sidx].key);
+ if ( fw < slen )
+ fw = slen;
+ }
+ }
+ fw += 3;
+
+ fprintf(f,
+ "\r\n"
+ " #-----------------%.*s#-----------\r\n"
+ " # delta string %*s# abs_time\r\n"
+ " #-----------------%.*s#-----------\r\n",
+ fw - 4, Minus300,
+ fw - 4, "",
+ fw - 4, Minus300 );
+
+ float prev = 0.0;
+ for ( l = 0, se = pe->selem; l < pe->selem_used; l++, se++ )
+ {
+ float time = bef4(&se->time);
+ uint sidx = ntohs(se->index);
+ ccp str = sidx < pat->s1_str.used ? pat->s1_str.field[sidx].key : "?";
+ fprintf(f,"%13.4f \"%s\" %*s# %9.4f\r\n",
+ time - prev,
+ str,
+ (int)(fw - strlen(str)), "",
+ time );
+ prev = time;
+ }
+
+ fprintf(f,
+ " #-----------------%.*s#-----------\r\n",
+ fw - 4, Minus300 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SaveTextPAT
+(
+ pat_t * pat, // pointer to valid PAT
+ ccp fname, // filename of destination
+ bool set_time // true: set time stamps
+)
+{
+ // use DOS/Windows line format -> unix can handle it ;)
+
+ DASSERT(pat);
+ DASSERT(fname);
+
+ pat_mode_t outmode = PAT_MODE & PATMD_M_BOTH;
+ if (!outmode)
+ outmode = IsSimplePAT(pat) ? PATMD_SIMPLE : PATMD_COMPLEX;
+ char outmode_info[20];
+ PrintPatMode(outmode_info,sizeof(outmode_info),outmode);
+
+ PRINT("SaveTextPAT(%s,%d) outmode=%x[%s]\n",
+ fname, set_time, outmode, outmode_info );
+
+ ccp calc_on_scan = " # calculated on scanning";
+
+
+ //--- open file
+
+ File_t F;
+ enumError err = CreateFileOpt(&F,true,fname,testmode,pat->fname);
+ if ( err > ERR_WARNING || !F.f )
+ return err;
+ SetFileAttrib(&F.fatt,&pat->fatt,0);
+
+
+ //--- print file header + syntax info
+
+ if ( print_header && !brief_count )
+ fprintf(F.f,text_pat_file_head_cr);
+ else
+ fprintf(F.f,"%s\r\n",PAT_TEXT_MAGIC);
+
+
+ //--- print info section
+
+ if (brief_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"
+ "\r\n"
+ "PAT-VERSION = %2u\r\n"
+ "N-SECTIONS = %2u\r\n"
+ "OUTPUT-MODE = %s\r\n"
+ "\r\n",
+ section_sep,
+ tool_name, SYSTEM, VERSION, REVISION_NUM, DATE,
+ pat->version, pat->n_sect, outmode_info );
+ }
+ else
+ {
+ fprintf(F.f, text_pat_setup_cr,
+ tool_name, SYSTEM, VERSION, REVISION_NUM, DATE,
+ pat->version, pat->n_sect, outmode_info );
+ }
+
+ if ( outmode & PATMD_SIMPLE )
+ {
+ SaveSimplePAT(pat,F.f,outmode);
+ if (!(outmode & PATMD_COMPLEX))
+ {
+ fputs(section_end,F.f);
+ ResetFile(&F,set_time);
+ return err;
+ }
+ }
+
+
+ //--- write PAT header
+
+ if (brief_count)
+ fprintf(F.f,
+ "%s[HEADER]\r\n"
+ "@REVISION = %u\r\n"
+ "\r\n",
+ section_sep,
+ REVISION_NUM );
+ else
+ fprintf(F.f,text_pat_header_cr,REVISION_NUM);
+
+ const pat_head_t *h = &pat->head;
+ fprintf(F.f,
+ "@NAME = \"%s\"\r\n"
+ "\r\n"
+ "@UNKNOWN-00 = %5u\r\n"
+ "@UNKNOWN-02 = %5u\r\n"
+ "@N-FRAMES = %5u\r\n"
+ "# N-SECT0 = %5u%s\r\n"
+ "# N-SECT1 = %5u%s\r\n"
+ "@UNKNOWN-0A = %5u\r\n"
+ "@UNKNOWN-0C = %5u\r\n"
+ "@CYCLIC = %5u\r\n"
+ "\r\n",
+ GetNamePAT(pat),
+ ntohs(h->unknown_00),
+ ntohs(h->unknown_02),
+ ntohs(h->n_frames),
+ ntohs(h->n_sect0), calc_on_scan,
+ ntohs(h->n_sect1), calc_on_scan,
+ ntohs(h->unknown_0a),
+ ntohs(h->unknown_0c),
+ ntohs(h->cyclic) );
+
+
+ //--- write PAT S0 base
+
+ if (brief_count)
+ fprintf(F.f,
+ "%s[S0_BASE]\r\n"
+ "@REVISION = %u\r\n"
+ "\r\n",
+ section_sep,
+ REVISION_NUM );
+ else
+ fprintf(F.f,text_pat_s0_base_cr,REVISION_NUM);
+
+ const pat_s0_bhead_t *bh = &pat->base;
+ fprintf(F.f,
+ "# SIZE = %#6x%s\r\n"
+ "@UNKNOWN-04 = %#6x\r\n"
+ "# N-ELEM = %6u%s\r\n"
+ "@UNKNOWN-08 = %#6x\r\n"
+ "@UNKNOWN-0A = %#6x\r\n"
+ "@N-UNKNOWN = %6u\r\n"
+ "@UNKNOWN-0E = %#6x\r\n"
+ "@UNKNOWN-10 = %#6x\r\n"
+ "@UNKNOWN-12 = %#6x\r\n"
+ "@UNKNOWN-14 = %#6x\r\n"
+ "@UNKNOWN-16 = %#6x\r\n"
+ "\r\n",
+ ntohl(bh->size), calc_on_scan,
+ ntohs(bh->unknown_04),
+ ntohs(bh->n_elem), calc_on_scan,
+ ntohs(bh->unknown_08),
+ ntohs(bh->unknown_0a),
+ ntohs(bh->n_unknown),
+ ntohs(bh->unknown_0e),
+ ntohs(bh->unknown_10),
+ ntohs(bh->unknown_12),
+ ntohs(bh->unknown_14),
+ ntohs(bh->unknown_16) );
+
+ fputs(
+ "\r\n"
+ "#-------------------+\r\n"
+ "# base element list |\r\n"
+ "#----------+--------+-------------------+------------------------------\r\n"
+ "# index | p00 p02 p04 p06 | sref name\r\n"
+ "#----------+----------------------------+------------------------------\r\n"
+ ,F.f );
+
+ uint i;
+ const pat_element_t *pe = pat->pelem;
+ for ( i = 0; i < pat->n_pelem; i++, pe++ )
+ {
+ fprintf(F.f," $BASE B%-2u %#6x %#6x %#6x %#6x R%-3u \"%s\"\r\n",
+ i,
+ ntohs(pe->belem.unknown_00),
+ ntohs(pe->belem.unknown_02),
+ ntohs(pe->belem.unknown_04),
+ ntohs(pe->belem.unknown_06),
+ i,
+ pe->bname );
+ }
+
+ fputs(
+ "#----------+----------------------------+------------------------------\r\n"
+ "\r\n"
+ "\r\n"
+ "#-----------------------+\r\n"
+ "# string reference list |\r\n"
+ "#----------+------------+-+------------------------------\r\n"
+ "# index | p04 type | sref name\r\n"
+ "#----------+--------------+------------------------------\r\n"
+ ,F.f );
+
+
+ for ( i = 0; i < pat->n_pelem; i++ )
+ {
+ const int idx = pat->sref_order[i];
+ const pat_element_t *pe = pat->pelem + idx;
+
+ fprintf(F.f," $SREF R%-2u %#6x %#6x L%-3u \"%s\"\r\n",
+ idx,
+ ntohs(pe->sref.unknown_04),
+ ntohs(pe->sref.type),
+ idx,
+ pe->sname );
+ }
+
+ fputs(
+ "#----------+--------------+------------------------------\r\n"
+ ,F.f );
+
+
+ //--- write PAT S0 list
+
+ if (brief_count)
+ fprintf(F.f,
+ "%s[S0_LIST]\r\n"
+ "@REVISION = %u\r\n",
+ section_sep,
+ REVISION_NUM );
+ else
+ fprintf(F.f,text_pat_s0_list_cr,REVISION_NUM);
+
+ for ( i = 0; i < pat->n_pelem; i++ )
+ {
+ const int idx = pat->shead_order[i];
+ const pat_element_t *pe = pat->pelem + idx;
+ const double factor = bef4(&pe->shead.factor);
+ fprintf(F.f,
+ "\r\n"
+ " $LIST L%-2u # N=%u, factor=1/%6.4f\r\n"
+ " %c@FACTOR = %11.6f # if defined, automatic calculation is disabled\r\n"
+ " @PADDING = %#11x # padding value at offset 2 of list header\r\n"
+ "\r\n"
+ " #-----------------------#-----------\r\n"
+ " # delta string p06 # abs_time\r\n"
+ " #-----------------------#-----------\r\n",
+ idx,
+ ntohs(pe->shead.n_elem),
+ 1.0/factor,
+ IsSameF(factor,CalcFactorSHEAD(pe->selem,pe->selem_used),1) ? '#' : ' ',
+ factor, ntohs(pe->shead.unknown_02) );
+
+ float prev = 0.0;
+ uint l;
+ const pat_s0_selem_t *se = pe->selem;
+ for ( l = 0; l < pe->selem_used; l++, se++ )
+ {
+ float tiome = bef4(&se->time);
+ fprintf(F.f,"%13.4f S%-3u %#6x # %9.4f\r\n",
+ tiome - prev,
+ ntohs(se->index),
+ ntohs(se->unknown_06),
+ tiome );
+ prev = tiome;
+ }
+
+ fputs(" #-----------------------#-----------\r\n",F.f);
+ }
+
+
+ //--- strings
+
+ if (brief_count)
+ fprintf(F.f,
+ "%s[STRINGS]\r\n"
+ "@REVISION = %u\r\n"
+ "\r\n",
+ section_sep,
+ REVISION_NUM );
+ else
+ fprintf(F.f,text_pat_strings_cr,REVISION_NUM);
+
+ fputs(
+ "#-------------------------------------------\r\n"
+ "# var attribute name of TEX pattern\r\n"
+ "#-------------------------------------------\r\n"
+ ,F.f );
+ ParamFieldItem_t *it = pat->s1_str.field;
+ for ( i = 0; i < pat->s1_str.used; i++, it++ )
+ fprintf(F.f," S%-2u %#10x \"%s\"\r\n", i, it->num, it->key );
+
+
+ //--- terminate
+
+ fputs(section_end,F.f);
+ ResetFile(&F,set_time);
+ return err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// CkeckPAT() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int CheckPAT
+(
+ // returns number of errors
+
+ const pat_t * pat, // pointer to valid PAT
+ CheckMode_t mode // print mode
+)
+{
+ // [[2do]] [[pat]] ???
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////