diff options
| author | root <root@dc13.lemmi> | 2020-07-21 10:00:46 +0200 |
|---|---|---|
| committer | root <root@dc13.lemmi> | 2020-07-21 10:00:46 +0200 |
| commit | 1192c4575ea7afdf38e3fa0df4587a08620af11e (patch) | |
| tree | 6910a62487c282d0840d2e668aa7eb40b7c8a3fd /project/src/wszst.c | |
| parent | 76de8a89f7c1d1b4522eb66e8f89d248e8c1781f (diff) | |
SZS v2.18a
Diffstat (limited to 'project/src/wszst.c')
| -rw-r--r-- | project/src/wszst.c | 5896 |
1 files changed, 5896 insertions, 0 deletions
diff --git a/project/src/wszst.c b/project/src/wszst.c new file mode 100644 index 0000000..21923e4 --- /dev/null +++ b/project/src/wszst.c @@ -0,0 +1,5896 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * 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 <time.h> +#include <dirent.h> + +#include "dclib-utf8.h" +#include "lib-szs.h" +#include "lib-brres.h" +#include "lib-xbmg.h" +#include "lib-kcl.h" +#include "lib-kmp.h" +#include "lib-lecode.h" +#include "lib-mdl.h" +#include "lib-pat.h" +#include "lib-breff.h" +#include "lib-image.h" +#include "lib-common.h" +#include "lib-rkg.h" +#include "crypt.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wszst.c" +#include "db-mkw.h" +#include "lib-object.h" +#include "lib-checksum.h" + +#if HAVE_WIIMM_EXT + #include "lib-vehicle.h" + #include "wcommand.h" +#endif + +#include "distrib.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WSZST_SHORT ": " WSZST_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +#define TOOLSET_TITLE TOOLSET_SHORT ": " TOOLSET_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wszst,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wszst,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void list_compressions_exit() +{ + SetupColors(); + const int fw = GetTermWidth(80,40) - 1; + PrintColoredLines(stdout,colout,0,fw,0,0, + "\n{setup|%s}\n\n{caption|Compression modes:}\n" + "\n" + "There are several compression methods and most of them" + " support different compression levels." + " Each methods and level can be address by a numerical mode" + " and some by a name.\n" + "\n" + "In general, 2 compression formats are supported: YAZ0/YAZ1 and BZ (bzip2).\n" + "\n" + "The following list shows all modes." + " A mode can by selected by option {opt|--compr=param} or {opt|-C param}.\n" + "|[3,9,23]\n" + "{heading| numbers\tnames\tdescription}\n" + "{heading|%.*s\n" + "\t{hl| -1\tUNCOMPRESSED}\t|" + "Don't compress.\n" + "\t{hl| 0\tNOCHUNKS}\t|" + "{cmd|YAZ} only: Use only byte copies.\n" + "\n" + "\t{hl| 1\tFAST}\t|" + "Fastest available standard compression.\n" + "\t{hl|1-8\t}\t|" + "Standard levels between {par|FAST} and {par|BEST}.\n" + "\t{hl| 9\tBEST}\t|" + "Best and {par|default} standard compression.\n" + "\n" + "\t\t{hl|TRY2 - TRY5}\t|" + "Because of many repeated data, the best {cmd|BZ} compression mode varies." + " Therefor the levels {par|TRY2} to {par|TRY5} (or short {par|T2} to {par|T5})" + " are defined to find the best compression mode with testing the first" + " N levels of {par|9, 1, 8, 2, 5}. {par|TRY2} (levels 9 and 1)" + " is used for normalizing." + " For other than {cmd|BZ} compressions, {par|BEST} is used instead.\n" + "\n" + "\t{hl| 10\tULTRA}\t|" + "A special time-consuming compression method for {cmd|YAZ} only." + " It is dedicated to competitions with strict size limitations.\n" + "\n" + " {hl|100-150}\t\t|" + "This {cmd|YAZ} compression uses a back tracking" + " algorithm with recursion depth between {par|0 and 50} (last 2 digits)." + " Depths 0 and 1 are similar to mode {par|9}." + " Each new depth doubles the number of calculated pathes." + " Because of some optimizations, a depth of +5 results" + " in 10 to 15 times and not in 32 (2^5) times like expected." + " This algorithm needs 4 times as much memory as the uncompressed file." + " So it is dedicated to small files only.\n" + #if HAVE_WIIMM_EXT && 0 + "{heading|%.*s\n" + " {bad|500-530}\t\t|" + "This {bad|experimental} {cmd|YAZ} compression uses a brute force" + " algorithm with recursion depth from {par|0 to 30} (last 2 digits)." + " It is a very time-consuming compression method" + " and creates smaller files than {par|ULTRA}.\n" + " {bad|600-630}\t\t|" + "This {bad|experimental} {cmd|YAZ} compression" + " is an optimized version of {par|100-130}.\n" + #endif + "{heading|%.*s\n" + "\n" + ,TITLE + ,3*fw,ThinLine300_3 + #if HAVE_WIIMM_EXT && 0 + ,3*fw,ThinLine300_3 + #endif + ,3*fw,ThinLine300_3 + ); + + 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=" WSZST_SHORT "\n" + "name=" WSZST_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 WSZST_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); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void set_all ( bool force_cut ) +{ + if ( all_count++ || force_cut ) + opt_cut = true; + opt_decode = opt_encode_all = true; + opt_mipmaps = 1; + opt_recurse = INT_MAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 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(" force: %16x = %12d (kmp=%d)\n",force_count,force_count,force_kmp); + printf(" sort: %16x = %12d\n",opt_sort,opt_sort); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + printf(" align: %16x = %12d\n",opt_align,opt_align); + printf(" align-u8: %16x = %12d\n",opt_align_u8,opt_align_u8); + printf(" align-pack: %16x = %12d\n",opt_align_pack,opt_align_pack); + printf(" align-brres: %16x = %12d\n",opt_align_brres,opt_align_brres); + printf(" align-breff: %16x = %12d\n",opt_align_breff,opt_align_breff); + printf(" align-breft: %16x = %12d\n",opt_align_breft,opt_align_breft); + printf(" tiny: %16x = %12d\n",opt_tiny,opt_tiny); + printf(" compression: mode %2d, level %d\n",opt_compr_mode,opt_compr); + printf(" recurse: %16x = %12d\n",opt_recurse,opt_recurse); + printf(" cmpr-default: valid=%d, RGB565: %04x %04x, RGB: %06x %06x\n", + opt_cmpr_valid, + be16(opt_cmpr_def), be16(opt_cmpr_def+2), + RGB565_to_RGB(be16(opt_cmpr_def)), + RGB565_to_RGB(be16(opt_cmpr_def+2)) ); + printf(" n-mipmaps: %16x = %12d\n",opt_n_images-1,opt_n_images-1); + printf(" max-mipmaps: %16x = %12d\n",opt_max_images-1,opt_max_images-1); + printf(" mipmap-size: %16x = %12d\n",opt_min_mipmap_size,opt_min_mipmap_size); + printf(" mipmaps: %16x = %12d\n",opt_mipmaps,opt_mipmaps); + printf(" fast-mipmaps:%16x = %12d\n",fast_resize_enabled,fast_resize_enabled); + printf(" analyse-mode:%16x = %12d\n",opt_analyze_mode,opt_analyze_mode); + printf(" set-flags: %16x = %12d\n",set_flags,set_flags); + printf(" kcl modes: %16llx = \"%s\"\n",(u64)KCL_MODE,GetKclMode()); + + printf(" kmp modes: %16llx = \"%s\"\n",(u64)KMP_MODE,GetKmpMode()); + printf(" kmp tform: %16x = \"%s\"\n",KMP_TFORM,GetKmpTform()); + if (speed_mod_active) + printf(" kmp speed modifier: %04x ~ %5.3f\n", + speed_mod_val, speed_mod_factor ); + printf(" mdl modes: %16x = \"%s\"\n",MDL_MODE,GetMdlMode()); + printf(" pat modes: %16x = \"%s\"\n",PAT_MODE,GetPatMode()); + + if (have_patch_count) + printf(" patch count: %16x = %12d\n",have_patch_count,have_patch_count); + printf(" patch files: %16x = \"%s\"\n",PATCH_FILE_MODE,GetFileClassInfo()); + 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() +{ + SetPatchFileModeReadonly(); + + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 1 + + u8 * p1 = MALLOC(10); + u8 * p2 = CALLOC(10,20); + u8 * p3 = MALLOC(3000); + DUMP_TRACE_ALLOC(stderr); + p1[-1] = 'a'; + FREE(p1); + FREE(p2); + FREE(p3); + DUMP_TRACE_ALLOC(stderr); + return ERR_OK; + + #elif 1 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + SetupParam_t sp; + InitializeSetupParam(&sp); + enumError err = ScanSetupParam(&sp,true,param->arg,0,0,false); + printf("err=%d, ff=%s, pt-dir=%d : %s\n", + err, GetNameFF(sp.fform_file,sp.fform_arch), + sp.have_pt_dir, param->arg ); + ResetSetupParam(&sp); + } + return ERR_OK; + + #elif 1 + + const u64 base = 0x0807060504030201ull; + u64 val; + char buf[16]; + + const endian_func_t * endian = &be_func; + for(;;) + { + putchar('\n'); + + memset(buf,0,sizeof(buf)); + endian->wr16(buf,base); + HexDump16(stdout,0,0x16,buf,sizeof(buf)); + val = endian->rd16(buf); + ASSERT_MSG( val == (u16)base, "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr24(buf,base); + HexDump16(stdout,0,0x24,buf,sizeof(buf)); + val = endian->rd24(buf); + ASSERT_MSG( val == ( (u32)base & 0xffffff ), "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr32(buf,base); + HexDump16(stdout,0,0x32,buf,sizeof(buf)); + val = endian->rd32(buf); + ASSERT_MSG( val == (u32)base, "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr48(buf,base); + HexDump16(stdout,0,0x48,buf,sizeof(buf)); + val = endian->rd48(buf); + ASSERT_MSG( val == ( base & 0xffffffffffff ), "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr64(buf,base); + HexDump16(stdout,0,0x64,buf,sizeof(buf)); + val = endian->rd64(buf); + ASSERT_MSG( val == base, "%llx %llx\n", val, base ); + + if ( endian == &le_func ) + break; + endian = &le_func; + } + putchar('\n'); + return ERR_OK; + + #elif 1 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + ccp arg = param->arg; + char buf[1000]; + ccp subpath = SplitSubPath(buf,sizeof(buf),arg); + if (!subpath) + printf("NOT FOUND: |%s|\n",buf); + else if (!*subpath) + printf("REAL FILE: |%s|\n",buf); + else + printf("SUB PATH: |%s|%s|\n", buf, subpath ); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command autoadd /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError exec_autoadd +( + UsedFileFILE_t *used, // vector with used files + ccp source // filename of source file +) +{ + DASSERT(used); + DASSERT(source); + PRINT("exec_autoadd() sizeof(used)==%zu\n",sizeof(*used)); + + char path_buf[PATH_MAX]; + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,source,true,opt_ignore>0,true); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sANALYZE %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF_SZS(&szs), szs.fname ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + CollectFilesSZS(&szs,true,0,-1,SORT_NONE); + + int idx; + for ( idx = 0; idx < N_DB_FILE_FILE; idx++ ) + { + if (used->d[idx]) + continue; + + const DbFileFILE_t *ptr = DbFileFILE + idx; + if (!DBF_ARCH_SUPPORT(ptr->flags)) + continue; + + szs_subfile_t *file, *file_end = szs.subfile.list + szs.subfile.used; + for ( file = szs.subfile.list; file < file_end; file++ ) + { + ccp fpath = file->path; + if( fpath[0] == '.' && fpath[1] == '/' ) + fpath += 2; + if (!strcmp(fpath,ptr->file)) + { + used->d[idx] = 2; + ccp path = PathCatPP(path_buf,sizeof(path_buf),opt_dest,ptr->file); + if ( verbose >= 0 || testmode ) + fprintf(stdlog," %sADD %7u, %s:%s\n", + testmode ? "WOULD " : "", + file->size, GetNameFF(0,ptr->fform), path ); + + File_t F; + CreateFileOpt(&F,true,path,testmode,0); + if (F.f) + { + if (opt_preserve) + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite( szs.data + file->offset, + 1, file->size, F.f ); + if ( wstat != file->size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %u bytes failed: %s\n", + file->size, path ); + } + ResetFile(&F,opt_preserve); + } + } + } + fflush(stdout); + } + + ResetSZS(&szs); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError find_autoadd +( + UsedFileFILE_t *used, // vector with used files + ccp source_dir, // filename of source directory + ccp name, // name of while without extension + ccp slot // NULL or slot prefix +) +{ + DASSERT(used); + DASSERT(source_dir); + DASSERT(name); + + struct stat st; + char pathbuf[PATH_MAX], fname[50]; + + int try; + for ( try = 0;; try++ ) + { + switch (try) + { + case 0: + snprintf(fname,sizeof(fname), + "%s", name ); + break; + + case 1: + snprintf(fname,sizeof(fname), + "%s_d", name ); + break; + + case 2: + if (!slot) + *fname = 0; + else + snprintf(fname,sizeof(fname), + "%s-%s", slot, name ); + + break; + + case 3: + if (!slot) + *fname = 0; + else + snprintf(fname,sizeof(fname), + "%s-%s_d", slot, name ); + break; + + default: + return ERR_OK; + } + + if (*fname) + { + ccp path = PathCatPPE(pathbuf,sizeof(pathbuf), source_dir, fname, ".szs" ); + PRINT("-> %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return exec_autoadd(used,path); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_autoadd() +{ + SetPatchFileModeReadonly(); + + char dest_path[PATH_MAX], path_buf[PATH_MAX]; + PathCatBufPP(dest_path,sizeof(dest_path),share_path,"auto-add/"); + CheckOptDest(dest_path,true); + + if ( !opt_mkdir && !ExistDirectory(opt_dest,0) ) + return ERROR0(ERR_CANT_CREATE_DIR, + "Directory does not exist: %s\n",opt_dest); + opt_mkdir = true; + + if (!n_param) + opt_overwrite = false; + + if ( !n_param || verbose >= 0 ) + fprintf(stdlog,"\nCURRENT AUTO-ADD PATH: %s\n",opt_dest); + + if ( !n_param || verbose >= 1 ) + { + SetupAutoAdd(); + ccp *aap; + for ( aap = auto_add_path; *aap; aap++ ) + fprintf(stdlog," SEARCH PATH[%td]: %s\n", + aap-auto_add_path,*aap); + } + + + //--- create file list + + UsedFileFILE_t used; + memset(&used,0,sizeof(used)); + uint n_invalid = 0; + + if ( !opt_overwrite || !n_param ) + { + if ( verbose >= 2 ) + fprintf(stdlog,"\nLIST OF FILES IN %s\n", opt_dest ); + + int count = 0; + const DbFileFILE_t *ptr; + for ( ptr = DbFileFILE; ptr->file; ptr++ ) + if (DBF_ARCH_SUPPORT(ptr->flags)) + { + ccp path = PathCatPP(path_buf,sizeof(path_buf),opt_dest,ptr->file); + struct stat st; + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + { + used.d[ptr-DbFileFILE] = 1; + if ( verbose >= 2 ) + { + count++; + fprintf(stdlog," %-5s %s\n", + GetNameFF(0,ptr->fform), ptr->file ); + } + + if ( verbose >= 1 ) + { + DASSERT( ptr->ref < N_DB_FILE_REF_FILE ); + const s16 ref = DbFileRefFILE[ptr->ref]; + noPRINT("%zd -> %d/%d : %d : %s\n", + ptr-DbFileFILE, ptr->ref ,ptr->group, ref, ptr->file ); + if ( ref >= 0 ) + { + DASSERT( ref < N_DB_FILE ); + const DbFile_t *db = DbFile + ref; + DASSERT( db->sha1 < N_DB_FILE_SHA1 ); + const u8 *sha1 = DbFileSHA1[db->sha1].sha1; + + sha1_size_t ss; + GetSSByFile(&ss,path,0); + if (memcmp(sha1,ss.hash,sizeof(ss.hash))) + { + n_invalid++; + used.d[ptr-DbFileFILE] = 2; + HexDump(stdout,0,0,4,20,sha1,20); + HexDump(stdout,0,0,4,20,ss.hash,20); + } + } + } + } + } + if (count) + fprintf(stdlog," => %u of %zu files found.\n",count,sizeof(used)); + } + + + //--- iterate parameters + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + if (!ExistDirectory(param->arg,0)) + { + enumError err = exec_autoadd(&used,param->arg); + if ( max_err < err ) + max_err = err; + } + else + { + const DbFileSZS_t *db; + for ( db = DbFileSZS; db->szs_name; db++ ) + if ( (db->type & (FLT_OLD|FLT_STD)) == FLT_STD ) + find_autoadd( &used, param->arg, db->szs_name, + db->idx < 0 ? 0 : db->id+1 ); + } + } + + + //--- print missing list + + if ( verbose >= 1 ) + { + fprintf(stdlog,"\nLIST OF MISSED FILES IN %s\n", opt_dest ); + + int count = 0; + const DbFileFILE_t *ptr; + for ( ptr = DbFileFILE; ptr->file; ptr++ ) + if ( !used.d[ptr-DbFileFILE] && DBF_ARCH_SUPPORT(ptr->flags) ) + { + count++; + fprintf(stdlog," %-5s %s\n", + GetNameFF(0,ptr->fform), ptr->file ); + } + if (count) + fprintf(stdlog," => %u of %zu files missed.\n",count,sizeof(used)); + } + + + //--- print invalid list + + if (n_invalid) + { + fprintf(stdlog,"\nLIST OF INVALID FILES IN %s\n", opt_dest ); + + const DbFileFILE_t *ptr; + for ( ptr = DbFileFILE; ptr->file; ptr++ ) + if ( used.d[ptr-DbFileFILE] == 2 ) + fprintf(stdlog," %-5s %s\n", + GetNameFF(0,ptr->fform), ptr->file ); + fprintf(stdlog," => %u of %zu files invalid.\n",n_invalid,sizeof(used)); + } + + if ( verbose >= 1 ) + fputs("\n",stdlog); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int files_func +( + ccp fname, // name of current file without '.szs' extension + ccp id, // unique ID of current file + FileList_t mode, // mode of current file + const TrackInfo_t *ti, // NULL or related track info + void *param // user defined parameter +) +{ + char name[BMG_MSG_BUF_SIZE]; + name[0] = 0; + const bmg_item_t *bi = 0; + + if ( ti && opt_load_bmg.item_used ) + { + bi = FindItemBMG(&opt_load_bmg,ti->track_id+MID_LE_TRACK_BEG); + if ( !bi || !bi->len ) + { + bi = FindItemBMG(&opt_load_bmg,ti->track_id+MID_CT_TRACK_BEG); + if ( !bi || !bi->len ) + { + bi = FindItemBMG(&opt_load_bmg,ti->bmg_mid); + if ( bi && !bi->len ) + bi = 0; + } + } + + if (bi) + { + PrintString16BMG(name,sizeof(name)-10,bi->text,bi->len,BMG_UTF8_MAX,0,0); + if (!strchr(name,'[')) + { + const uint pos = strlen(name); + snprintf(name+pos,sizeof(name)-pos," [%c%02u]", + mode & FLT_ARENA ? 'a' : 'r', ti->def_slot ); + } + } + } + + if (pipe_count) + { + if (verbose>0) + printf("%s|0x%02x|%s|%s\n",id,mode,fname,name); + else + printf("%s|%s|%s\n",id,fname,name); + } + else + { + if (verbose>0) + printf("%-5s 0x%02x ",id,mode); + else + printf("%-5s ",id); + + if (*name) + printf("%-21s %s\n",fname,name); + else + printf("%s\n",fname); + } + return 1; +} + +//----------------------------------------------------------------------------- + +static enumError cmd_tracks() +{ + SetPatchFileModeReadonly(); + + u32 mode = 0; + if (first_param) + { + ScanNumU32(first_param->arg,0,&mode,0,FLT_M_ALL); + mode &= FLT_M_ALL; + } + + if (!mode) + { + mode = FLT_TRACK | FLT_ARENA | FLT_STD; + + if ( all_count > 1 ) + mode = FLT_M_ALL; + else if ( all_count ) + mode |= FLT_D; + + if (long_count) + { + mode |= FLT_OTHER; + if ( long_count > 1 ) + mode |= FLT_OLD; + } + } + + IterateTrackFiles(mode,files_func,0); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command scancache /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_scancache() +{ + if (opt_cache) + AddParam(opt_cache); + if ( n_param != 1 ) + return ERROR0(ERR_SYNTAX,"Exact one parameter (directory) expected.\n"); + + if (!IsDirectory(first_param->arg,false)) + return ERROR0(ERR_SEMANTIC,"Directory expected: %s\n",first_param->arg); + + ScanSZSCache(first_param->arg,opt_purge); + return SaveSZSCache(); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetPatchFileModeReadonly(); + DefineDefaultParserFunc(); + return ExportHelper(""); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _CODE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_code() +{ + SetPatchFileModeReadonly(); + stdlog = stderr; + + if (!n_param) + AddParam("-"); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + if (verbose) + fprintf(stderr,"CODE %s\n",param->arg); + File_t F; + if (!OpenFILE(&F,true,param->arg,false,true)) + { + while ( !feof(F.f) && !ferror(F.f) ) + { + uint size = fread(iobuf,1,sizeof(iobuf),F.f); + u8 *src = (u8*)iobuf, *end = src + size; + while ( src < end ) + *src++ ^= 0xdc; + fwrite(iobuf,1,size,stdout); + } + } + ResetFile(&F,false); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _RECODE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef HAVE_WIIMM_EXT + + enumError cmd_recode() + { + ERROR0(ERR_NOT_IMPLEMENTED, + "Command _RECODE not implemented in this version!\n"); + exit(ERR_NOT_IMPLEMENTED); + } + +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _SUBFILE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_subfile() +{ + if (!first_param) + return ERR_OK; + + SubDir_t dir; + InitializeSubDir(&dir); + + NORMALIZE_FILENAME_PARAM(first_param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,first_param->arg,false,opt_ignore>0,true); + if (!err) + { + DecompressSZS(&szs,true,0); + #ifdef TEST + { + printf("SZS: %p %zu / %p %zu\n",szs.cdata,szs.csize,szs.data,szs.size); + + DumpInfoContainer(stdlog,collog,2,"SZS: ",&szs.container,0x20); + szs_file_t szs2; + InitializeSubSZS(&szs2,&szs,4,12,FF_UNKNOWN,0,false); + printf("SZS2: %p %zu / %p %zu\n",szs2.cdata,szs2.csize,szs2.data,szs2.size); + DumpInfoContainer(stdlog,collog,2,"SZS1: ",&szs.container,0x20); + DumpInfoContainer(stdlog,collog,2,"SZS2: ",&szs2.container,0x20); + ResetSZS(&szs2); + DumpInfoContainer(stdlog,collog,2,"SZS: ",&szs.container,0x20); + } + #endif + DASSERT( !szs.file_size || szs.file_size >= szs.size ); + have_patch_count -= 1000000; + PRINT("EXTRACT/%s[%s]: %s\n",__FUNCTION__,GetNameFF_SZS(&szs),szs.fname); + err = ExtractFilesSZS(&szs,0,false,&dir,0); + have_patch_count += 1000000; + + if ( opt_dest && *opt_dest ) + { + DumpSubDir(stdout,2,&dir); + SaveSubFiles(&dir,opt_dest); + } + + SubFile_t *sf = FindSubFile(&dir,MemByString("3DModels(NW4R)/course")); + if (sf) + { + printf("** %s found [%p,0x%x] **\n",sf->fname,sf->data,sf->size); + #if 1 + //ccp err = RenameForSlot42MDL(sf->data,sf->size,ContainerSZS(&szs)); + ccp err = RenameForSlot42MDL(sf->data,sf->size,0); + if (err) + printf("\e[35;1m %s \e[0m\n",err); + printf(">> SAVE %p,0x%x\n",sf->data,sf->size); + SaveFile("_mdl.tmp",0,FM_OVERWRITE|FM_TOUCH,sf->data,sf->size,0); + #else + mdl_t mdl; + ScanMDL(&mdl,true,sf->data,sf->size,ContainerSZS(&szs),0); + SetupSlot42Materials(); + AppendMDL(&mdl,&slot42_mdl); + SaveRawMDL(&mdl,"_mdl.tmp",false); + ResetMDL(&mdl); + #endif + } + + szs_file_t szs2; + InitializeSZS(&szs2); + enumError err = CreateSZS(&szs2,0,0,&dir,0,0, + verbose > 0 ? UINT_MAX : 0, false ); + if (!err) + SaveSZS(&szs2,"_szs.tmp",true,false); + ResetSZS(&szs2); + } + ResetSubDir(&dir); + ResetSZS(&szs); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command BRSUB /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[check_brsub_t]] + +typedef struct check_brsub_t +{ + szs_file_t * szs; // pointer to valid SZS + int mode; // 0:warnings, 1:+hints, 2:+info + + bool header_printed; // true: header with filename printed + bool print_sep; // true: print separator + int sep_fw; // field width of separator + uint sep_count; // number of printed lines after last sep + + enumError max_err; // error code + ccp brres_path; // path of current BRRES + uint line_count; // number of printed status lines +} +check_brsub_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int check_brsub +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + check_brsub_t *cb = (check_brsub_t*)it->param; + DASSERT(cb); + + if (term) + cb->brres_path = 0; + else if ( it->size >= 0x0c ) + { + u8 *data = it->szs->data + it->off; + const file_format_t fform = GetByMagicFF(data,it->size,it->size); + if ( fform == FF_BRRES ) + { + cb->brres_path = it->path; + cb->print_sep = print_header; + } + else if ( cb->brres_path && IsBRSUB(fform) ) + { + char stat_buf[50], vers_buf[50], ns_buf[50]; + const int version = it->endian->rd32(data+8); + const brsub_info_t *bi = GetInfoBRSUB(fform,version); + const brsub_info_t *bic = GetCorrectBRSUB(fform); + bool print_status = true; + enumError err = ERR_OK; + + if (!bi) + { + err = ERR_INVALID_DATA; + snprintf(stat_buf,sizeof(stat_buf), + "%sUnknown%s", colout->fatal, colout->reset ); + } + else + { + switch(bi->warn) + { + case BIMD_OK: + err = ERR_INVALID_DATA; + print_status = cb->mode > 1; + snprintf(stat_buf,sizeof(stat_buf), + "%sOk %s", colout->success, colout->reset ); + break; + + case BIMD_HINT: + err = ERR_INVALID_VERSION; + print_status = cb->mode > 0; + snprintf(stat_buf,sizeof(stat_buf), + "%sUnusual%s", colout->hint, colout->reset ); + break; + + case BIMD_FAIL: + err = ERR_INVALID_DATA; + snprintf(stat_buf,sizeof(stat_buf), + "%sFail %s", colout->warn, colout->reset ); + break; + + default: + //case BIMD_FATAL: + err = ERR_INVALID_DATA; + snprintf(stat_buf,sizeof(stat_buf), + "%sFreeze %s", colout->bad, colout->reset ); + break; + } + } + + if ( cb->max_err < err ) + cb->max_err = err; + + if (print_status) + { + if ( bic && version != bic->good_version ) + snprintf(vers_buf,sizeof(vers_buf), + "%s%3d!%s", colout->hint, version, colout->reset ); + else + snprintf(vers_buf,sizeof(vers_buf), "%3d ", version ); + + const int n_sect = GetGenericSectionNumBRSUB(data,it->size,it->endian); + if ( !bic || n_sect != bic->good_sect ) + snprintf(ns_buf,sizeof(ns_buf), + "%s%2d!%s", colout->fatal, n_sect, colout->reset ); + else + snprintf(ns_buf,sizeof(ns_buf), "%2d ", n_sect ); + + if (!cb->header_printed) + { + cb->header_printed = true; + cb->line_count = 999; + if (print_header) + printf( "\n%s%s:%s%s\n" + "%s%.*s\n" + "%s Status Type Ver N/s Filename of BRRES and sub file%s\n", + colout->caption, GetNameFF_SZS(cb->szs), + cb->szs->fname, colout->reset, + colout->heading, cb->sep_fw, ThinLine300_3, + colout->heading, colout->reset ); + else + printf("\n%s>%s:%s%s\n", + colout->caption, GetNameFF_SZS(cb->szs), + cb->szs->fname, colout->reset ); + } + + if ( cb->print_sep && cb->line_count > 2 ) + { + cb->print_sep = false; + cb->line_count = 0; + printf("%s%.*s%s\n", + colout->heading, cb->sep_fw, ThinLine300_3, colout->reset ); + } + + cb->sep_count++; + cb->line_count++; + printf(" %s %-3s %s %s %s / %s\n", + stat_buf, GetNameFF(0,bic?bic->fform1:fform), + vers_buf, ns_buf, + cb->brres_path, it->path ); + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_brsub() +{ + if (!n_param) + { + const int fw = 3*34; + if (print_header) + printf( "\n" + "%s%.*s\n" + "%s file format vers n(sect) warning%s\n", + colout->heading, fw, ThinLine300_3, + colout->heading, colout->reset ); + + file_format_t last_ff = 0; + const brsub_info_t *bi; + for ( bi = brsub_info; bi->fform1; bi++ ) + { + if ( print_header && last_ff != bi->fform1 ) + { + last_ff = bi->fform1; + if (print_header) + printf("%s%.*s%s\n", + colout->heading, fw, ThinLine300_3, colout->reset ); + } + + printf(" %-3s %-7s %3d %6u ", + GetNameFF(0,bi->fform1), + bi->fform2 ? GetNameFF(0,bi->fform2) : "-", + bi->version, bi->n_sect ); + switch(bi->warn) + { + case BIMD_OK: + fputs("-\n",stdout); + break; + + case BIMD_HINT: + printf("%sUNUSUAL%s\n",colout->hint,colout->reset); + break; + + case BIMD_FAIL: + printf("%sFAIL%s\n",colout->warn,colout->reset); + break; + + default: + //case BIMD_FATAL: + printf("%sFREEZE%s\n",colout->bad,colout->reset); + break; + } + } + + if ( print_header ) + printf("%s%.*s%s\n\n", + colout->heading, fw, ThinLine300_3, colout->reset ); + + return ERR_OK; + } + + //--- have param => check BRSUB versions + + szs_file_t szs; + InitializeSZS(&szs); + uint line_count = 0; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + ResetSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + if ( max_err < err ) + max_err = err; + + check_brsub_t cb; + memset(&cb,0,sizeof(cb)); + cb.szs = &szs; + cb.mode = long_count; + cb.sep_fw = 3*99; + IterateFilesSZS(&szs,check_brsub,&cb,false,-1,-1,SORT_NONE); + line_count += cb.line_count; + + if ( print_header && cb.header_printed ) + printf("%s%.*s%s\n", + colout->heading, cb.sep_fw, ThinLine300_3, colout->reset ); + + if ( max_err < cb.max_err ) + max_err = cb.max_err; + } + + if (line_count) + putchar('\n'); + ResetSZS(&szs); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int list_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if (term) + return 0; + + ccp col_reset = ""; + if (colorize_stdout) + { + if (it->is_dir) + { + fputs(colset->cmd,stdout); + col_reset = colset->reset; + } + else if ( it->has_subfiles ) + { + fputs(colset->heading,stdout); + col_reset = colset->reset; + } + } + + const int indent = 2 * it->recurse_level; + + if ( long_count > 1 ) + { + if (it->is_dir) + printf(" - - - - - %.*s%s%s\n", + indent, indent_msg, it->path, col_reset ); + else + { + char vers_buf[50]; + PrintVersionSZS(vers_buf,sizeof(vers_buf),it); + + const u8 * data = it->szs->data + it->off; + printf("%7x %7x %8u %-4s %s %.*s%s%s\n", + it->off, it->size, it->size, + PrintID(data, it->size < 4 ? it->size : 4, 0 ), + vers_buf, + indent,indent_msg, it->path, col_reset ); + } + } + else if ( long_count > 0 ) + { + if (it->is_dir) + printf(" - - %.*s%s%s\n", + indent,indent_msg, it->path, col_reset ); + else + printf("%8u %-4s %.*s%s%s\n", + it->size, + PrintID( it->szs->data+it->off, it->size < 4 ? it->size : 4, 0 ), + indent,indent_msg, it->path, col_reset ); + } + else + printf("%.*s%s%s\n",indent,indent_msg,it->path,col_reset); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_list ( int long_level ) +{ + SetPatchFileModeReadonly(); + + if ( opt_recurse < 0 ) + opt_recurse = 0; + + if ( long_level > 0 ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_LONG,long_level,false); + long_count += long_level; + } + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if (print_header) + { + printf("\n* Files of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + + static ccp f_or_d = "file or directory"; + if ( long_count > 2 ) + PrintFileHeadSZS(); + else if ( long_count > 1 ) + printf("\noff/hex siz/hex size/dec magic vers %s\n%.79s\n",f_or_d,Minus300); + else if ( long_count > 0 ) + printf("\nsize/dec magic %s\n%.79s\n",f_or_d,Minus300); + else + putchar('\n'); + } + else if ( n_param > 1 ) + printf("\n* Files of %s\n",param->arg); + + if ( long_count > 2 ) + IterateFilesSZS(&szs,PrintFileSZS,0,true,opt_recurse,opt_cut,opt_sort); + else + IterateFilesSZS(&szs,list_func,0,false,opt_recurse,opt_cut,opt_sort); + ResetSZS(&szs); + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command name-ref /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_name_ref() +{ + stdlog = stderr; + const SortMode_t std_sort = opt_sort == SORT_INAME ? SORT_INAME : SORT_BRRES; + + 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, + "course_model.brres",opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + { + ResetRawData(&raw); + return err; + } + + if ( raw.fform != FF_BRRES && !IsBRSUB(raw.fform) ) + { + printf("No BRRES family -> ignore %s:%s\n", + GetNameFF(raw.fform,0), raw.fname ); + continue; + } + + szs_file_t szs; + AssignSZS(&szs,true,raw.data,raw.data_size,false,raw.fform,raw.fname); + raw.fname = NULL; + + name_ref_t nr; + err = CreateNameRef(&nr,true,&szs,brief_count); + if (err) + return err; + + if ( long_count > 0 ) + { + ListNameRef( stdout,0, &nr, std_sort ); + ListNameRef( stdout,0, &nr, SORT_OFFSET ); + if ( long_count > 1 ) + ListNameRef( stdout,0, &nr, SORT_NONE ); + } + else + ListNameRef( stdout,0, &nr, opt_sort ); + + ResetNameRef(&nr); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command ilist /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int ilist_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if (term) + return 0; + + if (!it->is_dir) + { + const u8 * data = it->szs->data+it->off; +// [[analyse-magic]] + PrintImage( data, it->size, it->path, + 0, 2 * it->recurse_level, long_count, + !it->has_subfiles + || GetByMagicFF(data,it->size,it->size) == FF_BREFF ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_ilist ( int long_level ) +{ + SetPatchFileModeReadonly(); + + if ( opt_recurse < 0 ) + opt_recurse = 0; + + if ( long_level > 0 ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_LONG,long_level,false); + long_count += long_level; + } + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if (print_header) + PrintImageHead(0,long_count); + else if ( n_param > 1 ) + printf("\n* Files of %s\n",param->arg); + + IterateFilesSZS(&szs,ilist_func,0,false,-1,0,opt_sort); + ResetSZS(&szs); + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command memory /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct dumpmem_t +{ + szs_file_t * szs; // the container + u8 * data_base; // pointer to base of complete file + uint last_off; // offset of last analyzed data + uint last_end; // end of last analyzed data + bool print_abs_off; // true: print absolute offset + +} dumpmem_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void DumpUnused +( + dumpmem_t * dm, // dump info + uint offset, // offset of object to analyze and dump + file_format_t fform, // NULL for last entry or fileformat + uint recurse_level // recurse level +) +{ + DASSERT(dm); + + recurse_level *= 2; + if ( fform != FF_INVALID ) + { + if (dm->print_abs_off) + printf("%7x ",offset); + + ccp ff = GetNameFF(fform,0); + + if ( dm->last_end < offset ) + printf("%*s%-6.6s %6x ", + recurse_level, "", + ff,offset - dm->last_end ); + else if ( dm->last_end > offset ) + printf("%*s%-6.6s %6x-", + recurse_level, "", + ff, dm->last_end - offset ); + else + printf("%*s%-6.6s - ", + recurse_level, "", + ff ); + } + else if ( dm->last_end < offset ) + { + if (dm->print_abs_off) + printf("%7x ",offset); + + printf("%*s- %6x\n", + recurse_level, "", + offset - dm->last_end); + } + else if ( dm->last_end > offset ) + { + if (dm->print_abs_off) + printf("%7x ",offset); + + printf("%*s- %6x-\n", + recurse_level, "", + dm->last_end - offset); + } + + dm->last_off = dm->last_end = offset; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int memory_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + DASSERT(it->param); + + dumpmem_t *dm = it->param; + + if (term) + { + const uint abs_off = it->szs->data - dm->data_base + it->szs->size; + noPRINT("TERM: %x %x %x\n",dm->last_off,dm->last_end,abs_off); + DumpUnused(dm,abs_off,FF_INVALID,it->recurse_level); + } + else if (!it->is_dir) + { + if (!it->client_int) + { + noPRINT("LAST: %x %x\n",dm->last_off, dm->last_end); + it->client_int++; + dm->last_end = dm->last_off; + } + + const u8 *data = it->szs->data + it->off; +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,it->size,it->size); + const uint abs_off = it->szs->data - dm->data_base + it->off; + DumpUnused(dm,abs_off,fform,it->recurse_level); + printf(" %6x .. %6x %6x %s\n", + it->off, it->off + it->size, it->size, it->path ); + + dm->last_off = abs_off; + dm->last_end = abs_off + it->size; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_memory() +{ + SetPatchFileModeReadonly(); + + if ( opt_recurse < 0 ) + opt_recurse = 0; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + dumpmem_t dm; + memset(&dm,0,sizeof(dm)); + dm.szs = &szs; + dm.data_base = szs.data; + dm.print_abs_off = long_count > 0; + + if (print_header) + { + printf("\n* memory dump of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + + fputs( dm.print_abs_off ? "\nabs off " : "\n", stdout); + printf( "type unused begin .. end size file name\n" + "%.80s\n", + Minus300 ); + } + + IterateFilesSZS(&szs,memory_func,&dm,false,opt_recurse,opt_cut,SORT_OFFSET); + ResetSZS(&szs); + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command dump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_dump() +{ + SetPatchFileModeReadonly(); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( szs.fform_arch == FF_BRRES || IsBRSUB(szs.fform_arch) ) + { + printf("\n* Dump structure of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + DumpStructureBRRES(stdout,&szs); + } + else if (!opt_ignore) + ERROR0(ERR_WARNING,"No BRRES file -> ignored: %s\n",param->arg); + ResetSZS(&szs); + } + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command sha1 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_sha1() +{ + stdlog = stderr; + enumError max_err = ERR_OK; + SetPatchFileModeReadonly(); + SetupCoding64( opt_coding64, opt_db64 ? ENCODE_BASE64URL : ENCODE_BASE64 ); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( max_err < err ) + max_err = err; + if ( err > ERR_WARNING || err == ERR_NOT_EXISTS ) + { + if ( opt_db64 ) + { + //fputs("--------------------------------",stdout); + putchar('-'); + if (long_count) + fputs(" -1 -1 0.000 -",stdout); + if (brief_count) + putchar('\n'); + else + printf(" %s\n",param->arg); + } + + if ( err == ERR_NOT_EXISTS || opt_ignore ) + continue; + return err; + } + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + char checksum[100]; + CreateSSChecksumBySZS(checksum,sizeof(checksum),&szs); + + if (opt_verify) + { + const uint clen = strlen(checksum); + ccp fname = strrchr(param->arg,'/'); + fname = fname ? fname+1 : param->arg; + bool found = false; + for(;;) + { + ccp pos = strstr(fname,checksum); + if (!pos) + break; + + if ( pos == param->arg || pos[-1] == '.' || pos[-1] == '-' ) + { + char ch = pos[clen]; + if ( !ch || ch == '.' || ch == '-' ) + { + found = true; + break; + } + } + + fname = pos+1; // search more + } + + if (!found) + { + if ( max_err < ERR_DIFFER ) + max_err = ERR_DIFFER; + if ( verbose >= 0 ) + printf("Checksum differ: %s\n",param->arg); + } + else if ( verbose > 0 ) + printf("Checksum ok: %s\n",param->arg); + } + else + { + if (!opt_db64) + { + if (long_count>1) + { + struct tm * tm = localtime(&szs.fatt.mtime); + char timbuf[40]; + strftime(timbuf,sizeof(timbuf),"%F %T ",tm); + fputs(timbuf,stdout); + } + if (long_count) + printf("%9zu,",szs.size); + } + + fputs(checksum,stdout); + + if ( opt_db64 && long_count ) + { + int ckpt0_count = -1; // number of LC in CKPT + int lap_count = 3; // STGI lap counter + float speed_factor = 1.0; // STGI speed factor + ccp slot_info = CreateSlotInfo(&szs); + + FindSpecialFilesSZS(&szs,false); + if (szs.course_kmp_data) + { + kmp_t kmp; + InitializeKMP(&kmp); + err = ScanKMP(&kmp,false,szs.course_kmp_data,szs.course_kmp_size,0); + if ( err <= ERR_WARNING ) + { + 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; + ckpt0_count = 0; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + if (!ckpt->mode) + ckpt0_count++; + } + + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.dlist[KMP_STGI].list; + if ( kmp.dlist[KMP_STGI].used > 0 ) + { + lap_count = stgi->lap_count; + + if (stgi->speed_mod) + speed_factor = SpeedMod2float(stgi->speed_mod); + } + } + ResetKMP(&kmp); + } + + printf(" %d %d %5.3f %s",ckpt0_count,lap_count,speed_factor,slot_info); + } + + if (brief_count) + putchar('\n'); + else + printf(" %s\n", param->arg ); + } + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command analyze /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_analyze() +{ + static ccp def_path = "\1P/\1F\1?T"; + CheckOptDest("-",false); + char dest[PATH_MAX]; + enumError max_err = ERR_OK; + if (!strcmp(opt_dest,"-")) + fputc('\n',stdout); + + szs_file_t szs; + InitializeSZS(&szs); + + File_t fo; + InitializeFile(&fo); + + PrintScript_t ps; + InitializePrintScript(&ps); + ps.fo = &fo; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + ResetSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( max_err < err ) + max_err = err; + + if (!fo.f) + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(script_fform,0),false); + + if ( verbose > 0 ) + { + fprintf(stdlog,"%sANALYZE %s:%s => %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(szs.fform_file,szs.fform_arch), szs.fname, + GetNameFF(script_fform,0), dest ); + fflush(stdlog); + } + + + //--- checksums + + sha1_size_t info; + SHA1(szs.data,szs.size,info.hash); + info.size = htonl(szs.size); + sha1_hex_t sha1, sha1_norm; + Sha1Bin2Hex(sha1,info.hash); + memcpy(sha1_norm,sha1,sizeof(sha1_norm)); + + char db64[CHECKSUM_DB_SIZE+1]; + CreateSSChecksumDB(db64,sizeof(db64),&info); + FindSpecialFilesSZS(&szs,false); + + + //--- scan LEX + + lex_info_t lexinfo; + InitializeLexInfo(&lexinfo); + + if (szs.course_lex_data) + { + lex_t lex; + InitializeLEX(&lex); + err = ScanLEX(&lex,false,szs.course_lex_data,szs.course_lex_size); + szs.have_lex = lex.have_lex; + SetupLexInfo(&lexinfo,&lex); + ResetLEX(&lex); + } + + + //--- scan KMP + + sha1_hex_t sha1_kmp = {0}, sha1_kmp_norm = {0}; + + char ct_attrib[300]; + char *ct_dest = ct_attrib; + char *ct_end = ct_attrib + sizeof(ct_attrib); + ct_attrib[1] = 0; + + int ckpt0_count = -1; // number of LC in CKPT + int lap_count = 3; // STGI lap counter + float speed_factor = 1.0; // STGI speed factor + + ccp slot_info = CreateSlotInfo(&szs); + if (szs.is_arena) + ct_dest = StringCopyE(ct_dest,ct_end,",arena"); + else + { + if (!strcmp(slot_info,"+6.1")) + ct_dest = StringCopyE(ct_dest,ct_end,",61"); + else if (!strcmp(slot_info,"+6.2")) + ct_dest = StringCopyE(ct_dest,ct_end,",62"); + else if (!strcmp(slot_info,"+3.1,+7.1")) + ct_dest = StringCopyE(ct_dest,ct_end,",31+71"); + } + + kmp_finish_t kf; + InitializeFinishLine(&kf); + + kmp_usedpos_t up; + InitializeUsedPos(&up); + + char gobj_info[20] = {0}; + if (szs.course_kmp_data) + { + SHA1(szs.course_kmp_data,szs.course_kmp_size,info.hash); + Sha1Bin2Hex(sha1_kmp,info.hash); + memcpy(sha1_kmp_norm,sha1_kmp,sizeof(sha1_kmp_norm)); + + kmp_t kmp; + InitializeKMP(&kmp); + kmp.lexinfo = &lexinfo; + 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; + const u8 lap_count = stgi->lap_count; + const u16 speed_mod = stgi->speed_mod; + stgi->lap_count = 3; + stgi->speed_mod = 0; + + SHA1(szs.data,szs.size,info.hash); + Sha1Bin2Hex(sha1_norm,info.hash); + + SHA1(szs.course_kmp_data,szs.course_kmp_size,info.hash); + Sha1Bin2Hex(sha1_kmp_norm,info.hash); + + stgi->lap_count = lap_count; + stgi->speed_mod = speed_mod; + } + + CheckFinishLine(&kmp,&kf); + CheckUsedPos(&kmp,&up); + + 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; + ckpt0_count = 0; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + if (!ckpt->mode) + ckpt0_count++; + } + + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.dlist[KMP_STGI].list; + if ( kmp.dlist[KMP_STGI].used > 0 ) + { + lap_count = stgi->lap_count; + if (!szs.is_arena) + { + if ( ckpt0_count > 1 ) + { + if ( lap_count == 1 ) + ct_dest = StringCopyE(ct_dest,ct_end,",1lap"); + ct_dest = snprintfE(ct_dest,ct_end,",%ulc",ckpt0_count); + } + else if ( lap_count != 0 && lap_count != 3 ) + ct_dest = snprintfE(ct_dest,ct_end,",%ulap%s", + lap_count, lap_count == 1 ? "" : "s" ); + } + + if (stgi->speed_mod) + { + speed_factor = SpeedMod2float(stgi->speed_mod); + char buf[20]; + snprintf(buf,sizeof(buf),",x%4.2f",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 && ckpt0_count > 1 ) + ct_dest = snprintfE(ct_dest,ct_end,",%ulc",ckpt0_count); + + if ( kf.n_ktpt_m > 1 ) + ct_dest = StringCopyE(ct_dest,ct_end,",2ktpt"); + + kmp_ana_pflag_t ap; + AnalysePFlagScenarios(&ap,&kmp,GMD_M_DEFAULT); + snprintf(gobj_info,sizeof(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.kmp_special); + if ( szs.kmp_special[HAVEKMP_WOODBOX_HT] + || szs.kmp_special[HAVEKMP_MUSHROOM_CAR] + || szs.kmp_special[HAVEKMP_PENGUIN_POS] + || szs.kmp_special[HAVEKMP_EPROP_SPEED] + ) + { + ct_dest = StringCopyE(ct_dest,ct_end,",gobj"); + } + + if ( szs.kmp_special[HAVEKMP_X_PFLAGS] + || szs.kmp_special[HAVEKMP_X_COND] + || szs.kmp_special[HAVEKMP_X_DEFOBJ] + || szs.kmp_special[HAVEKMP_X_RANDOM] + ) + { + ct_dest = StringCopyE(ct_dest,ct_end,",xpf"); + } + + ResetKMP(&kmp); + } + + if ( szs.szs_special[HAVESZS_ITEM_SLOT_TABLE] ) + ct_dest = StringCopyE(ct_dest,ct_end,",itemslot"); + + if ( szs.szs_special[HAVESZS_OBJFLOW] ) + ct_dest = StringCopyE(ct_dest,ct_end,",objflow"); + + if ( szs.szs_special[HAVESZS_GHT_ITEM] + || szs.szs_special[HAVESZS_GHT_ITEM_OBJ] + || szs.szs_special[HAVESZS_GHT_KART] + || szs.szs_special[HAVESZS_GHT_KART_OBJ] + ) + { + ct_dest = StringCopyE(ct_dest,ct_end,",geohit"); + } + + if ( szs.szs_special[HAVESZS_MINIGAME] ) + ct_dest = StringCopyE(ct_dest,ct_end,",minigame"); + + + //--- scan KCL + + sha1_hex_t sha1_kcl = {0}; + if (szs.course_kcl_data) + { + SHA1(szs.course_kcl_data,szs.course_kcl_size,info.hash); + Sha1Bin2Hex(sha1_kcl,info.hash); + } + + + //--- add "lex" and terminate 'ct_dest' + + if (szs.course_lex_data) + ct_dest = StringCopyE(ct_dest,ct_end,",lex"); + *ct_dest = 0; + + + //--- course model (course_model.brres, course_d_model.brres) + + sha1_hex_t sha1_course = {0}; + if (szs.course_model_data) + { + SHA1(szs.course_model_data,szs.course_model_size,info.hash); + Sha1Bin2Hex(sha1_course,info.hash); + } + else if (szs.course_d_model_data) + { + SHA1(szs.course_d_model_data,szs.course_d_model_size,info.hash); + Sha1Bin2Hex(sha1_course,info.hash); + } + + + //--- vrcorn (vrcorn_model.brres) + + sha1_hex_t sha1_vrcorn = {0}; + if (szs.vrcorn_model_data) + { + SHA1(szs.vrcorn_model_data,szs.vrcorn_model_size,info.hash); + Sha1Bin2Hex(sha1_vrcorn,info.hash); + } + + + //--- minimap (map_model.brres) + + sha1_hex_t sha1_minimap = {0}; + if (szs.map_model_data) + { + SHA1(szs.map_model_data,szs.map_model_size,info.hash); + Sha1Bin2Hex(sha1_minimap,info.hash); + } + + + //--- status + + if ( err >= ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + if (!fo.f) + { + enumError err = CreateFile(&fo,false,dest,FM_STDIO|FM_OVERWRITE); + if (err) + { + max_err = err; + break; + } + PrintScriptHeader(&ps); + } + + + //--- name attributes ('dest' can be used now) + + PrintEscapedString(dest,sizeof(dest),param->arg,-1,CHMD_UTF8,'"',0); + + uint name_attrib_len = 0; + ccp name_attrib = strrchr(dest,'['); + if (name_attrib) + { + name_attrib++; + ccp end = strrchr(name_attrib,']'); + if (end) + name_attrib_len = end - name_attrib; + } + else + name_attrib = EmptyString; + + + //--- print result + + PrintScriptVars(&ps,1, + "file=\"%s\"\n" + "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" + "is_arena=%u\n" + "is_arena_name=\"%s\"\n" + "n_ckpt0=%d\n" + "lap_count=%d\n" + "speed_factor=%5.3f\n" + "slot_info=\"%s\"\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" + "special_kmp=\"%s\"\n" + "special_files=\"%s\"\n" + "lex_sections=\"%s\"\n" + "ct_attributes=\"%s\"\n" + "name_attributes=\"%.*s\"\n" + ,dest + ,szs.size + ,db64 + ,sha1 + ,sha1_norm + ,sha1_kcl + ,sha1_kmp + ,sha1_kmp_norm + ,sha1_course + ,sha1_vrcorn + ,sha1_minimap + ,szs.is_arena + ,is_arena_name[szs.is_arena] + ,ckpt0_count + ,lap_count + ,speed_factor + ,slot_info + ,up.orig.rating[0],WarnLevelNameLo[up.orig.rating[0]] + ,up.orig.min.x,up.orig.max.x + ,fabsf(up.orig.max.x-up.orig.min.x) + ,(up.orig.min.x+up.orig.max.x)/2 + ,up.orig.rating[1],WarnLevelNameLo[up.orig.rating[1]] + ,up.orig.min.y,up.orig.max.y + ,fabsf(up.orig.max.y-up.orig.min.y) + ,(up.orig.min.y+up.orig.max.y)/2 + ,up.orig.rating[2],WarnLevelNameLo[up.orig.rating[2]] + ,up.orig.min.z,up.orig.max.z + ,fabsf(up.orig.max.z-up.orig.min.z) + ,(up.orig.min.z+up.orig.max.z)/2 + ,GetUsedPosObjSuggestion(up.suggest,true,"") + ,kf.warn ? "warn" : kf.hint ? "hint" : "ok" + ,kf.distance + ,kf.dir_delta + ,gobj_info + ,CreateSpecialInfoKMP(szs.kmp_special,true,"") + ,CreateSpecialFileInfo(&szs,true,"") + ,CreateSectionInfoLEX(szs.have_lex,true,"") + ,ct_attrib+1 + ,name_attrib_len,name_attrib + ); + + if ( long_count > 0 ) + { + const uint n = szs.special_file ? szs.special_file->used : 0; + PrintScriptVars(&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(&ps,0,"subfile_%u=\"%u %s %s\"\n", + i, ptr->num, hex, ptr->key ); + } + } + + PutScriptVars(&ps,2,0); + + if (!script_array) + { + PrintScriptFooter(&ps); + ResetFile(&fo,0); + } + + ResetLexInfo(&lexinfo); + } + + PrintScriptFooter(&ps); + ResetPrintScript(&ps); + ResetFile(&fo,0); + ResetSZS(&szs); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command distribution /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanDistribFile + ( DistributionInfo_t *dinf, ccp fname, int ignore, bool assume_arena ) +{ + DASSERT(dinf); + if ( ignore && IsDirectory(fname,true) ) + return ERR_OK; + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,fname,true,ignore>0,true); + + if ( err > ERR_WARNING || err == ERR_NOT_EXISTS ) + { + ResetSZS(&szs); + return err == ERR_NOT_EXISTS || ignore ? ERR_OK : err; + } + + if ( szs.fform_arch != FF_U8 && szs.fform_arch != FF_WU8 ) + { + ResetSZS(&szs); + if ( ignore < 2 ) + return ERROR0(ERR_INVALID_DATA,"Not a SZS ot WBZ file: %s\n",fname); + return ERR_OK; + } + + if ( opt_norm || need_norm > 0 ) + NormalizeExSZS(&szs,false,false,false); + + if ( verbose > 0 ) + printf("Read %s\n",fname); + + + //--- cut filename + + char fname_buf[1000]; + + ccp slash = strrchr(fname,'/'); + if (slash) + fname = slash+1; + + StringCopyS(fname_buf,sizeof(fname_buf),fname); + fname = fname_buf; + + char *src, *dest = strrchr(fname_buf,'.'); + if ( dest && strlen(dest) <= 4 ) + *dest = 0; + + src = dest = fname_buf; + while (*src) + { + if (!memcmp(src,"ː",2)) + { + src += 2; + *dest++ = ':'; + } + else if (!memcmp(src,"%3a",3)) + { + src += 3; + *dest++ = ':'; + } + else + *dest++ = *src++; + } + *dest = 0; + + + //--- create checksum + + char checksum[100]; + CreateSSChecksumBySZS(checksum,sizeof(checksum),&szs); + ResetSZS(&szs); + + + //--- save result + + uint slot = FindSlotByTranslation(&dinf->translate,fname,checksum); + if (slot) + { + uint num; + char *next = ScanSlot(&num,fname,false); + if ( num == slot && (uchar)*next <= ' ' ) + fname = next; + else + { + num = strtoul(fname,&next,10); + if ( num == slot && (uchar)*next <= ' ' ) + fname = next; + } + + while ( *fname == ' ' ) + fname++; + } + + if (assume_arena) + slot += DISTRIBUTION_ARENA_DELTA; + + PRINT(">>> slot=%d[%d,%d] %s\n", + slot, IsValidDistribArena(slot), IsValidDistribTrack(slot), fname ); + + if (IsValidDistribTrack(slot)) + AppendParamField(dinf->track+slot,fname,false,slot,STRDUP(checksum)); + else if (IsValidDistribArena(slot)) + AppendParamField(dinf->arena+slot-DISTRIBUTION_ARENA_DELTA, + fname,false,slot,STRDUP(checksum)); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetDistribParamByNameS ( DistributionInfo_t *dinf, ccp name, ccp def ) +{ + DASSERT(dinf); + DASSERT(name); + ParamFieldItem_t *it = FindParamField(&dinf->param,name); + if ( it && it->data ) + { + it->num = 1; + return (ccp)it->data; + } + return def; +} + +/////////////////////////////////////////////////////////////////////////////// +// for name changes + +#if 0 + +static ccp GetDistribParamByNameS2 + ( DistributionInfo_t *dinf, ccp name1, ccp name2, ccp def ) +{ + DASSERT(dinf); + DASSERT(name); + ParamFieldItem_t *it = FindParamField(&dinf->param,name1); + if (!it) + it = FindParamField(&dinf->param,name2); + if ( it && it->data ) + { + it->num = 1; + return (ccp)it->data; + } + return def; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +static int GetDistribParamByNameIMM + ( DistributionInfo_t *dinf, ccp name, int def, int min, int max ) +{ + DASSERT(dinf); + DASSERT(name); + ParamFieldItem_t *it = FindParamField(&dinf->param,name); + if (!it) + return def; + it->num = 1; + + char *end; + int val = str2l((ccp)it->data,&end,10); + return *end || val < min || val > max ? def : val; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_distribution() +{ + SetPatchFileModeReadonly(); + SetupCoding64( opt_coding64, opt_db64 ? ENCODE_BASE64URL : ENCODE_BASE64 ); + + DistributionInfo_t dinf; + InitializeDistributionInfo(&dinf,true); + + char slotname[20]; + + + //--- define result file + + if ( !first_param || !first_param->arg || !*first_param->arg ) + return ERROR0(ERR_MISSING_PARAM, + "Definition of result file missed (first parameter)!\n" ); + + ParamList_t *param; + char result[PATH_MAX], path[PATH_MAX]; + + if (IsDirectory(first_param->arg,false)) + { + PathCatPP(result,sizeof(result),first_param->arg,DEFAULT_DISTIBUTION_FNAME); + param = first_param; + } + else + { + char *dest = StringCopyS(result,sizeof(result)-4,first_param->arg); + if ( dest < result+4 || strcasecmp(dest-4,".txt") ) + strcpy(dest,".txt"); + param = first_param->next; + } + + + //--- setup slot translation + + int i; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + { + const TrackInfo_t *ti = arena_info+i; + DefineSlotTranslation(&dinf.translate,true,ti->def_slot,ti->track_fname); + DefineSlotTranslation(&dinf.translate,true,ti->def_slot,ti->name_en); + DefineSlotTranslation(&dinf.translate,true,ti->def_slot,ti->name_de); + } + + for ( i = 0; i < MKW_N_TRACKS; i++ ) + { + const TrackInfo_t *ti = track_info+i; + DefineSlotTranslation(&dinf.translate,false,ti->def_slot,ti->track_fname); + DefineSlotTranslation(&dinf.translate,false,ti->def_slot,ti->name_en); + DefineSlotTranslation(&dinf.translate,false,ti->def_slot,ti->name_de); + } + + bool result_found = false; + for ( i = 0; i < source_list.used; i++ ) + { + ccp arg = source_list.field[i]; + if ( !i && !strcmp(arg,"0") ) + ResetParamField(&dinf.translate); + else if ( !result_found && !strcmp(result,arg) ) + { + result_found = true; + ScanSlotTranslation(&dinf.translate,&dinf.param,arg,false); + } + else + ScanSlotTranslation(&dinf.translate,0,arg,false); + } + + if (!result_found) + ScanSlotTranslation(&dinf.translate,&dinf.param,result,true); + + AddParamDistributionInfo(&dinf,false); + + + //--- logging + + if ( logging > 0 ) + { + fputs("--------------- translation ---------------\n",stdlog); + + uint next_slot = 0; + while ( next_slot < INT_MAX ) + { + uint slot = next_slot; + next_slot = INT_MAX; + + uint i; + const ParamFieldItem_t *it = dinf.translate.field; + for ( i = 0; i < dinf.translate.used; i++, it++ ) + { + if ( it->num == slot ) + { + if ( slot < DISTRIBUTION_ARENA_DELTA ) + fprintf(stdlog,"%4u. %3u.%u %s\n", + i+1, it->num/10, it->num%10, it->key ); + else + { + snprintf( slotname, sizeof(slotname), "A%u.%u", + (it->num-DISTRIBUTION_ARENA_DELTA)/10, + it->num%10 ); + fprintf(stdlog,"%4u. %5s %s\n", i+1, slotname, it->key ); + } + } + else if ( it->num > slot && it->num < next_slot ) + next_slot = it->num; + } + } + + fputs("---------------- parameter ----------------\n",stdlog); + + uint i; + const ParamFieldItem_t *it = dinf.param.field; + for ( i = 0; i < dinf.param.used; i++, it++ ) + fprintf(stdlog,"%4u. %-20s = %s\n", + i+1, it->key, (ccp)it->data ); + + fputs("---------------------------------------------\n",stdlog); + } + + + //--- iterate files & directories + + if ( verbose == 0 ) + printf("Scan SZS files\n"); + + for ( ; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + bool assume_arena = false; + ccp ptr = param->arg; + for(;;) + { + ptr = strchr(ptr,'/'); + if (!ptr) + break; + ptr++; + if (!strncasecmp(ptr,"arena",5)) + { + assume_arena = true; + PRINT("ASSUME ARENA: %s\n",param->arg); + break; + } + } + + if (IsDirectory(param->arg,false)) + { + DIR * dir = opendir(param->arg); + if (dir) + { + for(;;) + { + struct dirent * dent = readdir(dir); + if (!dent) + break; + + if ( dent->d_name && *dent->d_name != '.' ) + { + PathCatPP(path,sizeof(path),param->arg,dent->d_name); + #if HAVE_WIIMM_EXT // [[2do]] by option + if ( !strstr(path,",clan]") && !strstr(path,",head=") ) + #endif + { + enumError err = ScanDistribFile(&dinf,path,2,assume_arena); + if (err) + return err; + } + } + } + closedir(dir); + } + } + else + { + enumError err = ScanDistribFile(&dinf,param->arg,opt_ignore,assume_arena); + if (err) + return err; + } + } + + + //--- print results + + if ( verbose >= 0 ) + printf("Create file %s\n",result); + + StringCat2S(path,sizeof(path),result,".bak"); + rename(result,path); + + FILE *f = fopen(result,"wb"); + if (!f) + ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",result); + + ccp uuid = GetDistribParamByNameS(&dinf,"UUID",""); + fprintf(f,text_distrib_head_cr + + ,GetDistribParamByNameS(&dinf,"COMMENT1","") + ,GetDistribParamByNameS(&dinf,"COMMENT2","") + ,GetDistribParamByNameS(&dinf,"COMMENT3","") + ,GetDistribParamByNameS(&dinf,"COMMENT4","") + ,GetDistribParamByNameS(&dinf,"COMMENT5","") + + ,GetDistribParamByNameS(&dinf,"USER-CT-WIIMM","") + ,GetDistribParamByNameS(&dinf,"USER-WIIMMFI","") + ,GetDistribParamByNameS(&dinf,"USER-CT-WIIKI","") + ,GetDistribParamByNameS(&dinf,"USER-MISC","") + ,GetDistribParamByNameS(&dinf,"MAIL","") + ,GetDistribParamByNameS(&dinf,"NOTE-FOR-WIMMM","") + + ,GetDistribParamByNameS(&dinf,"NAME","?") + ,GetDistribParamByNameS(&dinf,"VERSION","?") + ,GetDistribParamByNameS(&dinf,"AUTHORS","?") + ,GetDistribParamByNameS(&dinf,"RELEASE-DATE","") + ,GetDistribParamByNameS(&dinf,"KEYWORDS","") + ,GetDistribParamByNameS(&dinf,"PREDECESSOR","") + + ,GetDistribParamByNameS(&dinf,"WIIMMFI-REGION","") + ,GetDistribParamByNameS(&dinf,"INFO-TEXT","") + ,GetDistribParamByNameS(&dinf,"INFO-URL","") + + ,uuid + ,GetDistribParamByNameIMM(&dinf,"DISPLAY-MODE",3,0,3) + ,GetDistribParamByNameIMM(&dinf,"DATABASE-NAME",1,0,1) + ,GetDistribParamByNameIMM(&dinf,"VIEW-COMMENT",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-NEW",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-AGAIN",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-FILL",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-UPDATE",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-BOOST",0,0,1) + + ,uuid + ,VERSION + ,REVISION_NUM + ,GetDistribParamByNameS(&dinf,"FIRST-CREATION","") + ,GetDistribParamByNameS(&dinf,"LAST-UPDATE","") + ); + + + + //--- mark some more params as 'used' + + GetDistribParamByNameS(&dinf,"WSZST-VERSION",""); + GetDistribParamByNameS(&dinf,"WSZST-REVISION",""); + + + //--- additional parameters + + ccp info = text_distrib_param_cr; + + const ParamFieldItem_t *it = dinf.param.field; + for ( i = 0; i < dinf.param.used; i++, it++ ) + if (!it->num) + { + int len = strlen(it->key); + ccp sep; + if ( len > 14 ) + sep = " "; + else if ( len > 6 ) + sep = "\t"; + else + sep = "\t\t"; + + fprintf(f,"%s@%s%s= %s\r\n", info, it->key, sep, (ccp)it->data ); + info = ""; + } + + + //--- battle arenas + + fputs(text_distrib_tracks_cr,f); + + int slot, slot10 = -1, count = 0; + for ( slot = 0; slot < MAX_DISTRIBUTION_ARENA; slot++ ) + { + ParamField_t *pf = dinf.arena + slot; + if ( pf->used ) + { + count++; + if ( slot10 != slot/10 ) + { + slot10 = slot/10; + fputs("\r\n",f); + } + + if (slot) + snprintf(slotname,sizeof(slotname),"A%u.%u",slot10,slot%10); + else + StringCopyS(slotname,sizeof(slotname),"arena"); + + uint i; + ParamFieldItem_t *it = pf->field; + for ( i = 0; i < pf->used; i++, it++ ) + fprintf(f,"%s %5s %s\r\n", (ccp)it->data, slotname, it->key ); + } + } + if (count) + fputs("\r\n",f); + + + //--- racing tracks + + + slot10 = -1; + count = 0; + + for ( slot = 0; slot < MAX_DISTRIBUTION_TRACK; slot++ ) + { + ParamField_t *pf = dinf.track + slot; + if ( pf->used ) + { + count++; + if ( slot10 != slot/10 ) + { + slot10 = slot/10; + fputs("\r\n",f); + } + + char slotname[10]; + if (slot) + snprintf(slotname,sizeof(slotname),"%3u.%u",slot10,slot%10); + else + StringCopyS(slotname,sizeof(slotname)," ---"); + + uint i; + ParamFieldItem_t *it = pf->field; + for ( i = 0; i < pf->used; i++, it++ ) + fprintf(f,"%s %s %s\r\n", (ccp)it->data, slotname, it->key ); + } + } + if (count) + fputs("\r\n",f); + fclose(f); + + ResetDistributionInfo(&dinf); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command diff /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError diff_files ( ccp fname1, ccp fname2 ) +{ + if ( verbose > 0 ) + printf("DIFF %s : %s\n", fname1, fname2 ); + + szs_file_t szs1, szs2; + InitializeSZS(&szs1); + InitializeSZS(&szs2); + + enumError err = LoadCreateSZS(&szs1,fname1,true,false,true); + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + err = LoadCreateSZS(&szs2,fname2,true,false,true); + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + err = DiffSZS( &szs1, &szs2, + opt_recurse < 0 ? 0 : opt_recurse, + opt_cut ? 1 : -1, + verbose < 0 ); + + if ( verbose >= -1 && err == ERR_DIFFER ) + printf("Content differ: %s : %s\n", fname1, fname2 ); + else if ( verbose >= 0 && err == ERR_OK ) + printf("Content identical: %s : %s\n", fname1, fname2 ); + + ResetSZS(&szs1); + ResetSZS(&szs2); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError diff_dest() +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,"%F",0,false); + const enumError err = diff_files(param->arg,dest); + if ( max_err < err ) + max_err = err; + if ( err > ERR_WARNING || err && verbose < -1 ) + break; + } + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_diff() +{ + SetPatchFileModeReadonly(); + + if (opt_dest) + return diff_dest(); + + if ( n_param != 2 ) + return ERROR0(ERR_SYNTAX,"Exact 2 sources expected if --dest is not set.\n"); + + ASSERT(first_param); + ASSERT(first_param->next); + return diff_files(first_param->arg,first_param->next->arg); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command check /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_check() +{ + SetPatchFileModeReadonly(); + + patch_action_log_disabled++; + if ( KCL_MODE & KCLMD_DROP_AUTO ) + SetKclMode(KCL_MODE|KCLMD_DROP_UNUSED); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + + 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,"\n%sCHECK %s:%s%s\n", + col.heading, GetNameFF_SZS(&szs), szs.fname, col.reset ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + switch (szs.fform_arch) + { + case FF_KCL: + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + { + kcl_t kcl; + err = ScanKCL(&kcl,true,szs.data,szs.size,true,mode); + ResetKCL(&kcl); + } + break; + + case FF_KMP: + case FF_KMP_TXT: + { + kmp_t kmp; + err = ScanKMP(&kmp,true,szs.data,szs.size,mode); + ResetKMP(&kmp); + } + break; + + case FF_U8: + case FF_WU8: + if (CheckSZS(&szs,mode,global_check_mode)) + err = ERR_DIFFER; + break; + + case FF_BRRES: + if (CheckBRRES(&szs,mode,0)) + err = ERR_DIFFER; + break; + + default: + ERROR0(ERR_INVALID_FFORM, + "CHECK doesn't support file format '%s': %s\n", + GetNameFF_SZS(&szs), param->arg ); + break; + } + fflush(stdout); + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command slot /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_slots() +{ + SetPatchFileModeReadonly(); + + KCL_MODE |= KCLMD_SILENT; + if ( KCL_MODE & KCLMD_DROP_AUTO ) + SetKclMode(KCL_MODE|KCLMD_DROP_UNUSED); + + const bool have_col = IsFileColorized(stdout); + ccp col_minus = GetTextMode(have_col,TTM_BOLD|TTM_RED); + ccp col_plus = GetTextMode(have_col,TTM_BOLD|TTM_GREEN); + ccp col_misc = GetTextMode(have_col,TTM_BOLD|TTM_CYAN); + ccp col_reset = GetTextMode(have_col,TTM_RESET); + MARK_USED(col_minus,col_plus,col_misc); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if (err) + { + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + continue; + } + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + //PatchSZS(&szs); + + int slot[MKW_N_TRACKS]; + bool need_special; + const int stat = FindSlotsSZS(&szs,slot,&need_special); + + noPRINT("IS_ARENA=%d\n",szs.is_arena); + if ( szs.is_arena >= ARENA_FOUND ) + { + if (brief_count) + printf("%sARENA%s : %s\n",col_misc,col_reset,param->arg); + else + printf(" %s ARENA %s : %s\n",col_misc,col_reset,param->arg); + } + else + { + if (brief_count) + { + uint i, count = 0; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + if ( slot[i] && slot[i] != stat ) + { + ccp col = slot[i] < 0 ? col_minus : slot[i] > 0 ? col_plus : col_misc; + printf("%s%c%u%u", + col, + count ? ',' : stat < 0 ? '+' : '-', + i/4+1, i%4+1 ); + count++; + } + + while ( count++ < 3 ) + fputs(" ",stdout); + } + else + { + uint i, count = 0; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + if ( slot[i] != stat ) + { + ccp col = slot[i] < 0 ? col_minus : slot[i] > 0 ? col_plus : col_misc; + printf(" %s%c%u.%u", + col, + slot[i] < 0 ? '-' : slot[i] > 0 ? '+' : '?' , + i/4+1, i%4+1 ); + count++; + } + else if ( !need_special && ( i == 13 || i == 20 || i == 21 )) + { + fputs(" ",stdout); + count++; + } + + while ( count++ < 3 ) + fputs(" ",stdout); + } + printf("%s : %s\n",col_reset,param->arg); + } + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command isarena /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_isarena() +{ + SetPatchFileModeReadonly(); + + if ( brief_count || verbose < 0 ) + print_header = false; + + if (print_header) + printf("\n%sstatus filename%s\n%s%.237s%s\n", + colout->heading, colout->reset, + colout->heading, ThinLine300_3, colout->reset ); + + int count = 0; + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + + ccp status = 0, color = 0; + + if ( err == ERR_NOT_EXISTS ) + { + if (opt_ignore) + err = ERR_OK; + else + { + status = "!NO-FILE"; + color = colout->bad; + } + } + else if (err) + { + status = "!ERROR"; + color = colout->bad; + } + else + { + FindSpecialFilesSZS(&szs,false); + if (!szs.course_kmp_data) + { + status = "!NO-KMP"; + color = colout->bad; + } + else + { + kmp_t kmp; + InitializeKMP(&kmp); + err = ScanKMP(&kmp,false,szs.course_kmp_data,szs.course_kmp_size,0); + if (err) + { + status = "!KMP-ERR"; + color = colout->bad; + } + else + { + const IsArena_t is_arena = IsArenaKMP(&kmp); + switch (is_arena) + { + case ARENA_NONE: + err = ERR_DIFFER; + status = "-RACE"; + color = colout->b_orange; + break; + + case ARENA_MAYBE: + err = ERR_DIFFER; + status = "?MAYBE"; + color = colout->b_yellow; + break; + + case ARENA_FOUND: + status = "+ARENA"; + color = colout->b_green; + break; + + case ARENA_DISPATCH: + status = "+DISPATCH"; + color = colout->b_green; + break; + + case ARENA__N: + break; + } + + if (!status) + { + status = "!UNKNOWN"; + color = colout->bad; + } + } + ResetKMP(&kmp); + } + } + + if (status) + { + count++; + if ( verbose >= 0 ) + { + DASSERT(color); + if (brief_count) + printf("%s%s%s\n", color, status, colout->reset ); + else + printf("%s%-9s%s %s\n", color, status, colout->reset, param->arg ); + } + } + + ResetSZS(&szs); + if ( max_err < err ) + max_err = err; + } + + if (print_header) + putchar('\n'); + + return count ? max_err : ERR_NOTHING_TO_DO; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command patch /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool ScanByExtFF + ( file_format_t *ff_arch, file_format_t *ff_compr, ccp fname ) +{ + DASSERT(ff_arch); + DASSERT(ff_compr); + *ff_arch = FF_UNKNOWN; + *ff_compr = FF_UNKNOWN; + + ccp point = strrchr(fname,'.'); + if (!point) + return false; + + ++point; + if (!strcmp(point,"szs")) + { + *ff_arch = FF_U8; + *ff_compr = FF_YAZ0; + return true; + } + + if (!strcmp(point,"wbz")) + { + *ff_arch = FF_WU8; + *ff_compr = FF_BZ; + return true; + } + + if (!strcmp(point,"u8")) + { + *ff_arch = FF_U8; + return true; + } + + if (!strcmp(point,"wu8")) + { + *ff_arch = FF_WU8; + return true; + } + + if (!strcmp(point,"xyz")) + { + *ff_arch = FF_XYZ; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +// old [[container]] will be replaced by Container_t & ContainerData_t +#define PRINT_DATA PRINT("##### %s() #%u: cd=%p,%zu,%d / d=%p,%zu,%d / c=%p,%d #####\n", \ + __FUNCTION__, __LINE__, \ + szs.cdata, szs.csize, szs.cdata_alloced, \ + szs.data, szs.size, szs.data_alloced, \ + szs.old_container ? szs.old_container->data : 0, \ + szs.old_container ? szs.old_container->ref_count : -1 ) + +//----------------------------------------------------------------------------- + +static enumError cmd_patch() +{ + #if HAVE_PRINT + file_format_t dest_ff_arch; + file_format_t dest_ff_compr; + const bool dest_ff_valid = ScanByExtFF(&dest_ff_arch,&dest_ff_compr,opt_dest); + PRINT("fform(dest) = %s [valid=%d]\n", + GetNameFF(dest_ff_compr,dest_ff_arch), dest_ff_valid ); + #endif + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + if (!param->arg) + continue; + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,false,opt_ignore>0,true); + + //PRINT_DATA; + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + u8 * orig_data; + uint orig_size; + + const bool was_compressed = szs.cdata != 0; + if (was_compressed) + { + DecompressSZS(&szs,false,0); + DASSERT(szs.cdata); + DASSERT(szs.cdata_alloced); + orig_data = szs.cdata; + orig_size = szs.csize; + szs.cdata = 0; + szs.cdata_alloced = false; + } + else + { + DASSERT(szs.data); + orig_data = MEMDUP(szs.data,szs.size); + orig_size = szs.size; + } + + #ifdef TEST0 + const ccp src_format = GetNameFF_SZS(&szs); + file_format_t local_ff_compr = opt_fform; + file_format_t local_ff_arch = fform_compr; + #else + const ccp src_format = GetNameFF_SZS(&szs); + const bool convert_wu8 = opt_fform == FF_WU8 + || opt_fform == FF_UNKNOWN && szs.fform_arch == FF_WU8; + PRINT("CONVERT_WU8=%d, FF=%s\n", convert_wu8, src_format ); + #endif + + DecodeWU8(&szs); + + //szs.allow_ext_data = opt_norm; + szs.allow_ext_data = true; + + bool dirty = PatchSZS(&szs); + if ( ( opt_norm || need_norm || opt_auto_add || dirty ) + && NormalizeSZS(&szs) ) + { + dirty = true; + } + if ( OptionUsed[OPT_MINIMAP] && AutoAdjustMinimap(&szs) ) + dirty = true; + + if (convert_wu8) + { + err = EncodeWU8(&szs); + if (err) + goto abort; + } + + + //--- dest file + + char dest[PATH_MAX]; + if ( opt_fform == FF_U8 || opt_fform == FF_WU8 ) + { + SubstDest( dest, sizeof(dest), param->arg, + opt_dest && *opt_dest ? opt_dest : "\1P/\1N\1?T", "\1N\1?T", + GetExtFF(szs.fform_file,opt_fform), false ); + } + else if ( opt_dest && *opt_dest ) + { + SubstDest( dest, sizeof(dest), param->arg, opt_dest, 0, + GetExtFF(szs.fform_file,szs.fform_arch), false ); + } + else + StringCopyS(dest,sizeof(dest),param->arg); + + + //--- compression and SZS cache + + if (was_compressed) + { + CompressSZS(&szs,true); + + //PRINT_DATA; + if ( opt_norm && !dirty ) + dirty = orig_size != szs.csize + || memcmp(orig_data,szs.cdata,orig_size); + FREE(orig_data); + orig_data = szs.cdata; + orig_size = szs.csize; + } + else + { + //PRINT_DATA; + if (!dirty) + dirty = orig_size != szs.size + || memcmp(orig_data,szs.data,orig_size); + FREE(orig_data); + orig_data = szs.data; + orig_size = szs.size; + } + + const ccp dest_format = GetNameFF_SZScurrent(&szs); + const bool src_dest_diff + = strcmp(src_format,dest_format) || strcmp(dest,param->arg); + + if ( dirty || src_dest_diff ) + { + if ( verbose >= 0 || testmode ) + { + if (src_dest_diff) + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + testmode ? "WOULD " : "", + opt_norm ? "NORMALIZE" : "PATCH", + szs.aiparam_removed ? " [-AIParam]" : "", + src_format, param->arg, dest_format, dest ); + else + fprintf(stdlog,"%s%s%s %s:%s\n", + testmode ? "WOULD " : "", + opt_norm ? "NORMALIZE" : "PATCH", + szs.aiparam_removed ? " [-AIParam]" : "", + dest_format, dest ); + fflush(stdlog); + } + + //PRINT_DATA; + File_t F; + err = CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite(orig_data,1,orig_size,F.f); + if ( wstat != orig_size ) + err = FILEERROR1(&F, + ERR_WRITE_FAILED, + "Writing %u bytes failed [%zu bytes written]: %s\n", + orig_size, wstat, dest ); + } + ResetFile(&F,opt_preserve); + if ( !err && opt_remove_src ) + RemoveSource(param->arg,dest,verbose>=0,testmode); + } + else if ( verbose > 0 ) + { + fprintf(stdlog,"ALREADY NORMALIZED: %s:%s\n", src_format, param->arg ); + fflush(stdlog); + } + + LinkCacheRAW(szs.cache_fname,dest,orig_data,orig_size); + + } + abort: + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command normalize /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_normalize() +{ + RegisterOptionByIndex(&InfoUI_wszst,OPT_NORM,1,false); + opt_norm = true; + return cmd_patch(); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command copy /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_copy() +{ + RegisterOptionByIndex(&InfoUI_wszst,OPT_OVERWRITE,1,false); + opt_overwrite = true; + + 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; + } + + return cmd_patch(); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command duplicate /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_duplicate() +{ + RegisterOptionByIndex(&InfoUI_wszst,OPT_OVERWRITE,1,false); + opt_overwrite = true; + + + //--- check parameters + + if (!first_param) + return ERROR0(ERR_MISSING_PARAM, "Missing source file!\n" ); + + if (!opt_dest) + { + if (!first_param->next) + return ERROR0(ERR_MISSING_PARAM, "Missing destination parameter!\n" ); + + SetDest(first_param->next->arg,false); + n_param--; + } + + if ( n_param != 1 ) + return ERROR0(ERR_SEMANTIC,"Exact 1 source file expected!\n" ); + + NORMALIZE_FILENAME_PARAM(first_param); + ccp suffix, dest1 = opt_dest, dest2; + { + ccp slash = strrchr(dest1,'/'); + if (!slash) + slash = dest1; + + char *pos = strchr(slash,'@'); + if (pos) + { + *pos = 0; + dest2 = pos+1; + while ( *dest2 == ' ' ) + dest2++; + suffix = !*dest2 || *dest2 == '.' ? "" : " "; + while ( pos > dest1 && pos[-1] == ' ' ) + *--pos = 0; + } + else + { + pos = strrchr(slash,'.'); + if (pos) + { + *pos = 0; + dest2 = pos+1; + suffix = "."; + } + else + dest2 = suffix = EmptyString; + } + } + + + //--- open source + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,first_param->arg,false,opt_ignore>0,true); + if (err) + return err; + + const bool was_compressed = szs.cdata != 0; + if (was_compressed) + DecompressSZS(&szs,false,0); + + const ccp fform = GetNameFF_SZS(&szs); + if ( verbose >= 0 ) + { + fprintf(stdlog,"READ %s:%s\n",fform,first_param->arg); + fflush(stdlog); + } + + if ( szs.fform_current != FF_U8 && szs.fform_current != FF_WU8 ) + return ERROR0(ERR_INVALID_DATA,"A track file (SZS or WBZ) expected!\n"); + + force_lex_test = true; // force patch to insert LEX/TEST + PatchSZS(&szs); + + + //--- find KMP + + FindSpecialFilesSZS(&szs,false); + if (!szs.course_kmp_data) + return ERROR0(ERR_INVALID_DATA,"KMP not found → abort: %s\n",first_param->arg); + + kmp_t kmp; + err = ScanKMP(&kmp,true,szs.course_kmp_data,szs.course_kmp_size,0); + + + //--- find LEX/TEST + + if (!szs.course_lex_data) + return ERROR0(ERR_INTERNAL,0); + + lex_element_t *lex_test = FindLexElement( (lex_header_t*)szs.course_lex_data, + szs.course_lex_size, LEXS_TEST ); + if (!lex_test) + return ERROR0(ERR_INTERNAL,0); + + kmp_ana_pflag_t ap; + AnalysePFlagScenarios(&ap,&kmp,opt_gamemodes); + + char buf[10]; + const int fw = snprintf(buf,sizeof(buf),"%u",ap.n_res); + + uint ri; + const kmp_ana_pflag_res_t *res; + for ( ri = 0, res = ap.res_list; ri < ap.n_res; ri++, res++ ) + { + memcpy(lex_test->data,&res->lex_test,sizeof(res->lex_test)); + char fname[PATH_MAX]; + snprintf(fname,sizeof(fname),"%s {%02u,%s}%s%s", + dest1, res->version+1, res->name, suffix, dest2 ); + if ( verbose >= 0 || testmode > 0 ) + { + fprintf(stdlog,"%sCREATE %*u/%u %s:%s\n", + testmode > 0 ? "WOULD " : "", fw, ri+1, ap.n_res, fform, fname ); + fflush(stdlog); + } + + if (was_compressed) + { + ClearCompressedSZS(&szs); + CompressSZS(&szs,false); + } + + SaveSZS(&szs,fname,true,was_compressed); + } + + ResetKMP(&kmp); + ResetSZS(&szs); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command minimap /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_mmap ( ccp title, float3 *a, float3 *b ) +{ + DASSERT(title); + DASSERT(a); + DASSERT(b); + + printf("%-13s %9.1f %9.1f %9.1f %20.1f %9.1f %9.1f\n", + title, a->x, a->y, a->z, b->x, b->y, b->z ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_matrix ( ccp title, float *a, float *b ) +{ + DASSERT(title); + DASSERT(a); + DASSERT(b); + + uint i; + for ( i = 0; i < 3; i++ ) + { + printf("%s-Matrix-%c: %9.1f %9.1f %9.1f %9.1f %9.1f %9.1f %9.1f %9.1f\n", + title, 'X'+i, + a[0], a[1], a[2], a[3], + b[0], b[1], b[2], b[3] ); + a += 4; + b += 4; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_data +( + mdl_minimap_t *mmap, // valid minimap record + mdl_sect1_t *root, // NULL or valid 'root' data + mdl_sect1_t *ld, // valid 'posLD' data + mdl_sect1_t *ru, // valid 'posRU' data + uint mode, // general print mode + uint modified // 0:none, 1:translate, 2:all, 3:all+minimum +) +{ + if ( modified != 1 ) + { + printf("Idx+Id+Flags: %9u %9u %#9x %21u %9u %#9x\n", + ld->index, ld->id, ld->flags, + ru->index, ru->id, ru->flags ); + print_mmap( "Scale:", &ld->scale, &ru->scale ); + print_mmap( "Rotation", &ld->rotate, &ru->rotate ); + } + + print_mmap( "Translation:", &ld->translate, &ru->translate ); + if ( mmap->rec_trans_valid && ( !modified || mode > 0 ) ) + print_mmap(" Recommend.:",&mmap->rec_trans_ld,&mmap->rec_trans_ru); + + if ( mode > 1 && !modified ) + { + print_mmap( "Minimum:", &ld->minimum, &ru->minimum ); + print_mmap( "Maximum:", &ld->maximum, &ru->maximum ); + } + + if ( mode > 0 ) + { + putchar('\n'); + print_matrix("Tra",ld->trans_matrix.v,ru->trans_matrix.v); + putchar('\n'); + print_matrix("Inv",ld->inv_matrix.v,ru->inv_matrix.v); + } + printf("%.95s\n",Minus300); + + if ( mode > 2 && !modified || modified > 2 ) + { + printf("Vertex-Min: %11.1f %9.1f %9.1f\n", + mmap->min.x, mmap->min.y, mmap->min.z ); + printf("Vertex-Max: %11.1f %9.1f %9.1f\n", + mmap->max.x, mmap->max.y, mmap->max.z ); + if (mmap->root) + { + printf("Root-Min: %11.1f %9.1f %9.1f\n", + root->minimum.x, root->minimum.y, root->minimum.z ); + printf("Root-Max: %11.1f %9.1f %9.1f\n", + root->maximum.x, root->maximum.y, root->maximum.z ); + } + printf("%.95s\n",Minus300); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void center_mmap ( uint idx, float3 *a, float3 *b ) +{ + DASSERT(idx<3); + DASSERT(a); + DASSERT(b); + + const float new_val = ( a->v[idx] - b->v[idx] ) / 2; + a->v[idx] = +new_val; + b->v[idx] = -new_val; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_minimap() +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,false,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + if ( verbose >= 0 ) + fprintf(stdlog,"\nMinimap data of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + + const bool was_compressed = szs.cdata != 0; + if (was_compressed) + DecompressSZS(&szs,true,0); + + bool dirty = false; + mdl_minimap_t mmap; + if (FindMinimapData(&mmap,&szs)) + { + DASSERT(mmap.posLD); + DASSERT(mmap.posRU); + PRINT("MMAP FOUND!\n"); + + mdl_sect1_t root, ld, ru; + ntoh_MDLs1(&root,mmap.root); + ntoh_MDLs1(&ld,mmap.posLD); + ntoh_MDLs1(&ru,mmap.posRU); + + if ( verbose >= 0 ) + { + printf("\n%53s%42s\n%.95s\n", + "______________left-down_______________", + "_______________right-up_______________", + Minus300); + print_data(&mmap,&root,&ld,&ru,long_count,0); + } + + if ( transform_active + || have_set_scale + || have_set_rot + || have_set_value + || OptionUsed[OPT_TOUCH] + || OptionUsed[OPT_AUTO] + || OptionUsed[OPT_XCENTER] + || OptionUsed[OPT_YCENTER] + || OptionUsed[OPT_ZCENTER] + || OptionUsed[OPT_CENTER] + || set_flags != M1(set_flags) + ) + { + uint modified = 1; + if ( OptionUsed[OPT_AUTO] && mmap.rec_trans_valid ) + { + modified = 2; + ClearMDLs1(&ld,false); + ld.translate = mmap.rec_trans_ld; + ClearMDLs1(&ru,false); + ru.translate = mmap.rec_trans_ru; + } + + if ( set_flags != M1(set_flags) ) + { + modified = 2; + ld.flags = ru.flags = set_flags; + } + + if (have_set_scale) + { + modified = 2; + ld.scale.x = ru.scale.x = set_scale.x; + ld.scale.y = ru.scale.y = set_scale.y; + ld.scale.z = ru.scale.z = set_scale.z; + } + + if (have_set_rot) + { + modified = 2; + ld.rotate.x = ru.rotate.x = set_rot.x; + ld.rotate.y = ru.rotate.y = set_rot.y; + ld.rotate.z = ru.rotate.z = set_rot.z; + } + + if ( have_set_value & 1 ) + { + ld.translate.x = set_value_min.x; + ru.translate.x = set_value_max.x; + } + if ( have_set_value & 2 ) + { + ld.translate.y = set_value_min.y; + ru.translate.y = set_value_max.y; + } + if ( have_set_value & 4 ) + { + ld.translate.z = set_value_max.z; + ru.translate.z = set_value_min.z; + } + + if ( OptionUsed[OPT_XCENTER] || OptionUsed[OPT_CENTER] ) + center_mmap(0,&ld.translate,&ru.translate); + if ( OptionUsed[OPT_YCENTER] || OptionUsed[OPT_CENTER] ) + center_mmap(1,&ld.translate,&ru.translate); + if ( OptionUsed[OPT_ZCENTER] || OptionUsed[OPT_CENTER] ) + center_mmap(2,&ld.translate,&ru.translate); + + TransformPosFloat3D(ld.translate.v,1,0); + TransformPosFloat3D(ru.translate.v,1,0); + CalcMatrixMDLs1(&ld); + CalcMatrixMDLs1(&ru); + + hton_MDLs1(&ld,&ld); + hton_MDLs1(&ru,&ru); + dirty = memcmp(mmap.posLD,&ld,sizeof(ld)) + || memcmp(mmap.posRU,&ru,sizeof(ru)); + +#if 1 + if (mmap.root) + { + root.minimum = mmap.min; + root.maximum = mmap.max; + + #if 0 // workaround fails + // workaround for a bug in KMP modifier + if ( root.minimum.x > root.minimum.z ) + root.minimum.x = root.minimum.z; + if ( root.maximum.x < root.maximum.z ) + root.maximum.x = root.maximum.z; + #endif + + hton_MDLs1(&root,&root); + if (memcmp(mmap.root,&root,sizeof(root))) + { + memcpy(mmap.root,&root,sizeof(root)); + modified = 3; + dirty = true; + } + ntoh_MDLs1(&root,&root); + } +#else + if ( mmap.root + && ( memcmp(&root.minimum,&mmap.min,sizeof(root.minimum)) + || memcmp(&root.maximum,&mmap.max,sizeof(root.maximum)) )) + { + root.minimum = mmap.min; + root.maximum = mmap.max; + + hton_MDLs1(mmap.root,&root); + modified = 3; + dirty = true; + } +#endif + + if (dirty) + { + memcpy(mmap.posLD,&ld,sizeof(ld)); + memcpy(mmap.posRU,&ru,sizeof(ru)); + ntoh_MDLs1(&ld,&ld); + ntoh_MDLs1(&ru,&ru); + if ( verbose >= 0 ) + print_data(&mmap,&root,&ld,&ru,long_count,modified); + } + } + } + + if (dirty) + { + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sPATCH MINIMAP %s:%s\n", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), param->arg ); + fflush(stdlog); + } + + if (!testmode) + SaveSZS(&szs,szs.fname,true,was_compressed); + } + } + fflush(stdlog); + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command compress /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_compress() +{ + static const char dest_fname[] = "\1P/\1N\1?T"; + CheckOptDest(dest_fname,false); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,true,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + file_format_t ff_dest = szs.fform_arch; + if ( ff_dest == FF_U8 && opt_fform == FF_WU8 + || ff_dest == FF_WU8 && opt_fform == FF_U8 ) + { + ff_dest = opt_fform; + } + PRINT("FF = %s , %s => %s\n", + GetNameFF_SZS(&szs), + GetNameFF_SZScurrent(&szs), + GetNameFF(0,ff_dest) ); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,dest_fname, + GetExtFF(fform_compr,ff_dest),false); + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sCOMPRESS %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), param->arg, + GetNameFF(fform_compr,ff_dest), dest ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + PatchSZS(&szs); + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( ff_dest == FF_WU8 ) + err = EncodeWU8(&szs); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + szs.dest_fname = dest; + CompressSZS(&szs,true); + szs.dest_fname = 0; + + if ( szs.cache_used && (int)max_err < ERR_CACHE_USED ) + max_err = ERR_CACHE_USED; + + File_t F; + err = CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite(szs.cdata,1,szs.csize,F.f); + if ( wstat != szs.csize ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + szs.csize, dest); + } + ResetFile(&F,opt_preserve); + LinkCacheRAW(szs.cache_fname,dest,szs.cdata,szs.csize); + + if ( !err && opt_remove_src ) + RemoveSource(param->arg,dest,verbose>=0,testmode); + } + } + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command decompress /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_decompress() +{ + static const char dest_fname[] = "\1P/\1N\1?T"; + CheckOptDest(dest_fname,false); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,true,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + file_format_t ff_dest = szs.fform_arch; + if ( ff_dest == FF_U8 && opt_fform == FF_WU8 + || ff_dest == FF_WU8 && opt_fform == FF_U8 ) + { + ff_dest = opt_fform; + } + PRINT("FF = %s , %s => %s\n", + GetNameFF_SZS(&szs), + GetNameFF_SZScurrent(&szs), + GetNameFF(0,ff_dest) ); + + char dest[PATH_MAX]; + ccp ext = GetExtFF(ff_dest,0); + SubstDest(dest,sizeof(dest),param->arg,opt_dest,ext,ext,false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sDECOMPRESS %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), param->arg, + GetNameFF(0,ff_dest), dest ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + PatchSZS(&szs); + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( ff_dest == FF_WU8 ) + err = EncodeWU8(&szs); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + File_t F; + err = CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite(szs.data,1,szs.size,F.f); + if ( wstat != szs.size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + szs.size, dest); + } + ResetFile(&F,opt_preserve); + if ( !err && opt_remove_src ) + RemoveSource(param->arg,dest,verbose>=0,testmode); + } + } + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command create /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_create ( bool create ) +{ + static const char dest_fname[] = "\1P/\1N\1?T"; + CheckOptDest(dest_fname,false); + const bool save_auto_add = opt_auto_add; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + opt_auto_add = save_auto_add; // restore auto-add settings + + int src_len = strlen(param->arg); + while ( src_len > 0 && param->arg[src_len-1] == '/' ) + src_len--; + param->arg[src_len] = 0; + + SetupParam_t sp; + InitializeSetupParam(&sp); + ScanSetupParam(&sp,true,param->arg,SZS_SETUP_FILE,0,true); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,dest_fname, + GetExtFF(sp.fform_file,sp.fform_arch),false); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = CreateSZS(&szs,dest,param->arg,0,&sp,0, + verbose > 0 ? UINT_MAX : 0, false ); + + //--- create file + + if ( verbose >= 0 || testmode ) + { + if (create) + fprintf(stdlog,"%s%sCREATE %s/ -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + param->arg, + GetNameFF_SZS(&szs), + dest ); + else + fprintf(stdlog,"%s%sENCODE %s/\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + param->arg ); + fflush(stdlog); + } + + if ( create && err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + File_t F; + CreateFileOpt(&F,true,dest,testmode,0); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + const u8 * data = szs.cdata ? szs.cdata : szs.data; + const size_t size = szs.cdata ? szs.csize : szs.size; + const size_t wstat = fwrite(data,1,size,F.f); + if ( wstat != size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + size, dest); + } + ResetFile(&F,opt_preserve); + LinkCacheSZS(&szs,dest); + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + ResetSetupParam(&sp); + } + opt_auto_add = save_auto_add; // restore auto-add settings + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command update /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct update_param_t +{ + ccp source; // source path + bool update_sub; // true: update sub archives + bool encode; // true: encode files if possible + int recurse_level; // current recurse level + int indent; // indention of log messages + +} update_param_t; + +//----------------------------------------------------------------------------- + +static enumError job_update +( + szs_file_t *szs, // valid szs file + int recurse_level // recursion level +); + +/////////////////////////////////////////////////////////////////////////////// + +static int update_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->param); + szs_file_t * szs = it->szs; + DASSERT(szs); + update_param_t * up = it->param; + DASSERT(up); + DASSERT(up->source); + + + //--- ignore directories + + if (it->is_dir) + return 0; + + + //--- setup path + + ccp local_path = it->path; + if ( *local_path == '.' && local_path[1] == '/' ) + local_path += 2; + + char path_buf[PATH_MAX]; + ccp path = PathCatPP(path_buf,sizeof(path_buf),up->source,local_path); + + u8 * data = szs->data + it->off; +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,it->size,it->size); + + + //--- [2do] encode files first + + + //--- read updated file + + struct stat st; + if (!stat(path,&st)) + { + if (!S_ISREG(st.st_mode)) + { + if (!opt_ignore) + ERROR0(ERR_WARNING,"No regular file: %s\n",path); + } + else if ( st.st_size != it->size ) + { + if (!opt_ignore) + ERROR0(ERR_WARNING, + "File size mismatch: have %llu, need %u: %s\n", + (u64)st.st_size ,it->size, path ); + } + else + { + if ( verbose > 1 ) + fprintf(stdlog,"%*s- LOAD %s\n", up->indent,"", path ); + LoadFILE(path,0,0,data,it->size,0,0,false); + } + } + + + //--- update sub archives first + + if ( up->update_sub && IsArchiveFF(fform) ) + { + szs_file_t subszs; // [[2do]] [[subfile]] + InitializeSZS(&subszs); + subszs.data = data ; + subszs.size = it->size; + subszs.fname = path; + subszs.fform_file = fform; + subszs.fform_arch = fform; + job_update(&subszs,up->recurse_level+1); + subszs.fname = EmptyString; + ResetSZS(&subszs); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError job_update +( + szs_file_t *szs, // valid szs file + int recurse_level // recursion level +) +{ + DASSERT(szs); + MEM_CHECK; + PRINT("JOB UPDATE: %d/%d %s\n",recurse_level,opt_recurse,szs->fname); + + char source[PATH_MAX]; + StringCat2S(source,sizeof(source),szs->fname,".d"); + if ( !recurse_level && ( opt_source || !IsDirectory(source,false) ) ) + { + ccp my_source = opt_source; + if (!my_source) + { + switch (szs->fform_arch) + { + case FF_U8: + case FF_WU8: + case FF_RARC: + case FF_PACK: + case FF_RKC: + //case FF_BRRES: + my_source = "\1P/\1N.d/"; + break; + + default: + my_source = "\1P/\1F.d/"; + break; + } + } + SubstDest(source,sizeof(source),szs->fname,my_source,0,0,true); + } + + const int indent = 2 * recurse_level; + if ( verbose >= 0 && !recurse_level || verbose > 0 || testmode ) + fprintf(stdlog,"%s%*s- UPDATE %s:%s <- %s\n", + verbose > 1 ? "\n" : "", + indent,"", + GetNameFF_SZS(szs), szs->fname, source ); + + + update_param_t up; + memset(&up,0,sizeof(up)); + up.source = source; + up.encode = !opt_no_encode; + up.recurse_level = recurse_level; + up.update_sub = recurse_level < opt_recurse; + up.indent = indent + 2; + + IterateFilesSZS(szs,update_func,&up,false,0,-1,SORT_NONE); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_update() +{ + if ( opt_source && !*opt_source ) + opt_source = 0; + if ( opt_dest && !*opt_dest ) + opt_dest = 0; + opt_mkdir = true; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,false); + if (!err) + err = job_update(&szs,0); + if ( max_err < err ) + max_err = err; + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,0,0,false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sCREATE %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), + dest ); + fflush(stdlog); + } + + File_t F; + CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + if (IsCompressedFF(szs.fform_file)) + CompressSZS(&szs,true); + SetFileAttrib(&F.fatt,&szs.fatt,0); + const u8 * data = szs.cdata ? szs.cdata : szs.data; + const size_t size = szs.cdata ? szs.csize : szs.size; + const size_t wstat = fwrite(data,1,size,F.f); + if ( wstat != size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + size, dest); + } + ResetFile(&F,opt_preserve); + } + + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command extract /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_extract ( enumCommands mode ) +{ + ccp basedir = GetOptBasedir(); + if ( mode == CMD_XDECODE ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_DECODE,1,false); + opt_decode = true; + } + else if ( mode == CMD_XEXPORT ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_DECODE,1,false); + opt_decode = true; + RegisterOptionByIndex(&InfoUI_wszst,OPT_EXPORT,1,false); + export_count++; + } + else if ( mode == CMD_XALL ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_ALL,1,false); + opt_recurse = INT_MAX; + opt_decode = true; + opt_mipmaps = 1; + } + else if ( mode == CMD_XCOMMON ) + { + opt_recurse = INT_MAX; + opt_decode = false; + basedir = "common/"; + } + + if ( opt_dest && !*opt_dest ) + opt_dest = 0; + opt_mkdir = true; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,false); + + if (!err) + { + DASSERT( !szs.file_size || szs.file_size >= szs.size ); + + if ( analyze_fname && IsBRSUB(szs.fform_arch) ) + AnalyzeBRSUB(&szs,szs.data,szs.size,param->arg); + + have_patch_count -= 1000000; + PRINT("EXTRACT/%s[%s]: %s\n",__FUNCTION__,GetNameFF_SZS(&szs),szs.fname); + err = ExtractFilesSZS(&szs,0,false,0,basedir); + have_patch_count += 1000000; + } + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// convert helper /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool ConvertHelper +( + const u8 * data, // valid data + uint data_size, // size of 'data' + ContainerData_t * cdata, // NULL or container data + ccp src_fname, // filename of source + ccp dest_fname, // filename of destination + bool binary_dest, // output: false:text, true:binary + enumError * err // not NULL: store error code here +) +{ + enumError temp_err; + if (!err) + err = &temp_err; + *err = ERR_OK; + +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch(fform) + { + case FF_BMG: + case FF_BMG_TXT: + FreeContainerData(cdata); + { + bmg_t bmg; + *err = ScanBMG(&bmg,true,src_fname,data,data_size); + if (*err) + return true; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawXBMG(&bmg,dest_fname,true) + : SaveTextXBMG(&bmg,dest_fname,true); + ResetBMG(&bmg); + } + return true; + + case FF_GH_ITEM: + case FF_GH_IOBJ: + case FF_GH_KART: + case FF_GH_KOBJ: + case FF_GH_ITEM_TXT: + case FF_GH_IOBJ_TXT: + case FF_GH_KART_TXT: + case FF_GH_KOBJ_TXT: + FreeContainerData(cdata); + { + geohit_t geohit; + InitializeGEOHIT(&geohit,fform); + geohit.fname = src_fname; + *err = ScanGEOHIT(&geohit,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawGEOHIT(&geohit,dest_fname,true) + : SaveTextGEOHIT(&geohit,dest_fname,true,minimize_level); + geohit.fname = 0; + ResetGEOHIT(&geohit); + } + return true; + + case FF_KCL: + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + FreeContainerData(cdata); + { + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fname = src_fname; + *err = ScanKCL(&kcl,false,data,data_size,global_check_mode,true); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawKCL(&kcl,dest_fname,true) + : SaveTextKCL(&kcl,dest_fname,true); + kcl.fname = 0; + ResetKCL(&kcl); + } + return true; + + case FF_ITEMSLT: + case FF_ITEMSLT_TXT: + FreeContainerData(cdata); + { + itemslot_t itemslot; + InitializeITEMSLOT(&itemslot,fform); + itemslot.fname = src_fname; + *err = ScanITEMSLOT(&itemslot,false,data,data_size,src_fname); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawITEMSLOT(&itemslot,dest_fname,true) + : SaveTextITEMSLOT(&itemslot,dest_fname,true,minimize_level); + itemslot.fname = 0; + ResetITEMSLOT(&itemslot); + } + return true; + + case FF_KMG: + case FF_KMG_TXT: + FreeContainerData(cdata); + { + minigame_t minigame; + InitializeMINIGAME(&minigame,fform); + minigame.fname = src_fname; + *err = ScanMINIGAME(&minigame,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawMINIGAME(&minigame,dest_fname,true) + : SaveTextMINIGAME(&minigame,dest_fname,true,minimize_level); + minigame.fname = 0; + ResetMINIGAME(&minigame); + } + return true; + + case FF_KMP: + case FF_KMP_TXT: + FreeContainerData(cdata); + { + kmp_t kmp; + InitializeKMP(&kmp); + kmp.fname = src_fname; + *err = ScanKMP(&kmp,false,data,data_size,global_check_mode); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawKMP(&kmp,dest_fname,true) + : SaveTextKMP(&kmp,dest_fname,true); + kmp.fname = 0; + ResetKMP(&kmp); + } + return true; + + case FF_LEX: + case FF_LEX_TXT: + FreeContainerData(cdata); + { + lex_t lex; + InitializeLEX(&lex); + lex.fname = src_fname; + *err = ScanLEX(&lex,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawLEX(&lex,dest_fname,true) + : SaveTextLEX(&lex,dest_fname,true); + lex.fname = 0; + ResetLEX(&lex); + } + return true; + + case FF_MDL: + case FF_MDL_TXT: // [[2do]] add format to UI commands BINARY and TEXT + { + mdl_t mdl; + InitializeMDL(&mdl); + mdl.fname = src_fname; + #if USE_NEW_CONTAINER_MDL + *err = ScanMDL(&mdl,false,data,data_size,cdata,global_check_mode); + #else + *err = ScanMDL(&mdl,false,data,data_size,0,global_check_mode); + #endif + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawMDL(&mdl,dest_fname,true) + : SaveTextMDL(&mdl,dest_fname,true); + mdl.fname = 0; + ResetMDL(&mdl); + } + return true; + + case FF_OBJFLOW: + case FF_OBJFLOW_TXT: + FreeContainerData(cdata); + { + objflow_t objflow; + InitializeOBJFLOW(&objflow); + objflow.fname = src_fname; + *err = ScanOBJFLOW(&objflow,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawOBJFLOW(&objflow,dest_fname,true) + : SaveTextOBJFLOW(&objflow,dest_fname,true,minimize_level); + objflow.fname = 0; + ResetOBJFLOW(&objflow); + } + return true; + + case FF_PAT: + case FF_PAT_TXT: + { + pat_t pat; + InitializePAT(&pat); + pat.fname = src_fname; + #if USE_NEW_CONTAINER_PAT + *err = ScanPAT(&pat,false,data,data_size,cdata,global_check_mode); + #else + *err = ScanPAT(&pat,false,data,data_size,0,global_check_mode); + #endif + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawPAT(&pat,dest_fname,true) + : SaveTextPAT(&pat,dest_fname,true); + pat.fname = 0; + ResetPAT(&pat); + } + return true; + + default: + break; + } + + *err = ERR_WRONG_FILE_TYPE; + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command binary/text /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_convert ( bool binary ) // cmd_binary() cmd_text() +{ + static ccp def_path = "\1P/\1N\1?T"; + CheckOptDest(def_path,false); + char dest[PATH_MAX]; + enumError max_err = ERR_OK; + + 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,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + { + ResetRawData(&raw); + return err; + } + + file_format_t ff_dest = binary + ? GetBinFF(raw.fform) : GetTextFF(raw.fform); + if (!ff_dest) + { + ERROR0(ERR_WARNING,"File format %s not supported: %s\n", + GetNameFF(raw.fform,0), param->arg ); + if ( max_err < ERR_WARNING ) + max_err = ERR_WARNING; + continue; + } + + SubstDest( dest, sizeof(dest), param->arg, opt_dest, + def_path, GetExtFF(0,ff_dest), false ); + + if ( verbose >= 0 ) + { + fprintf(stdlog,"%sCREATE/%s %s:%s => %s:%s\n", + verbose > 0 ? "\n" : "", + binary ? "BINARY" : "TEXT", + GetNameFF(raw.fform,0), param->arg, + GetNameFF(ff_dest,0), dest ); + fflush(stdlog); + } + + SetupContainerRawData(&raw); + ConvertHelper( raw.data, raw.data_size, + LinkContainerData(&raw.container), + param->arg, dest, binary, &err ); + if (err) + { + ERROR0(err,0); + if ( max_err < err ) + max_err = err; + } + } + + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat() +{ + stdlog = stderr; + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_extract_t eszs; + enumError err = ExtractSZS(&eszs,true,param->arg,0,opt_ignore>0); + PRINT("stat=%u, data=%p, found=%d,%d, path=%s\n", + err, eszs.data, !eszs.subfile_found, eszs.szs_found, eszs.subpath ); + + if (eszs.data) + { + if ( verbose > 0 || testmode ) + { + fprintf(stdlog,"%s%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(eszs.fform_file,eszs.fform_arch), + eszs.fname ); + fflush(stdlog); + } + + if (!testmode) + { + if (opt_decode) + { + ConvertHelper( eszs.data, eszs.data_size, + LinkContainerESZS(&eszs), + eszs.fname, "-", false, &err ); + } + else + { + size_t wstat = fwrite(eszs.data,1,eszs.data_size,stdout); + if ( wstat != eszs.data_size ) + err = ERROR1(ERR_WRITE_FAILED,"Write to stdout failed\n"); + } + } + } + else if ( !err && !opt_ignore ) + err = PrintErrorExtractSZS(&eszs,param->arg); + fflush(stdout); + + if ( max_err < err ) + max_err = err; + ResetExtractSZS(&eszs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command <FFORM> /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_FFORM ( file_format_t fform, ccp filename ) +{ + stdlog = stderr; + char pathbuf[PATH_MAX]; + enable_kcl_drop_auto++; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),param->arg,filename); + + szs_extract_t eszs; + enumError err = ExtractSZS(&eszs,true,path,0,opt_ignore>0); + PRINT("stat=%u, data=%p, found=%d,%d, path=%s\n", + err, eszs.data, !eszs.subfile_found, eszs.szs_found, eszs.subpath ); + +// [[analyse-magic]] + if ( eszs.data && GetByMagicFF(eszs.data,eszs.data_size,eszs.data_size) == fform ) + { + if ( verbose > 0 || testmode ) + fprintf(stdlog,"%s%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(eszs.fform_file,eszs.fform_arch), + eszs.fname ); + + if (!testmode) + ConvertHelper( eszs.data, eszs.data_size, + LinkContainerESZS(&eszs), + eszs.fname, "-", false, &err ); + } + else if ( !err && !opt_ignore ) + err = PrintErrorExtractSZS(&eszs,param->arg); + + if ( max_err < err ) + max_err = err; + ResetExtractSZS(&eszs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_info() +{ + stdlog = stderr; + + char pathbuf[PATH_MAX]; + ccp search_tab[] = { "info.txt", "credits.txt", 0 }; + + szs_extract_t eszs; + InitializeExtractSZS(&eszs); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + ccp * search; + for ( search = search_tab; ; search++ ) + { + if (!*search) + { + if (!opt_ignore) + printf("\n* NO INFO FOUND: %s\n\n",param->arg); + max_err = ERR_WARNING; + break; + } + + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),param->arg,*search); + ResetExtractSZS(&eszs); + enumError err = ExtractSZS(&eszs,false,path,0,true); + if ( !err && eszs.data ) + { + if (print_header) + printf("\n* %s\n\n",eszs.fname); + fwrite(eszs.data,1,eszs.data_size,stdout); + if ( eszs.data_size && eszs.data[eszs.data_size-1] != '\n' ) + putchar('\n'); + if (print_header) + putchar('\n'); + break; + } + } + } + ResetExtractSZS(&eszs); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command ghost /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_ghost() +{ + static ccp def_path = "\1P/\1F\1?T"; + CheckOptDest(def_path,false); + char dest[PATH_MAX]; + + raw_data_t raw; + InitializeRawData(&raw); + + rkg_info_t ri; + InitializeRKGInfo(&ri); + enumError max_err = ERR_OK; + + File_t fo; + InitializeFile(&fo); + + PrintScript_t ps; + InitializePrintScript(&ps); + ps.fo = &fo; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if (!fo.f) + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(script_fform,0),false); + + if ( verbose >= 0 ) + { + fprintf(stdlog,"%sANALYZE %s:%s => %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(script_fform,0), dest ); + fflush(stdlog); + } + + err = ScanRawDataGHOST(&ri,false,&raw); + if ( err >= ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + if (!fo.f) + { + enumError err = CreateFile(&fo,false,dest,FM_STDIO|FM_OVERWRITE); + if (err) + { + max_err = err; + break; + } + PrintScriptHeader(&ps); + } + + PrintScriptVars(&ps,3, + "compressed=%d\n" + "size=%u\n" + "score=%u\n" + "time_button=%u\n" + "time_direction=%u\n" + "time_trick=%u\n" + ,ri.is_compressed + ,ri.data_size + ,ri.score + ,ri.time_but + ,ri.time_dir + ,ri.time_trick + ); + #if 0 + printf("score = %u, compr = %d, size = %u\n", + ri.score, ri.is_compressed, ri.data_size ); + printf("but = %u,%u, dir = %u,%u, trick=%u,%u\n", + ri.n_but, ri.time_but, + ri.n_dir, ri.time_dir, + ri.n_trick, ri.time_trick ); + #endif + + if (!script_array) + { + PrintScriptFooter(&ps); + ResetFile(&fo,0); + } + } + + PrintScriptFooter(&ps); + ResetPrintScript(&ps); + ResetFile(&fo,0); + ResetRKGInfo(&ri); + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command decompress /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_yazdump() +{ + CheckOptDest("-",false); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,false,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS + && ( szs.fform_file == FF_YAZ0 || szs.fform_file == FF_YAZ1 )) + { + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,".txt",".txt",false); + + File_t F; + CreateFileOpt(&F,true,dest,testmode,0); + if (F.f) + { + if ( verbose >= 0 || testmode ) + fprintf(F.f,"%sYAZ DUMP of %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF_SZS(&szs), param->arg ); + err = DecompressSZS(&szs,true,F.f); + } + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command vehicle /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef HAVE_WIIMM_EXT + + enumError cmd_vehicle() + { + ERROR0(ERR_NOT_IMPLEMENTED, + "Command VEHICLE not implemented in this version!\n"); + exit(ERR_NOT_IMPLEMENTED); + } + +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 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; + ccp filter_bmg = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wszst,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_TOUCH: break; + case GO_AUTO: break; + case GO_SET_FLAGS: err += ScanOptSetFlags(optarg); break; + case GO_SET_SCALE: err += ScanOptSetScale(optarg); break; + case GO_SET_ROT: err += ScanOptSetRot(optarg); break; + case GO_SET_X: err += ScanOptSet(0,optarg); break; + case GO_SET_Y: err += ScanOptSet(1,optarg); break; + case GO_SET_Z: err += ScanOptSet(2,optarg); break; + case GO_XCENTER: break; + case GO_YCENTER: break; + case GO_ZCENTER: break; + case GO_CENTER: 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_YPOS: err += ScanOptYPos(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_RM_GOBJ: err += ScanOptRmGobj(optarg); break; + case GO_BATTLE: err += ScanOptBattle(optarg); break; + case GO_EXPORT_FLAGS: err += ScanOptExportFlags(optarg); break; + case GO_ROUTE_OPTIONS: err += ScanOptRouteOptions(optarg); break; + case GO_WIM0: err += ScanOptWim0(optarg); break; + case GO_SLOT: err += ScanOptSlot(optarg); break; + case GO_LOAD_KCL: err += ScanOptLoadKcl(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_KMP: err += ScanOptKmp(optarg); break; + case GO_SPEED_MOD: err += ScanOptSpeedMod(optarg); break; + case GO_KTPT2: err += ScanOptKtpt2(optarg); break; + case GO_TFORM_KMP: err += ScanOptTformKmp(optarg); break; + case GO_GAMEMODES: err += ScanOptGamemodes(optarg); break; + case GO_REPAIR_XPF: err += ScanOptRepairXPF(optarg); break; + + case GO_MDL: err += ScanOptMdl(optarg); break; + case GO_MINIMAP: break; + case GO_PAT: err += ScanOptPat(optarg); break; + case GO_PATCH_FILE: err += ScanOptPatchFile(optarg); break; + + case GO_KMG_LIMIT: err += ScanOptKmgLimit(optarg); break; + case GO_KMG_COPY: err += ScanOptKmgCopy(optarg); break; + + case GO_LT_CLEAR: opt_lt_clear = true; break; + case GO_LT_ONLINE: err += ScanOptLtOnline(optarg); break; + case GO_LT_N_PLAYERS: err += ScanOptLtNPlayers(optarg); break; + case GO_LT_COND_BIT: err += ScanOptLtCondBit(optarg); break; + case GO_LT_GAME_MODE: err += ScanOptLtGameMode(optarg); break; + case GO_LT_ENGINE: err += ScanOptLtEngine(optarg); break; + case GO_LT_RANDOM: err += ScanOptLtRandom(optarg); break; + case GO_LEX_PURGE: opt_lex_purge = true; 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; + + case GO_ANALYZE: analyze_fname = optarg; break; + case GO_ANALYZE_MODE: err += ScanOptAnalyzeMode(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_SOURCE: SetSource(optarg); 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_SRC: opt_remove_src = 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_IGNORE_SETUP: opt_ignore_setup = true; break; + case GO_PURGE: opt_purge = true; break; + + case GO_YAZ0: SetCompressionFF(0,FF_YAZ0); break; + case GO_YAZ1: SetCompressionFF(0,FF_YAZ1); break; + case GO_XYZ: SetCompressionFF(0,FF_XYZ); break; + case GO_BZ: SetCompressionFF(0,FF_BZ); break; + + case GO_U8: SetCompressionFF(FF_U8,0); break; + case GO_SZS: SetCompressionFF(FF_U8,FF_YAZ0); break; + case GO_WU8: SetCompressionFF(FF_WU8,0); break; + case GO_XWU8: SetCompressionFF(FF_WU8,FF_XYZ); break; + case GO_WBZ: SetCompressionFF(FF_WU8,FF_BZ); break; + //case GO_ARC: SetCompressionFF(FF_RARC,0); break; + case GO_BRRES: SetCompressionFF(FF_BRRES,0); break; + case GO_BREFF: SetCompressionFF(FF_BREFF,0); break; + case GO_BREFT: SetCompressionFF(FF_BREFT,0); break; + case GO_PACK: SetCompressionFF(FF_PACK,0); break; + + case GO_JSON: script_fform = FF_JSON; break; + case GO_SH: script_fform = FF_SH; break; + case GO_BASH: script_fform = FF_BASH; break; + case GO_PHP: script_fform = FF_PHP; break; + case GO_MAKEDOC: script_fform = FF_MAKEDOC; break; + case GO_VAR: script_varname = optarg; break; + case GO_ARRAY: script_array++; break; + case GO_AVAR: script_array++; script_varname = optarg; break; + + case GO_PT_DIR: err += ScanOptPtDir(optarg); break; + case GO_LINKS: opt_links = true; break; + case GO_RM_AIPARAM: opt_rm_aiparam = true; break; + case GO_ALIGN_U8: err += ScanOptAlignU8(optarg); break; + case GO_ALIGN_PACK: err += ScanOptAlignPACK(optarg); break; + case GO_ALIGN_BRRES: err += ScanOptAlignBRRES(optarg); break; + case GO_ALIGN_BREFF: err += ScanOptAlignBREFF(optarg); break; + case GO_ALIGN_BREFT: err += ScanOptAlignBREFT(optarg); break; + case GO_ALIGN: err += ScanOptAlign(optarg); break; + case GO_TRANSFORM: err += ScanOptTransform(optarg); break; + case GO_STRIP: opt_strip = true; break; + + case GO_NO_COMPRESS: opt_compr_mode = -1; break; + case GO_COMPRESS: err += ScanOptCompr(optarg); break; + case GO_FAST: opt_fast = true; err += ScanOptCompr("fast"); break; + case GO_NORM: opt_norm = true; 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_LOAD_BMG: err += ScanOptLoadBMG(optarg); break; + case GO_PATCH_BMG: err += ScanOptPatchMessage(optarg); break; + case GO_MACRO_BMG: err += ScanOptMacroBMG(optarg); break; + case GO_FILTER_BMG: filter_bmg = optarg; break; + case GO_AUTOADD_PATH: DefineAutoAddPath(optarg); break; + + case GO_ENCODE_ALL: opt_encode_all = true; break; + case GO_ENCODE_IMG: opt_encode_img = true; break; + case GO_NO_ENCODE: opt_no_encode = true; break; + case GO_NO_RECURSE: opt_no_recurse = true; break; + case GO_AUTO_ADD: opt_auto_add = true; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_NO_CHECK: opt_no_check = true; break; + case GO_BASEDIR: opt_basedir = optarg; break; + case GO_RECURSE: err += ScanOptRecurse(optarg); break; + case GO_EXT: opt_ext++; break; + case GO_DECODE: opt_decode = true; break; + case GO_CMPR_DEFAULT: err += ScanOptCmprDefault(optarg); break; + case GO_N_MIPMAPS: err += ScanOptNMipmaps(optarg); break; + case GO_MAX_MIPMAPS: err += ScanOptMaxMipmaps(optarg); break; + case GO_MIPMAP_SIZE: err += ScanOptMipmapSize(optarg); break; + case GO_MIPMAPS: opt_mipmaps = +1; break; + case GO_NO_MIPMAPS: opt_mipmaps = -1; break; + case GO_FAST_MIPMAPS: fast_resize_enabled = true; break; + case GO_CUT: opt_cut = true; break; + case GO_RAW: opt_raw = true; break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; +// case GO_FULL: full_count++; break; + case GO_EXPORT: export_count++; break; + case GO_SORT: err += ScanOptSort(optarg); break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_PIPE: pipe_count++; break; + case GO_DELTA: delta_count++; break; + case GO_DIFF: diff_count++; break; + case GO_NO_PARAM: print_param = false; break; + case GO_EPSILON: err += ScanOptEpsilon(optarg); break; + + case GO_BMG_ENCODING: err += ScanOptBmgEncoding(optarg); break; + case GO_BMG_INF_SIZE: err += ScanOptBmgInfSize(optarg,false); break; + case GO_BMG_MID: err += ScanOptBmgMid(optarg); break; + case GO_FORCE_ATTRIB: err += ScanOptForceAttrib(optarg); break; + case GO_DEF_ATTRIB: err += ScanOptDefAttrib(optarg); break; + case GO_NO_ATTRIB: opt_bmg_no_attrib = true; break; + case GO_X_ESCAPES: opt_bmg_x_escapes = true; break; + case GO_OLD_ESCAPES: opt_bmg_old_escapes = true; break; + case GO_SINGLE_LINE: opt_bmg_single_line++; break; + case GO_NO_BMG_COLORS: opt_bmg_colors = 0; break; + case GO_BMG_COLORS: opt_bmg_colors = 2; break; + case GO_NO_BMG_INLINE: opt_bmg_inline_attrib = false; break; + case GO_CACHE: opt_cache = optarg; break; + case GO_CNAME: opt_cname = optarg; break; + case GO_ID: opt_id = true; break; + case GO_BASE64: opt_base64 = true; err += ScanOptCoding64(optarg); break; + case GO_DB64: opt_db64 = true; err += ScanOptCoding64(optarg); break; + case GO_CODING: err += ScanOptCoding64(optarg); break; + case GO_VERIFY: opt_verify = true; break; + case GO_SECTIONS: print_sections++; break; + case GO_ALL: set_all(0); break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + if ( opt_compr_mode == -9 ) + { +// [[?]] + if (opt_compr) + opt_colorize = 1; + list_compressions_exit(); + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wszst,TRACE_FILE,11); + #endif + CloseTransformation(); + NormalizeOptions( verbose > 3 && !is_env ? 3 : 0 ); + SetupBMG(filter_bmg); + SetupKCL(); + SetupKMP(); + SetupMDL(); + + // true until final version of 'post-patch-mkw.sh' + SetupSZSCache(opt_cache,true); + + if (!err) + { + enumError err = SetupPatchingListBMG(); + if ( err > ERR_WARNING ) + return err; + } + + 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_wszst,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wszst); + + 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_wszst,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_BRSUB: err = cmd_brsub(); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupParserVars()); break; + case CMD_FUNCTIONS: SetupReferenceKCL(0); + err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupParserVars()); break; + case CMD_MATRIX: err = cmd_matrix(); break; + case CMD_FLOAT: err = cmd_float(); break; + case CMD_VR_CALC: err = cmd_vr_calc(); break; + case CMD_VR_RACE: err = cmd_vr_race(); break; + case CMD_AUTOADD: err = cmd_autoadd(); break; + case CMD_TRACKS: err = cmd_tracks(); break; + case CMD_SCANCACHE: err = cmd_scancache(); break; + case CMD_EXPORT: err = cmd_export(); break; + case CMD_CODE: err = cmd_code(); break; + case CMD_RECODE: err = cmd_recode(); break; + case CMD_SUBFILE: err = cmd_subfile(); break; + + case CMD_LIST: err = cmd_list(0); break; + case CMD_LIST_L: err = cmd_list(1); break; + case CMD_LIST_LL: err = cmd_list(2); break; + case CMD_LIST_LLL: err = cmd_list(3); break; + case CMD_LIST_A: set_all(1); err = cmd_list(1); break; + case CMD_LIST_LA: set_all(1); err = cmd_list(2); break; + + case CMD_NAME_REF: err = cmd_name_ref(); break; + + case CMD_ILIST: err = cmd_ilist(0); break; + case CMD_ILIST_L: err = cmd_ilist(1); break; + case CMD_ILIST_LL: err = cmd_ilist(2); break; + case CMD_ILIST_A: set_all(0); err = cmd_ilist(0); break; + case CMD_ILIST_LA: set_all(0); err = cmd_ilist(1); break; + + case CMD_DUMP: err = cmd_dump(); break; + case CMD_MEMORY: err = cmd_memory(); break; + case CMD_MEMORY_A: set_all(1); err = cmd_memory(); break; + case CMD_SHA1: err = cmd_sha1(); break; + case CMD_ANALYZE: err = cmd_analyze(); break; + case CMD_DISTRIBUTION: err = cmd_distribution(); break; + case CMD_DIFF: err = cmd_diff(); break; + + case CMD_CHECK: err = cmd_check(); break; + case CMD_SLOTS: err = cmd_slots(); break; + case CMD_STGI: err = cmd_stgi(); break; + case CMD_ISARENA: err = cmd_isarena(); break; + case CMD_PATCH: err = cmd_patch(); break; + case CMD_COPY: err = cmd_copy(); break; + case CMD_DUPLICATE: err = cmd_duplicate(); break; + case CMD_NORMALIZE: err = cmd_normalize(); break; + case CMD_MINIMAP: err = cmd_minimap(); break; + case CMD_COMPRESS: err = cmd_compress(); break; + case CMD_DECOMPRESS: err = cmd_decompress(); break; + case CMD_ENCODE: err = cmd_create(false); break; + case CMD_CREATE: err = cmd_create(true); break; + case CMD_UPDATE: err = cmd_update(); break; + case CMD_EXTRACT: + case CMD_XDECODE: + case CMD_XEXPORT: + case CMD_XALL: + case CMD_XCOMMON: err = cmd_extract(cmd_ct->id); break; + + case CMD_BINARY: err = cmd_convert(true); break; + case CMD_TEXT: err = cmd_convert(false); break; + case CMD_CAT: err = cmd_cat(); break; + case CMD_BMG: err = cmd_bmg_cat(true); break; + case CMD_KCL: err = cmd_FFORM(FF_KCL,"course.kcl"); break; + case CMD_KMP: err = cmd_FFORM(FF_KMP,"course.kmp"); break; + case CMD_LEX: err = cmd_FFORM(FF_LEX,"course.lex"); break; + case CMD_INFO: err = cmd_info(); break; + case CMD_GHOST: err = cmd_ghost(); break; + case CMD_YAZDUMP: err = cmd_yazdump(); break; + + case CMD_VEHICLE: err = cmd_vehicle(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + SaveSZSCache(); + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main_wszst() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int main_wszst ( int argc, char ** argv ) +{ + print_title_func = print_title; + SetupLib(argc,argv,WSZST_SHORT); + SetupExtendedSZS(); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WSZST_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WSZST_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + CloseAnalyzeFile(); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// tool wrapper /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef int (*main_func) ( int argc, char ** argv ); + +extern int main_wszst ( int argc, char ** argv ); +extern int main_wbmgt ( int argc, char ** argv ); +extern int main_wctct ( int argc, char ** argv ); +extern int main_wimgt ( int argc, char ** argv ); +extern int main_wkclt ( int argc, char ** argv ); +extern int main_wkmpt ( int argc, char ** argv ); +extern int main_wlect ( int argc, char ** argv ); +extern int main_wmdlt ( int argc, char ** argv ); +extern int main_wpatt ( int argc, char ** argv ); +extern int main_wstrt ( int argc, char ** argv ); +extern int main_wrapper ( int argc, char ** argv ); +extern int main_getopt ( int argc, char ** argv ); + +typedef struct wrapper_t +{ + uint hide; // 0:off, 1:hide, 2:is wrapper + main_func func; + ccp name; + ccp info; +} +wrapper_t; + +static const wrapper_t wrapper_tab[] = +{ + { 0, main_wszst, WSZST_SHORT, WSZST_LONG }, + { 0, main_wbmgt, WBMGT_SHORT, WBMGT_LONG }, + { 0, main_wctct, WCTCT_SHORT, WCTCT_LONG }, + { 0, main_wimgt, WIMGT_SHORT, WIMGT_LONG }, + { 0, main_wkclt, WKCLT_SHORT, WKCLT_LONG }, + { 0, main_wkmpt, WKMPT_SHORT, WKMPT_LONG }, + { 0, main_wlect, WLECT_SHORT, WLECT_LONG }, + { 0, main_wmdlt, WMDLT_SHORT, WMDLT_LONG }, + { 0, main_wpatt, WPATT_SHORT, WPATT_LONG }, + { 0, main_wstrt, WSTRT_SHORT, WSTRT_LONG }, + { 2, main_getopt, "getopt", 0 }, + { 2, main_wrapper, "wrapper", 0 }, + {0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const wrapper_t * FindWrapper ( int argc, char ** argv ) +{ + if ( argc > 0 && argv[0] ) + { + ccp name = strrchr(argv[0],'/'); + name = name ? name+1 : argv[0]; + if ( *name == 'w' || *name == 'W' ) + name++; + + const wrapper_t *w; + for ( w = wrapper_tab; w->func; w++ ) + if ( !strncasecmp(w->name+1,name,3) ) + return w; + } + + return wrapper_tab; +} + +/////////////////////////////////////////////////////////////////////////////// + +int main_wrapper ( int argc, char ** argv ) +{ + SetupLib(argc,argv,TOOLSET_SHORT); + + enum + { + C_CREATE = 0x001, + C_OVERWRITE = 0x002, + + C_HARDLINKS = 0x010, + C_SOFTLINKS = 0x020, + C_CYGWIN = 0x040, + C_SHELL = 0x080, + C_BASH = 0x100, + + C_QUIET = 0x200, + C_VERBOSE = 0x400, + + C_M_DEST = C_HARDLINKS|C_SOFTLINKS|C_CYGWIN|C_SHELL, + }; + + static const KeywordTab_t keytab[] = + { + { C_CREATE, "CREATE", 0, 3 }, + { C_CREATE|C_OVERWRITE, "OVERWRITE", 0, 4 }, + + { C_HARDLINKS, "HARDLINKS", 0, 4 }, + #ifdef __CYGWIN__ + { C_HARDLINKS, "BESTLINKS", 0, 4 }, + #endif + { C_SOFTLINKS, "SOFTLINKS", 0, 4 }, + #ifndef __CYGWIN__ + { C_SOFTLINKS, "BESTLINKS", 0, 4 }, + #endif + { C_CYGWIN, "CYGWIN", 0, 3 }, + { C_SHELL, "SHELL", 0, 2 }, + { C_SHELL|C_BASH, "BASH", 0, 4 }, + + { C_QUIET, "QUIET", 0, 1 }, + { C_VERBOSE, "VERBOSE", 0, 1 }, + + {0,0,0,0} + }; + + uint i, param = 0; + for ( i = 1; i < argc; i++ ) + { + const KeywordTab_t *key = ScanKeyword(0,argv[i],keytab); + if ( !key || strlen(argv[i]) < key->opt ) + goto abort; + param |= key->id; + } + + const uint quiet = param & C_QUIET; + const uint verbose = param & C_VERBOSE; + const uint overwrite= param & C_OVERWRITE; + const uint bash = param & C_BASH; + const uint desttype = param & C_M_DEST; + + char mypath[PATH_MAX], dest[PATH_MAX+20]; + GetProgramPath(mypath,sizeof(mypath),true,argv[0]); + if (verbose) + printf("My Path: %s\n",mypath); + + if ( param & C_CREATE + && Count1Bits(&desttype,sizeof(desttype)) == 1 + && *mypath ) + { + memcpy(dest,mypath,sizeof(dest)); + char *fname = strrchr(dest,'/'); + fname = fname ? fname+1 : dest; + if (!*fname) + goto abort; + + u16 cygwin_link[1000]; + uint cygwin_len = 0; + if ( desttype == C_CYGWIN ) + { + ccp src = fname; + while ( cygwin_len < sizeof(cygwin_link)/sizeof(*cygwin_link) ) + { + u32 code = ScanUTF8AnsiChar(&src); + if (!code) + break; + write_le16(cygwin_link+cygwin_len++,code); + } + } + + ccp srcname = mypath + (fname-dest); + char srcname_buf[PATH_MAX]; + if ( desttype == C_SHELL ) + { + ccp src; + for ( src = srcname; *src; src++ ) + { + const int ch = *src; + if ( !isalnum(ch) && ch != '_' && ( ch != '-' || src == srcname ) ) + { + uint len; + PrintEscapedString( srcname_buf+1, + sizeof(srcname_buf)-2, srcname, -1, CHMD__ALL, '"', &len ); + if (bash) + srcname = srcname_buf+1; + else + { + srcname_buf[0] = srcname_buf[len] = '"'; + srcname = srcname_buf; + } + break; + } + } + } + + FILE *f = 0; + const wrapper_t *w; + + for ( w = wrapper_tab; w->func; w++ ) + { + if (w->hide) + continue; + + strcpy(fname,w->name); + if (!strcmp(mypath,dest)) + continue; + + struct stat st; + if ( !overwrite && !stat(dest,&st) ) + continue; + unlink(dest); + + switch(desttype) + { + case C_HARDLINKS: + if (!quiet) + printf("CREATE HARDLINK %s\n",dest); + link(mypath,dest); + break; + + case C_SOFTLINKS: + if (!quiet) + printf("CREATE SOFTLINK %s\n",dest); + symlink(srcname,dest); + break; + + case C_CYGWIN: + if (!quiet) + printf("CREATE CYGWIN SOFTLINK %s\n",dest); + f = fopen(dest,"w"); + if (f) + { + fwrite("!<symlink>\xff\xfe",1,12,f); + fwrite(cygwin_link,2,cygwin_len,f); + fwrite("\0\0",1,2,f); + fclose(f); + } + break; + + case C_SHELL: + if (!quiet) + printf("CREATE %sSH SCRIPT %s\n", bash ? "BA" : "", dest); + f = fopen(dest,"w"); + if (f) + { + if (bash) + fprintf(f,"#!/usr/bin/env bash\n" + "\"$(dirname \"$BASH_SOURCE\")/%s\" %s \"$@\"\n", + srcname, w->name ); + else + fprintf(f,"#!/bin/sh\n%s %s \"$@\"\n",srcname,w->name); + fclose(f); + chmod(dest,0755); + } + break; + //"$(dirname "$BASH_SOURCE")" + } + } + return ERR_OK; + } + + abort:; + fprintf(stdout, + "\n%s\n%s\n\n Usage: %s TOOLNAME ...\n\n" + "This is a wrapper for the following tools:\n\n", + text_logo, TOOLSET_TITLE, progname ); + + const wrapper_t *w, *active = FindWrapper(argc,argv); + DASSERT(active); + for ( w = wrapper_tab; w->func; w++ ) + if ( w == active || !w->hide ) + fprintf(stdout," %c %-5s : %s\n", + w == active ? '*' : ' ', w->name, w->info ); + + fprintf(stdout, + "\nWrapper commands:\n\n" + " WRAPPER HELP\n" + " WRAPPER CREATE [QUIET] HARDLINKS|SOFTLINKS|BESTLINKS|CYGWIN|SHELL|BASH\n" + " WRAPPER OVERWRITE [QUIET] HARDLINKS|SOFTLINKS|BESTLINKS|CYGWIN|SHELL|BASH\n" + "\n" + " H[ELP] : Print this help and exit.\n" + " CRE[ATE] : Create links or scripts, but don't overwrite.\n" + " OVER[WRITE] : Create links or scripts and remove existing files before.\n" + " HARD[LINKS] : Create hard links to the main program.\n" + " SOFT[LINKS] : Create soft links to the main program.\n" + " BEST[LINKS] : For Cygwin same as HARDLINKS, for all other same as SOFTLINKS.\n" + " CYG[WIN] : Create softlinks for Cygwin (plain files with special content).\n" + " SH[ELL] : Create simple `sh´ scripts assuming the main program is in PATH.\n" + " BASH : Create `bash´ scripts with run time path detection.\n" + " Q[UIET] : Option to suppress creation messages.\n" + "\n" + " See https://szs.wiimm.de/doc/wrapper for details.\n" + " \n"); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int main ( int argc, char ** argv ) +{ + //PRINT("ERR__N=%u\n",ERR__N); + if ( argc > 1 ) + { + const wrapper_t *w; + for ( w = wrapper_tab; w->func; w++ ) + if (!strcasecmp(w->name,argv[1])) + { + argv[1] = argv[0]; + return w->func(argc-1,argv+1); + } + } + + const wrapper_t *w = FindWrapper(argc,argv); + DASSERT(w); + return w->func(argc,argv); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + |
