aboutsummaryrefslogtreecommitdiff
path: root/project/src/wkclt.c
diff options
context:
space:
mode:
authorroot <root@dc13.lemmi>2020-07-21 10:00:46 +0200
committerroot <root@dc13.lemmi>2020-07-21 10:00:46 +0200
commit1192c4575ea7afdf38e3fa0df4587a08620af11e (patch)
tree6910a62487c282d0840d2e668aa7eb40b7c8a3fd /project/src/wkclt.c
parent76de8a89f7c1d1b4522eb66e8f89d248e8c1781f (diff)
SZS v2.18a
Diffstat (limited to 'project/src/wkclt.c')
-rw-r--r--project/src/wkclt.c1356
1 files changed, 1356 insertions, 0 deletions
diff --git a/project/src/wkclt.c b/project/src/wkclt.c
new file mode 100644
index 0000000..891f7e1
--- /dev/null
+++ b/project/src/wkclt.c
@@ -0,0 +1,1356 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * 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-kcl.h"
+#include "lib-szs.h"
+#include "ui.h" // [[dclib]] wrapper
+#include "ui-wkclt.c"
+#include <math.h>
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// definitions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define TITLE WKCLT_SHORT ": " WKCLT_LONG " v" VERSION " r" REVISION \
+ " " SYSTEM " - " AUTHOR " - " DATE
+
+static const char autoname[] = "/course.kcl";
+
+extern const char text_kcl_flag_template_cr[];
+
+//
+///////////////////////////////////////////////////////////////////////////////
+
+static void help_exit ( bool xmode )
+{
+ fputs( TITLE "\n", stdout );
+
+ if (xmode)
+ {
+ int cmd;
+ for ( cmd = 0; cmd < CMD__N; cmd++ )
+ PrintHelpCmd(&InfoUI_wkclt,stdout,0,cmd,0,0,URI_HOME);
+ }
+ else
+ PrintHelpCmd(&InfoUI_wkclt,stdout,0,0,"HELP",0,URI_HOME);
+
+ exit(ERR_OK);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void print_version_section ( bool print_sect_header )
+{
+ if (print_sect_header)
+ fputs("[version]\n",stdout);
+
+ const u32 base = 0x04030201;
+ const u8 * e = (u8*)&base;
+ const u32 endian = be32(e);
+
+ printf( "prog=" WKCLT_SHORT "\n"
+ "name=" WKCLT_LONG "\n"
+ "version=" VERSION "\n"
+ "beta=%d\n"
+ "revision=" REVISION "\n"
+ "system=" SYSTEM "\n"
+ "endian=%u%u%u%u %s\n"
+ "author=" AUTHOR "\n"
+ "date=" DATE "\n"
+ "url=" URI_HOME WKCLT_SHORT "\n"
+ "\n"
+ , BETA_VERSION
+ , e[0], e[1], e[2], e[3]
+ , endian == 0x01020304 ? "little"
+ : endian == 0x04030201 ? "big" : "mixed" );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void version_exit()
+{
+ if ( brief_count > 1 )
+ fputs( VERSION "\n", stdout );
+ else if (brief_count)
+ fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout );
+ else if (print_sections)
+ print_version_section(true);
+ else if (long_count)
+ print_version_section(false);
+ else
+ fputs( TITLE "\n", stdout );
+
+ exit(ERR_OK);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void print_title ( FILE * f )
+{
+ static bool done = false;
+ if (!done)
+ {
+ done = true;
+ if (print_sections)
+ print_version_section(true);
+ else if ( verbose >= 1 && f == stdout )
+ fprintf(f,"\n%s\n\n",TITLE);
+ else
+ fprintf(f,"***** %s *****\n",TITLE);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t * current_command = 0;
+
+static void hint_exit ( enumError stat )
+{
+ if ( current_command )
+ fprintf(stderr,
+ "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n",
+ progname, CommandInfo[current_command->id].name1 );
+ else
+ fprintf(stderr,
+ "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n",
+ progname, progname );
+ exit(stat);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SYNTAX_ERROR syntax_error(__FUNCTION__,__FILE__,__LINE__)
+
+static void syntax_error ( ccp func, ccp file, uint line )
+{
+ if ( current_command
+ && current_command->id >= 0
+ && current_command->id < InfoUI_wkclt.n_cmd )
+ {
+ const InfoCommand_t *ic = InfoUI_wkclt.cmd_info + current_command->id;
+ if (strchr(ic->syntax,'\n'))
+ {
+ ccp src = ic->syntax;
+ char *dest = iobuf;
+ while (*src)
+ if ( (*dest++ = *src++) == '\n' )
+ {
+ *dest++ = ' ';
+ *dest++ = ' ';
+ *dest++ = ' ';
+ }
+ *dest = 0;
+ PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax:\n %s\n",iobuf);
+ }
+ else
+ PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax: %s\n",ic->syntax);
+ }
+ else
+ PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax Error!\n");
+ exit(ERR_SYNTAX);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command test ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_test_options()
+{
+ printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible());
+
+ printf(" test: %16x = %12d\n",testmode,testmode);
+ printf(" verbose: %16x = %12d\n",verbose,verbose);
+ printf(" width: %16x = %12d\n",opt_width,opt_width);
+ printf(" escape-char: %16x = %12d\n",escape_char,escape_char);
+ printf(" kcl modes: %16llx = \"%s\"\n",(u64)KCL_MODE,GetKclMode());
+ printf(" tri-area: %16g\n",opt_tri_area);
+ printf(" tri-height: %16g\n",opt_tri_height);
+
+ if (opt_xtridata)
+ printf(" xtridata: %16x = %12d [support %sabled]\n",
+ opt_xtridata, opt_xtridata, SUPPORT_KCL_XTRIDATA ? "en" : "dis" );
+
+ if ( kcl_script_list.used == 1 )
+ printf(" kcl script: %s\n",kcl_script_list.field[0]);
+ else if ( kcl_script_list.used > 1 )
+ {
+ printf(" %u kcl scripts:\n",kcl_script_list.used);
+ uint i;
+ for ( i = 0; i < kcl_script_list.used; i++ )
+ printf("%6d.: %s\n",i+1,kcl_script_list.field[i]);
+ }
+ if (opt_slot)
+ printf(" slot: %16x = \"%s\"\n",opt_slot,PrintSlotMode(opt_slot));
+
+ DumpTransformationOpt();
+
+ if (opt_tracks)
+ DumpTrackList(0,0,0);
+ if (opt_arenas)
+ DumpArenaList(0,0,0);
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_test()
+{
+ #if 1 || !defined(TEST) // test options
+
+ return cmd_test_options();
+
+ #else
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ //NORMALIZE_FILENAME_PARAM(param);
+ }
+ return ERR_OK;
+
+ #endif
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command export ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_export()
+{
+ SetupVarsKCL();
+ return ExportHelper("kcl");
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command cat ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_cat()
+{
+ stdlog = stderr;
+ enable_kcl_drop_auto++;
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ enumError cmd_err = ERR_OK;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ CheckMode_t mode = GetCheckMode(false,false,true,false);
+ if ( verbose >= 0 || testmode )
+ {
+ mode = GetCheckMode(true,false,true,false);
+ fprintf(stdlog,"%sCAT %s:%s\n",
+ verbose > 0 ? "\n" : "",
+ GetNameFF(raw.fform,0), raw.fname );
+ fflush(stdlog);
+ }
+
+ kcl_t kcl;
+ err = ScanRawDataKCL(&kcl,true,&raw,mode,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ if (!testmode)
+ {
+ err = SaveTextKCL(&kcl,"-",false);
+ fflush(stdout);
+ if ( err > ERR_WARNING )
+ return err;
+ }
+ ResetKCL(&kcl);
+ }
+
+ ResetRawData(&raw);
+ return cmd_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command encode/decode ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_convert ( int cmd_id, ccp cmd_name, ccp def_path )
+{
+ if ( cmd_id == CMD_DECODE )
+ enable_kcl_drop_auto++;
+ CheckOptDest(def_path,false);
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ char dest[PATH_MAX];
+ const file_format_t dest_ff = cmd_id == CMD_ENCODE ? FF_KCL : FF_KCL_TXT;
+
+ SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path,
+ GetExtFF(dest_ff,0),false);
+
+ if ( verbose >= 0 || testmode )
+ {
+ fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n",
+ verbose > 0 ? "\n" : "",
+ testmode ? "WOULD " : "", cmd_name,
+ GetNameFF(raw.fform,0), raw.fname,
+ GetNameFF(dest_ff,0), dest );
+ fflush(stdlog);
+ }
+
+ kcl_t kcl;
+ InitializeKCL(&kcl);
+ kcl.fform_outfile = dest_ff;
+ err = ScanRawDataKCL(&kcl,false,&raw,global_check_mode,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ if (!testmode)
+ {
+ err = dest_ff == FF_KCL
+ ? SaveRawKCL(&kcl,dest,opt_preserve)
+ : SaveTextKCL(&kcl,dest,opt_preserve);
+ fflush(stdout);
+ if ( err > ERR_WARNING )
+ return err;
+ }
+ ResetKCL(&kcl);
+ }
+
+ ResetRawData(&raw);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command copy ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_copy()
+{
+ if (!opt_dest)
+ {
+ if (!first_param)
+ return ERROR0(ERR_MISSING_PARAM, "Missing destination parameter!\n" );
+
+ ParamList_t *param;
+ for ( param = first_param; param->next; param = param->next )
+ ;
+ ASSERT(param);
+ ASSERT(!param->next);
+ SetDest(param->arg,false);
+ param->arg = 0;
+ }
+
+ enable_kcl_drop_auto = 0;
+ file_format_t fform_dest = FF_UNKNOWN;
+ ccp point = strrchr(opt_dest,'.');
+ if (point)
+ {
+ if (!strcasecmp(point,".kcl"))
+ fform_dest = FF_KCL;
+ else if ( !strcasecmp(point,".obj") || !strcasecmp(point,".txt") )
+ {
+ fform_dest = FF_WAV_OBJ;
+ enable_kcl_drop_auto++;
+ }
+ }
+ PRINT("fform(dest) = %s: %s\n",GetNameFF(0,fform_dest),opt_dest);
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ int done_count = 0;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ if (!param->arg)
+ continue;
+ NORMALIZE_FILENAME_PARAM(param);
+ done_count++;
+
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ const file_format_t dest_ff = fform_dest != FF_UNKNOWN
+ ? fform_dest
+ : raw.fform;
+
+ kcl_t kcl;
+ InitializeKCL(&kcl);
+ kcl.fform_outfile = dest_ff;
+ err = ScanRawDataKCL(&kcl,false,&raw,global_check_mode,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ char dest[PATH_MAX];
+ SubstDest(dest,sizeof(dest),param->arg,opt_dest,"\1P/\1N\1?T",
+ GetExtFF(dest_ff,0),false);
+ if ( verbose >= 0 || testmode )
+ {
+ fprintf(stdlog,"%s%sCOPY %s:%s -> %s:%s\n",
+ verbose > 0 ? "\n" : "",
+ testmode ? "WOULD " : "",
+ GetNameFF(raw.fform,0), kcl.fname,
+ GetNameFF(dest_ff,0), dest );
+ fflush(stdlog);
+ }
+
+ if (!testmode)
+ {
+ err = dest_ff == FF_KCL
+ ? SaveRawKCL(&kcl,dest,opt_preserve)
+ : SaveTextKCL(&kcl,dest,opt_preserve);
+ fflush(stdout);
+ if ( err > ERR_WARNING )
+ return err;
+ }
+
+ ResetKCL(&kcl);
+ }
+ ResetRawData(&raw);
+
+ if (!done_count)
+ SYNTAX_ERROR;
+
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command cff ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp cff_func_f ( u16 flag )
+{
+ const uint bufsize = 20;
+ char *buf = GetCircBuf(bufsize);
+ DASSERT(buf);
+ snprintf(buf,bufsize,"f(0x%02x,0x%03x)",
+ flag % N_KCL_TYPE, flag / N_KCL_TYPE );
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp cff_func_a ( u16 flag )
+{
+ const uint bufsize = 20;
+ char *buf = GetCircBuf(bufsize);
+ DASSERT(buf);
+ snprintf(buf,bufsize,"a(0x%02x,%u,%u,%u,%u)",
+ flag % N_KCL_TYPE,
+ flag >> 13 & 7,
+ flag >> 11 & 3,
+ flag >> 8 & 7,
+ flag >> 5 & 7 );
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp cff_func_i ( u16 flag )
+{
+ const uint bufsize = 20;
+ char *buf = GetCircBuf(bufsize);
+ DASSERT(buf);
+ snprintf(buf,bufsize,"i(0x%02x,%u,%u)",
+ flag % N_KCL_TYPE,
+ flag >> 5 & 7,
+ flag >> 8 & 15 );
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_cff()
+{
+ CheckOptDest(KCL_FLAG_FILE1,false);
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+ uint *flag_count = AllocFlagCount();
+ char dest_buf[PATH_MAX];
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ kcl_t kcl;
+ err = ScanRawDataKCL(&kcl,true,&raw,global_check_mode,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ LoadFlagFileKCL(&kcl);
+
+ if ( kcl.fform == FF_KCL )
+ {
+ CountFlagsKCL(&kcl,flag_count);
+
+ uint i;
+ for ( i = 0; i < N_KCL_FLAG; i++ )
+ if (flag_count[i])
+ {
+ StringCopyS(dest_buf,sizeof(dest_buf),GetGroupNameKCL(i,false));
+ char *p;
+ for ( p = dest_buf; *p; p++ )
+ *p = toupper((int)*p);
+ GetFlagByNameKCL(&kcl,dest_buf,0);
+ }
+ }
+
+ bool append = true;
+ ccp dest = kcl.flag_fname;
+ PRINT("DEST: %s\n",dest);
+ if (!dest)
+ {
+ SubstDest(dest_buf,sizeof(dest_buf),param->arg,opt_dest,
+ 0,KCL_FLAG_EXT,false);
+ dest = dest_buf;
+ append = false;
+ }
+
+ if ( verbose >= 0 || testmode )
+ {
+ fprintf(stdlog,"%s%s%s %s for %s:%s\n",
+ verbose > 0 ? "\n" : "",
+ testmode ? "WOULD " : "",
+ !kcl.flag_missing.used ? "SKIP "
+ : append ? "APPEND" : "CREATE",
+ dest, GetNameFF(kcl.fform,0), kcl.fname );
+ fflush(stdlog);
+ }
+
+ if ( !testmode && kcl.flag_missing.used )
+ {
+ File_t F;
+ enumError err;
+ if (append)
+ {
+ err = AppendFILE(&F,true,dest,false,opt_mkdir);
+ if (!err)
+ fputs("\r\n",F.f);
+ }
+ else
+ {
+ err = CreateFileOpt(&F,true,dest,false,0);
+ if (!err)
+ fputs(text_kcl_flag_template_cr,F.f);
+ }
+
+ if (!err)
+ {
+ uint i, fw = 29;
+ for ( i = 0; i < kcl.flag_missing.used; i++ )
+ {
+ FormatFieldItem_t *it = kcl.flag_missing.list + i;
+ const uint len = strlen(it->key);
+ if ( fw < len )
+ fw = len;
+ }
+ fw = ( fw + 10 ) / 8 * 8 + 5;
+
+ for ( i = 0; i < kcl.flag_missing.used; i++ )
+ {
+ FormatFieldItem_t *it = kcl.flag_missing.list + i;
+ int tablen = (fw-strlen(it->key))/8;
+ if ( tablen < 1 )
+ tablen = 1;
+ const bool is_auto = IsAutoFlagKCL(it->num);
+ const u16 num = is_auto ? 0 : it->num; // unsigned with reduced num of bits
+ if ( long_count > 1 )
+ {
+ if (brief_count)
+ fprintf(F.f,
+ " %s%.*s= %s",
+ it->key, tablen, Tabs20,
+ cff_func_a(num) );
+ else
+ fprintf(F.f,
+ " %s%.*s= %s # %s %s 0x%04x",
+ it->key, tablen, Tabs20,
+ cff_func_a(num), cff_func_f(num), cff_func_i(num), num );
+ }
+ else if (long_count)
+ {
+ if (brief_count)
+ fprintf(F.f,
+ " %s%.*s= %s",
+ it->key, tablen, Tabs20,
+ cff_func_f(num) );
+ else
+ fprintf(F.f,
+ " %s%.*s= %s # %s %s 0x%04x",
+ it->key, tablen, Tabs20,
+ cff_func_f(num), cff_func_a(num), cff_func_i(num), num );
+ }
+ else if (brief_count)
+ fprintf(F.f,
+ " %s%.*s= 0x%04x",
+ it->key, tablen, Tabs20, num );
+ else
+ fprintf(F.f,
+ " %s%.*s= 0x%04x # %s %s %s",
+ it->key, tablen, Tabs20,
+ num, cff_func_f(num), cff_func_a(num), cff_func_i(num) );
+
+ fputs(is_auto ? " # edit this line!\r\n" : "\r\n", F.f );
+ }
+ fputs("\r\n",F.f);
+ }
+
+ CloseFile(&F,false);
+ if ( err > ERR_WARNING )
+ return err;
+ }
+ ResetKCL(&kcl);
+ }
+
+ FREE(flag_count);
+ ResetRawData(&raw);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command flags ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_flags()
+{
+ stdlog = stderr;
+ uint *count = AllocFlagCount();
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ enumError cmd_err = ERR_OK;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ if ( verbose >= 0 || testmode )
+ fprintf(stdout,"%sKCL Flags of %s:%s\n",
+ verbose > 0 ? "\n" : "",
+ GetNameFF(raw.fform,0), raw.fname );
+
+ kcl_t kcl;
+ err = ScanRawDataKCL(&kcl,true,&raw,global_check_mode,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ if (!testmode)
+ {
+ const uint n = kcl.tridata.used;
+ CountFlagsKCL(&kcl,count);
+ uint tcount[N_KCL_TYPE];
+ memset(tcount,0,sizeof(tcount));
+
+ if ( print_header && !brief_count )
+ printf("\n flag type variant triangle count\n"
+ "-----------------------------------------------\n");
+
+ uint i, j;
+ for ( i = 0; i < N_KCL_TYPE; i++ )
+ {
+ int done = 0;
+ for ( j = i; j < N_KCL_FLAG; j += N_KCL_TYPE )
+ if (count[j])
+ {
+ tcount[i] += count[j];
+ if (!brief_count)
+ {
+ done++;
+ printf(" 0x%04x = 0x%02x | 0x%03x << 5 :%7u = %6.2f%%\n",
+ j, j & 0x1f, j >> 5,
+ count[j], 100.0 * count[j] / n );
+ }
+ }
+ if ( done && print_header )
+ putchar('\n');
+ }
+
+ if (print_header)
+ printf( "\n"
+ " type triangle count type description\n"
+ "--------------------------------------------------\n");
+
+ for ( i = 0; i < N_KCL_TYPE; i++ )
+ if (tcount[i])
+ printf(" T 0x%02x :%7u = %6.2f%% %s\n",
+ i, tcount[i], 100.0 * tcount[i] / n,
+ kcl_type[i].info );
+ putchar('\n');
+ }
+ ResetKCL(&kcl);
+ fflush(stdout);
+ }
+
+ ResetRawData(&raw);
+ FREE(count);
+ return cmd_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command dump ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_dump ( bool brief_level, int cmd )
+{
+ stdlog = stderr;
+
+ if ( brief_level > 0 )
+ {
+ RegisterOptionByIndex(&InfoUI_wkclt,OPT_BRIEF,brief_level,false);
+ brief_count += brief_level;
+ }
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ enumError cmd_err = ERR_OK;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ if ( verbose >= 0 || testmode )
+ fprintf(stdout,"%s%s of %s:%s\r\n",
+ verbose > 0 ? "\r\n" : "",
+ cmd == CMD_LIST
+ ? "LIST" : cmd == CMD_TRIANGLES
+ ? "TRIANGLES" : "DUMP",
+ GetNameFF(raw.fform,0), raw.fname );
+
+ kcl_t kcl;
+ InitializeKCL(&kcl);
+ kcl.fform_outfile = FF_KCL;
+ err = ScanRawDataKCL(&kcl,false,&raw,global_check_mode,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ if (!testmode)
+ {
+ err = cmd == CMD_LIST
+ ? DumpTrianglesKCL(&kcl,"-")
+ : cmd == CMD_TRIANGLES
+ ? ListTrianglesKCL(&kcl,"-",!brief_count,long_count)
+ : DumpKCL(&kcl,"-",!brief_count,long_count);
+ fflush(stdout);
+ if ( err > ERR_WARNING )
+ return err;
+ }
+ ResetKCL(&kcl);
+ }
+
+ ResetRawData(&raw);
+ return cmd_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command traverse ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_traverse ( bool fall )
+{
+ stdlog = stderr;
+
+ uint width = 100;
+ u32 type_mask = M1(type_mask);
+
+ char fall_hint[50] = {0};
+ if (fall)
+ {
+ const Var_t *var = FindConst("WIDTH");
+ if ( var && var->mode != VAR_UNSET )
+ {
+ int temp = GetIntV(var);
+ if ( temp > 0 )
+ width = temp;
+ }
+
+ var = FindConst("MASK");
+ if ( var && var->mode != VAR_UNSET )
+ type_mask = GetIntV(var);
+
+ snprintf(fall_hint,sizeof(fall_hint),
+ " [width=%u,mask=0x%x]", width, type_mask );
+ }
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ ParamList_t *param = first_param;
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,first_param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING )
+ return err;
+
+ if ( verbose >= 0 || testmode )
+ {
+ fprintf(stdlog,"\nANALYZE OCTREE OF %s:%s%s\n\n",
+ GetNameFF(raw.fform,0), raw.fname, fall_hint );
+ fflush(stdlog);
+ }
+
+ kcl_t kcl;
+ err = ScanRawDataKCL(&kcl,true,&raw,0,true);
+ if ( err > ERR_WARNING || !param )
+ return err;
+
+ uint np = 1;
+ enumError max_err = ERR_OK;
+ for ( param = param->next; param; param = param->next, np++ )
+ {
+ char name[50];
+ snprintf(name,sizeof(name),"Point #%u",np);
+
+ DEFINE_VAR(pt);
+ err = ScanVectorExpr(param->arg,name,&pt);
+ if ( max_err < err )
+ max_err = err;
+
+ if (fall)
+ {
+ int kcl_flag;
+ double height = FallKCL(&kcl,stdout,pt.d3,width,
+ long_count,type_mask,&kcl_flag);
+ if (brief_count>1)
+ printf("%11.3f\n",height);
+ else if (brief_count)
+ printf("%11.3f %11.3f %11.3f\n",
+ pt.d3.x, height, pt.d3.z );
+ else if ( kcl_flag >= 0 )
+ printf("%11.3f %11.3f %11.3f 0x%04x\n",
+ pt.d3.x, height, pt.d3.z, kcl_flag );
+ else
+ printf("%11.3f %11.3f %11.3f -1\n",
+ pt.d3.x, height, pt.d3.z );
+ }
+ else
+ err = TraverseOctreeKCL( &kcl, stdout, pt.d3,
+ brief_count<=0, long_count );
+ fflush(stdout);
+ if ( max_err < err )
+ max_err = err;
+ }
+
+ ResetKCL(&kcl);
+ ResetRawData(&raw);
+ return max_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command check ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_check()
+{
+ patch_action_log_disabled++;
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ enumError cmd_err = ERR_OK;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ const bool check_verbose = verbose > 0 || long_count > 0;
+ CheckMode_t mode = GetCheckMode(false,brief_count>0,verbose<0,check_verbose);
+ if ( verbose >= 0 || testmode )
+ {
+ ColorSet_t col;
+ SetupColorSet(&col,stdlog);
+ mode = GetCheckMode(true,brief_count>0,false,check_verbose);
+ fprintf(stdlog,"%s%sCHECK %s:%s%s\n",
+ verbose > 0 ? "\n" : "",
+ col.heading, GetNameFF(raw.fform,0), raw.fname, col.reset );
+ fflush(stdlog);
+ }
+
+ kcl_t kcl;
+ err = ScanRawDataKCL(&kcl,true,&raw,0,true);
+ if ( err <= ERR_WARNING && ( !mode || CheckKCL(&kcl,mode) ))
+ cmd_err = ERR_DIFFER;
+ fflush(stdout);
+ ResetKCL(&kcl);
+ if ( err > ERR_WARNING )
+ return err;
+ }
+
+ ResetRawData(&raw);
+ return cmd_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command analyze ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void analyze_kcl ( kcl_t *kcl )
+{
+ DASSERT(kcl);
+
+ double v_min = INFINITY;
+ double v_max_neg = -INFINITY;
+ double v_min_pos = INFINITY;
+ double v_max = -INFINITY;
+
+ float n_min = INFINITY;
+ float n_max_neg = -INFINITY;
+ float n_min_pos = INFINITY;
+ float n_max = -INFINITY;
+
+ uint n = 0;
+ const uint n_tri = kcl->tridata.used;
+
+ kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list;
+ kcl_tridata_t *td, *td_end = td_base + n_tri;
+ for ( td = td_base; td < td_end; td++ )
+ {
+ if ( td->status & TD_INVALID_NORM )
+ continue;
+
+ n++;
+
+ double *d = td->pt->v;
+ uint nd = 3;
+ for ( ; nd > 0; nd--, d++ )
+ {
+ if ( v_min > *d ) v_min = *d;
+ if ( v_max < *d ) v_max = *d;
+ if ( *d > 0.0 && v_min_pos > *d ) v_min_pos = *d;
+ if ( *d < 0.0 && v_max_neg < *d ) v_max_neg = *d;
+ }
+
+ float *f = td->normal->v;
+ uint nf = sizeof(td->normal)/sizeof(float);
+ for ( ; nf > 0; nf--, f++ )
+ {
+ if ( n_min > *f ) n_min = *f;
+ if ( n_max < *f ) n_max = *f;
+ if ( *f > 0.0 && n_min_pos > *f ) n_min_pos = *f;
+ if ( *f < 0.0 && n_max_neg < *f ) n_max_neg = *f;
+ //printf("XX: %08x\n",*(u32*)f);
+ }
+ }
+
+
+ printf(
+ "\n"
+ "%5u / %-5u vertex normal\n"
+ "--------------------------------------------\n"
+ " min: %13.6e %13.6e\n"
+ " max negative: %13.6e %13.6e\n"
+ " min positive: %13.6e %13.6e\n"
+ " max: %13.6e %13.6e\n"
+ "\n",
+ n, n_tri,
+ v_min, n_min,
+ v_max_neg, n_max_neg,
+ v_min_pos, n_min_pos,
+ v_max, n_max );
+
+ // Value found in Nintendos tracks:
+ // -v_max_neg == v_min_pos == 1.001358e-05 == 0x1.5p-17 == 21.0>>21
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_analyze()
+{
+ stdlog = stderr;
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ enumError cmd_err = ERR_OK;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ if ( verbose >= 0 || testmode )
+ fprintf(stdout,"%sAnalyze %s:%s\r\n",
+ verbose > 0 ? "\r\n" : "",
+ GetNameFF(raw.fform,0), raw.fname );
+
+ kcl_t kcl;
+ err = ScanRawDataKCL(&kcl,true,&raw,0,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ analyze_kcl(&kcl);
+ ResetKCL(&kcl);
+ }
+
+ ResetRawData(&raw);
+ return cmd_err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError cmd_blow()
+{
+ stdlog = stderr;
+
+ raw_data_t raw;
+ InitializeRawData(&raw);
+
+ enumError cmd_err = ERR_OK;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL);
+ if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore )
+ continue;
+ if ( err > ERR_WARNING )
+ return err;
+
+ kcl_t kcl;
+ err = ScanRawDataKCL(&kcl,true,&raw,0,true);
+ if ( err > ERR_WARNING )
+ return err;
+
+ uint blow = CalcBlowSizeKCL(&kcl);
+ fprintf(stdout,"%6u = blow size of %s:%s\r\n",
+ blow, GetNameFF(kcl.fform,0), kcl.fname );
+ ResetKCL(&kcl);
+ }
+
+ ResetRawData(&raw);
+ return cmd_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// check options ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError CheckOptions ( int argc, char ** argv, bool is_env )
+{
+ TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind);
+
+ optind = 0;
+ int err = 0;
+
+ for(;;)
+ {
+ const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0);
+ if ( opt_stat == -1 )
+ break;
+
+ RegisterOptionByName(&InfoUI_wkclt,opt_stat,1,is_env);
+
+ switch ((enumGetOpt)opt_stat)
+ {
+ case GO__ERR: err++; break;
+
+ case GO_VERSION: version_exit();
+ case GO_HELP: help_exit(false);
+ case GO_XHELP: help_exit(true);
+ case GO_ALLOW_ALL: allow_all = true; break;
+ case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break;
+ case GO_WIDTH: err += ScanOptWidth(optarg); break;
+ case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break;
+ case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break;
+ case GO_LOGGING: logging++; break;
+ case GO_DE: use_de = true; break;
+ case GO_CT_CODE: ctcode_enabled = true; break;
+ case GO_LE_CODE: lecode_enabled = true; break;
+ case GO_COLORS: err += ScanOptColorize(0,optarg,0); break;
+ case GO_NO_COLORS: opt_colorize = COLMD_OFF; break;
+
+ case GO_CHDIR: err += ScanOptChdir(optarg); break;
+ case GO_CONST: err += ScanOptConst(optarg); break;
+ case GO_SCALE: err += ScanOptScale(optarg); break;
+ case GO_SHIFT: err += ScanOptShift(optarg); break;
+ case GO_XSS: err += ScanOptXSS(0,optarg); break;
+ case GO_YSS: err += ScanOptXSS(1,optarg); break;
+ case GO_ZSS: err += ScanOptXSS(2,optarg); break;
+ case GO_ROT: err += ScanOptRotate(optarg); break;
+ case GO_XROT: err += ScanOptXRotate(0,optarg); break;
+ case GO_YROT: err += ScanOptXRotate(1,optarg); break;
+ case GO_ZROT: err += ScanOptXRotate(2,optarg); break;
+ case GO_TRANSLATE: err += ScanOptTranslate(optarg); break;
+ case GO_NULL: force_transform |= 1; break;
+ case GO_NEXT: err += NextTransformation(false); break;
+ case GO_ASCALE: err += ScanOptAScale(optarg); break;
+ case GO_AROT: err += ScanOptARotate(optarg); break;
+ case GO_TFORM_SCRIPT: err += ScanOptTformScript(optarg); break;
+ case GO_KCL: err += ScanOptKcl(optarg); break;
+ case GO_KCL_FLAG: err += ScanOptKclFlag(optarg); break;
+ case GO_KCL_SCRIPT: err += ScanOptKclScript(optarg); break;
+ case GO_TRI_AREA: err += ScanOptTriArea(optarg); break;
+ case GO_TRI_HEIGHT: err += ScanOptTriHeight(optarg); break;
+ case GO_FLAG_FILE: opt_flag_file = optarg; break;
+ case GO_XTRIDATA: opt_xtridata = optarg ? str2ul(optarg,0,10) : 1; break;
+ case GO_SLOT: err += ScanOptSlot(optarg); break;
+
+ case GO_UTF_8: use_utf8 = true; break;
+ case GO_NO_UTF_8: use_utf8 = false; break;
+
+ case GO_TEST: testmode++; break;
+ case GO_FORCE: force_count++; break;
+ case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break;
+ case GO_TINY: err += ScanOptTiny(optarg); break;
+
+ #if OPT_OLD_NEW
+ case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break;
+ case GO_STD: opt_new = 0; break;
+ case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break;
+ #endif
+ case GO_EXTRACT: opt_extract = optarg; break;
+
+ case GO_ESC: err += ScanEscapeChar(optarg) < 0; break;
+ case GO_DEST: SetDest(optarg,false); break;
+ case GO_DEST2: SetDest(optarg,true); break;
+ case GO_OVERWRITE: opt_overwrite = true; break;
+ case GO_NUMBER: opt_number = true; break;
+ case GO_REMOVE_DEST: opt_remove_dest = true; break;
+ case GO_UPDATE: opt_update = true; break;
+ case GO_PRESERVE: opt_preserve = true; break;
+ case GO_IGNORE: opt_ignore++; break;
+
+ case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break;
+ case GO_TRACKS: err += ScanOptTracks(optarg); break;
+ case GO_ARENAS: err += ScanOptArenas(optarg); break;
+
+ case GO_ROUND: opt_round = true; break;
+ case GO_LONG: long_count++; break;
+ case GO_NO_HEADER: print_header = false; break;
+ case GO_BRIEF: brief_count++; break;
+ case GO_NO_PARAM: print_param = false; break;
+ case GO_NO_ECHO: opt_no_echo = true; break;
+ case GO_NO_CHECK: opt_no_check = true; break;
+ case GO_SECTIONS: print_sections++; break;
+
+ // no default case defined
+ // => compiler checks the existence of all enum values
+ }
+ }
+
+ #ifdef DEBUG
+ DumpUsedOptions(&InfoUI_wkclt,TRACE_FILE,11);
+ #endif
+ CloseTransformation();
+ NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 );
+ SetupKCL();
+
+ return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// check command ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError CheckCommand ( int argc, char ** argv )
+{
+ const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab);
+ if (!cmd_ct)
+ hint_exit(ERR_SYNTAX);
+
+ TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1);
+ current_command = cmd_ct;
+
+ if (!allow_all)
+ {
+ enumError err = VerifySpecificOptions(&InfoUI_wkclt,cmd_ct);
+ if (err)
+ hint_exit(err);
+ }
+ WarnDepractedOptions(&InfoUI_wkclt);
+
+ if ( cmd_ct->id != CMD_ARGTEST )
+ {
+ argc -= optind+1;
+ argv += optind+1;
+
+ if ( cmd_ct->id == CMD_TEST )
+ while ( argc-- > 0 )
+ AddParam(*argv++);
+ else
+ while ( argc-- > 0 )
+ AtFileHelper(*argv++,AddParam);
+ }
+
+ enumError err = ERR_OK;
+ switch ((enumCommands)cmd_ct->id)
+ {
+ case CMD_VERSION: version_exit();
+ case CMD_HELP: PrintHelp(&InfoUI_wkclt,stdout,0,"HELP",0,URI_HOME,
+ first_param ? first_param->arg : 0 ); break;
+ case CMD_ARGTEST: err = cmd_argtest(argc,argv); break;
+ case CMD_TEST: err = cmd_test(); break;
+ case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break;
+ case CMD_ERROR: err = cmd_error(); break;
+ case CMD_FILETYPE: err = cmd_filetype(); break;
+ case CMD_FILEATTRIB: err = cmd_fileattrib(); break;
+ case CMD_EXPORT: err = cmd_export(); break;
+
+ case CMD_SYMBOLS: err = DumpSymbols(SetupVarsKCL()); break;
+ case CMD_FUNCTIONS: SetupVarsKCL(); err = ListParserFunctions(); break;
+ case CMD_CALCULATE: err = ParserCalc(SetupVarsKCL()); break;
+ case CMD_MATRIX: err = cmd_matrix(); break;
+ case CMD_FLOAT: err = cmd_float(); break;
+
+ case CMD_CAT: err = cmd_cat(); break;
+ case CMD_DECODE: err = cmd_convert(cmd_ct->id,"DECODE","\1P/\1N.obj"); break;
+ case CMD_ENCODE: err = cmd_convert(cmd_ct->id,"ENCODE","\1P/\1N\1?T"); break;
+ case CMD_COPY: err = cmd_copy(); break;
+ case CMD_CFF: err = cmd_cff(); break;
+ case CMD_FLAGS: err = cmd_flags(); break;
+ case CMD_DUMP: err = cmd_dump(0,CMD_DUMP); break;
+ case CMD_DBRIEF: err = cmd_dump(1,CMD_DUMP); break;
+ case CMD_LIST: err = cmd_dump(0,CMD_LIST); break;
+ case CMD_TRIANGLES: err = cmd_dump(0,CMD_TRIANGLES); break;
+ case CMD_TRAVERSE: err = cmd_traverse(false); break;
+ case CMD_FALL: err = cmd_traverse(true); break;
+ case CMD_CHECK: err = cmd_check(); break;
+ case CMD_ANALYZE: err = cmd_analyze(); break;
+ case CMD_BLOW: err = cmd_blow(); break;
+
+ // no default case defined
+ // => compiler checks the existence of all enum values
+
+ case CMD__NONE:
+ case CMD__N:
+ help_exit(false);
+ }
+
+ return PrintErrorStat(err,verbose,cmd_ct->name1);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// main() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#if SZS_WRAPPER
+ int main_wkclt ( int argc, char ** argv )
+#else
+ int main ( int argc, char ** argv )
+#endif
+{
+ print_title_func = print_title;
+ SetupLib(argc,argv,WKCLT_SHORT);
+
+ //----- process arguments
+
+ if ( argc < 2 )
+ {
+ printf("\n%s\n%s\nVisit %s%s for more info.\n\n",
+ text_logo, TITLE, URI_HOME, WKCLT_SHORT );
+ hint_exit(ERR_OK);
+ }
+
+ enumError err = CheckEnvOptions("WKCLT_OPT",CheckOptions);
+ if (err)
+ hint_exit(err);
+
+ err = CheckOptions(argc,argv,false);
+ if (err)
+ hint_exit(err);
+
+ err = CheckCommand(argc,argv);
+ DUMP_TRACE_ALLOC(TRACE_FILE);
+
+ if (SIGINT_level)
+ err = ERROR0(ERR_INTERRUPT,"Program interrupted by user.");
+ return err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////