/*************************************************************************** * _______ _______ _______ * * | ___ |____ | ___ | * * | | |_| / /| | |_| * * | |_____ / / | |_____ * * |_____ | / / |_____ | * * _ | | / / _ | | * * | |___| |/ /____| |___| | * * |_______|_______|_______| * * * * Wiimms SZS Tools * * https://szs.wiimm.de/ * * * *************************************************************************** * * * This file is part of the SZS project. * * Visit https://szs.wiimm.de/ for project details and sources. * * * * Copyright (c) 2011-2022 by Dirk Clemens * * * *************************************************************************** * * * 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 #include #include "dclib-utf8.h" #include "lib-analyze.h" #include "lib-szs.h" #include "lib-brres.h" #include "lib-xbmg.h" #include "lib-kcl.h" #include "lib-kmp.h" #include "lib-ledis.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 "lib-bzip2.h" #include "lib-pack.h" #include "lib-rarc.h" #include "lib-rkc.h" #include "lib-staticr.h" #include "db-dol.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 // /////////////////////////////////////////////////////////////////////////////// /////////////// definitions /////////////// /////////////////////////////////////////////////////////////////////////////// #define TITLE WSZST_SHORT ": " WSZST_LONG " v" VERSION " r" REVISION \ " " SYSTEM2 " - " AUTHOR " - " DATE #define TOOLSET_TITLE TOOLSET_SHORT ": " TOOLSET_LONG " v" VERSION " r" REVISION \ " " SYSTEM2 " - " AUTHOR " - " DATE // /////////////////////////////////////////////////////////////////////////////// static void help_exit ( bool xmode ) { SetupPager(); 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); ClosePager(); 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 paths." " 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 ) { cmd_version_section(print_sect_header,WSZST_SHORT,WSZST_LONG,long_count-1); } /////////////////////////////////////////////////////////////////////////////// static void version_exit() { if ( brief_count > 1 ) fputs( VERSION "\n", stdout ); else if (brief_count) fputs( VERSION " r" REVISION " " SYSTEM2 "\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", ProgInfo.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", ProgInfo.progname, ProgInfo.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; } // /////////////////////////////////////////////////////////////////////////////// /////////////// sizeof data /////////////// /////////////////////////////////////////////////////////////////////////////// static const sizeof_info_t sizeof_info_szs_list[] = { SIZEOF_INFO_TITLE("SZS basics") SIZEOF_INFO_ENTRY(compatible_info_t) SIZEOF_INFO_ENTRY(SubFile_t) SIZEOF_INFO_ENTRY(SubFileList_t) SIZEOF_INFO_ENTRY(SubDirList_t) SIZEOF_INFO_ENTRY(SubDir_t) SIZEOF_INFO_ENTRY(SubFileIterator_t) SIZEOF_INFO_ENTRY(DataContainer_t) SIZEOF_INFO_ENTRY(search_filter_t) SIZEOF_INFO_ENTRY(SubstString_t) SIZEOF_INFO_ENTRY(repair_ff_t) SIZEOF_INFO_ENTRY(slot_info_t) SIZEOF_INFO_ENTRY(FormatFieldItem_t) SIZEOF_INFO_ENTRY(FormatField_t) SIZEOF_INFO_ENTRY(tiny_param_t) SIZEOF_INFO_ENTRY(ParamList_t) SIZEOF_INFO_ENTRY(List_t) SIZEOF_INFO_ENTRY(MemItem_t) SIZEOF_INFO_ENTRY(SetupDef_t) SIZEOF_INFO_ENTRY(ListDef_t) SIZEOF_INFO_ENTRY(SetupParam_t) SIZEOF_INFO_ENTRY(config_t) SIZEOF_INFO_ENTRY(data_tab_t) SIZEOF_INFO_ENTRY(output_mode_t) SIZEOF_INFO_TITLE("SZS basics: numeric") SIZEOF_INFO_ENTRY(tri_metrics_t) SIZEOF_INFO_ENTRY(octahedron_t) SIZEOF_INFO_ENTRY(cuboid_t) SIZEOF_INFO_ENTRY(IndexList_t) SIZEOF_INFO_ENTRY(FuncParam_t) SIZEOF_INFO_ENTRY(FuncTable_t) SIZEOF_INFO_ENTRY(FuncInfo_t) SIZEOF_INFO_ENTRY(Var_t) SIZEOF_INFO_ENTRY(VarMap_t) SIZEOF_INFO_ENTRY(ScanLoop_t) SIZEOF_INFO_ENTRY(ScanMacro_t) SIZEOF_INFO_ENTRY(ScanFile_t) SIZEOF_INFO_ENTRY(ScanInfo_t) SIZEOF_INFO_ENTRY(ScanParam_t) SIZEOF_INFO_ENTRY(Line_t) SIZEOF_INFO_TITLE("Archives and packing") SIZEOF_INFO_ENTRY(BZIP2_t) SIZEOF_INFO_ENTRY(BZ2Manager_t) SIZEOF_INFO_ENTRY(BZ2Source_t) SIZEOF_INFO_ENTRY(sha1_size_t) SIZEOF_INFO_ENTRY(sha1_type_t) SIZEOF_INFO_ENTRY(sha1_db_t) SIZEOF_INFO_ENTRY(DistributionInfo_t) SIZEOF_INFO_ENTRY(pack_header_t) SIZEOF_INFO_ENTRY(pack_metric_t) SIZEOF_INFO_TITLE("BREFF & BREFT") SIZEOF_INFO_ENTRY(breff_root_head_t) SIZEOF_INFO_ENTRY(breff_root_t) SIZEOF_INFO_ENTRY(breff_item_name_t) SIZEOF_INFO_ENTRY(breff_item_data_t) SIZEOF_INFO_ENTRY(breff_item_list_t) SIZEOF_INFO_ENTRY(breft_image_t) SIZEOF_INFO_TITLE("BRRES") SIZEOF_INFO_ENTRY(brres_info_t) SIZEOF_INFO_ENTRY(brsub_list_t) SIZEOF_INFO_ENTRY(brsub_info_t) SIZEOF_INFO_ENTRY(brsub_iterator_t) SIZEOF_INFO_ENTRY(brres_t) SIZEOF_INFO_ENTRY(tex_info_t) SIZEOF_INFO_ENTRY(name_ref_elem_t) SIZEOF_INFO_ENTRY(name_ref_var_t) SIZEOF_INFO_ENTRY(name_ref_t) SIZEOF_INFO_TITLE("Common files") SIZEOF_INFO_ENTRY(object_type_t) SIZEOF_INFO_ENTRY(object_mgr_t) SIZEOF_INFO_ENTRY(itemslot_bin_t) SIZEOF_INFO_ENTRY(itemslot_t) SIZEOF_INFO_ENTRY(minigame_kmg_head_t) SIZEOF_INFO_ENTRY(minigame_kmg_t) SIZEOF_INFO_ENTRY(minigame_t) SIZEOF_INFO_ENTRY(objflow_bin_t) SIZEOF_INFO_ENTRY(objflow_t) SIZEOF_INFO_ENTRY(geohit_bin_t) SIZEOF_INFO_ENTRY(geohit_t) SIZEOF_INFO_TITLE("CT-CODE") SIZEOF_INFO_ENTRY(ctcode_sect_info_t) SIZEOF_INFO_ENTRY(ctcode_header_t) SIZEOF_INFO_ENTRY(ctcode_cup1_data_t) SIZEOF_INFO_ENTRY(ctcode_cup1_head_t) SIZEOF_INFO_ENTRY(ctcode_crs1_data_t) SIZEOF_INFO_ENTRY(ctcode_crs1_head_t) SIZEOF_INFO_ENTRY(ctcode_cupref_t) SIZEOF_INFO_ENTRY(ctcode_arena_t) SIZEOF_INFO_ENTRY(ctcode_t) SIZEOF_INFO_TITLE("DOL") SIZEOF_INFO_ENTRY(DolSectionMap_t) SIZEOF_INFO_ENTRY(DolAddressMap_t) SIZEOF_INFO_TITLE("File DB") SIZEOF_INFO_ENTRY(DbFile_t) SIZEOF_INFO_ENTRY(DbFileSZS_t) SIZEOF_INFO_ENTRY(DbFileSHA1_t) SIZEOF_INFO_ENTRY(DbFileFILE_t) SIZEOF_INFO_ENTRY(DbFileGROUP_t) SIZEOF_INFO_TITLE("Images") SIZEOF_INFO_ENTRY(MipmapOptions_t) SIZEOF_INFO_ENTRY(ImageGeometry_t) SIZEOF_INFO_ENTRY(Image_t) SIZEOF_INFO_ENTRY(GenericImgParam_t) SIZEOF_INFO_ENTRY(cmpr_info_t) SIZEOF_INFO_ENTRY(tpl_header_t) SIZEOF_INFO_ENTRY(tpl_imgtab_t) SIZEOF_INFO_ENTRY(tpl_pal_header_t) SIZEOF_INFO_ENTRY(tpl_img_header_t) SIZEOF_INFO_ENTRY(bti_header_t) SIZEOF_INFO_TITLE("KCL") SIZEOF_INFO_ENTRY(kcl_attrib_name_t) SIZEOF_INFO_ENTRY(kcl_class_t) SIZEOF_INFO_ENTRY(kcl_type_t) SIZEOF_INFO_ENTRY(kcl_analyze_t) SIZEOF_INFO_ENTRY(kcl_head_t) SIZEOF_INFO_ENTRY(kcl_triangle_t) SIZEOF_INFO_ENTRY(kcl_tridata_flt_t) SIZEOF_INFO_ENTRY(kcl_tridata_dbl_t) SIZEOF_INFO_ENTRY(kcl_tridata0_t) SIZEOF_INFO_ENTRY(kcl_tridata1_t) SIZEOF_INFO_ENTRY(kcl_t) SIZEOF_INFO_ENTRY(kcl_tri_param_t) SIZEOF_INFO_ENTRY(kcl_cube_t) #if SUPPORT_KCL_CUBE SIZEOF_INFO_ENTRY(kcl_cube_list_t) #endif SIZEOF_INFO_ENTRY(kcl_tri_t) SIZEOF_INFO_TITLE("KMP basics") SIZEOF_INFO_ENTRY(kmp_file_gen_t) SIZEOF_INFO_ENTRY(kmp_file_mkw_t) SIZEOF_INFO_ENTRY(kmp_file_wim0_t) SIZEOF_INFO_ENTRY(kmp_head_info_t) SIZEOF_INFO_ENTRY(kmp_list_head_t) SIZEOF_INFO_ENTRY(kmp_ktpt_entry_t) SIZEOF_INFO_ENTRY(kmp_enpt_entry_t) SIZEOF_INFO_ENTRY(kmp_enph_entry_t) SIZEOF_INFO_ENTRY(kmp_ckpt_entry_t) SIZEOF_INFO_ENTRY(kmp_gobj_entry_t) SIZEOF_INFO_ENTRY(kmp_poti_group_t) SIZEOF_INFO_ENTRY(kmp_poti_point_t) SIZEOF_INFO_ENTRY(kmp_area_entry_t) SIZEOF_INFO_ENTRY(kmp_came_entry_t) SIZEOF_INFO_ENTRY(kmp_jgpt_entry_t) SIZEOF_INFO_ENTRY(kmp_stgi_entry_t) SIZEOF_INFO_ENTRY(kmp_wim0_t) SIZEOF_INFO_ENTRY(kmp_wim0_sect_t) SIZEOF_INFO_ENTRY(kmp_wim0_info_t) SIZEOF_INFO_ENTRY(kmp_section_t) SIZEOF_INFO_ENTRY(kmp_rtype_info_t) SIZEOF_INFO_ENTRY(kmp_gopt_t) SIZEOF_INFO_ENTRY(kmp_gopt2_t) SIZEOF_INFO_ENTRY(kmp_group_elem_t) SIZEOF_INFO_ENTRY(kmp_group_t) SIZEOF_INFO_ENTRY(kmp_group_list_t) SIZEOF_INFO_ENTRY(kmp_rtobj_t) SIZEOF_INFO_ENTRY(kmp_rtobj_list_t) SIZEOF_INFO_ENTRY(kmp_ph_t) SIZEOF_INFO_ENTRY(kmp_flag_t) SIZEOF_INFO_ENTRY(kmp_t) SIZEOF_INFO_ENTRY(Itembox_t) SIZEOF_INFO_ENTRY(DrawKCL_t) SIZEOF_INFO_ENTRY(pos_param_t) SIZEOF_INFO_ENTRY(pos_file_t) SIZEOF_INFO_ENTRY(kmp_obj_info_t) SIZEOF_INFO_TITLE("KMP analysis") SIZEOF_INFO_ENTRY(kmp_linfo_t) SIZEOF_INFO_ENTRY(kmp_finish_t) SIZEOF_INFO_ENTRY(kmp_usedpos_obj_t) SIZEOF_INFO_ENTRY(kmp_usedpos_t) SIZEOF_INFO_ENTRY(kmp_pflags_t) SIZEOF_INFO_ENTRY(gobj_cond_ref_t) SIZEOF_INFO_ENTRY(gobj_cond_mask_t) SIZEOF_INFO_ENTRY(cond_ref_info_t) SIZEOF_INFO_ENTRY(kmp_ana_defobj_t) SIZEOF_INFO_ENTRY(kmp_ana_ref_t) SIZEOF_INFO_ENTRY(gobj_condition_t) SIZEOF_INFO_ENTRY(gobj_condition_set_t) SIZEOF_INFO_ENTRY(kmp_gobj_info_t) SIZEOF_INFO_ENTRY(kmp_ana_gobj_t) SIZEOF_INFO_ENTRY(kmp_ana_pflag_res_t) SIZEOF_INFO_ENTRY(kmp_ana_pflag_t) SIZEOF_INFO_TITLE("LE-CODE basics") SIZEOF_INFO_ENTRY(lecode_debug_info_t) SIZEOF_INFO_ENTRY(lecode_debug_ex_t) SIZEOF_INFO_ENTRY(le_lpar_t) SIZEOF_INFO_ENTRY(le_binary_head_v3_t) SIZEOF_INFO_ENTRY(le_binary_head_v4_t) SIZEOF_INFO_ENTRY(le_binary_head_v5_34_t) SIZEOF_INFO_ENTRY(le_binary_head_v5_38_t) SIZEOF_INFO_ENTRY(le_binary_head_v5_t) SIZEOF_INFO_ENTRY(le_binary_head_t) SIZEOF_INFO_ENTRY(le_binary_param_t) SIZEOF_INFO_ENTRY(le_binpar_v1_35_t) SIZEOF_INFO_ENTRY(le_binpar_v1_37_t) SIZEOF_INFO_ENTRY(le_binpar_v1_f8_t) SIZEOF_INFO_ENTRY(le_binpar_v1_1b8_t) SIZEOF_INFO_ENTRY(le_binpar_v1_1bc_t) SIZEOF_INFO_ENTRY(le_binpar_v1_260_t) SIZEOF_INFO_ENTRY(le_binpar_v1_264_t) SIZEOF_INFO_ENTRY(le_binpar_v1_t) SIZEOF_INFO_ENTRY(le_cup_par_t) SIZEOF_INFO_ENTRY(le_course_par_t) SIZEOF_INFO_ENTRY(le_analyze_t) SIZEOF_INFO_ENTRY(le_region_t) SIZEOF_INFO_ENTRY(le_cup_track_t) SIZEOF_INFO_ENTRY(le_property_t) SIZEOF_INFO_ENTRY(le_music_t) SIZEOF_INFO_ENTRY(le_flags8_t) SIZEOF_INFO_ENTRY(le_flags_t) SIZEOF_INFO_ENTRY(LecodeFlags_t) SIZEOF_INFO_TITLE("LE-CODE distribution") SIZEOF_INFO_ENTRY(le_group_t) SIZEOF_INFO_ENTRY(le_track_type_t) SIZEOF_INFO_ENTRY(le_track_status_t) SIZEOF_INFO_ENTRY(le_track_text_t) SIZEOF_INFO_ENTRY(le_options_t) SIZEOF_INFO_ENTRY(le_cup_ref_t) SIZEOF_INFO_ENTRY(le_cup_t) SIZEOF_INFO_ENTRY(le_strpar_t) SIZEOF_INFO_ENTRY(le_track_id_t) SIZEOF_INFO_ENTRY(le_track_t) SIZEOF_INFO_ENTRY(le_track_arch_t) SIZEOF_INFO_ENTRY(le_auto_setup_t) SIZEOF_INFO_ENTRY(le_type_settings_t) SIZEOF_INFO_ENTRY(le_settings_t) SIZEOF_INFO_ENTRY(le_group_info_t) SIZEOF_INFO_ENTRY(le_distrib_t) SIZEOF_INFO_ENTRY(DistributionInfo_t) SIZEOF_INFO_TITLE("LEX") SIZEOF_INFO_ENTRY(features_szs_t) SIZEOF_INFO_ENTRY(check_features_szs_t) SIZEOF_INFO_ENTRY(have_lex_info_t) SIZEOF_INFO_ENTRY(lex_header_t) SIZEOF_INFO_ENTRY(lex_element_t) SIZEOF_INFO_ENTRY(lex_set1_t) SIZEOF_INFO_ENTRY(lex_hipt_rule_t) SIZEOF_INFO_ENTRY(lex_test_t) SIZEOF_INFO_ENTRY(lex_item_t) SIZEOF_INFO_ENTRY(lex_t) SIZEOF_INFO_ENTRY(lex_info_t) SIZEOF_INFO_TITLE("MDL") SIZEOF_INFO_ENTRY(mdl_head_t) SIZEOF_INFO_ENTRY(mdl_t) SIZEOF_INFO_ENTRY(mdl_sect0_t) SIZEOF_INFO_ENTRY(mdl_sect1_t) SIZEOF_INFO_ENTRY(mdl_sect2_t) SIZEOF_INFO_ENTRY(mdl_sect8_t) SIZEOF_INFO_ENTRY(mdl_sect8_layer_t) SIZEOF_INFO_ENTRY(Slot42MaterialInfo_t) SIZEOF_INFO_ENTRY(Slot42MaterialStat_t) SIZEOF_INFO_ENTRY(mdl_sect10_t) SIZEOF_INFO_ENTRY(mdl_minimap_t) SIZEOF_INFO_ENTRY(StringIteratorMDL_t) SIZEOF_INFO_TITLE("MKW") SIZEOF_INFO_ENTRY(TrackInfo_t) SIZEOF_INFO_ENTRY(MusicInfo_t) SIZEOF_INFO_TITLE("Object DB") SIZEOF_INFO_ENTRY(ObjSettingFormat_t) SIZEOF_INFO_ENTRY(ObjectInfo_t) SIZEOF_INFO_ENTRY(ObjSpec_t) SIZEOF_INFO_ENTRY(UsedObject_t) SIZEOF_INFO_ENTRY(UsedFile_t) SIZEOF_INFO_ENTRY(UsedFileSZS_t) SIZEOF_INFO_ENTRY(UsedFileSHA1_t) SIZEOF_INFO_ENTRY(UsedFileFILE_t) SIZEOF_INFO_ENTRY(UsedFileGROUP_t) SIZEOF_INFO_TITLE("PAT") SIZEOF_INFO_ENTRY(pat_head_t) SIZEOF_INFO_ENTRY(pat_s0_belem_t) SIZEOF_INFO_ENTRY(pat_s0_bhead_t) SIZEOF_INFO_ENTRY(pat_s0_sref_t) SIZEOF_INFO_ENTRY(pat_s0_selem_t) SIZEOF_INFO_ENTRY(pat_s0_shead_t) SIZEOF_INFO_ENTRY(pat_analyse_t) SIZEOF_INFO_ENTRY(pat_element_t) SIZEOF_INFO_ENTRY(pat_t) SIZEOF_INFO_TITLE("RARC") SIZEOF_INFO_ENTRY(rarc_file_header_t) SIZEOF_INFO_ENTRY(rarc_header_t) SIZEOF_INFO_ENTRY(rarc_node_t) SIZEOF_INFO_ENTRY(rarc_entry_t) SIZEOF_INFO_TITLE("RKC") SIZEOF_INFO_ENTRY(rkco_t) SIZEOF_INFO_ENTRY(rkct_t) SIZEOF_INFO_TITLE("RKG") SIZEOF_INFO_ENTRY(rkg_head_t) SIZEOF_INFO_ENTRY(rkg_info_t) SIZEOF_INFO_TITLE("StaticR") SIZEOF_INFO_ENTRY(str_server_list_t) SIZEOF_INFO_ENTRY(str_server_patch_t) SIZEOF_INFO_ENTRY(staticr_t) SIZEOF_INFO_ENTRY(VersusPointsInfo_t) SIZEOF_INFO_ENTRY(rel_header_t) SIZEOF_INFO_ENTRY(rel_sect_info_t) SIZEOF_INFO_ENTRY(rel_imp_t) SIZEOF_INFO_ENTRY(rel_data_t) SIZEOF_INFO_ENTRY(rel_info_t) SIZEOF_INFO_ENTRY(wpf_t) SIZEOF_INFO_ENTRY(wpf_head_t) SIZEOF_INFO_ENTRY(addr_port_t) SIZEOF_INFO_ENTRY(addr_port_version_t) SIZEOF_INFO_TITLE("SZS") SIZEOF_INFO_ENTRY(yaz0_header_t) SIZEOF_INFO_ENTRY(u8_header_t) SIZEOF_INFO_ENTRY(u8_node_t) SIZEOF_INFO_ENTRY(string_pool_t) SIZEOF_INFO_ENTRY(szs_subfile_t) SIZEOF_INFO_ENTRY(szs_subfile_list_t) SIZEOF_INFO_ENTRY(szs_have_t) SIZEOF_INFO_ENTRY(szs_file_t) SIZEOF_INFO_ENTRY(szs_norm_t) // SIZEOF_INFO_ENTRY(check_szs_t) SIZEOF_INFO_ENTRY(slot_ana_t) SIZEOF_INFO_ENTRY(iterator_param_t) SIZEOF_INFO_ENTRY(szs_iterator_t) SIZEOF_INFO_ENTRY(brres_header_t) SIZEOF_INFO_ENTRY(brres_entry_t) SIZEOF_INFO_ENTRY(brres_group_t) SIZEOF_INFO_ENTRY(brres_root_t) SIZEOF_INFO_ENTRY(brsub_header_t) SIZEOF_INFO_ENTRY(brsub_cut_t) SIZEOF_INFO_ENTRY(szs_extract_t) SIZEOF_INFO_ENTRY(raw_data_t) SIZEOF_INFO_ENTRY(wbz_header_t) SIZEOF_INFO_ENTRY(analyze_param_t) SIZEOF_INFO_ENTRY(analyze_szs_t) SIZEOF_INFO_ENTRY(file_type_t) SIZEOF_INFO_ENTRY(mkw_prefix_t) SIZEOF_INFO_ENTRY(mkw_category_t) SIZEOF_INFO_ENTRY(mkw_category_list_t) SIZEOF_INFO_ENTRY(split_filename_t) SIZEOF_INFO_ENTRY(print_split_par_t) SIZEOF_INFO_ENTRY(sha1_reference_t) SIZEOF_INFO_TERM() }; //----------------------------------------------------------------------------- static const sizeof_info_t *sizeof_info_szs[] = { sizeof_info_szs_list, 0 }; // /////////////////////////////////////////////////////////////////////////////// /////////////// 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(" warn modes: %16llx = \"%s\"\n",(u64)WARN_MODE,GetWarnMode()); 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 path_buf[PATH_MAX]; const config_t *config = GetConfig(); CheckOptDest(config->autoadd_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; autoadd_destination = opt_dest; 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 ) { const StringField_t *sf = GetAutoaddList(); int i; ccp *str = sf->field; for ( i = 0; i < sf->used; i++, str++ ) fprintf(stdlog," SEARCH PATH[%d]: %s\n",i,*str); } //--- 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 sizeof /////////////// /////////////////////////////////////////////////////////////////////////////// static enumError cmd_sizeof() { sizeof_info_order_t order; switch (GetSortMode(opt_sort,0,SORT_NONE)) { case SORT_INAME: order = SIZEOF_ORDER_NAME; break; case SORT_SIZE: order = SIZEOF_ORDER_SIZE; break; default: order = SIZEOF_ORDER_NONE; break; } SetupPager(); PrintMode_t pm = { .fout = stdout, .cout = colout, .debug = logging }; ArgManager_t am = { .force_case = LOUP_LOWER }; for ( ParamList_t *param = first_param; param; param = param->next ) AppendArgManager(&am,param->arg,0,false); const sizeof_info_t **si_list; if (brief_count) si_list = sizeof_info_szs; else { AddListToSizeofInfoMgr(sizeof_info_szs); si_list = GetSizeofInfoMgrList(); } ListSizeofInfo(&pm,si_list,&am,order); ClosePager(); return ERR_OK; } // /////////////////////////////////////////////////////////////////////////////// /////////////// 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 _TESTNORM /////////////// /////////////////////////////////////////////////////////////////////////////// void PrintNorm ( const szs_norm_t *norm ) { if ( !norm || !norm->f ) return; } /////////////////////////////////////////////////////////////////////////////// void SetupNormByOptions ( szs_norm_t *norm ) { DASSERT(norm); memset(norm,0,sizeof(*norm)); memset(norm->modified,'-',SZI__N); } /////////////////////////////////////////////////////////////////////////////// enumError cmd_testnorm() { if (!first_param) return ERR_OK; 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_INFO: 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_INFO: 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; IterateFilesParSZS(&szs,check_brsub,&cb,false,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 ) IterateFilesParSZS(&szs,PrintFileSZS,0,false,true,opt_recurse,opt_cut,opt_sort); else IterateFilesParSZS(&szs,list_func,0,false,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); IterateFilesParSZS(&szs,ilist_func,0,false,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 ); } IterateFilesParSZS(&szs,memory_func,&dm,false,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 || opt_rm_aiparam ) 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.tv_sec); 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() { SetupPager(); 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); analyze_param_t ap = {0}; InitializeSZS(&ap.szs); InitializeFile(&ap.fo); SetupPrintScriptByOptions(&ap.ps); patch_action_log_disabled++; CheckTextureRefSZS(&ap.szs,0); ParamList_t *param; for ( param = first_param; param; param = param->next ) { NORMALIZE_FILENAME_PARAM(param); ResetSZS(&ap.szs); enumError err = LoadCreateSZS(&ap.szs,param->arg,true,opt_ignore>0,true); if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) continue; if ( err > ERR_WARNING ) { if ( max_err < err ) max_err = err; continue; } if (!ap.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(ap.szs.fform_file,ap.szs.fform_arch), ap.szs.fname, GetNameFF(script_fform,0), dest ); fflush(stdlog); } if ( err >= ERR_WARNING ) { if ( max_err < err ) max_err = err; continue; } if (!ap.fo.f) { enumError err = CreateFile(&ap.fo,false,dest,FM_STDIO|FM_OVERWRITE); if (err) { max_err = err; break; } ap.ps.f = ap.fo.f; PrintScriptHeader(&ap.ps); } ap.fname = param->arg; //--- analyze by file format switch(ap.szs.fform_arch) { case FF_U8: case FF_WU8: err = ExecAnalyzeSZS(&ap); break; case FF_LE_BIN: err = ExecAnalyzeLECODE(&ap); break; default: PrintHeaderAP(&ap,""); PrintFooterAP(&ap,false,0,"File format not supported."); break; } if (!script_array) { PrintScriptFooter(&ap.ps); ap.ps.f = 0; ResetFile(&ap.fo,0); } } PrintScriptFooter(&ap.ps); ResetPrintScript(&ap.ps); ResetFile(&ap.fo,0); ResetSZS(&ap.szs); return max_err; } // /////////////////////////////////////////////////////////////////////////////// /////////////// command split /////////////// /////////////////////////////////////////////////////////////////////////////// static void print_split_val ( PrintScript_t *ps, ccp name, mem_t val ) { DASSERT(ps); DASSERT(name); if ( val.ptr && val.len ) { char buf[1000]; PrintEscapedString(buf,sizeof(buf),val.ptr,val.len,CHMD_UTF8,'"',0); PrintScriptVars(ps,0,"%s=\"%s\"\n",name,buf); } else PrintScriptVars(ps,0,"%s=\"\"\n",name); } //----------------------------------------------------------------------------- static enumError cmd_split() { static ccp def_path = "\1P/\1F\1?T"; CheckOptDest("-",false); char dest[PATH_MAX]; enumError max_err = ERR_OK; if ( opt_split <= 0 && !strcmp(opt_dest,"-") ) fputc('\n',stdout); split_filename_t spf; InitializeSPF(&spf); print_split_par_t psp = { .format = opt_printf, .split_level = opt_split, .u_use_list = true }; File_t fo; InitializeFile(&fo); PrintScript_t ps; SetupPrintScriptByOptions(&ps); ParamList_t *param; for ( param = first_param; param; param = param->next ) { NORMALIZE_FILENAME_PARAM(param); if (!fo.f) SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, GetExtFF(script_fform,0),false); if (!fo.f) { enumError err = CreateFile(&fo,false,dest,FM_STDIO|FM_OVERWRITE); if (err) { max_err = err; break; } ps.f = fo.f; PrintScriptHeader(&ps); } const u_nsec_t start_nsec = GetTimerNSec(); AnalyseSPF(&spf,false,param->arg,0,CPM_LINK,opt_plus); const u_nsec_t duration_nsec = GetTimerNSec() - start_nsec; if ( opt_split > 0 ) { exmem_t res = PrintSPF(0,&spf,&psp); printf("%s\n",res.data.ptr); FreeExMem(&res); continue; } PutScriptVars(&ps,1,0); print_split_val(&ps,"directory",spf.directory); print_split_val(&ps,"source",spf.source); print_split_val(&ps,"file_name",spf.f_name); print_split_val(&ps,"file_d",spf.f_d); print_split_val(&ps,"file_ext",spf.f_ext); print_split_val(&ps,"normed",spf.norm); print_split_val(&ps,"plus",spf.plus); PrintScriptVars(&ps,0,"plus_order=%d\n",spf.plus_order); print_split_val(&ps,"boost",spf.boost); print_split_val(&ps,"game1",spf.game1); print_split_val(&ps,"game2",spf.game2); print_split_val(&ps,"game",spf.game); PrintScriptVars(&ps,0,"game_order=%d\ngame1_color=%d\ngame2_color=%d\n", spf.game_order, spf.game1_color, spf.game2_color ); print_split_val(&ps,"name",spf.name); print_split_val(&ps,"extra",spf.extra); print_split_val(&ps,"version",spf.version); print_split_val(&ps,"authors",spf.authors); print_split_val(&ps,"editors",spf.editors); print_split_val(&ps,"attribs",spf.attribs); PrintScriptVars(&ps,0,"attrib_order=%d\n",spf.attrib_order); PrintScriptVars(&ps,0,"distrib_flags=\"%u %s\"\n",spf.distrib_flags,PrintDTA(spf.distrib_flags,false)); PrintScriptVars(&ps,0,"le_flags=\"%u %s\"\n",spf.le_flags,PrintLEFL8(spf.le_flags,false)); print_split_val(&ps,"le_group",spf.le_group); const mkw_category_t *tcat = GetCategory(spf.track_cat,MKW_CAT_UNKNOWN); if (tcat) PrintScriptVars(&ps,0,"track_cat=\"%u %s %u\"\n",spf.track_cat,tcat->name,tcat->mode); else PrintScriptVars(&ps,0,"track_cat=\"%u - 0\"\n",spf.track_cat); PrintScriptVars(&ps,0,"lap_count=%u\n",spf.lap_count); if ( spf.speed_factor > 0.0 ) PrintScriptVars(&ps,0,"speed_factor=%5.3f\n",spf.speed_factor); else PutScriptVars(&ps,0,"speed_factor=0\n"); if (opt_printf) { exmem_t res = PrintSPF(0,&spf,&psp); print_split_val(&ps,"printf",res.data); FreeExMem(&res); } PrintScriptVars(&ps,2,"duration_nsec=%llu\n",duration_nsec); fflush(ps.f); if (!script_array) { PrintScriptFooter(&ps); ps.f = 0; ResetFile(&fo,0); } } PrintScriptFooter(&ps); ResetPrintScript(&ps); ResetFile(&fo,0); ResetPSP(&psp); ResetSPF(&spf); return max_err; } // /////////////////////////////////////////////////////////////////////////////// /////////////// command is-texture /////////////// /////////////////////////////////////////////////////////////////////////////// static enumError cmd_is_texture() { disable_checks++; int max_err = 0; szs_file_t szs; InitializeSZS(&szs); patch_action_log_disabled++; uint max_fw = 0; struct { FastBuf_t b; char space[500]; } fbuf; if (long_count) InitializeFastBuf(&fbuf,sizeof(fbuf)); for ( ParamList_t *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_WARNING ) { if (long_count) { AppendFastBuf(&fbuf.b,"-1=err",7); if ( max_fw < 7 ) max_fw = 7; } if ( max_err < err ) max_err = err; continue; } ccp texture_info = 0; PrepareCheckTextureSZS(&szs); CheckTextureRefSZS(&szs,&texture_info); if (long_count) { ccp x = texture_info ? texture_info : "-1"; const int len = strlen(x); AppendFastBuf(&fbuf.b,x,len+1); if ( max_fw < len ) max_fw = len; } else printf("%s\n", texture_info ? texture_info : "-1" ); } if (long_count) { ccp par = fbuf.b.buf; for ( ParamList_t *param = first_param; param; param = param->next ) { printf("%-*s : %s\n",max_fw,par,param->arg); par += strlen(par) + 1; } ResetFastBuf(&fbuf.b); } return max_err; } // /////////////////////////////////////////////////////////////////////////////// /////////////// command features /////////////// /////////////////////////////////////////////////////////////////////////////// static enumError cmd_features() { static ccp def_path = "\1P/\1F\1?T"; CheckOptDest("-",false); if (!strcmp(opt_dest,"-")) SetupPager(); char dest[PATH_MAX]; enumError max_err = ERR_OK; if (!strcmp(opt_dest,"-")) fputc('\n',stdout); szs_file_t szs; InitializeSZS(&szs); analyze_szs_t as; InitializeAnalyzeSZS(&as); features_szs_t fs; InitializeFeaturesSZS(&fs); const int comments = brief_count > 0 ? -brief_count : long_count; File_t fo; InitializeFile(&fo); PrintScript_t ps; SetupPrintScriptByOptions(&ps); ps.eq_tabstop = 3; if ( ps.fform == PSFF_UNKNOWN && comments >= 0 ) ps.fform = PSFF_ASSIGN; 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 ) { if ( max_err < err ) max_err = err; continue; } 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); } //--- analyse szs AnalyzeSZS(&as,false,&szs,param->arg); SetupFeaturesSZS(&fs,&as.have,false); //--- 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; } ps.f = fo.f; PrintScriptHeader(&ps); } //--- name attributes ('dest' can be used now) PrintEscapedString(dest,sizeof(dest),param->arg,-1,CHMD_UTF8,'"',0); //--- print result PrintScriptVars(&ps,1,"file=\"%s\"\n",dest); PrintFeaturesSZS( &ps, &fs, false, comments, 1, long_count, opt_fmodes_include, opt_fmodes_exclude ); PutScriptVars(&ps,2,0); if (!script_array) { PrintScriptFooter(&ps); ps.f = 0; ResetFile(&fo,0); } } PrintScriptFooter(&ps); ResetPrintScript(&ps); ResetFile(&fo,0); ResetFeaturesSZS(&fs); ResetAnalyzeSZS(&as); 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 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.ld,arg,false); } else ScanSlotTranslation(&dinf.translate,0,arg,false); } if (!result_found) ScanSlotTranslation(&dinf.translate,&dinf.ld,result,true); AddParamDistributionInfo(&dinf,false); //--- logging if ( logging >= 1 ) { 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.ld.dis_param.field; for ( i = 0; i < dinf.ld.dis_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 != '.' ) { 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); CreateDistribLD(f,&dinf.ld,true); //--- battle arenas 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 ColorMode_t colmode = GetFileColorized(stdout); ccp col_minus = GetTextMode(colmode,TTM_BOLD|TTM_RED); ccp col_plus = GetTextMode(colmode,TTM_BOLD|TTM_GREEN); ccp col_misc = GetTextMode(colmode,TTM_BOLD|TTM_CYAN); ccp col_reset = GetTextMode(colmode,TTM_RESET); 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]; int required_slot; const int stat = FindSlotsSZS(&szs,slot,&required_slot); 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 ( required_slot != 31 && ( 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; IterateFilesParSZS(szs,update_func,&up,false,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,true,global_check_mode); 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 /////////////// /////////////////////////////////////////////////////////////////////////////// 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; SetupPrintScriptByOptions(&ps); 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; } ps.f = fo.f; 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); ps.f = 0; 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_CONFIG: opt_config = optarg; break; case GO_ALLOW_ALL: allow_all = true; break; case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; case GO_WIDTH: err += ScanOptWidth(optarg); break; case GO_MAX_WIDTH: err += ScanOptMaxWidth(optarg); break; case GO_NO_PAGER: opt_no_pager = true; 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_EXT_ERRORS: ext_errors++; break; case GO_TIMING: log_timing++; break; case GO_WARN: err += ScanOptWarn(optarg); break; case GO_DE: use_de = true; break; case GO_CT_CODE: ctcode_enabled = true; break; case GO_LE_CODE: lecode_enabled = true; break; // optional argument ignored 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_LOAD_PREFIX: err += ScanOptLoadPrefix(optarg); break; case GO_LOAD_CATEGORY: err += ScanOptLoadCategory(optarg); break; case GO_PLUS: opt_plus = optarg; break; case GO_SPLIT: opt_split = str2l(optarg,0,10); break; case GO_PRINTF: opt_printf = optarg; 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_FEATURES: opt_lex_features = true; break; case GO_LEX_RM_FEAT: opt_lex_rm_features = true; 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_REFERENCE: SetReference(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_CASE: err += ScanOptCase(optarg); break; case GO_FMODES: err += ScanOptFModes(optarg); break; case GO_INSTALL: opt_install++; 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_ENDIAN: err += ScanOptBmgEndian(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 ); 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 : ProgInfo.max_error ? ProgInfo.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: PrintHelpColor(&InfoUI_wszst); break; case CMD_CONFIG: err = cmd_config(); break; case CMD_INSTALL: err = cmd_install(); break; case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; case CMD_EXPAND: err = cmd_expand(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_SIZEOF: err = cmd_sizeof(); break; case CMD_CODE: err = cmd_code(); break; case CMD_RECODE: err = cmd_recode(); break; case CMD_SUBFILE: err = cmd_subfile(); break; case CMD_TESTNORM: err = cmd_testnorm(); 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_SPLIT: err = cmd_split(); break; case CMD_IS_TEXTURE: err = cmd_is_texture(); break; case CMD_FEATURES: err = cmd_features(); 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(), main() /////////////// /////////////////////////////////////////////////////////////////////////////// int main_wszst ( int argc, char ** argv ) { tool_name = "wszst"; print_title_func = print_title; SetupLib(argc,argv,WSZST_SHORT,VERSION,TITLE); 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 = CheckEnvOptions2("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,VERSION,TITLE); 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("!\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, ProgInfo.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 ) { ArgManager_t am = {0}; SetupArgManager(&am,LOUP_AUTO,argc,argv,false); ExpandAtArgManager(&am,AMXM_SHORT,10,false); argc = am.argc; argv = am.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); const int err = w->func(argc,argv); ClosePager(); return err; } // /////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// ///////////////////////////////////////////////////////////////////////////////