aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-std.c
diff options
context:
space:
mode:
authorroot <root@dc13.lemmi>2020-07-21 10:00:46 +0200
committerroot <root@dc13.lemmi>2020-07-21 10:00:46 +0200
commit1192c4575ea7afdf38e3fa0df4587a08620af11e (patch)
tree6910a62487c282d0840d2e668aa7eb40b7c8a3fd /project/src/lib-std.c
parent76de8a89f7c1d1b4522eb66e8f89d248e8c1781f (diff)
SZS v2.18a
Diffstat (limited to '')
-rw-r--r--project/src/lib-std.c6604
1 files changed, 6604 insertions, 0 deletions
diff --git a/project/src/lib-std.c b/project/src/lib-std.c
new file mode 100644
index 0000000..fd1d201
--- /dev/null
+++ b/project/src/lib-std.c
@@ -0,0 +1,6604 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * Wiimms SZS Tools *
+ * https://szs.wiimm.de/ *
+ * *
+ ***************************************************************************
+ * *
+ * This file is part of the SZS project. *
+ * Visit https://szs.wiimm.de/ for project details and sources. *
+ * *
+ * Copyright (c) 2011-2020 by Dirk Clemens <wiimm@wiimm.de> *
+ * *
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
+ * *
+ ***************************************************************************/
+
+#include <sys/types.h>
+#ifndef WIN_SZS_LIB
+ #include <sys/ioctl.h>
+#endif
+#include <fcntl.h>
+
+#include "lib-std.h"
+#include "lib-szs.h"
+#include "lib-rarc.h"
+#include "lib-pack.h"
+#include "lib-brres.h"
+#include "lib-breff.h"
+#include "lib-xbmg.h"
+#include "lib-kcl.h"
+#include "lib-kmp.h"
+#include "lib-lecode.h"
+#include "lib-mdl.h"
+#include "lib-pat.h"
+#include "lib-image.h"
+#include "lib-object.h"
+#include "lib-staticr.h"
+#include "lib-ctcode.h"
+#include "lib-common.h"
+#include "lib-bzip2.h"
+#include "dclib-utf8.h"
+#include "dclib-ui.h"
+#include "lib-mkw.h"
+#include "db-mkw.h"
+#include "logo.inc"
+
+#ifdef WIN_SZS_LIB
+ #include <direct.h>
+#endif
+
+#if defined(TEST) && !defined(__APPLE__) && !defined(__CYGWIN__)
+ #include <mcheck.h>
+#endif
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// vars ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ccp tool_name = "?";
+ccp share_path = 0;
+ccp search_path[6] = {0};
+ccp auto_add_path[6] = {0}; // same size as search path
+volatile int SIGINT_level = 0;
+volatile int verbose = 0;
+volatile int logging = 0;
+bool allow_all = false;
+char escape_char = '%';
+bool use_utf8 = true;
+bool use_de = false;
+bool ctcode_enabled = false;
+bool lecode_enabled = false;
+uint opt_ct_mode = 0; // calculated by ctcode_enabled & lecode_enabled
+int testmode = 0;
+int force_count = 0;
+uint opt_tiny = 0;
+bool force_kmp = false;
+OffOn_t opt_battle_mode = OFFON_AUTO;
+OffOn_t opt_export_flags = OFFON_AUTO;
+int opt_route_options = 0;
+OffOn_t opt_wim0 = OFFON_AUTO;
+ccp opt_source = 0;
+StringField_t source_list = {0};
+ccp opt_dest = 0;
+bool opt_mkdir = false;
+bool opt_overwrite = false;
+bool opt_number = false;
+bool opt_remove_src = false;
+bool opt_remove_dest = false;
+bool opt_update = false;
+bool opt_preserve = false;
+ccp opt_extract = 0;
+int opt_ignore = 0;
+bool opt_ignore_setup = false;
+bool opt_purge = false;
+file_format_t opt_fform = FF_UNKNOWN;
+file_format_t fform_compr = FF_YAZ0;
+file_format_t fform_compr_force = 0;
+file_format_t script_fform = FF_TXT;
+ccp script_varname = "res";
+int script_array = 0;
+int opt_pt_dir = 0;
+bool opt_links = false;
+bool opt_rm_aiparam = false;
+u32 opt_align = 0;
+u32 opt_align_u8 = U8_DEFAULT_ALIGN;
+u32 opt_align_pack = PACK_SUBFILE_ALIGN;
+u32 opt_align_brres = BRRES_DEFAULT_ALIGN;
+u32 opt_align_breff = BREFF_DEFAULT_ALIGN;
+u32 opt_align_breft = 0;
+bool print_header = true;
+bool print_param = true;
+int print_sections = 0;
+double opt_tri_area = 1.0;
+double opt_tri_height = 1.0;
+bool opt_kmp_flags = true;
+bool opt_id = false;
+bool opt_base64 = false;
+bool opt_db64 = false;
+EncodeMode_t opt_coding64 = ENCODE_OFF;
+bool opt_verify = false;
+ccp opt_cache = 0;
+ccp opt_cname = 0;
+bool opt_round = false;
+int brief_count = 0;
+int long_count = 0;
+//int full_count = 0;
+int pipe_count = 0;
+int delta_count = 0;
+int diff_count = 0;
+int minimize_level = 0;
+int export_count = 0;
+int all_count = 0;
+bool raw_mode = false;
+SortMode_t opt_sort = SORT_NONE;
+u32 opt_max_file_size = 100*MiB;
+int opt_compr_mode = 0;
+u32 opt_compr = 9;
+bool opt_norm = false;
+int need_norm = 0; // enabled if > 0
+bool opt_fast = false;
+char *opt_basedir = 0;
+int opt_recurse = -1;
+int opt_ext = 0;
+bool opt_decode = false;
+bool opt_cut = false;
+bool opt_cmpr_valid = false;
+u8 opt_cmpr_def[8] = { 0x00,0x00, 0x00,0x20, 0xff,0xff,0xff,0xff };
+uint opt_n_images = 0;
+uint opt_max_images = 5;
+uint opt_min_mipmap_size = 8; // minimal mipmap size
+int opt_mipmaps = 0;
+bool opt_strip = false;
+bool opt_pre_convert = false;
+bool opt_encode_all = false;
+bool opt_encode_img = false;
+bool opt_no_encode = false;
+bool opt_no_recurse = false;
+bool opt_auto_add = false;
+bool opt_no_echo = false;
+bool opt_generic = false;
+bool opt_raw = false;
+bool opt_tracks = false;
+bool opt_arenas = false;
+bool opt_order_all = false;
+ccp opt_order_by = 0;
+ccp opt_write_tracks = 0;
+bool opt_no_check = false;
+CheckMode_t global_check_mode = CMOD_DEFAULT;
+ccp opt_flag_file = 0;
+int opt_xtridata = 0;
+int have_patch_count = 0; // <=0: disable patching at all
+int have_kmp_patch_count = 0;
+int have_kcl_patch_count = 0;
+ParamField_t patch_bmg_list = {0};
+FormatField_t patch_image_list = {0};
+StringField_t ct_dir_list = {0};
+ccp analyze_fname = 0;
+FILE * analyze_file = 0;
+
+double epsilon_pos = DEF_EPSILON_POS; // 'p' Position
+double epsilon_rot = DEF_EPSILON_ROT; // 'r' Rotation
+double epsilon_scale = DEF_EPSILON_SCALE; // 's' Scale
+double epsilon_time = DEF_EPSILON_TIME; // 't' Time
+
+char iobuf [0x400000]; // global io buffer
+//const char zerobuf[0x100] = {0}; // global zero buffer
+
+const char indent_msg[] = "> > > > > > > > > > > > > > > > > > > > ";
+
+const char section_sep[] =
+ "\r\n"
+ "#\f\r\n"
+ "###############################################################################\r\n"
+ "\r\n";
+
+const char section_end[] =
+ "\r\n"
+ "#\f\r\n"
+ "###############################################################################\r\n"
+ "\r\n"
+ "[END]\r\n"
+ "# This section is ignored.\r\n"
+ "\r\n";
+
+// begin of warning+hint line, for checks
+const char check_bowl[] = " + WARNING: ";
+const char check_bohl[] = " - HINT: ";
+const char check_bosl[] = " * SLOT: ";
+const char check_boil[] = " * INFO: ";
+const char check_bosugl[] = " * SUGGESTION: ";
+
+int global_warn_count = 0;
+int global_hint_count = 0;
+int global_info_count = 0;
+
+const ColorSet_t *colset = 0;
+void (*print_title_func) ( FILE * f ) = 0;
+
+const char WarnLevelNameLo[WLEVEL__N][8] =
+{
+ "unknown",
+ "na",
+ "ok",
+ "fixed",
+ "hint",
+ "warn",
+ "error",
+ "fail",
+ "fatal",
+ "invalid",
+};
+
+const char WarnLevelNameUp[WLEVEL__N][8] =
+{
+ "UNKNOWN",
+ "NA",
+ "OK",
+ "FIXED",
+ "HINT",
+ "WARN",
+ "ERROR",
+ "FAIL",
+ "FATAL",
+ "INVALID",
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// tables ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+// PAL: file offset: 3a5a08, 3 cannons, 4 floats each
+// 3a5a08: 500.00000 0 6000.00000 -1.00000
+// 3a5a18: 500.00000 5000.00000 6000.00000 -1.00000
+// 3a5a28: 120.00000 2000.00000 1000.00000 45.00000
+
+const u8 CannonDataLEX[4+3*16] =
+{
+ 0,0,0,3, // number of cannons
+ 0x43,0xfa,0x00,0x00, 0x00,0x00,0x00,0x00, 0x45,0xbb,0x80,0x00, 0xbf,0x80,0x00,0x00,
+ 0x43,0xfa,0x00,0x00, 0x45,0x9c,0x40,0x00, 0x45,0xbb,0x80,0x00, 0xbf,0x80,0x00,0x00,
+ 0x42,0xf0,0x00,0x00, 0x44,0xfa,0x00,0x00, 0x44,0x7a,0x00,0x00, 0x42,0x34,0x00,0x00,
+};
+
+//-----------------------------------------------------------------------------
+
+const u8 TestDataLEX[8*1] =
+{
+ 0, // offline_online
+ 0, // n_offline
+ 0, // n_online
+ ~0, // cond_bit
+ 0, // game_mode
+ 0, // random
+ 0,0
+};
+
+//-----------------------------------------------------------------------------
+
+const char is_arena_name[ARENA__N][9] =
+{
+ "no", // ARENA_NONE
+ "maybe", // ARENA_MAYBE
+ "found", // ARENA_FOUND
+ "dispatch", // ARENA_DISPATCH
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// Setup ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SetupColors()
+{
+ //----- setup colors
+
+ colorize_stdout = colorize_stdlog = opt_colorize; // reset it first
+ colorize_stdout = GetFileColorized(stdout);
+ colorize_stdlog = GetFileColorized(stdlog);
+
+ colout = GetColorSet(colorize_stdout);
+ collog = GetColorSet(colorize_stdlog);
+ colset = collog;
+ SetupStdMsg();
+
+ PRINT("COLORIZE: out=%d[%zd], err=%d[%zd], log=%d[%zd], msg=%d[%zd]\n",
+ colorize_stdout, strlen(colout->reset),
+ colorize_stderr, strlen(colerr->reset),
+ colorize_stdlog, strlen(collog->reset),
+ colorize_stdmsg, strlen(colmsg->reset) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SetupLib ( int argc, char ** argv, ccp p_progname )
+{
+ if ( p_progname && *p_progname )
+ progname = p_progname;
+
+ SetupTimezone(true);
+ GetTimerMSec();
+ SetupColors();
+
+ #if defined(TEST) && !defined(__APPLE__) && !defined(__CYGWIN__)
+ mtrace();
+ #endif
+
+ INIT_TRACE_ALLOC;
+ //MALLOC(1234);
+
+ GetErrorNameHook = LibGetErrorName;
+ GetErrorTextHook = LibGetErrorText;
+
+ #ifdef DEBUG
+ if (!TRACE_FILE)
+ {
+ char fname[100];
+ snprintf(fname,sizeof(fname),"_trace-%s.tmp",p_progname);
+ TRACE_FILE = fopen(fname,"w");
+ if (!TRACE_FILE)
+ fprintf(stderr,"open TRACE_FILE failed: %s\n",fname);
+ }
+ #endif
+
+ #ifdef __BYTE_ORDER
+ TRACE("__BYTE_ORDER=%d\n",__BYTE_ORDER);
+ #endif
+ #ifdef LITTLE_ENDIAN
+ TRACE("LITTLE_ENDIAN=%d\n",LITTLE_ENDIAN);
+ #endif
+ #ifdef BIG_ENDIAN
+ TRACE("BIG_ENDIAN=%d\n",BIG_ENDIAN);
+ #endif
+
+ // numeric types
+
+ TRACE("- int\n");
+ TRACE_SIZEOF(bool);
+ TRACE_SIZEOF(short);
+ TRACE_SIZEOF(int);
+ TRACE_SIZEOF(long);
+ TRACE_SIZEOF(long long);
+ TRACE_SIZEOF(size_t);
+ TRACE_SIZEOF(off_t);
+
+ #ifndef WIN_SZS_LIB
+ TRACE_SIZEOF(int8_t);
+ TRACE_SIZEOF(int16_t);
+ TRACE_SIZEOF(int32_t);
+ TRACE_SIZEOF(int64_t);
+ TRACE_SIZEOF(uint8_t);
+ TRACE_SIZEOF(uint16_t);
+ TRACE_SIZEOF(uint32_t);
+ TRACE_SIZEOF(uint64_t);
+ #endif
+
+ TRACE_SIZEOF(u8);
+ TRACE_SIZEOF(u16);
+ TRACE_SIZEOF(u32);
+ TRACE_SIZEOF(u64);
+ TRACE_SIZEOF(s8);
+ TRACE_SIZEOF(s16);
+ TRACE_SIZEOF(s32);
+ TRACE_SIZEOF(s64);
+ TRACE_SIZEOF(be16_t);
+ TRACE_SIZEOF(be32_t);
+ TRACE_SIZEOF(be64_t);
+
+ TRACE_SIZEOF(float);
+ TRACE_SIZEOF(float32);
+ TRACE_SIZEOF(float3);
+ TRACE_SIZEOF(float3List_t);
+
+ TRACE_SIZEOF(double);
+ TRACE_SIZEOF(double3);
+ TRACE_SIZEOF(double3List_t);
+
+ TRACE_SIZEOF(float34);
+ TRACE_SIZEOF(double34);
+ TRACE_SIZEOF(MatrixD_t);
+
+ TRACE("- arch\n");
+ TRACE_SIZEOF(yaz0_header_t);
+ TRACE_SIZEOF(wbz_header_t);
+ TRACE_SIZEOF(szs_subfile_t);
+ TRACE_SIZEOF(szs_subfile_list_t);
+ TRACE_SIZEOF(szs_file_t);
+ TRACE_SIZEOF(szs_extract_t);
+ TRACE_SIZEOF(szs_iterator_t);
+ TRACE_SIZEOF(string_pool_t);
+ TRACE_SIZEOF(u8_header_t);
+ TRACE_SIZEOF(u8_node_t);
+ TRACE_SIZEOF(pack_header_t);
+ TRACE_SIZEOF(pack_metric_t);
+ TRACE_SIZEOF(rarc_file_header_t);
+ TRACE_SIZEOF(rarc_header_t);
+ TRACE_SIZEOF(rarc_node_t);
+ TRACE_SIZEOF(rarc_entry_t);
+
+ TRACE("- sub file system\n");
+ TRACE_SIZEOF(SubDirList_t);
+ TRACE_SIZEOF(SubDir_t);
+ TRACE_SIZEOF(SubFileList_t);
+ TRACE_SIZEOF(SubFile_t);
+
+ TRACE("- brres\n");
+ TRACE_SIZEOF(brres_t);
+ TRACE_SIZEOF(brres_header_t);
+ TRACE_SIZEOF(brres_root_t);
+ TRACE_SIZEOF(brres_group_t);
+ TRACE_SIZEOF(brres_entry_t);
+ TRACE_SIZEOF(brsub_header_t);
+ TRACE_SIZEOF(brsub_list_t);
+ TRACE_SIZEOF(brsub_cut_t);
+ TRACE_SIZEOF(grp_entry_t);
+ TRACE_SIZEOF(tex_info_t);
+
+ TRACE("- breff\n");
+ TRACE_SIZEOF(breff_item_data_t);
+ TRACE_SIZEOF(breff_item_list_t);
+ TRACE_SIZEOF(breff_item_name_t);
+ TRACE_SIZEOF(breff_root_t);
+ TRACE_SIZEOF(breff_root_head_t);
+ TRACE_SIZEOF(breft_image_t);
+
+ TRACE("- bmg\n");
+ TRACE_SIZEOF(bmg_header_t);
+ TRACE_SIZEOF(bmg_section_t);
+ TRACE_SIZEOF(bmg_inf_t);
+ TRACE_SIZEOF(bmg_inf_item_t);
+ TRACE_SIZEOF(bmg_dat_t);
+ TRACE_SIZEOF(bmg_mid_t);
+ TRACE_SIZEOF(bmg_flw_t);
+ TRACE_SIZEOF(bmg_fli_t);
+ TRACE_SIZEOF(bmg_t);
+ TRACE_SIZEOF(bmg_item_t);
+
+ TRACE("- ctcode\n");
+ TRACE_SIZEOF(ctcode_header_t);
+ TRACE_SIZEOF(ctcode_sect_info_t);
+ TRACE_SIZEOF(ctcode_cup1_head_t);
+ TRACE_SIZEOF(ctcode_cup1_data_t);
+ TRACE_SIZEOF(ctcode_crs1_head_t);
+ TRACE_SIZEOF(ctcode_crs1_data_t);
+ TRACE_SIZEOF(ctcode_t);
+
+ TRACE("- kcl\n");
+ TRACE_SIZEOF(kcl_mode_t);
+ TRACE_SIZEOF(kcl_t);
+ TRACE_SIZEOF(kcl_head_t);
+ TRACE_SIZEOF(kcl_triangle_t);
+ TRACE_SIZEOF(kcl_tridata_status_t);
+ TRACE_SIZEOF(kcl_tridata_flt_t);
+ TRACE_SIZEOF(kcl_tridata_dbl_t);
+ TRACE_SIZEOF(kcl_tridata0_t);
+ TRACE_SIZEOF(kcl_tridata1_t);
+ TRACE_SIZEOF(kcl_tridata_t);
+ TRACE_SIZEOF(kcl_tri_param_t);
+ TRACE_SIZEOF(kcl_cube_t);
+ TRACE_SIZEOF(kcl_tri_t);
+
+ TRACE("- kmp\n");
+ TRACE_SIZEOF(kmp_t);
+ TRACE_SIZEOF(kmp_ph_t);
+ TRACE_SIZEOF(kmp_flag_t);
+ TRACE_SIZEOF(kmp_file_mkw_t);
+ TRACE_SIZEOF(kmp_gopt_t);
+ TRACE_SIZEOF(kmp_gopt2_t);
+ TRACE_SIZEOF(kmp_group_t);
+ TRACE_SIZEOF(kmp_group_list_t);
+ TRACE_SIZEOF(kmp_group_name_t);
+ TRACE_SIZEOF(kmp_rtobj_t);
+ TRACE_SIZEOF(kmp_rtobj_list_t);
+ TRACE_SIZEOF(kmp_linfo_mode);
+ TRACE_SIZEOF(kmp_linfo_t);
+
+ TRACE_SIZEOF(kmp_area_entry_t);
+ TRACE_SIZEOF(kmp_came_entry_t);
+ TRACE_SIZEOF(kmp_ckph_entry_t);
+ TRACE_SIZEOF(kmp_ckpt_entry_t);
+ TRACE_SIZEOF(kmp_cnpt_entry_t);
+ TRACE_SIZEOF(kmp_enph_entry_t);
+ TRACE_SIZEOF(kmp_enpt_entry_t);
+ TRACE_SIZEOF(kmp_gobj_entry_t);
+ TRACE_SIZEOF(kmp_itph_entry_t);
+ TRACE_SIZEOF(kmp_itpt_entry_t);
+ TRACE_SIZEOF(kmp_jgpt_entry_t);
+ TRACE_SIZEOF(kmp_ktpt_entry_t);
+ TRACE_SIZEOF(kmp_mspt_entry_t);
+ TRACE_SIZEOF(kmp_poti_group_t);
+ TRACE_SIZEOF(kmp_poti_point_t);
+ TRACE_SIZEOF(kmp_stgi_entry_t);
+
+ TRACE_SIZEOF(pos_param_t);
+ TRACE_SIZEOF(pos_file_t);
+
+ TRACE("- mdl\n");
+ TRACE_SIZEOF(mdl_t);
+ TRACE_SIZEOF(mdl_head_t);
+ TRACE_SIZEOF(mdl_sect0_t);
+ TRACE_SIZEOF(mdl_sect1_t);
+ TRACE_SIZEOF(mdl_sect2_t);
+ TRACE_SIZEOF(mdl_sect8_t);
+ TRACE_SIZEOF(mdl_sect8_layer_t);
+ TRACE_SIZEOF(mdl_sect10_t);
+ TRACE_SIZEOF(mdl_minimap_t);
+
+ TRACE("- pat\n");
+ TRACE_SIZEOF(pat_t);
+ TRACE_SIZEOF(pat_element_t);
+ TRACE_SIZEOF(pat_analyse_t);
+ TRACE_SIZEOF(pat_head_t);
+ TRACE_SIZEOF(pat_s0_bhead_t);
+ TRACE_SIZEOF(pat_s0_belem_t);
+ TRACE_SIZEOF(pat_s0_sref_t);
+ TRACE_SIZEOF(pat_s0_shead_t);
+ TRACE_SIZEOF(pat_s0_selem_t);
+
+ TRACE("- static rel\n");
+ TRACE_SIZEOF(rel_header_t);
+ TRACE_SIZEOF(rel_sect_info_t);
+ TRACE_SIZEOF(rel_imp_t);
+ TRACE_SIZEOF(rel_data_t);
+ TRACE_SIZEOF(rel_type_t);
+
+ TRACE("- tpl, bti\n");
+ TRACE_SIZEOF(tpl_header_t);
+ TRACE_SIZEOF(tpl_imgtab_t);
+ TRACE_SIZEOF(tpl_pal_header_t);
+ TRACE_SIZEOF(tpl_img_header_t);
+ TRACE_SIZEOF(bti_header_t);
+
+ TRACE("- common.szs\n");
+ TRACE_SIZEOF(geohit_bin_t);
+ TRACE_SIZEOF(geohit_t);
+ TRACE_SIZEOF(itemslot_bin_t);
+ TRACE_SIZEOF(itemslot_t);
+ TRACE_SIZEOF(minigame_kmg_head_t);
+ TRACE_SIZEOF(minigame_kmg_t);
+
+ TRACE("---\n");
+ TRACE_SIZEOF(Color_t);
+ TRACE_SIZEOF(KeywordTab_t);
+ TRACE_SIZEOF(Container_t);
+ TRACE_SIZEOF(ContainerData_t);
+ TRACE_SIZEOF(DataContainer_t);
+ TRACE_SIZEOF(File_t);
+ TRACE_SIZEOF(FileAttrib_t);
+ TRACE_SIZEOF(FormatField_t);
+ TRACE_SIZEOF(FormatFieldItem_t);
+ TRACE_SIZEOF(FuncInfo_t);
+ TRACE_SIZEOF(FuncParam_t);
+ TRACE_SIZEOF(Image_t);
+ TRACE_SIZEOF(ImageGeometry_t);
+ TRACE_SIZEOF(iobuf);
+ TRACE_SIZEOF(List_t);
+ TRACE_SIZEOF(MemItem_t);
+ TRACE_SIZEOF(ParamList_t);
+ TRACE_SIZEOF(PatchImage_t);
+ TRACE_SIZEOF(ScanFile_t);
+ TRACE_SIZEOF(ScanInfo_t);
+ TRACE_SIZEOF(ScanLoop_t);
+ TRACE_SIZEOF(ScanMacro_t);
+ TRACE_SIZEOF(ScanParamMode);
+ TRACE_SIZEOF(ScanParam_t);
+ TRACE_SIZEOF(SetupDef_t);
+ TRACE_SIZEOF(SetupParam_t);
+ TRACE_SIZEOF(StringField_t);
+ TRACE_SIZEOF(SubstString_t);
+ TRACE_SIZEOF(TrackInfo_t);
+ TRACE_SIZEOF(VarMode_t);
+ TRACE_SIZEOF(IntMode_t);
+ TRACE_SIZEOF(Var_t);
+ TRACE_SIZEOF(VarMap_t);
+
+ TRACE("- DbFile\n");
+ TRACE_SIZEOF(DbFile_t);
+ TRACE_SIZEOF(DbFile);
+ TRACE_SIZEOF(UsedFile_t);
+ TRACE_SIZEOF(DbFileSZS_t);
+ TRACE_SIZEOF(DbFileSZS);
+ TRACE_SIZEOF(DbFileRefSZS);
+ TRACE_SIZEOF(UsedFileSZS_t);
+ TRACE_SIZEOF(DbFileSHA1_t);
+ TRACE_SIZEOF(DbFileSHA1);
+ TRACE_SIZEOF(DbFileRefSHA1);
+ TRACE_SIZEOF(UsedFileSHA1_t);
+ TRACE_SIZEOF(DbFileFILE_t);
+ TRACE_SIZEOF(DbFileFILE);
+ TRACE_SIZEOF(DbFileRefFILE);
+ TRACE_SIZEOF(UsedFileFILE_t);
+ TRACE_SIZEOF(DbFileGROUP_t);
+ TRACE_SIZEOF(DbFileGROUP);
+ TRACE_SIZEOF(DbFileRefGROUP);
+ TRACE_SIZEOF(UsedFileGROUP_t);
+
+ TRACE("- DbObject\n");
+ TRACE_SIZEOF(ObjectInfo);
+ TRACE_SIZEOF(UsedObject_t);
+
+ // assertions
+
+ TRACE("-\n");
+ DASSERT( 1 == sizeof(u8) );
+ DASSERT( 2 == sizeof(u16) );
+ DASSERT( 4 == sizeof(u32) );
+ DASSERT( 8 == sizeof(u64) );
+ DASSERT( 1 == sizeof(s8) );
+ DASSERT( 2 == sizeof(s16) );
+ DASSERT( 4 == sizeof(s32) );
+ DASSERT( 8 == sizeof(s64) );
+ DASSERT( 4 == sizeof(float32) );
+ DASSERT( 12 == sizeof(float3) );
+ DASSERT( 24 == sizeof(double3) );
+
+ DASSERT( 1 == sizeof(bmg_encoding_t) );
+ DASSERT( 32 == sizeof(bmg_header_t) );
+
+ DASSERT( 300 == strlen(Minus300) );
+ DASSERT( 4 == sizeof(Color_t) );
+ DASSERT( 0x40 == sizeof(ctcode_cup1_head_t) );
+ DASSERT( 0x100 == sizeof(ctcode_cup1_data_t) );
+ DASSERT( 0x40 == sizeof(ctcode_crs1_head_t) );
+ DASSERT( 0x100 == sizeof(ctcode_crs1_data_t) );
+
+ TRACE("-\n");
+
+ //----- some assertion
+
+ #if HAVE_ASSERT && !defined(WIN_SZS_LIB) // ??? [[mvc]]
+ {
+ double3 d3;
+ ASSERT( (double*)&d3 == d3.v );
+ ASSERT( d3.v+0 == &d3.x );
+ ASSERT( d3.v+1 == &d3.y );
+ ASSERT( d3.v+2 == &d3.z );
+
+ DEFINE_VAR(var);
+ ASSERT( &var.d == var.v );
+ ASSERT( &var.d == var.d3.v );
+ ASSERT( var.v+0 == &var.x );
+ ASSERT( var.v+1 == &var.y );
+ ASSERT( var.v+2 == &var.z );
+
+ ASSERT( sizeof(FuncParam_t) <= sizeof(double3) );
+ ASSERT( sizeof(FuncInfo_t) <= sizeof(double3) );
+
+ ASSERT( sizeof(mdl_sect1_t) == 0xd0 );
+ }
+ #endif
+
+ //----- initialize data structures
+
+ InitializeParamField(&patch_bmg_list);
+ InitializeFormatField(&patch_image_list);
+ stdlog = stdout;
+ MySeedByTime();
+
+
+ //----- setup progname
+
+ tool_name = p_progname;
+ if ( argc > 0 && *argv && **argv )
+ p_progname = *argv;
+ progname = strrchr(p_progname,'/');
+ progname = progname ? progname+1 : p_progname;
+ //argv[0] = (char*)progname;
+ if ( !tool_name || !*tool_name )
+ tool_name = progname;
+
+ TRACE("##PROG## PROG-NAME=%s\n",progname);
+
+
+ //----- setup search_path
+
+ char path[PATH_MAX];
+ ccp *sp = search_path, *sp2;
+ DASSERT( sizeof(search_path)/sizeof(*search_path) > 5 );
+
+ #ifndef WIN_SZS_LIB
+ // determine program path
+ char proc_path[30];
+ snprintf(proc_path,sizeof(proc_path),"/proc/%u/exe",getpid());
+ TRACE("PROC-PATH: %s\n",proc_path);
+
+ static char share[] = "/share/szs/";
+ static char local_share[] = "/usr/local/share/szs/";
+ #ifndef __CYGWIN__
+ share_path = local_share;
+ #endif
+
+ if (readlink(proc_path,path,sizeof(path)))
+ {
+ // program path found!
+ TRACE("PROG-PATH: %s\n",path);
+
+ char * file_ptr = strrchr(path,'/');
+ if ( file_ptr )
+ {
+ // seems to be a real path -> terminate string behind '/'
+ *++file_ptr = 0;
+
+ #ifdef TEST
+ // for development: append 'share' dir
+ StringCopyE(file_ptr,path+sizeof(path),"share/");
+ *sp = STRDUP(path);
+ TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
+ sp++;
+ *file_ptr = 0;
+ #endif
+
+ *sp = STRDUP(path);
+ TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
+ #ifdef __CYGWIN__
+ share_path = *sp;
+ #endif
+ sp++;
+
+ if ( file_ptr-5 >= path && !memcmp(file_ptr-4,"/bin/",5) )
+ {
+ StringCopyS(file_ptr-5,sizeof(path),share);
+ *sp = STRDUP(path);
+ TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
+ sp++;
+ }
+ }
+ }
+
+ // insert 'local_share' if not already done
+
+ for ( sp2 = search_path; sp2 < sp && strcmp(*sp2,local_share); sp2++ )
+ ;
+ if ( sp2 == sp )
+ {
+ *sp = STRDUP(local_share);
+ TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
+ sp++;
+ }
+ #endif
+
+ // insert CWD if not already done
+ getcwd(path,sizeof(path)-1);
+ strcat(path,"/");
+ for ( sp2 = search_path; sp2 < sp && strcmp(*sp2,path); sp2++ )
+ ;
+ if ( sp2 == sp )
+ {
+ *sp = STRDUP("./");
+ TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
+ sp++;
+ }
+
+ *sp = 0;
+ ASSERT( sp - search_path < sizeof(search_path)/sizeof(*search_path) );
+
+ #ifdef TEST0
+ CHECK_TRACE_ALLOC;
+ MEM_CHECK_SETUP(search_path[1]-0x20,0x40);
+ #endif
+
+ SetupStandardSZS();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void NormalizeOptions
+(
+ uint log_level // 0: no log
+ // 1: print PROGRAM_NAME
+ // 2: + print SHARE_PATH + SEARCH_PATH
+ // 3: + print AUTOADD_PATH
+)
+{
+ SetupColors();
+
+
+ //--- diff, delta and minimze
+
+ minimize_level = delta_count < 9 ? delta_count : 9;
+ if ( diff_count > 0 )
+ {
+ minimize_level = 10;
+ if ( delta_count < 2 )
+ delta_count = 2;
+ if ( brief_count < 2 )
+ brief_count = 2;
+ print_header = false;
+ }
+
+
+ //--- compatibility settings
+
+ if ( compatible < COMPAT_1_46 )
+ opt_kmp_flags = false;
+
+ if ( verbose < 0 && PATCH_FILE_MODE & PFILE_F_DEFAULT )
+ PATCH_FILE_MODE = PFILE_M_SILENT;
+
+ global_check_mode = opt_no_check
+ ? 0
+ : GetCheckMode(false,brief_count>0,verbose<0,long_count>0);
+
+
+ //--- CT-CODE & LE-CODE
+
+ opt_ct_mode = CTM_NINTENDO;
+ if (lecode_enabled)
+ {
+ ctcode_enabled = true;
+ opt_ct_mode = CTM_LECODE2;
+ }
+ else if (ctcode_enabled)
+ opt_ct_mode = CTM_CTCODE;
+ PRINT0("OPT: %s\n",GetCtLeInfo());
+
+
+ //--- loglevel
+
+ if (force_count)
+ force_kmp = true;
+
+ if ( log_level > 0 )
+ {
+ if (print_title_func)
+ print_title_func(stdout);
+
+ fprintf(stdlog,"PROGRAM_NAME = %s\n",progname);
+ if ( compatible != COMPAT_CURRENT )
+ fprintf(stdlog,"COMPATIBILITY = %s\n",PrintOptCompatible());
+
+ if ( log_level > 2 )
+ {
+ fprintf(stdlog,"SHARE_PATH = %s\n",share_path);
+
+ ccp * sp;
+ for ( sp = search_path; *sp; sp++ )
+ fprintf(stdlog,"SEARCH_PATH[%td] = %s\n",sp-search_path,*sp);
+
+ if ( log_level > 1 )
+ {
+ SetupAutoAdd();
+ for ( sp = auto_add_path; *sp; sp++ )
+ fprintf(stdlog,"AUTOADD_PATH[%td] = %s\n",sp-auto_add_path,*sp);
+ }
+ }
+ fprintf(stdlog,"\n");
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetCtLeInfo()
+{
+ return PrintCircBuf("ct=%d, le=%d, mode=%d[%s]",
+ ctcode_enabled, lecode_enabled, opt_ct_mode,
+ GetCtModeNameBMG(opt_ct_mode,true) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const KeywordTab_t * CheckCommandHelper
+ ( int argc, char ** argv, const KeywordTab_t * key_tab )
+{
+ DASSERT(argv);
+ DASSERT(key_tab);
+ TRACE("CheckCommandHelper(%d,) optind=%d\n",argc,optind);
+
+ if ( optind >= argc )
+ {
+ ERROR0(ERR_SYNTAX,"Missing command.\n");
+ return 0;
+ }
+
+ int cmd_stat;
+ ccp arg = argv[optind];
+ const KeywordTab_t * cmd = ScanKeyword(&cmd_stat,arg,key_tab);
+
+ if (!cmd)
+ {
+ if ( cmd_stat < 2 && toupper((int)*arg) == 'C' )
+ {
+ if ( arg[1] == '-' )
+ {
+ arg += 2;
+ cmd = ScanKeyword(&cmd_stat,arg,key_tab);
+ }
+ else
+ {
+ int cmd_stat2;
+ cmd = ScanKeyword(&cmd_stat2,arg+1,key_tab);
+ }
+ }
+
+ if (!cmd)
+ PrintKeywordError(key_tab,arg,cmd_stat,0,0);
+ else if ( opt_colorize < COLMD_ON )
+ {
+ opt_colorize = COLMD_ON;
+ SetupColors();
+ }
+ }
+
+ return cmd;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// error messages ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ccp LibGetErrorName ( int stat, ccp ret_not_found )
+{
+ switch(stat)
+ {
+ case ERR_CACHE_USED: return "SZS CACHE USED";
+ case ERR_INVALID_IFORM: return "INVALID IMAGE FORMAT";
+ case ERR_INVALID_FFORM: return "INVALID FILE FORMAT";
+ case ERR_BZIP2: return "BZIP2 ERROR";
+ case ERR_PNG: return "PNG ERROR";
+ }
+ return ret_not_found;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp LibGetErrorText ( int stat, ccp ret_not_found )
+{
+ switch(stat)
+ {
+ case ERR_CACHE_USED: return "SZS cache used";
+ case ERR_INVALID_IFORM: return "Invalid image format";
+ case ERR_INVALID_FFORM: return "Invalid file format";
+ case ERR_BZIP2: return "BZIP2 error";
+ case ERR_PNG: return "PNG error";
+ }
+ return ret_not_found;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// compatibility ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+compatible_t compatible = COMPAT_CURRENT;
+
+const compatible_info_t compatible_info[COMPAT__N] =
+{
+ { COMPAT_0, 2385, "0.01" },
+ { COMPAT_1_23, 4682, "1.23" },
+ { COMPAT_1_39, 6123, "1.39" },
+ { COMPAT_1_44, 6558, "1.44" },
+ { COMPAT_1_46, 6614, "1.46" },
+ { COMPAT_2_08, 7961, "2.08" },
+ { COMPAT_CURRENT, REVISION_NUM, VERSION_NUM }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+const compatible_info_t * ScanCompatible ( ccp arg )
+{
+ if ( !arg || !*arg )
+ return compatible_info + COMPAT_CURRENT;
+
+ if ( *arg == 'r' || *arg == 'R' )
+ {
+ char *end;
+ const uint rev = strtoul(arg+1,&end,10);
+ if ( end == arg || *end )
+ return 0;
+
+ compatible_t c = COMPAT_CURRENT;
+ while ( c > COMPAT_0 && rev < compatible_info[c].revision )
+ c--;
+ return compatible_info + c;
+ }
+
+ if ( *arg == 'v' || *arg == 'V' )
+ arg++;
+
+ if ( *arg >= '0' && *arg <= '9' )
+ {
+ compatible_t c = COMPAT_CURRENT;
+ while ( c > COMPAT_0 && strcmp(arg,compatible_info[c].version) < 0 )
+ c--;
+ return compatible_info + c;
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptCompatible ( ccp arg )
+{
+ const compatible_info_t *ci = ScanCompatible(arg);
+ if (!ci)
+ {
+ ERROR0(ERR_SYNTAX,
+ "Wrong argument for option --compatible: %s\n",arg);
+ return 1;
+ }
+
+ compatible = ci->compatible;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+char * PrintOptCompatible()
+{
+ uint buf_size = 20;
+ char *buf = GetCircBuf(buf_size);
+
+ const compatible_info_t *ci = compatible_info
+ + ( (uint)compatible < COMPAT__N ? compatible : COMPAT_CURRENT );
+ snprintf( buf, buf_size, "v%s, r%u", ci->version, ci->revision );
+ return buf;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// output_mode_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+output_mode_t output_mode = {0};
+
+void InitializeOutputMode ( output_mode_t * outmode )
+{
+ DASSERT(outmode);
+ outmode->syntax = true;
+ outmode->mode = 1;
+ outmode->cross_ref = true;
+ outmode->hex = false;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// [[obsolete+]] old_container ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// old [[container]] will be replaced by Container_t & ContainerData_t
+///////////////////////////////////////////////////////////////////////////////
+
+DataContainer_t * UseSubContainer
+(
+ DataContainer_t *dc, // old_container to reuse, if
+ // InDataContainer(dc,data) && ref:count == 1
+ ContainerData_t *cdata_src // NULL or container data source
+)
+{
+ if ( dc && dc->container.cdata == cdata_src )
+ return dc;
+
+ if (dc)
+ FreeContainer(dc);
+
+ dc = MALLOC(sizeof(*dc));
+ noPRINT("CONTAINER: NEW(%p), dc_alloced=%d\n",dc,dc_alloced);
+ DASSERT(dc);
+ memset(dc,0,sizeof(*dc));
+
+ if (cdata_src)
+ cdata_src->ref_count++;
+ CatchContainerData(&dc->container,false,cdata_src);
+ ContainerData_t *cdata = dc->container.cdata;
+ if (cdata)
+ {
+ dc->data = cdata->data;
+ dc->size = cdata->size;
+ }
+ dc->ref_count = 1;
+
+ return dc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+DataContainer_t * NewContainer
+(
+ DataContainer_t *dc, // old_container to reuse,
+ // if InDataContainer(dc,data)
+ const void *data, // data to copy/move/link
+ uint size, // size of 'data'
+ CopyMode_t mode // copy mode on creation
+)
+{
+ if ( InDataContainer(dc,data) && dc->ref_count == 1 )
+ return dc;
+
+ if (dc)
+ FreeContainer(dc);
+
+ dc = MALLOC(sizeof(*dc));
+ noPRINT("CONTAINER: NEW(%p), dc_alloced=%d\n",dc,dc_alloced);
+ DASSERT(dc);
+ memset(dc,0,sizeof(*dc));
+
+ CreateContainer(&dc->container,false,data,size,mode);
+ ContainerData_t *cdata = dc->container.cdata;
+ if (cdata)
+ {
+ dc->data = cdata->data;
+ dc->size = cdata->size;
+ }
+ dc->ref_count = 1;
+ return dc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// old [[container]] will be replaced by Container_t & ContainerData_t
+
+DataContainer_t * DupContainer
+(
+ DataContainer_t *dup_dc, // old_container to reuse, if
+ // InDataContainer(dc,data) && ref:count == 1
+ // => complete dc->data is copied
+ const void *data, // data to copy/move/link
+ uint size, // size of 'data'
+ bool include_behind, // on creation: include 'dc->data'
+ // behind 'data+size' if possible
+ const void **new_data // not NULL: store new data pointer here
+)
+{
+ if (InDataContainer(dup_dc,data))
+ {
+ if ( dup_dc->ref_count == 1 )
+ {
+ if (new_data)
+ *new_data = data;
+ return dup_dc;
+ }
+
+ if (include_behind)
+ {
+ const uint max = dup_dc->data + dup_dc->size - (u8*)data;
+ if ( size < max )
+ size = max;
+ }
+ }
+
+ DataContainer_t *dc = MALLOC(sizeof(*dc));
+ noPRINT("CONTAINER: NEW(%p), dc_alloced=%d\n",dc,dc_alloced);
+ DASSERT(dc);
+ memset(dc,0,sizeof(*dc));
+
+ CreateContainer(&dc->container,false,data,size,CPM_COPY);
+ ContainerData_t *cdata = dc->container.cdata;
+ if (cdata)
+ {
+ dc->data = cdata->data;
+ dc->size = cdata->size;
+ }
+ dc->ref_count = 1;
+
+ if (dup_dc)
+ FreeContainer(dup_dc);
+
+ if (new_data)
+ *new_data = dc->data;
+
+ return dc;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// old [[container]] will be replaced by Container_t & ContainerData_t
+
+DataContainer_t * AllocContainer
+(
+ // increments the reference counter and returns 'dc'
+
+ DataContainer_t *dc // NULL or valid old_container
+)
+{
+ if (dc)
+ {
+ dc->ref_count++;
+ noPRINT("CONTAINER: ALLOC(%p), ref_count=%d\n",dc,dc->ref_count);
+ }
+ return dc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// old [[container]] will be replaced by Container_t & ContainerData_t
+
+DataContainer_t * FreeContainer
+(
+ // decrements the reference counter and returns 'NULL'
+ // if reference counter == 0: free data and/or dc
+
+ DataContainer_t *dc // NULL or valid old_container
+)
+{
+ if (dc)
+ {
+ noPRINT("CONTAINER: FREE(%p) ref_count=%d\n", dc, dc->ref_count );
+ DASSERT( dc->ref_count > 0 );
+
+ if (!--dc->ref_count)
+ {
+ ResetContainer(&dc->container);
+
+ if (dc->data_alloced)
+ FREE((void*)dc->data);
+
+ if (dc->dc_alloced)
+ FREE(dc);
+ else
+ {
+ dc->data = (void*)EmptyString;
+ dc->size = 0;
+ dc->data_alloced = false;
+ }
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// old [[container]] will be replaced by Container_t & ContainerData_t
+
+bool InDataContainer
+(
+ // return true, if ptr is in old_container
+
+ const DataContainer_t *dc, // NULL or valid old_container
+ const void *ptr // NULL or pointer to test
+)
+{
+ return dc && ptr && (u8*)ptr >= dc->data && (u8*)ptr <= dc->data + dc->size;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// string helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int NintendoCmp ( ccp path1, ccp path2 )
+{
+ noPRINT("NintendoCmp( | %s | %s | )\n",path1,path2);
+
+ // try to sort in a nintendo like way
+ // 1.) ignoring case but sort carefully directories
+ // 2.) files before sub directories
+
+ static uchar transform[0x100] = {0,0};
+ if (!transform[1]) // ==> setup needed once!
+ {
+ // define some special characters
+
+ uint index = 1;
+ transform[(u8)'/'] = index++;
+ transform[(u8)'.'] = index++;
+
+ // define all characters until and excluding 'A'
+
+ uint i;
+ for ( i = 1; i < 'A'; i++ )
+ if (!transform[i])
+ transform[i] = index++;
+
+ // define letters
+
+ for ( i = 'A'; i <= 'Z'; i++ )
+ transform[i] = transform[i-'A'+'a'] = index++;
+
+ // define all other
+
+ for ( i = 1; i < sizeof(transform); i++ )
+ if (!transform[i])
+ transform[i] = index++;
+
+ DASSERT( index <= sizeof(transform) );
+ #if defined(TEST) && defined(DEBUG)
+ //HexDump16(stderr,0,0,transform,sizeof(transform));
+ #endif
+ }
+
+
+ //--- eliminate equal characters
+
+ while ( *path1 == *path2 )
+ {
+ if (!*path1)
+ return 0;
+ path1++;
+ path2++;
+ }
+
+ //--- start the case+path test
+
+ const uchar * p1 = (const uchar *)path1;
+ const uchar * p2 = (const uchar *)path2;
+ while ( *p1 || *p2 )
+ {
+ int ch1 = transform[*p1++];
+ int ch2 = transform[*p2++];
+ int stat = ch1 - ch2;
+ noPRINT("%02x,%02x,%c %02x,%02x,%c -> %2d\n",
+ p1[-1], ch1, ch1, p2[-1], ch2, ch2, stat );
+ if (stat)
+ {
+ #ifdef WSZST // special case for Wiimms SZS Tools
+ // sort files before dirs
+ const bool indir1 = strchr((ccp)p1-1,'/') != 0;
+ const bool indir2 = strchr((ccp)p2-1,'/') != 0;
+ return indir1 != indir2 ? indir1 - indir2 : stat;
+ #else
+ return stat;
+ #endif
+ }
+
+ if ( ch1 == 1 )
+ break;
+ }
+ return *path1 - *path2;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void PrintNumF
+(
+ FILE *f, // output stream
+ const u8 *data, // valid pointer to data
+ uint size, // size of 'data'
+ ccp type_str // NULL or type string
+)
+{
+ DASSERT(f);
+ DASSERT(data);
+
+ const u8 *end = data + size;
+ while ( type_str && data < end )
+ {
+ switch( type_str[0] << 8 | type_str[1] )
+ {
+ case 'u' << 8 | '1':
+ fprintf(f," %4u",*data++);
+ type_str += 2;
+ break;
+
+ case 'u' << 8 | '2':
+ fprintf(f," %4u",be16(data));
+ data += 2;
+ type_str += 2;
+ break;
+
+ case 'x' << 8 | '1':
+ fprintf(f," 0x%02x",*data++);
+ type_str += 2;
+ break;
+
+ case 'x' << 8 | '2':
+ fprintf(f," 0x%02x",be16(data));
+ data += 2;
+ type_str += 2;
+ break;
+
+ case 'f' << 8 | '4':
+ fprintf(f," %6.3f",bef4(data));
+ data += 4;
+ type_str += 2;
+ break;
+
+ default:
+ type_str = 0;
+ break;
+ }
+ }
+
+ while ( data < end )
+ fprintf(f," 0x%02x",*data++);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// word compare functions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+uint CreateHaystackListWCMP
+(
+ char * buf, // valid pointer to output buffer
+ uint buf_size, // soue of 'buf'
+ ccp * source // NULL terminated list with source Strings
+)
+{
+ DASSERT(buf);
+ DASSERT( buf_size > 10 );
+ DASSERT(source);
+
+ char *dest = buf;
+ char * dest_end = buf + buf_size - 4;
+
+ uint src_count = 0;
+ while (*source)
+ {
+ ccp src = *source++;
+ if (src_count++)
+ *dest++ = '|';
+
+ *dest++ = ' ';
+ bool have_space = true;
+ while ( dest < dest_end )
+ {
+ const uchar ch = *src++;
+ if ( ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' || ch == '$' )
+ {
+ *dest++ = ch;
+ have_space = false;
+ }
+ else if ( ch >= 'A' && ch <= 'Z' )
+ {
+ *dest++ = ch + ('a'-'A');
+ have_space = false;
+ }
+ else if (!ch)
+ {
+ break;
+ }
+ else if (!have_space)
+ {
+ *dest++ = ' ';
+ have_space = true;
+ }
+ }
+ if (!have_space)
+ *dest++ = ' ';
+ }
+
+ DASSERT( dest < buf + buf_size );
+ *dest = 0;
+ return dest - buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint CreateHaystackWCMP
+(
+ char * buf, // valid pointer to output buffer
+ uint buf_size, // soue of 'buf'
+ ccp source // source String
+)
+{
+ ccp list[2] = { source, 0 };
+ return CreateHaystackListWCMP(buf,buf_size,list);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint CreateNeedleWCMP
+(
+ char * buf, // valid pointer to output buffer
+ uint buf_size, // soue of 'buf'
+ ccp source // NULL terminated list with source Strings
+)
+{
+ DASSERT(buf);
+ DASSERT( buf_size > 10 );
+ DASSERT(source);
+
+ char *dest = buf;
+ char * dest_end = buf + buf_size - 4;
+
+ if ( *source == '*' )
+ source++;
+ else
+ *dest++ = ' ';
+
+ bool have_space = true;
+ while ( dest < dest_end )
+ {
+ const uchar ch = *source++;
+ if ( ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' || ch == '$' )
+ {
+ *dest++ = ch;
+ have_space = false;
+ }
+ else if ( ch >= 'A' && ch <= 'Z' )
+ {
+ *dest++ = ch + ('a'-'A');
+ have_space = false;
+ }
+ else if (!ch)
+ {
+ break;
+ }
+ else if (!have_space)
+ {
+ *dest++ = ' ';
+ have_space = true;
+ }
+ }
+
+ DASSERT( dest < buf + buf_size );
+ *dest = 0;
+ return dest - buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool FindWCMP
+(
+ ccp haystack, // search in this string
+ ccp needle, // try to find this string
+ uint flags, // bit field:
+ // bit-0 set = 1: 'haystack' already in WCMP format
+ // bit-1 set = 2: 'needle' already in WCMP format
+ char * tempbuf, // NULL or fast buffer for temporary WCMP conversions
+ uint tempbuf_size // size of 'buf'
+)
+{
+ if ( !haystack || !*haystack )
+ return false;
+ if ( !needle || !*needle )
+ return true;
+
+ const uint extra_size = 3;
+
+ char *alloced_haystack = 0;
+ if (!(flags&1))
+ {
+ ccp haystack_list[2] = { haystack, 0 };
+
+ uint len = strlen(haystack) + extra_size;
+ if ( tempbuf && len <= tempbuf_size )
+ {
+ len = CreateHaystackListWCMP(tempbuf,tempbuf_size,haystack_list);
+ haystack = tempbuf;
+
+ // adjust temp buf for next access
+ tempbuf += len+1;
+ tempbuf_size -= len+1;
+ }
+ else
+ {
+ alloced_haystack = MALLOC(len);
+ CreateHaystackListWCMP(alloced_haystack,len,haystack_list);
+ needle = alloced_haystack;
+ }
+ }
+
+ char *alloced_needle = 0;
+ if (!(flags&2))
+ {
+ uint len = strlen(needle) + extra_size;
+ if ( tempbuf && len <= tempbuf_size )
+ {
+ len = CreateNeedleWCMP(tempbuf,tempbuf_size,needle);
+ needle = tempbuf;
+
+ // adjust of temp buf not needed
+ //tempbuf += len+1;
+ //tempbuf_size -= len+1;
+ }
+ else
+ {
+ alloced_needle = MALLOC(len);
+ CreateNeedleWCMP(alloced_needle,len,needle);
+ needle = alloced_needle;
+ }
+ }
+
+ const bool stat = strstr(haystack,needle) != 0;
+
+ if (alloced_haystack)
+ FREE(alloced_haystack);
+ if (alloced_needle)
+ FREE(alloced_needle);
+
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+search_filter_t * AllocSearchFilter ( uint n )
+{
+ search_filter_t *f = CALLOC(n+1,sizeof(search_filter_t));
+ f->mode = n;
+ return f+1;
+}
+
+void FreeSearchFilter ( search_filter_t *f )
+{
+ if (f)
+ {
+ int i, n = f[-1].mode;
+ for ( i = 0; i < n; i++ )
+ FreeString(f[i].string);
+ FREE(f-1);
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// string substitutions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanEscapeChar ( ccp arg )
+{
+ if ( arg && strlen(arg) > 1 )
+ {
+ ERROR0(ERR_SYNTAX,"Invalid character (option --esc): '%s'\n",arg);
+ return -1;
+ }
+
+ escape_char = arg ? *arg : 0;
+ return (unsigned char)escape_char;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+char * SubstString
+(
+ char * buf, // destination buffer
+ size_t bufsize, // size of 'buf'
+ SubstString_t * tab, // replacement table
+ ccp source, // source text
+ int * count, // not NULL: number of replacements
+ char escape_char // the escape character, usually '%'
+)
+{
+ ASSERT(buf);
+ ASSERT(bufsize > 1);
+ ASSERT(tab);
+ TRACE("SubstString(%s)\n",source);
+
+ char tempbuf[PATH_MAX];
+ int conv_count = 0;
+
+ char *dest = buf;
+ char *end = buf + bufsize + 1;
+ if (source)
+ while ( dest < end && *source )
+ {
+ if ( *source != escape_char && *source != 1 )
+ {
+ *dest++ = *source++;
+ continue;
+ }
+ if ( source[0] == source[1] )
+ {
+ source++;
+ *dest++ = *source++;
+ continue;
+ }
+ ccp start = source++;
+
+ bool check_prev = *source == '?';
+ if (check_prev)
+ source++;
+
+ u32 p1, p2, stat;
+ source = ScanRangeU32(source,&stat,&p1,&p2,0,~(u32)0);
+ if ( stat == 1 )
+ p1 = 0;
+ else if ( stat < 1 )
+ {
+ p1 = 0;
+ p2 = ~(u32)0;
+ }
+
+ char ch = *source++;
+ int convert = 0;
+ if ( ch == 'u' || ch == 'U' )
+ {
+ convert++;
+ ch = *source++;
+ }
+ else if ( ch == 'l' || ch == 'L' )
+ {
+ convert--;
+ ch = *source++;
+ }
+ if (!ch)
+ break;
+
+ size_t count = source - start;
+
+ SubstString_t * ptr;
+ for ( ptr = tab; ptr->c1; ptr++ )
+ if ( ch == ptr->c1 || ch == ptr->c2 )
+ {
+ if (ptr->str)
+ {
+ const size_t slen = strlen(ptr->str);
+ if ( p1 > slen )
+ p1 = slen;
+ if ( p2 > slen )
+ p2 = slen;
+ count = p2 - p1;
+ start = ptr->str+p1;
+ conv_count++;
+ }
+ else
+ count = 0;
+ break;
+ }
+
+ if (!ptr->c1) // invalid conversion
+ convert = 0;
+
+ if ( count > sizeof(tempbuf)-1 )
+ count = sizeof(tempbuf)-1;
+ TRACE("COPY '%.*s' conv=%d\n",(int)count,start,convert);
+ if ( convert > 0 )
+ {
+ char * tp = tempbuf;
+ size_t copy_count = count;
+ while ( copy_count-- > 0 )
+ *tp++ = toupper((int)*start++);
+ *tp = 0;
+ }
+ else if ( convert < 0 )
+ {
+ char * tp = tempbuf;
+ size_t copy_count = count;
+ while ( copy_count-- > 0 )
+ *tp++ = tolower((int)*start++); // cygwin needs the '(int)'
+ *tp = 0;
+ }
+ else
+ {
+ memcpy(tempbuf,start,count);
+ tempbuf[count] = 0;
+ }
+ char * new_dest
+ = NormalizeFileName(dest,end-dest,tempbuf,ptr->allow_slash,use_utf8);
+ if ( check_prev && *dest == '.' )
+ {
+ int len = new_dest - dest;
+ if ( dest - buf > len && !memcmp(dest-len,dest,len) )
+ new_dest = dest;
+ }
+ dest = new_dest;
+ }
+
+ if (count)
+ *count = conv_count;
+ *dest = 0;
+ return dest;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SubstDest
+(
+ char * buf, // destination buffer
+ size_t bufsize, // size of 'buf'
+ ccp source, // source text
+ ccp dest, // destination path
+ ccp dest_fname, // if 'dest' has no file part
+ // then extract filename from here or source
+ ccp default_ext, // NULL or default extension (e.g. '.ext')
+ bool dest_is_dir // destination is a directory
+)
+{
+ DASSERT(buf);
+ DASSERT(source);
+
+ if ( !dest || !*dest )
+ {
+ StringCopyS(buf,bufsize,source);
+ return 0;
+ }
+
+ if ( source && !strcmp(source,"-") && ( !opt_dest || !strcmp(opt_dest,dest) ))
+ {
+ StringCopyS(buf,bufsize,"-");
+ return 0;
+ }
+
+
+ //---- check source path
+
+ char src_path[PATH_MAX];
+ StringCopyS(src_path,sizeof(src_path),source);
+
+ char * p = strrchr(src_path,'/');
+ ccp dir, name, fname;
+ if (p)
+ {
+ dir = src_path;
+ *p++ = 0;
+ name = p;
+ fname = source + ( name - src_path );
+ }
+ else
+ {
+ dir = ".";
+ name = src_path;
+ fname = source;
+ }
+
+ p = strrchr(name,'.');
+ ccp ext;
+ if (p)
+ {
+ *p = 0;
+ ext = source + ( p - src_path );
+ }
+ else
+ ext = "";
+
+ SubstString_t subst_tab[] =
+ {
+ { 'q', 'Q', 1, source },
+ { 'p', 'P', 1, dir },
+ { 'f', 'F', 1, fname },
+ { 'n', 'N', 1, name },
+ { 'e', 'E', 1, ext },
+ { 't', 'T', 1, default_ext ? default_ext : ext },
+ {0,0,0,0}
+ };
+
+ #if HAVE_PRINT0
+ {
+ const SubstString_t * ptr;
+ for ( ptr = subst_tab; ptr->c1; ptr++ )
+ PRINT(" %c %c %u -> %s\n",
+ ptr->c1, ptr->c2, ptr->allow_slash, ptr->str );
+ }
+ #endif
+
+ char dest_path[PATH_MAX];
+ if (dest_is_dir)
+ dest = PathCatPP(dest_path,sizeof(dest_path),dest,"/");
+ else if (IsDirectory(dest,0))
+ {
+ if (!dest_fname)
+ dest_fname = source;
+ ccp p = strrchr(dest_fname,'/');
+ dest = PathCatPP(dest_path,sizeof(dest_path), dest, p ? p+1 : dest_fname );
+ }
+
+ int conv_count;
+ SubstString(buf,bufsize,subst_tab,dest,&conv_count,escape_char);
+ if ( opt_number && !opt_overwrite )
+ NumberedFilename(buf,bufsize,buf,default_ext,0,true);
+
+ return conv_count;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// FormatField_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeFormatField ( FormatField_t * ff )
+{
+ DASSERT(ff);
+ memset(ff,0,sizeof(*ff));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetFormatField ( FormatField_t * ff )
+{
+ ASSERT(ff);
+ if ( ff && ff->used > 0 )
+ {
+ ASSERT(ff->list);
+ FormatFieldItem_t *ptr = ff->list, *end;
+ for ( end = ptr + ff->used; ptr < end; ptr++ )
+ FreeString(ptr->key);
+ FREE(ff->list);
+ }
+ InitializeFormatField(ff);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void MoveFormatField ( FormatField_t * dest, FormatField_t * src )
+{
+ DASSERT(src);
+ DASSERT(dest);
+ if ( src != dest )
+ {
+ ResetFormatField(dest);
+ dest->list = src->list;
+ dest->used = src->used;
+ dest->size = src->size;
+ InitializeFormatField(src);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint FindFormatFieldHelper ( const FormatField_t * ff, bool * p_found, ccp key )
+{
+ ASSERT(ff);
+
+ int beg = 0;
+ if ( ff && key )
+ {
+ int end = ff->used - 1;
+ while ( beg <= end )
+ {
+ uint idx = (beg+end)/2;
+ int stat = strcmp(key,ff->list[idx].key);
+ if ( stat < 0 )
+ end = idx - 1 ;
+ else if ( stat > 0 )
+ beg = idx + 1;
+ else
+ {
+ TRACE("FindFormatFieldHelper(%s) FOUND=%d/%d/%d\n",
+ key, idx, ff->used, ff->size );
+ if (p_found)
+ *p_found = true;
+ return idx;
+ }
+ }
+ }
+
+ TRACE("FindFormatFieldHelper(%s) failed=%d/%d/%d\n",
+ key, beg, ff->used, ff->size );
+
+ if (p_found)
+ *p_found = false;
+ return beg;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int FindFormatFieldIndex ( const FormatField_t * ff, ccp key, int not_found_value )
+{
+ bool found;
+ const int idx = FindFormatFieldHelper(ff,&found,key);
+ return found ? idx : not_found_value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormatFieldItem_t * FindFormatField ( const FormatField_t * ff, ccp key )
+{
+ bool found;
+ const int idx = FindFormatFieldHelper(ff,&found,key);
+ return found ? ff->list + idx : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormatFieldItem_t * FindFormatFieldNC ( const FormatField_t * ff, ccp key )
+{
+ DASSERT(ff);
+ DASSERT(key);
+
+ FormatFieldItem_t *ptr = ff->list, *end;
+ for ( end = ptr + ff->used; ptr < end; ptr++ )
+ if (!strcasecmp(ptr->key,key))
+ return ptr;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormatFieldItem_t * MatchFormatField ( const FormatField_t * ff, ccp key )
+{
+ DASSERT(ff);
+ DASSERT(key);
+
+ FormatFieldItem_t *ptr = ff->list, *end;
+ for ( end = ptr + ff->used; ptr < end; ptr++ )
+ if (MatchPattern(ptr->key,key,'/'))
+ return ptr;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static FormatFieldItem_t * InsertFormatFieldHelper ( FormatField_t * ff, int idx )
+{
+ DASSERT(ff);
+ DASSERT( ff->used <= ff->size );
+ noPRINT("+FF: %u/%u/%u\n",idx,ff->used,ff->size);
+ if ( ff->used == ff->size )
+ {
+ ff->size += 0x100;
+ ff->list = REALLOC(ff->list,ff->size*sizeof(*ff->list));
+ }
+ DASSERT( idx <= ff->used );
+ FormatFieldItem_t * dest = ff->list + idx;
+ memmove(dest+1,dest,(ff->used-idx)*sizeof(*dest));
+ ff->used++;
+ return dest;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool RemoveFormatField ( FormatField_t * ff, ccp key )
+{
+ bool found;
+ uint idx = FindFormatFieldHelper(ff,&found,key);
+ if (found)
+ {
+ ff->used--;
+ ASSERT( idx <= ff->used );
+ FormatFieldItem_t * dest = ff->list + idx;
+ FREE((char*)dest);
+ memmove(dest,dest+1,(ff->used-idx)*sizeof(*dest));
+ }
+ return found;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void SetFormatFieldParam
+(
+ FormatFieldItem_t * item, // valid attrib item
+ ccp key, // comma list of keys
+ ccp keyend // end of key
+)
+{
+ DASSERT(item);
+ DASSERT(key);
+ DASSERT(keyend);
+
+ bool first = true;
+ for(;;)
+ {
+ while ( key < keyend && ( *key >= 0 && *key <= ' ' || *key == ',' ))
+ key++;
+ char buf[100];
+ char *bufptr = buf, *bufend = buf + sizeof(buf) - 1;
+ while ( key < keyend && *key > ' ' && *key != ',' )
+ {
+ if ( bufptr < bufend )
+ *bufptr++ = toupper((int)*key);
+ key++;
+ }
+
+ if ( bufptr == buf )
+ break;
+ *bufptr = 0;
+ noPRINT("TEST: |%s| %zu\n",buf,bufptr-buf);
+
+ if ( *buf >= '0' && *buf <= '9' )
+ {
+ char *end;
+ const uint num = strtoul(buf,&end, buf[0]=='0' && buf[1]=='x' ? 16 : 10 );
+ if ( end > buf && !*end )
+ {
+ item->num = num;
+ continue;
+ }
+ }
+
+ if (first)
+ {
+ first = false;
+ const file_format_t fform = GetByNameFF(buf);
+ if ( fform != FF_UNKNOWN )
+ {
+ item->fform = fform;
+ continue;
+ }
+ }
+
+ int iform = ScanImageFormat(buf);
+ if ( iform >= 0 )
+ {
+ item->iform = iform;
+ continue;
+ }
+
+ int pform = ScanPaletteFormat(buf);
+ if ( pform >= 0 )
+ {
+ item->pform = pform;
+ continue;
+ }
+
+ if (ScanPatchImage(buf,&item->patch_mode))
+ continue;
+ }
+
+ noPRINT("SET FORMAT: %d %d %d = %s\n",
+ item->fform, item->iform, item->pform, item->key );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormatFieldItem_t * InsertFormatField
+(
+ FormatField_t * ff, // valid attrib field
+ ccp key, // key to insert
+ bool scan_form, // true: scan 'key' for FORM= prefix
+ bool move_key, // true: move string, false: strdup()
+ bool * found // not null: store found status
+)
+{
+ if (!key)
+ return 0;
+
+ if (scan_form)
+ {
+ ccp eq = strchr(key,'=');
+ if (eq)
+ {
+ ccp slash = strchr(key,'/');
+ if ( !slash || slash > eq )
+ {
+ ccp new_key = eq + 1;
+ while ( *new_key > 0 && *new_key <= ' ' )
+ new_key++;
+ noPRINT("INSERT: %s\n",new_key);
+ FormatFieldItem_t * item = InsertFormatField(ff,new_key,false,false,found);
+ SetFormatFieldParam(item,key,eq);
+ if (move_key)
+ FreeString(key);
+ return item;
+ }
+ }
+ }
+
+ bool my_found;
+ const int idx = FindFormatFieldHelper(ff,&my_found,key);
+ if (found)
+ *found = my_found;
+
+ FormatFieldItem_t * item;
+ if (my_found)
+ {
+ item = ff->list + idx;
+ if (move_key)
+ FreeString(key);
+ }
+ else
+ {
+ item = InsertFormatFieldHelper(ff,idx);
+ item->key = move_key ? key : STRDUP(key);
+ }
+
+ item->fform = -1;
+ item->iform = -1;
+ item->pform = -1;
+ item->num = 0;
+ return item;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormatFieldItem_t * InsertFormatFieldFF
+(
+ FormatField_t * ff, // valid attrib field
+ ccp key, // key to insert
+ file_format_t fform, // file format
+ bool scan_form, // true: scan 'key' for FORM= prefix
+ bool move_key, // true: move string, false: strdup()
+ bool * found // not null: store found status
+)
+{
+ FormatFieldItem_t *item = InsertFormatField(ff,key,scan_form,move_key,found);
+ DASSERT(item);
+ item->fform = fform;
+ return item;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormatFieldItem_t * AppendFormatField
+(
+ FormatField_t * ff, // valid attrib field
+ ccp key, // key to insert
+ bool scan_form, // true: scan 'key' for FORM= prefix
+ bool move_key // true: move string, false: strdup()
+)
+{
+ if (!key)
+ return 0;
+
+ if (scan_form)
+ {
+ ccp eq = strchr(key,'=');
+ if (eq)
+ {
+ ccp slash = strchr(key,'/');
+ if ( !slash || slash > eq )
+ {
+ ccp new_key = eq + 1;
+ while ( *new_key > 0 && *new_key <= ' ' )
+ new_key++;
+ noPRINT("INSERT: %s\n",new_key);
+ FormatFieldItem_t * item = AppendFormatField(ff,new_key,false,false);
+ SetFormatFieldParam(item,key,eq);
+ if (move_key)
+ FreeString(key);
+ return item;
+ }
+ }
+ }
+
+ DASSERT( ff->used <= ff->size );
+ noPRINT(">FF: %u/%u\n",ff->used,ff->size);
+ if ( ff->used == ff->size )
+ {
+ ff->size += 0x100;
+ ff->list = REALLOC(ff->list,ff->size*sizeof(*ff->list));
+ }
+ TRACE("AppendFormatField(%s,%d) %d/%d\n",key,move_key,ff->used,ff->size);
+
+ FormatFieldItem_t * item = ff->list + ff->used++;
+ item->key = move_key ? key : STRDUP(key);
+ item->fform = -1;
+ item->iform = -1;
+ item->pform = -1;
+ item->num = 0;
+ return item;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#if 0 // [[not-needed]] not needed yet 2011-06
+///////////////////////////////////////////////////////////////////////////////
+
+enumError LoadFormatField
+(
+ FormatField_t * ff, // attrib field
+ bool init_ff, // true: initialize 'ff' first
+ bool sort, // true: sort 'ff'
+ ccp filename, // filename of source file
+ bool silent // true: don't print open/read errors
+)
+{
+ ASSERT(ff);
+ ASSERT(filename);
+ ASSERT(*filename);
+
+ TRACE("LoadFormatField(%p,%d,%d,%s,%d)\n",ff,init_att,sort,filename,silent);
+
+ if (init_att)
+ InitializeFormatField(ff);
+
+ FILE * f = fopen(filename,"rb");
+ if (!f)
+ {
+ if (!silent)
+ ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",filename);
+ return ERR_CANT_OPEN;
+ }
+
+ while (fgets(iobuf,sizeof(iobuf)-1,f))
+ {
+ char * ptr = iobuf;
+ while (*ptr)
+ ptr++;
+ if ( ptr > iobuf && ptr[-1] == '\n' )
+ {
+ ptr--;
+ if ( ptr > iobuf && ptr[-1] == '\r' )
+ ptr--;
+ }
+
+ if ( ptr > iobuf )
+ {
+ *ptr++ = 0;
+ const size_t len = ptr-iobuf;
+ ptr = MALLOC(len);
+ memcpy(ptr,iobuf,len);
+ if (sort)
+ InsertFormatField(ff,ptr,true);
+ else
+ AppendFormatField(ff,ptr,true);
+ }
+ }
+
+ fclose(f);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#endif
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SaveFormatField
+(
+ FormatField_t * ff, // valid attrib field
+ ccp filename, // filename of dest file
+ bool rm_if_empty // true: rm dest file if 'ff' is empty
+)
+{
+ ASSERT(ff);
+ ASSERT(filename);
+ ASSERT(*filename);
+
+ TRACE("SaveFormatField(%p,%s,%d)\n",ff,filename,rm_if_empty);
+
+ if ( !ff->used && rm_if_empty )
+ {
+ unlink(filename);
+ return ERR_OK;
+ }
+
+ FILE * f = fopen(filename,"wb");
+ if (!f)
+ return ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",filename);
+
+ enumError err = WriteFormatField(f,filename,ff,0,0,0,0);
+ fclose(f);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError WriteFormatField
+(
+ FILE * f, // open destination file
+ ccp filename, // NULL or filename (needed on write error)
+ FormatField_t * ff, // valid format field
+ ccp line_prefix, // not NULL: insert prefix before each line
+ ccp key_prefix, // not NULL: insert prefix before each key
+ ccp eol, // end of line text (if NULL: use LF)
+ uint hex_fw // if >0: print num as hex with field width
+)
+{
+ if (!key_prefix)
+ key_prefix = "";
+ if (!eol)
+ eol = "\n";
+
+ FormatFieldItem_t *ptr = ff->list, *end;
+ for ( end = ptr + ff->used; ptr < end; ptr++ )
+ {
+ if ( line_prefix && fprintf(f,"%s",line_prefix) < 0 )
+ goto abort;
+
+ int n = 0;
+ ccp sep = "";
+ if ( ptr->fform >= 0 )
+ {
+ const int stat = fprintf(f,"%s%s",sep,GetNameFF(0,ptr->fform));
+ if ( stat < 0 )
+ goto abort;
+ n += stat;
+ sep = ",";
+ }
+
+ ccp iname = GetImageFormatName(ptr->iform,0);
+ if (iname)
+ {
+ const int stat = fprintf(f,"%s%s",sep,iname);
+ if ( stat < 0 )
+ goto abort;
+ n += stat;
+ sep = ",";
+ }
+
+ ccp pname = GetPaletteFormatName(ptr->pform,0);
+ if (pname)
+ {
+ const int stat = fprintf(f,"%s%s",sep,pname);
+ if ( stat < 0 )
+ goto abort;
+ n += stat;
+ sep = ",";
+ }
+
+ if (ptr->num)
+ {
+ const int stat = hex_fw
+ ? fprintf(f,"%s%#*x",sep,hex_fw,ptr->num)
+ : fprintf(f,"%s%u",sep,ptr->num);
+ if ( stat < 0 )
+ goto abort;
+ n += stat;
+ //sep = ",";
+ }
+
+ if ( fprintf(f,"%s= %s%s%s", n<8 ? "\t\t" : "\t", key_prefix,ptr->key,eol) < 0 )
+ goto abort;
+ }
+ return ERR_OK;
+
+ abort:
+ return ERROR1(ERR_WRITE_FAILED,
+ "Error while writing attrib field: %s\n",
+ filename ? filename : "?" );
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// string lists ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int AtFileHelper
+(
+ ccp arg,
+ int (*func) ( ccp arg )
+)
+{
+ if ( !arg || !*arg || !func )
+ return 0;
+
+ TRACE("AtFileHelper(%s)\n",arg);
+ if ( *arg != '@' )
+ return func(arg);
+
+ FILE * f;
+ char buf[PATH_MAX];
+ const bool use_stdin = arg[1] == '-' && !arg[2];
+
+ if (use_stdin)
+ f = stdin;
+ else
+ {
+ #ifdef __CYGWIN__
+ char buf[PATH_MAX];
+ NormalizeFilenameCygwin(buf,sizeof(buf),arg+1);
+ f = fopen(buf,"r");
+ #else
+ f = fopen(arg+1,"r");
+ #endif
+ if (!f)
+ return func(arg);
+ }
+
+ ASSERT(f);
+
+ u32 max_stat = 0;
+ while (fgets(buf,sizeof(buf)-1,f))
+ {
+ char * ptr = buf;
+ while (*ptr)
+ ptr++;
+ if ( ptr > buf && ptr[-1] == '\n' )
+ ptr--;
+ if ( ptr > buf && ptr[-1] == '\r' )
+ ptr--;
+ *ptr = 0;
+ const u32 stat = func(buf);
+ if ( max_stat < stat )
+ max_stat = stat;
+ }
+ fclose(f);
+ return max_stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint n_param = 0, id6_param_found = 0;
+ParamList_t * first_param = 0;
+ParamList_t ** append_param = &first_param;
+
+///////////////////////////////////////////////////////////////////////////////
+
+static ParamList_t* GetPoolParam()
+{
+ static ParamList_t * pool = 0;
+ static int n_pool = 0;
+
+ if (!n_pool)
+ {
+ const int alloc_count = 100;
+ pool = (ParamList_t*) CALLOC(alloc_count,sizeof(ParamList_t));
+ n_pool = alloc_count;
+ }
+
+ n_pool--;
+ return pool++;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int AddParam ( ccp arg )
+{
+ if ( !arg || !*arg )
+ return 0;
+
+ TRACE("ARG#%02d: %s\n",n_param,arg);
+
+ ParamList_t *param = GetPoolParam();
+ param->arg = STRDUP(arg);
+
+ while (*append_param)
+ append_param = &(*append_param)->next;
+
+ noTRACE("INS: A=%p->%p P=%p &N=%p->%p\n",
+ append_param, *append_param,
+ param, &param->next, param->next );
+ *append_param = param;
+ append_param = &param->next;
+ noTRACE(" => A=%p->%p\n", append_param, *append_param );
+ n_param++;
+
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AtExpandParam ( ParamList_t ** p_param )
+{
+ if ( !p_param || !*p_param )
+ return;
+
+ ParamList_t *param = *p_param;
+ if ( param->is_expanded || !param->arg || *param->arg != '@' )
+ return;
+
+ FILE * f;
+ char buf[PATH_MAX];
+ const bool use_stdin = param->arg[1] == '-' && !param->arg[2];
+ if (use_stdin)
+ f = stdin;
+ else
+ {
+ #ifdef __CYGWIN__
+ NormalizeFilenameCygwin(buf,sizeof(buf),param->arg+1);
+ f = fopen(buf,"r");
+ #else
+ f = fopen(param->arg+1,"r");
+ #endif
+ if (!f)
+ return;
+ }
+
+ ASSERT(f);
+
+ u32 count = 0;
+ while (fgets(buf,sizeof(buf)-1,f))
+ {
+ char * ptr = buf;
+ while (*ptr)
+ ptr++;
+ if ( ptr > buf && ptr[-1] == '\n' )
+ ptr--;
+ if ( ptr > buf && ptr[-1] == '\r' )
+ ptr--;
+ *ptr = 0;
+
+ if (count++)
+ {
+ // insert a new item
+ ParamList_t * new_param = GetPoolParam();
+ new_param->next = param->next;
+ param->next = new_param;
+ param = new_param;
+ n_param++;
+ }
+ param->arg = STRDUP(buf);
+ param->is_expanded = true;
+ }
+ fclose(f);
+
+ if (!count)
+ {
+ *p_param = param->next;
+ n_param--;
+ }
+
+ append_param = &first_param;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AtExpandAllParam ( ParamList_t ** p_param )
+{
+ if (p_param)
+ for ( ; *p_param; p_param = &(*p_param)->next )
+ AtExpandParam(p_param);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// tiny support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[TinyParam]]
+
+const tiny_param_t TinyParam[N_TINY_MODES] =
+{
+ // TINY 0 == OFF
+ {
+ 0, // kcl_round
+ 0, // kcl_mask
+
+ 999, // pos_round
+ 999, // rot_round
+ 999, // scale_round
+
+ 999, // route_round
+ 999, // cannon_round
+ },
+
+ // TINY 1 == LOSSLESS
+ {
+ 8, // kcl_round
+ 0xfffffff0, // kcl_mask
+
+ 999, // pos_round
+ 999, // rot_round
+ 999, // scale_round
+
+ 999, // route_round
+ 999, // cannon_round
+ },
+
+ // TINY 2 == RECREATE
+ {
+ 8, // kcl_round
+ 0xffffffc0, // kcl_mask
+
+ 999, // pos_round
+ 999, // rot_round
+ 999, // scale_round
+
+ 999, // route_round
+ 999, // cannon_round
+ },
+
+ // TINY 3 == MINIMAL
+ {
+ 7, // kcl_round
+ 0xffffff00, // kcl_mask
+
+ 6, // pos_round
+ 3, // rot_round
+ 7, // scale_round
+
+ 0, // route_round
+ 12, // cannon_round
+ },
+
+ // TINY 4 == GOOD
+ {
+ 6, // kcl_round
+ 0xfffffc00, // kcl_mask
+
+ 4, // pos_round
+ 2, // rot_round
+ 6, // scale_round
+
+ 0, // route_round
+ 11, // cannon_round
+ },
+
+ // TINY 5 == MEDIUM
+ {
+ 5, // kcl_round
+ 0xfffff000, // kcl_mask
+
+ 2, // pos_round
+ 1, // rot_round
+ 5, // scale_round
+
+ -1, // route_round
+ 10, // cannon_round
+ },
+
+ // TINY 6 == EXTREME
+ {
+ 4, // kcl_round
+ 0xffffc000, // kcl_mask
+
+ 0, // pos_round
+ 0, // rot_round
+ 4, // scale_round
+
+ -2, // route_round
+ 9, // cannon_round
+ },
+
+ // TINY 7 == DANGEROUS
+ {
+ 3, // kcl_round
+ 0xffff0000, // kcl_mask
+
+ -2, // pos_round
+ -1, // rot_round
+ 3, // scale_round
+
+ -3, // route_round
+ 8, // cannon_round
+ },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptTiny ( ccp arg )
+{
+ static const KeywordTab_t tab[] =
+ {
+ { 0, "TINY-0", "OFF", 0 },
+ { 1, "TINY-1", "LOSSLESS", 0 },
+ { 2, "TINY-2", "RECREATE", 0 },
+ { 3, "TINY-3", "MINIMAL", 0 },
+ { 4, "TINY-4", "GOOD", 0 },
+ { 5, "TINY-5", "MEDIUM", 0 },
+ { 6, "TINY-6", "EXTREME", 0 },
+ { 7, "TINY-7", "DANGEROUS", 0 },
+ { 0,0,0,0 }
+ };
+
+ char *end;
+ const ulong num = str2ul(arg,&end,10);
+ if ( end > arg && !*end && num < N_TINY_MODES )
+ opt_tiny = num;
+ else
+ {
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (!cmd)
+ {
+ ERROR0(ERR_SYNTAX,"Invalid --tiny mode: '%s'\n",arg);
+ return 1;
+ }
+
+ opt_tiny = cmd->opt;
+ }
+
+ if ( opt_tiny && !opt_compr_mode )
+ {
+ opt_compr_mode = 11;
+ opt_compr = 23 + opt_tiny;
+ }
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// general list support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeList ( List_t * list, uint elem_size )
+{
+ DASSERT(list);
+ DASSERT(elem_size>0);
+ memset(list,0,sizeof(*list));
+ list->elem_size = elem_size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetList ( List_t * list )
+{
+ DASSERT(list);
+ FREE(list->list);
+ const uint elem_size = list->elem_size;
+ memset(list,0,sizeof(*list));
+ list->elem_size = elem_size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void * InsertList ( List_t * list, int index )
+{
+ DASSERT( list );
+ DASSERT( list->elem_size > 0 );
+ DASSERT( list->used <= list->size );
+ DASSERT( !list->list == !list->size );
+
+ if ( list->used == list->size )
+ {
+ list->size = 3*list->size/2 + 100;
+ list->list = REALLOC( list->list, list->size * list->elem_size ) ;
+ }
+
+ DASSERT( list->list );
+ DASSERT( list->used < list->size );
+
+ if ( index < 0 )
+ {
+ index += list->used++;
+ if ( index < 0 )
+ index = 0;
+ }
+ else if ( index > list->used )
+ index = list->used;
+ DASSERT( index >= 0 && index <= list->used );
+ u8 * ptr = list->list + index * list->elem_size;
+ if ( index < list->used )
+ memmove( ptr+list->elem_size, ptr, list->elem_size * (list->used-index) );
+ memset(ptr,0,list->elem_size);
+
+ list->used++;
+ DASSERT( list->used <= list->size );
+
+ return ptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void * AppendList ( List_t * list )
+{
+ DASSERT( list );
+ DASSERT( list->elem_size > 0 );
+ DASSERT( list->used <= list->size );
+ DASSERT( !list->list == !list->size );
+
+ if ( list->used == list->size )
+ {
+ list->size = 3*list->size/2 + 100;
+ list->list = REALLOC( list->list, list->size * list->elem_size ) ;
+ }
+
+ DASSERT( list->list );
+ DASSERT( list->used < list->size );
+ u8 *ptr = list->list + list->used++ * list->elem_size;
+ memset(ptr,0,list->elem_size);
+ return ptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void * GrowList ( List_t * list, uint n )
+{
+ DASSERT( list );
+ DASSERT( list->elem_size > 0 );
+ DASSERT( list->used <= list->size );
+ DASSERT( !list->list == !list->size );
+
+ const uint new_used = list->used + n;
+ if ( new_used > list->size || !list->size )
+ {
+ list->size = new_used > 10 ? new_used : 10;
+ list->list = REALLOC( list->list, list->size * list->elem_size ) ;
+ }
+
+ DASSERT( list->list );
+ DASSERT( new_used <= list->size );
+
+ void *result = list->list + list->used * list->elem_size;
+ list->used = new_used;
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void * GrowListSize ( List_t * list, uint n, uint add_size )
+{
+ DASSERT( list );
+ DASSERT( list->elem_size > 0 );
+ DASSERT( list->used <= list->size );
+ DASSERT( !list->list == !list->size );
+
+ const uint new_used = list->used + n;
+ if ( new_used > list->size || !list->size )
+ {
+ noPRINT("GROW-LIST: %u -> %u (size=%u)\n",
+ list->size, new_used + add_size, (new_used + add_size)*list->elem_size );
+ list->size = new_used + add_size;
+ list->list = REALLOC( list->list, list->size * list->elem_size ) ;
+ }
+
+ DASSERT( list->list );
+ DASSERT( new_used <= list->size );
+
+ void *result = list->list + list->used * list->elem_size;
+ list->used = new_used;
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void * GetListElem
+ ( const List_t * list, int index, const void * return_not_found )
+{
+ DASSERT( list );
+ DASSERT( list->elem_size > 0 );
+ DASSERT( list->used <= list->size );
+ DASSERT( !list->list == !list->size );
+
+ if ( index < 0 ) // allow acces to elements relative to end
+ index += list->used;
+
+ return (uint)index < list->used
+ ? list->list + index * list->elem_size
+ : (void*)return_not_found;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeMIL ( List_t * list )
+{
+ DASSERT(list);
+ InitializeList(list,sizeof(MemItem_t));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetMIL ( List_t * list )
+{
+ DASSERT(list);
+
+ MemItem_t *eptr = GetMemListElem(list,0,0);
+ MemItem_t *eend = eptr + list->used;
+ for ( ; eptr < eend; eptr++ )
+ {
+ if (eptr->name_alloced)
+ FreeString(eptr->name);
+ if (eptr->data_alloced)
+ FREE(eptr->data);
+ FREE(eptr->str_idx);
+ }
+ ResetList(list);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int sort_mem_list_by_idx ( const MemItem_t * a, const MemItem_t * b )
+{
+ int stat = a->idx1 - b->idx1;
+ if (!stat)
+ {
+ stat = a->idx2 - b->idx2;
+ if (!stat)
+ stat = a->sort_idx - b->sort_idx;
+ }
+ return stat;
+}
+
+//-----------------------------------------------------------------------------
+
+static int sort_mem_list_by_sort ( const MemItem_t * a, const MemItem_t * b )
+{
+ int stat = a->sort_idx - b->sort_idx;
+ if (!stat)
+ {
+ stat = a->idx1 - b->idx1;
+ if (!stat)
+ stat = a->idx2 - b->idx2;
+ }
+ return stat;
+}
+
+//-----------------------------------------------------------------------------
+
+void SortMIL ( List_t * list, bool use_sort_idx )
+{
+ DASSERT(list);
+ if ( list->used > 1 )
+ {
+ noPRINT("qsort(%p,%d,%zd)\n", list->list, list->used, sizeof(MemItem_t) );
+ qsort( list->list, list->used, sizeof(MemItem_t),
+ use_sort_idx
+ ? (qsort_func)sort_mem_list_by_sort
+ : (qsort_func)sort_mem_list_by_idx );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MemItem_t * AppendMIL ( List_t * list )
+{
+ MemItem_t * item = AppendList(list); // data is zeroed!
+ item->sort_idx = 10*list->used;
+ item->str_idx = 0;
+ item->str_idx_size = 0;
+ return item;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MemItem_t * GrowMIL ( List_t * list, uint n )
+{
+ return GrowList(list,n);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MemItem_t * GetMemListElem
+ ( const List_t * list, int index, const MemItem_t * return_not_found )
+{
+ return GetListElem(list,index,return_not_found);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+u32 * GetStrIdxMIL ( MemItem_t * mi, uint n_need )
+{
+ DASSERT(mi);
+ const uint BUF_MAX = sizeof(mi->str_idx_buf)/sizeof(*mi->str_idx_buf);
+
+ if ( n_need <= BUF_MAX || n_need <= mi->str_idx_size )
+ return mi->str_idx ? mi->str_idx : mi->str_idx_buf;
+
+ typeof(mi->str_idx) new_list = CALLOC(n_need,sizeof(*new_list));
+ if ( mi->str_idx )
+ {
+ memcpy(new_list,mi->str_idx,mi->str_idx_size*sizeof(*new_list));
+ FREE(mi->str_idx);
+ }
+ else
+ memcpy(new_list,mi->str_idx_buf,BUF_MAX*sizeof(*new_list));
+
+ mi->str_idx = new_list;
+ mi->str_idx_size = n_need;
+ return mi->str_idx;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// modes and options ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptCompr ( ccp arg )
+{
+ static const KeywordTab_t alias[] =
+ {
+ { 0, "LIST", 0, -9 },
+ { 1, "C-LIST", "CLIST", -9 },
+
+ { 0, "UNCOMPRESSED", 0, -1 },
+
+ { 0, "NOCHUNKS", 0, 1 },
+ { 1, "FAST", 0, 1 },
+ { 9, "BEST", 0, 1 },
+ { 10, "ULTRA", 0, 1 },
+
+ { 9, "T2", "TRY2", 2 },
+ { 9, "T3", "TRY3", 3 },
+ { 9, "T4", "TRY4", 4 },
+ { 9, "T5", "TRY5", 5 },
+
+ { 0,0,0,0 }
+ };
+
+ char *end;
+ const long num = str2l(arg,&end,10);
+ if ( end > arg && !*end )
+ {
+ struct tab_t { s16 from, to, add, mode; };
+ static const struct tab_t tab[] =
+ {
+ { -1, -1, 10, -1 },
+ { 0, 10, 0, 1 },
+ { 91, 93, 0, 9 },
+ { 100, 150, -100, 11 },
+ { -9,0,0,0}
+ };
+
+ static const struct tab_t *ptr;
+ for ( ptr = tab; ptr->from != -9; ptr++ )
+ if ( num >= ptr->from && num <= ptr->to )
+ {
+ opt_compr = num + ptr->add;
+ opt_compr_mode = ptr->mode;
+ return 0;
+ }
+ }
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,alias);
+ if (!cmd)
+ {
+ ERROR0(ERR_SYNTAX,"Invalid compression level (option --compr): '%s'\n",arg);
+ return 1;
+ }
+
+ opt_compr_mode = cmd->opt;
+ if ( opt_compr_mode != -1 )
+ opt_compr = cmd->id;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptPtDir ( ccp arg )
+{
+ if ( !arg || !*arg )
+ {
+ opt_pt_dir = 1;
+ return 0;
+ }
+
+ static const KeywordTab_t tab[] =
+ {
+ { -1, "REMOVE", "0", 0 },
+ { 0, "AUTO", 0, 0 },
+ { 1, "FORCE", "1", 0 },
+
+ { 0,0,0,0 }
+ };
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (cmd)
+ {
+ opt_pt_dir = cmd->id;
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid `point directory' mode (option --pdir): '%s'\n",arg);
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptRecurse ( ccp arg )
+{
+ if ( !arg || !*arg )
+ {
+ opt_recurse = INT_MAX;
+ return 0;
+ }
+
+ static const KeywordTab_t tab[] =
+ {
+ { 0, "NONE", 0, 0 },
+ { INT_MAX, "UNLIMITED", "ALL", 0 },
+
+ { 0,0,0,0 }
+ };
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (cmd)
+ {
+ opt_recurse = cmd->id;
+ return 0;
+ }
+
+ char * end;
+ ulong num = strtoul(arg,&end,10);
+ if ( end > arg && !*end && num <= INT_MAX )
+ {
+ opt_recurse = num;
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid recurse level (option --recurse): '%s'\n",arg);
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SlotMode_t opt_slot = SLOTMD_NONE;
+
+static const KeywordTab_t opt_slot_tab[] =
+{
+ { SLOTMD_NONE, "NONE", "-", 0 },
+
+ { SLOTMD_M_DAISY, "DAISY", "DESERT", 0 },
+ { SLOTMD_M_DAISY, "3.1", "31", 0 },
+ { SLOTMD_M_DAISY, "7.1", "71", 0 },
+
+ { SLOTMD_M_SHERBET, "SHERBET", "T61", 0 },
+ { SLOTMD_M_SHERBET, "6.1", "61", 0 },
+
+ { SLOTMD_M_SHYGUY, "SHYGUY", "T62", 0 },
+ { SLOTMD_M_SHYGUY, "6.2", "62", 0 },
+
+ { SLOTMD_M_STANDARD, "STANDARD", "STD", 0 },
+
+ { SLOTMD_M_MOST, "MOST", "ALL", 0 },
+
+ { SLOTMD_RM_ICE, "RM-ICE", "RMICE", 1 },
+ { SLOTMD_RM_SUNDS, "RM-SUNDS", "RMSUNDS", 1 },
+ { SLOTMD_RM_SHYGUY, "RM-SHYGUY", "RMSHYGUY", 1 },
+ { SLOTMD_ADD_GICE, "ADD-GICE", "ADDGICE", 1 },
+ { SLOTMD_ADD_BICE, "ADD-BICE", "ADDBICE", 1 },
+ { SLOTMD_ADD_SHYGUY, "ADD-SHYGUY", "ADDSHYGUY", 1 },
+ { SLOTMD_ICE_TO_WATER, "ICE2WATER", "ICETOWATER", 1 },
+ { SLOTMD_WATER_TO_ICE, "WATER2ICE", "WATERTOICE", 1 },
+
+ //{ -1, "4.2", "42", 0 },
+
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptSlot ( ccp arg )
+{
+ if ( !arg || !*arg )
+ return 0;
+
+ const KeywordTab_t *cmd = ScanKeyword(0,arg,opt_slot_tab);
+ if (!cmd)
+ {
+ const KeywordTab_t *c2 = ScanKeyword(0,arg,track_name_tab);
+ if ( c2 && c2->id != 10 ) // 10 == moonview highway
+ {
+ char buf[10];
+ snprintf(buf,sizeof(buf),"%llu",c2->id);
+ cmd = ScanKeyword(0,buf,opt_slot_tab);
+ if (!cmd)
+ cmd = ScanKeyword(0,"STANDARD",opt_slot_tab);
+ }
+ }
+
+ if ( cmd && cmd->id >= 0 )
+ {
+ if ( opt_slot & SLOTMD_JOB_RM_SZS )
+ need_norm--;
+ if ( opt_slot & SLOTMD_JOB_ADD_SZS )
+ have_patch_count--;
+ if ( opt_slot & SLOTMD_JOB_KMP )
+ have_patch_count--, have_kmp_patch_count--;
+ if ( opt_slot & SLOTMD_JOB_KCL )
+ have_patch_count--, have_kcl_patch_count--;
+
+ opt_slot = cmd->id;
+
+ if ( opt_slot & SLOTMD_JOB_RM_SZS )
+ need_norm++;
+ if ( opt_slot & SLOTMD_JOB_ADD_SZS )
+ have_patch_count++;
+ if ( opt_slot & SLOTMD_JOB_KMP )
+ have_patch_count++, have_kmp_patch_count++;
+ if ( opt_slot & SLOTMD_JOB_KCL )
+ have_patch_count++, have_kcl_patch_count++;
+
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid keyword for option --slot: '%s'\n",arg);
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp PrintSlotMode ( SlotMode_t mode )
+{
+ const KeywordTab_t *cmd;
+ for ( cmd = opt_slot_tab; cmd->name1; cmd++ )
+ if ( mode == (u32)cmd->id )
+ return cmd->name1;
+ return "?";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ccp NormalizeTrackName ( char *buf, uint bufsize, ccp arg )
+{
+ DASSERT(buf );
+ DASSERT(bufsize>10);
+ DASSERT(arg);
+
+ // skip leading separators
+ while ( *arg > 1 && *arg <= ' ' || *arg == ',' || *arg == ':' || *arg == '=' )
+ arg++;
+
+ char *dest = buf, *dest_end = buf + bufsize - 2;
+ while ( dest < dest_end && *arg && *arg != ',' && *arg != ':' && *arg != '=' )
+ {
+ int code = ScanUTF8AnsiChar(&arg);
+ if (isalnum(code))
+ *dest++ = toupper(code);
+ else switch(code)
+ {
+ case '.':
+ *dest++ = '.';
+ break;
+
+ case 0xe4: // ä
+ case 0xc4: // Ä
+ *dest++ = 'A';
+ *dest++ = 'E';
+ break;
+
+ case 0xf6: // ö
+ case 0xd6: // Ö
+ *dest++ = 'O';
+ *dest++ = 'E';
+ break;
+
+ case 0xfc: // ü
+ case 0xdc: // Ü
+ *dest++ = 'U';
+ *dest++ = 'E';
+ break;
+
+ case 0xdf: // ß
+ *dest++ = 'S';
+ *dest++ = 'S';
+ break;
+ }
+ }
+
+ *dest = 0;
+ return arg;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int scan_track_arena
+(
+ ccp arg,
+ const u32 * def_tab,
+ u32 * tab,
+ u32 * r_tab,
+ const TrackInfo_t * info,
+ uint n,
+ ccp opt_name,
+ int (*scan_func) ( ccp name )
+)
+{
+ DASSERT(def_tab);
+ DASSERT(tab);
+ DASSERT(r_tab);
+ DASSERT(info);
+ DASSERT(scan_func);
+ DASSERT( n > 0 );
+
+ if (!arg)
+ return 0;
+
+ memcpy(tab,def_tab,n*sizeof(*tab));
+
+ uint track_index = 0;
+ bool swap_tracks = true;
+
+ for(;;)
+ {
+ char name[100];
+ arg = NormalizeTrackName(name,sizeof(name),arg);
+ if (!*name)
+ break;
+
+ if (!strcmp(name,"0"))
+ {
+ uint i;
+ for ( i = 0; i < n; i++ )
+ tab[i] = i;
+ continue;
+ }
+
+ int index = scan_func(name);
+ if ( index < 0 )
+ {
+ ERROR0(ERR_SYNTAX,"Option --%ss: Not a %s name: %s\n",
+ opt_name, opt_name, name);
+ return 1;
+ }
+
+ if ( *arg == ':' || *arg == '=' )
+ {
+ swap_tracks = *arg == '=';
+ PRINT("INDEX --%ss: %2u -> %2u\n",
+ opt_name, track_index, info[index].def_index );
+ track_index = info[index].def_index;
+
+ if ( *arg == ':' ) arg++;
+ if ( *arg == '=' ) arg++;
+ continue;
+ }
+
+ if ( track_index >= n )
+ {
+ ERROR0(ERR_SEMANTIC,"Option --%ss: Invald track index: %u (max=%u)\n",
+ opt_name, track_index, n-1 );
+ return 1;
+ }
+
+ u32 * tptr = tab;
+ if (swap_tracks)
+ while ( tptr < tab+n && *tptr != index )
+ tptr++;
+
+ if ( swap_tracks && tptr < tab+n )
+ {
+ PRINT("SWAP --%s: %2u=%2u, %2zu=%2u\n",
+ opt_name, track_index, *tptr,
+ tptr-tab, tab[track_index] );
+ *tptr = tab[track_index];
+ }
+ tab[track_index] = index;
+ track_index++;
+ }
+
+ uint i;
+ for ( i = 0; i < n; i++ )
+ r_tab[tab[i]] = i;
+ //HEXDUMP16(0,0,r_tab,n*sizeof(*tab));
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptTracks ( ccp arg )
+{
+ opt_tracks = true;
+ return scan_track_arena ( arg, track_pos_default, track_pos, track_pos_r,
+ track_info, MKW_N_TRACKS, "track", ScanTrack );
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptArenas ( ccp arg )
+{
+ opt_arenas = true;
+ return scan_track_arena ( arg, arena_pos_default, arena_pos, arena_pos_r,
+ arena_info, MKW_N_ARENAS, "arena", ScanArena );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void DumpTrackList
+(
+ FILE * f, // NULL(=stdout) or file
+ const u32 * tab, // NULL or list with track indices
+ int brief_mode // 0: full table
+ // 1: suppress header
+ // 2: file names only
+)
+{
+ if (!f)
+ f = stdout;
+ if (!tab)
+ tab = track_pos;
+
+ if ( brief_mode > 1 )
+ {
+ uint i;
+ for ( i = 0; i < MKW_N_TRACKS; i++ )
+ {
+ const uint idx = *tab++;
+ const TrackInfo_t * info = idx < MKW_N_TRACKS ? track_info + idx : &InvalidTrack;
+ fprintf(f,"%s\n",info->track_fname);
+ }
+ return;
+ }
+
+ if (brief_mode<1)
+ {
+ if (long_count)
+ fprintf(f,
+ "\n"
+ " new std file name sound standard fast sound track track\n"
+ " idx slot slot of track slot sound file name f.name abbr. name\n"
+ );
+ else
+ fprintf(f,
+ "\n"
+ " new std file name standard fast sound track\n"
+ " idx slot slot of track sound file name f.name name\n"
+ );
+ }
+
+ const uint width = long_count ? 99 : 87;
+
+ uint i;
+ for ( i = 0; i < MKW_N_TRACKS; i++ )
+ {
+ if ( brief_mode<1 &&!(i%4) )
+ fprintf(f,"%.*s\n",width,Minus300);
+
+ const uint idx = *tab++;
+ const TrackInfo_t * info = idx < MKW_N_TRACKS ? track_info + idx : &InvalidTrack;
+ fprintf(f,"%3u. %u.%u %u.%u %-*s ",
+ idx,
+ i/4+1, i%4+1,
+ info->def_slot/10, info->def_slot%10,
+ FW_TRACK_FNAME, info->track_fname );
+
+ if (long_count)
+ fprintf(f,"0x%02x ",info->music_id);
+
+ fprintf(f,"%-*s %s ",
+ FW_TRACK_SOUND_N, info->sound_n_fname,
+ info->sound_f_fname + strlen(info->sound_f_fname) - 2 );
+
+ if (long_count)
+ fprintf(f,"%-5s ",info->abbrev);
+
+ fprintf(f,"%s\n", use_de ? info->name_de : info->name_en );
+ }
+ if (brief_mode<1)
+ fprintf(f,"%.*s\n\n",width,Minus300);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void DumpArenaList
+(
+ FILE * f, // NULL(=stdout) or file
+ const u32 * tab, // NULL or list with arena indices
+ int brief_mode // 0: full table
+ // 1: suppress header
+ // 2: file names only
+)
+{
+ if (!f)
+ f = stdout;
+ if (!tab)
+ tab = arena_pos;
+
+ if ( brief_mode > 1 )
+ {
+ uint i;
+ for ( i = 0; i < MKW_N_ARENAS; i++ )
+ {
+ const uint idx = *tab++;
+ const TrackInfo_t * info = idx < MKW_N_ARENAS ? arena_info + idx : &InvalidTrack;
+ fprintf(f,"%s\n",info->track_fname);
+ }
+ return;
+ }
+
+ if (brief_mode<1)
+ {
+ if (long_count)
+ fprintf(f,
+ "\n"
+ " new std file name sound standard + fast arena arena\n"
+ " idx slot slot of arena slot sound file name abbr. name\n"
+ );
+ else
+ fprintf(f,
+ "\n"
+ " new std file name standard + fast arena\n"
+ " idx slot slot of arena sound file name name\n"
+ );
+ }
+
+ const uint width = long_count ? 88 : 76;
+
+ uint i;
+ for ( i = 0; i < MKW_N_ARENAS; i++ )
+ {
+ if ( brief_mode<1 &&!(i%5) )
+ fprintf(f,"%.*s\n",width,Minus300);
+
+ const uint idx = *tab++;
+ const TrackInfo_t * info = idx < MKW_N_ARENAS ? arena_info + idx : &InvalidTrack;
+ fprintf(f,"%3u. %u.%u %u.%u %-*s ",
+ idx,
+ i/5+1, i%5+1,
+ info->def_slot/10, info->def_slot%10,
+ FW_ARENA_FNAME, info->track_fname );
+
+ if (long_count)
+ fprintf(f,"0x%02x ",info->music_id);
+
+ fprintf(f,"%-*s %s ",
+ FW_ARENA_SOUND_N, info->sound_n_fname,
+ info->sound_f_fname + strlen(info->sound_f_fname) - 2 );
+
+ if (long_count)
+ fprintf(f,"%-5s ",info->abbrev);
+
+ fprintf(f,"%s\n", use_de ? info->name_de : info->name_en );
+ }
+ if (brief_mode<1)
+ fprintf(f,"%.*s\n\n",width,Minus300);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void DumpTrackFileList
+(
+ FILE * f, // NULL(=stdout) or file
+ const u32 * track_tab, // NULL or list with track indices
+ const u32 * arena_tab // NULL or list with arena indices
+)
+{
+ if (!f)
+ f = stdout;
+ if (!track_tab)
+ track_tab = track_pos;
+ if (!arena_tab)
+ arena_tab = arena_pos;
+
+ uint i;
+ for ( i = 0; i < MKW_N_TRACKS; i++ )
+ {
+ const uint idx = *track_tab++;
+ const TrackInfo_t * info = idx < MKW_N_TRACKS ? track_info + idx : &InvalidTrack;
+
+ fprintf(f,"T%u.%u %s %s %s\n",
+ i/4+1, i%4+1,
+ info->track_fname, info->sound_n_fname, info->sound_f_fname );
+ }
+
+ for ( i = 0; i < MKW_N_ARENAS; i++ )
+ {
+ const uint idx = *arena_tab++;
+ const TrackInfo_t * info = idx < MKW_N_ARENAS ? arena_info + idx : &InvalidTrack;
+
+ fprintf(f,"A%u.%u %s %s %s\n",
+ i/5+1, i%5+1,
+ info->track_fname, info->sound_n_fname, info->sound_f_fname );
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// sort mode ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SortMode_t GetSortMode
+(
+ SortMode_t sort_mode, // wanted sort mode
+ file_format_t fform, // use it if 'sort_mode==SORT_AUTO'
+ SortMode_t default_mode // default if all other fails
+)
+{
+ switch (sort_mode)
+ {
+ case SORT_NONE:
+ case SORT_INAME:
+ case SORT_OFFSET:
+ case SORT_U8:
+ case SORT_PACK:
+ case SORT_BRRES:
+ case SORT_BREFF:
+ return sort_mode;
+
+ case SORT_AUTO:
+ switch(fform)
+ {
+ case FF_U8:
+ case FF_WU8:
+ case FF_RARC:
+ return SORT_U8;
+
+ case FF_PACK:
+ return SORT_PACK;
+
+ case FF_BRRES:
+ return SORT_BRRES;
+
+ case FF_BREFF:
+ case FF_BREFT:
+ return SORT_BREFF;
+
+ default:
+ return default_mode;
+ }
+ }
+
+ return SORT_INAME;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t opt_sort_tab[] =
+{
+ { SORT_NONE, "NONE", "0", 0 },
+ { SORT_INAME, "NAME", "INAME", 0 },
+ { SORT_OFFSET, "OFFSET", 0, 0 },
+
+ { SORT_U8, "U8", 0, 0 },
+ { SORT_PACK, "PACK", 0, 0 },
+ { SORT_BRRES, "BRRES", "BRES", 0 },
+ { SORT_BREFF, "BREFF", "BREFT", 0 },
+
+ { SORT_AUTO, "AUTO", 0, 0 },
+
+ { 0,0,0,0 }
+};
+
+//-----------------------------------------------------------------------------
+
+ccp GetSortName ( int id, ccp res_not_found )
+{
+ return GetKewordNameById(opt_sort_tab,id,res_not_found);
+}
+
+//-----------------------------------------------------------------------------
+
+int ScanOptSort ( ccp arg )
+{
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,opt_sort_tab);
+ if (cmd)
+ {
+ opt_sort = cmd->id;
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid sort mode (option --sort): '%s'\n",arg);
+ return 1;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command argtest ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_argtest ( int argc, char ** argv )
+{
+ printf("ARGUMENT TEST: %u arguments:\n",argc);
+
+ int idx;
+ for ( idx = 0; idx < argc; idx++ )
+ printf("%4u.: |%s|\n",idx,argv[idx]);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command error ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_error()
+{
+ if (!n_param)
+ {
+ if ( print_sections )
+ {
+ int i;
+ for ( i=0; i<ERR__N; i++ )
+ printf("\n[error-%02u]\ncode=%u\nname=%s\ntext=%s\n",
+ i, i, GetErrorName(i,0), GetErrorText(i,0));
+ }
+ else
+ {
+ if (print_header)
+ {
+ if (print_title_func)
+ print_title_func(stdout);
+ printf("\nList of error codes\n\n");
+ }
+
+ // calc max_wd
+ int i, max_wd = 0;
+ for ( i = 0; i < ERR__N; i++ )
+ {
+ const int len = strlen(GetErrorName(i,""));
+ if ( max_wd < len )
+ max_wd = len;
+ }
+
+ // print table
+ for ( i = 0; i < ERR__N; i++ )
+ {
+ ccp ename = GetErrorName(i,"");
+ ccp etext = GetErrorText(i,"");
+ if ( *ename || *etext )
+ printf("%3d : %-*s : %s\n", i, max_wd, ename, etext );
+ }
+
+ if (print_header)
+ printf("\n");
+ }
+
+ return ERR_OK;
+ }
+
+ int stat = ERR_OK;
+ long num = ERR__N;
+ if ( n_param != 1 )
+ stat = ERR_SYNTAX;
+ else
+ {
+ char buf[100];
+ StringUpperS(buf,sizeof(buf),first_param->arg);
+ int i;
+ for ( i = 0; i < ERR__N; i++ )
+ if (!strcmp(GetErrorName(i,""),buf))
+ {
+ num = i;
+ break;
+ }
+
+ if ( num == ERR__N )
+ {
+ char * end;
+ num = strtoul(first_param->arg,&end,10);
+ stat = *end ? ERR_SYNTAX : num < 0 || num >= ERR__N ? ERR_SEMANTIC : ERR_OK;
+ }
+ }
+
+ if (print_sections)
+ printf("\n[error]\ncode=%lu\nname=%s\ntext=%s\n",
+ num, GetErrorName(num,"?"), GetErrorText(num,"?"));
+ else if (long_count)
+ printf("%s\n",GetErrorText(num,"?"));
+ else if (brief_count)
+ printf("%lu\n",num);
+ else
+ printf("%s\n",GetErrorName(num,"?"));
+ return stat;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command filetype ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_filetype()
+{
+ #ifdef __CYGWIN__
+ {
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ NORMALIZE_FILENAME_PARAM(param);
+ }
+ #endif
+
+ if (print_header)
+ {
+ uint max_len = 0;
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ uint len = strlen(param->arg);
+ if ( max_len < len )
+ max_len = len;
+ }
+
+ if ( long_count > 1 )
+ printf("\n"
+ "type decomp vers valid file name\n"
+ "%.*s\n", max_len + 25, Minus300 );
+ else if ( long_count )
+ printf("\n"
+ "type decomp vers file name\n"
+ "%.*s\n", max_len + 20, Minus300 );
+ else
+ printf("\n"
+ "type file name\n"
+ "%.*s\n", max_len + 8, Minus300 );
+ }
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ char buf1[CHECK_FILE_SIZE], buf2[CHECK_FILE_SIZE], bufstat[10];
+ FileAttrib_t fatt;
+ enumError err = LoadFILE(param->arg,0,0,buf1,sizeof(buf1),1,&fatt,false);
+ if ( err <= ERR_WARNING || !opt_ignore )
+ {
+ file_format_t fform1;
+ if (S_ISDIR(fatt.mode))
+ {
+ err = ERR_OK;
+ fform1 = FF_DIRECTORY;
+ }
+ else
+// [[analyse-magic]]
+ fform1 = GetByMagicFF(buf1,sizeof(buf1),fatt.size);
+
+ ccp stat1 = err > ERR_WARNING ? "-" : GetNameFF(0,fform1);
+ ccp buf = buf1;
+ if (long_count)
+ {
+ size_t written = fatt.size;
+ file_format_t fform2 = fform1;
+ ccp stat2 = "-";
+ int version = -1;
+ char suffix = 0;
+ bool load_full = false;
+ if (IsYazFF(fform1))
+ {
+ if ( fform1 == FF_XYZ )
+ DecodeXYZ((u8*)buf1,(u8*)buf1,sizeof(buf1));
+
+ fatt.size = be32(&((yaz0_header_t*)buf1)->uncompressed_size);
+ DecompressYAZ( buf1 + sizeof(yaz0_header_t),
+ sizeof(buf1) - sizeof(yaz0_header_t),
+ buf2, sizeof(buf2),
+ &written, param->arg,
+ GetYazVersionFF(fform1), true, 0 );
+ DASSERT( written <= sizeof(buf2) );
+// [[analyse-magic]]
+ fform2 = GetByMagicFF(buf2,written,written);
+ version = GetVersionFF(fform2,buf2,written,&suffix);
+ stat2 = GetNameFF(0,fform2);
+ buf = buf2;
+ }
+ else if ( fform1 == FF_BZ )
+ {
+ load_full = true;
+ wbz_header_t *wh = (wbz_header_t*)buf1;
+ const file_format_t ff_temp
+// [[analyse-magic]]
+ = GetByMagicFF(wh->first_8,sizeof(wh->first_8),be32(wh->bz_data));
+ stat2 = GetNameFF(0,ff_temp);
+ version = GetVersionFF(ff_temp,wh->first_8,sizeof(wh->first_8),&suffix);
+ if ( fatt.size >= 20 )
+ {
+ char compr = buf1[19];
+ if ( compr >= '1' && compr <= '9' )
+ {
+ snprintf(bufstat,sizeof(bufstat),"%s.%c",stat1,compr);
+ stat1 = bufstat;
+ }
+ }
+ }
+ else if ( fform1 == FF_BZ2 )
+ {
+ snprintf(bufstat,sizeof(bufstat),"BZ2.%u",IsBZIP2(buf1,sizeof(buf1)));
+ stat1 = bufstat;
+
+ uint wr;
+ DecodeBZIP2part(buf2,sizeof(buf2),&wr,buf1,sizeof(buf1));
+// [[analyse-magic]]
+ fform2 = GetByMagicFF(buf2,wr,wr);
+ stat2 = GetNameFF(0,fform2);
+ }
+ else
+ version = GetVersionFF(fform1,(u8*)buf1,sizeof(buf1),&suffix);
+
+ char vbuf[20] = " -";
+ if ( version >= 0 )
+ snprintf(vbuf,sizeof(vbuf),"%4u%c", version, suffix ? suffix : ' ' );
+
+ if ( long_count > 1 )
+ {
+ valid_t valid = VALID_UNKNOWN_FF;
+ if (load_full)
+ {
+ szs_file_t szs;
+ InitializeSZS(&szs);
+ if (!LoadSZS(&szs,param->arg,true,true,true))
+ valid = IsValidSZS(&szs,false);
+ ResetSZS(&szs);
+ }
+ else
+ valid = IsValid(buf,written,fatt.size,0,fform2,param->arg);
+ printf("%-6s %-6s %s %-5s %s\n",
+ stat1, stat2, vbuf, valid_text[valid], param->arg );
+ }
+ else
+ printf("%-6s %-6s %s %s\n",stat1,stat2,vbuf,param->arg);
+ }
+ else
+ printf("%-6s %s\n",stat1,param->arg);
+ }
+ }
+
+ if (print_header)
+ putchar('\n');
+
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command fileattrib ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const KeywordTab_t cmdtab_file_attrib[] =
+{
+ { FFT_VALID, "VALID", 0, 0 },
+ { FFT_VALID, "INVALID", "DIRECTORY", 1 },
+
+ { FFT_COMPRESS, "COMPRESSED", 0, 0 },
+ { FFT_ARCHIVE, "ARCHIVES", 0, 0 },
+ { FFT_GRAPHIC, "GRAPHICS", "IMAGES", 0 },
+ { FFT_TEXT, "TEXT", "TXT", 0 },
+ { FFT_M_FTYPE, "ETC", 0, 1 },
+
+ { FFT_BRSUB, "BRSUB", "BR-SUB", 0 },
+ { FFT_BRSUB, "BRESSUB", "BRES-SUB", 0 },
+ { FFT_BRSUB, "BRRESSUB", "BRRES-SUB", 0 },
+ { FFT_BRSUB2, "BRSUB2", "BR-SUB2", 0 },
+ { FFT_BRSUB2, "BRESSUB2", "BRES-SUB2", 0 },
+ { FFT_BRSUB2, "BRRESSUB2", "BRRES-SUB2", 0 },
+ { FFT_EXTERNAL, "EXTERNALS", 0, 0 },
+ { FFT_EXTERNAL, "INTERNALS", 0, 1 },
+
+ { FFT_CREATE, "CREATE", 0, 0 },
+ { FFT_EXTRACT, "EXTRACT", 0, 0 },
+ { FFT_CUT, "CUT", 0, 0 },
+ { FFT_DECODE, "DECODE", 0, 0 },
+ { FFT_ENCODE, "ENCODE", 0, 0 },
+ { FFT_PARSER, "PARSER", 0, 0 },
+
+ { 0,0,0,0 }
+};
+
+//-----------------------------------------------------------------------------
+
+enumError cmd_fileattrib()
+{
+ enumError stat = ERR_OK;
+
+ u8 show[FF_N];
+ if (n_param)
+ {
+ memset(show,0,sizeof(show));
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ uint fform = GetByNameFF(param->arg);
+ if ( fform != FF_UNKNOWN && fform < FF_N )
+ show[fform] = 1;
+ else
+ {
+ const KeywordTab_t * cmd
+ = ScanKeyword(0,param->arg,cmdtab_file_attrib);
+ if (!cmd)
+ stat = ERROR0(ERR_WARNING,
+ "Unknown keyword ignored: %s\n", param->arg);
+ else if (cmd->opt)
+ {
+ int i;
+ for ( i = 0; i < FF_N; i++ )
+ if ( !( GetAttribFF(i) & cmd->id ) )
+ show[i] = 1;
+ }
+ else
+ {
+ int i;
+ for ( i = 0; i < FF_N; i++ )
+ if ( GetAttribFF(i) & cmd->id )
+ show[i] = 1;
+ }
+ }
+ }
+ }
+ else
+ memset(show,1,sizeof(show));
+
+ if (print_header)
+ printf("\n"
+ "type classification possible operations%30s\n"
+ "%.80s\n",
+ "file extensions", Minus300 );
+
+ ccp last = "";
+ for(;;)
+ {
+ ccp found = 0;
+ int i, found_idx = 0;
+ for ( i = 1; i < FF_N; i++ )
+ {
+ if (show[i])
+ {
+ ccp name = GetNameFF(0,i);
+ if ( strcmp(name,last) > 0 && ( !found || strcmp(name,found) < 0 ) )
+ {
+ found = name;
+ found_idx = i;
+ }
+ }
+ }
+
+ if (!found)
+ break;
+
+ printf("%-7s",GetNameFF(0,found_idx));
+
+ const file_type_t *fft = GetFileTypeFF(found_idx);
+ ASSERT(fft);
+ ff_attrib_t fa = fft->attrib;
+ ccp info;
+
+ switch ( fa & (FFT_VALID|FFT_M_FTYPE) )
+ {
+ case 0: info = "directory"; break;
+ case FFT_VALID|FFT_COMPRESS: info = "compress"; break;
+ case FFT_VALID|FFT_ARCHIVE: info = "archive"; break;
+ case FFT_VALID|FFT_GRAPHIC: info = "graphic"; break;
+ case FFT_VALID|FFT_TEXT: info = "text"; break;
+ default: info = "misc"; break;
+ }
+ printf(" %-9s",info);
+
+ switch ( fa & FFT_M_CLASS )
+ {
+ case FFT_BRSUB|FFT_BRSUB2: info = "bres-sub2"; break;
+ case FFT_BRSUB: info = "bres-sub"; break;
+ case FFT_EXTERNAL: info = "external"; break;
+ default: info = "-"; break;
+ }
+ printf(" %-9s",info);
+
+ printf(" %s %s %s %s %s",
+ fa & FFT_CREATE ? "create" : "- ",
+ fa & FFT_EXTRACT ? "extract" : "- ",
+ fa & FFT_CUT ? "cut" : "- ",
+ fa & FFT_DECODE ? "decode"
+ : fa & FFT_LINK ? "link " : "- ",
+ fa & FFT_PARSER ? "parser"
+ : fa & FFT_ENCODE ? "encode"
+ : fa & FFT_PATCH ? "patch " : "- " );
+
+ printf(" %-6s",fft->ext);
+ if ( fa & FFT_ARCHIVE && strcmp(fft->ext_compr,fft->ext) )
+ printf(" %s",fft->ext_compr);
+ if ( strcmp(fft->ext_magic,fft->ext) )
+ printf(" %s",fft->ext_magic);
+ putchar('\n');
+
+ last = found;
+ }
+
+ if (print_header)
+ printf("%.80s\n\n",Minus300);
+
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#undef DEF_ENTRY
+#define DEF_ENTRY(a) { FFT_##a, #a },
+
+void ExportFileAttribMakedoc ( FILE * f )
+{
+ DASSERT(f);
+
+ fprintf(f,"\n#\f\n%.79s\n%.15s%17s%-32s%.15s\n%.79s\n\n",
+ Hash200, Hash200, "", "File Attributes", Hash200, Hash200 );
+
+ struct info_t { int val; ccp name; };
+ static const struct info_t info_tab[] =
+ {
+ DEF_ENTRY(VALID)
+
+ DEF_ENTRY(COMPRESS)
+ DEF_ENTRY(ARCHIVE)
+ DEF_ENTRY(GRAPHIC)
+ DEF_ENTRY(TEXT)
+ DEF_ENTRY(M_FTYPE)
+
+ DEF_ENTRY(BRSUB)
+ DEF_ENTRY(BRSUB2)
+ DEF_ENTRY(EXTERNAL)
+ DEF_ENTRY(M_CLASS)
+
+ DEF_ENTRY(CREATE)
+ DEF_ENTRY(EXTRACT)
+ DEF_ENTRY(CUT)
+ DEF_ENTRY(DECODE)
+ DEF_ENTRY(ENCODE)
+ DEF_ENTRY(PARSER)
+ DEF_ENTRY(M_SUPPORT)
+
+ DEF_ENTRY(M_ALL)
+
+ {0,0}
+ };
+
+
+ fprintf(f,"#!--- define file attribute flags\n\n");
+ const struct info_t *info_ptr;
+ for ( info_ptr = info_tab; info_ptr->name; info_ptr++ )
+ fprintf(f,"#gdef FILEATT_%-10s = %#10x\n",info_ptr->name,info_ptr->val);
+
+ fprintf(f, "\n#!--- define file attribute map\n\n"
+ "#gdef fileatt$flag = @map\n\n");
+ for ( info_ptr = info_tab; info_ptr->name; info_ptr++ )
+ fprintf(f,"#gdef fileatt$flag['%s'] %*s %#10x\n",
+ info_ptr->name,
+ (int)( 10 - strlen(info_ptr->name) ), "=",
+ info_ptr->val );
+
+
+ fprintf(f, "\n#!--- define file attributes\n\n"
+ "#gdef $fileatt = @map\n\n");
+
+ int fform;
+ for ( fform = 1; fform < FF_N; fform++ )
+ {
+ ccp name = GetNameFF(0,fform);
+ fprintf(f,
+ "#pdef d = @map\n"
+ "#pdef d['idx'] = %2u\n"
+ "#pdef d['name'] = '%s'\n"
+ "#pdef d['ext'] = '%s'\n"
+ "#pdef d['c-ext'] = '%s'\n"
+ "#pdef d['attrib'] = %#x\n"
+ "#gdef $fileatt['%s'] = move(d)\n\n"
+ ,fform
+ ,name
+ ,GetExtFF(0,fform)
+ ,GetExtFF(fform_compr,fform)
+ ,GetAttribFF(fform)
+ ,name
+ );
+ }
+}
+
+#undef DEF_ENTRY
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command float ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_float()
+{
+ u16 w4[2], w8[4];
+ memset(w4,0,sizeof(w4));
+ memset(w8,0,sizeof(w8));
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ ccp arg = param->arg;
+ if (!arg)
+ continue;
+ while ( *arg && (uchar)*arg <= ' ' )
+ arg++;
+
+ if (!strcmp(arg,"+"))
+ {
+ write_be32(w4,be32(w4)+1);
+ write_be64(w8,be64(w8)+1);
+ }
+ else if (!strcmp(arg,"-"))
+ {
+ write_be32(w4,be32(w4)-1);
+ write_be64(w8,be64(w8)-1);
+ }
+ else
+ {
+ memset(w4,0,sizeof(w4));
+ memset(w8,0,sizeof(w8));
+
+ uint skip;
+ if ( arg[0] == '0' && ( arg[1] == 'x' || arg[1] == 'X' ))
+ skip = 2;
+ else if ( arg[0] == 'x' || arg[0] == 'X' )
+ skip = 1;
+ else
+ skip = 0;
+
+ if (skip)
+ {
+ ccp src = arg + skip;
+ ScanHexString(w8,sizeof(w8),&src,0,true);
+ memcpy(w4,w8,sizeof(w4));
+ }
+ else
+ {
+ double d = strtod(arg,0);
+ write_bef4(w4,d);
+ write_bef8(w8,d);
+ }
+ }
+
+ printf(" %04x %04x = %12.9g : %04x %04x %04x %04x = %22.18g : |%s|\n",
+ be16(w4+0), be16(w4+1), bef4(w4),
+ be16(w8+0), be16(w8+1), be16(w8+2), be16(w8+3), bef8(w8),
+ arg );
+
+ if (opt_round)
+ {
+ u16 r4[2], r8[4];
+
+ write_bef4(r4,RoundF3bytes(bef4(w4)));
+ write_bef8(r8,RoundD7bytes(bef8(w8)));
+ printf(" %04x %04x = %12.9g : %04x %04x %04x %04x = %22.18g"
+ " : rounded to 3|7 bytes\n",
+ be16(r4+0), be16(r4+1), bef4(r4),
+ be16(r8+0), be16(r8+1), be16(r8+2), be16(r8+3), bef8(r8) );
+
+ write_bef4(r4,RoundF2bytes(bef4(w4)));
+ write_bef8(r8,RoundD6bytes(bef8(w8)));
+ printf(" %04x %04x = %12.9g : %04x %04x %04x %04x = %22.18g"
+ " : rounded to 2|6 bytes\n\n",
+ be16(r4+0), be16(r4+1), bef4(r4),
+ be16(r8+0), be16(r8+1), be16(r8+2), be16(r8+3), bef8(r8) );
+ }
+ }
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command vr-calc ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int get_vr_value ( ccp arg, int res_on_err )
+{
+ if (!arg)
+ return res_on_err;
+
+ while ( *arg == ' ' || *arg == '\t' )
+ arg++;
+
+ if (!*arg)
+ return res_on_err;
+
+ bool neg = *arg == '-' || *arg == '/';
+ if (neg)
+ arg++;
+ int val = str2ul(arg,0,10);
+ if ( val > 99999 )
+ val = 99999;
+
+ return neg ? -val : val;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_vr_calc()
+{
+ #if HAVE_PRINT
+ GetVrDiffByTab(0); // force logging here and now
+ #endif
+
+ if (brief_count)
+ {
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ ccp arg = param->arg;
+ printf("%4u\n",GetVrDiffByTab(get_vr_value(arg,0)));
+ }
+ }
+ else
+ {
+ const uint seplen = 3*27;
+ if (print_header)
+ printf(
+ "\n"
+ "%s%.*s%s\n"
+ "%s vr vr-increment for%s\n"
+ "%s delta win-lose lose-win%s\n"
+ "%s%.*s%s\n",
+ colout->heading, seplen, ThinLine300_3, colout->reset,
+ colout->heading, colout->reset,
+ colout->heading, colout->reset,
+ colout->heading, seplen, ThinLine300_3, colout->reset );
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ ccp arg = param->arg;
+ const int delta = get_vr_value(arg,0);
+ printf("%7d %6u %9u\n",
+ delta, GetVrDiffByTab(delta), GetVrDiffByTab(-delta) );
+ }
+
+ if (print_header)
+ printf("%s%.*s%s\n\n",
+ colout->heading, seplen, ThinLine300_3, colout->reset );
+ }
+
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command vr-race ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_vr_race()
+{
+ #if HAVE_PRINT
+ GetVrDiffByTab(0); // force logging here and now
+ #endif
+
+ enum { MAX = 30 };
+ int n = 0, vr0[MAX], delta[MAX] = {0}, delta2[MAX][MAX] = {{0}};
+
+ //--- scan param
+
+ ParamList_t *param;
+ for ( param = first_param; param && n < MAX; param = param->next )
+ {
+ ccp arg = param->arg;
+ int repeat = 1;
+ ccp star = strchr(arg,'*');
+ if (star)
+ {
+ repeat = str2l(arg,0,10);
+ arg = star+1;
+ }
+
+ const int vr = get_vr_value(arg,-1);
+ if ( vr < 0 )
+ continue;
+
+ while ( repeat-- > 0 && n < MAX )
+ {
+ vr0[n] = vr;
+ n++;
+ }
+ }
+
+ if ( n < 2 )
+ return ERROR0(ERR_SEMANTIC,"Need VR values of 2..%u players.",MAX);
+
+
+ //--- calculate delta
+
+ int i, j;
+ for ( i = 0; i < n-1; i++ )
+ {
+ for ( j = i+1; j < n; j++ )
+ {
+ const int d = GetVrDiffByTab( vr0[i] - vr0[j] );
+ delta[i] += d;
+ delta[j] -= d;
+ delta2[i][j] = d;
+ delta2[j][i] = -d;
+ }
+ }
+
+
+ //--- print table
+
+ if (long_count)
+ {
+ const uint seplen = 3*(20+5*n);
+ printf( "\n"
+ "%s%.*s%s\n"
+ "%s vr : %*s= vr%s\n"
+ "%srk. start : %*s= final%s\n"
+ "%s%.*s%s\n",
+ colout->heading, seplen, ThinLine300_3, colout->reset,
+ colout->heading, -5*n, " vr", colout->reset,
+ colout->heading, -5*n, "delta...", colout->reset,
+ colout->heading, seplen, ThinLine300_3, colout->reset );
+
+ for ( i = 0; i < n; i++ )
+ {
+ printf("%2d. %5d :", i+1, vr0[i] );
+ ccp col = colout->reset;
+ for ( j = 0; j < n; j++ )
+ {
+ if ( i == j )
+ {
+ printf("%s •",colout->b_cyan);
+ col = colout->b_cyan;
+ }
+ else
+ {
+ const int d = delta2[i][j];
+ ccp next_col = d < 0 ? colout->b_red
+ : d > 0 ? colout->b_green : colout->reset;
+ if ( col != next_col )
+ {
+ col = next_col;
+ printf(" %s%4d",col,d);
+ }
+ else
+ printf(" %4d",d);
+ }
+ }
+
+
+ const int d = delta[i];
+ ccp next_col = d < 0 ? colout->b_red : d > 0 ? colout->b_green : "";
+
+ printf(" %s= %s%5d%s\n",
+ col == colout->reset ? "" : colout->reset,
+ next_col, vr0[i] + delta[i],
+ *next_col ? colout->reset : "" );
+ }
+ printf("%s%.*s%s\n\n",
+ colout->heading, seplen, ThinLine300_3, colout->reset );
+ }
+ else
+ {
+ const uint seplen = 3*28;
+ printf( "\n"
+ "%s%.*s%s\n"
+ "%s vr vr vr%s\n"
+ "%s rank start delta final%s\n"
+ "%s%.*s%s\n",
+ colout->heading, seplen, ThinLine300_3, colout->reset,
+ colout->heading, colout->reset,
+ colout->heading, colout->reset,
+ colout->heading, seplen, ThinLine300_3, colout->reset );
+
+ for ( i = 0; i < n; i++ )
+ printf("%4d. %6d %+6d = %5d\n",
+ i+1, vr0[i], delta[i], vr0[i]+delta[i] );
+
+ printf("%s%.*s%s\n\n",
+ colout->heading, seplen, ThinLine300_3, colout->reset );
+ }
+
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command rawdump ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_rawdump ( const data_tab_t *data_tab )
+{
+ CheckOptDest( first_param ? first_param->arg : "rawdump.tmp/", true );
+ opt_mkdir = true;
+
+ const data_tab_t *dt;
+ for ( dt = data_tab; dt->name; dt++ )
+ {
+ char path_buf[PATH_MAX];
+ ccp dest = PathCatPPE(path_buf,sizeof(path_buf),opt_dest,dt->name,".bin");
+
+ const u8 *data = dt->data;
+ uint size = dt->size;
+ if ( dt->mode & 1 )
+ DecodeBZIP2((u8**)&data,&size,0,dt->data,dt->size);
+
+ printf("SAVE %7u = %6x %x %s\n",size,size,dt->mode,dest);
+ SaveFILE(dest,0,true,data,size,0);
+
+ u8 *bz_data = 0;
+ uint bz_size = 0, bz_compr = 0;
+ uint compr;
+
+ for ( compr = 1; compr <= 9; compr++ )
+ {
+ u8 *temp_data;
+ uint temp_size;
+ enumError err = EncodeBZIP2( &temp_data, &temp_size, true,
+ 0, data, size, compr );
+ if ( !err && ( !bz_data || temp_size < bz_size ) )
+ {
+ FREE(bz_data);
+ bz_data = temp_data;
+ bz_size = temp_size;
+ bz_compr = compr;
+ }
+ else
+ FREE(temp_data);
+ }
+
+ if (bz_data)
+ {
+ dest = PathCatPPE(path_buf,sizeof(path_buf),opt_dest,dt->name,".bz-bin");
+ printf("SAVE %7u = %6x %s [c%u,%u%%]\n",
+ bz_size, bz_size, dest, bz_compr, bz_size*100/size );
+ SaveFILE(dest,0,true,bz_data,bz_size,0);
+ }
+
+ if ( data != dt->data )
+ FREE((u8*)data);
+ }
+
+ return ERR_OK;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/////////////// export keys ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+export_mode_t ScanExportMode() // scan parameters
+{
+ static const KeywordTab_t tab[] =
+ {
+ { -1, "HELP", "?", 0 },
+
+// { EXPORT_C, "C", 0, EXPORT_CMD_MASK },
+ { EXPORT_PHP, "PHP", 0, EXPORT_CMD_MASK },
+ { EXPORT_MAKEDOC, "MAKEDOC", 0, EXPORT_CMD_MASK },
+
+ { EXPORT_F_FUNCTIONS, "FUNCTIONS", 0, 0 },
+ { EXPORT_F_FILEATTRIB, "FILEATTRIB", "FA", 0 },
+ { EXPORT_F_TRACKS, "TRACKS", "T", 0 },
+
+ { 0,0,0,0 }
+ };
+
+ export_mode_t exmode = 0;
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ NORMALIZE_FILENAME_PARAM(param);
+ ccp arg = param->arg;
+ if (!*arg)
+ continue;
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (!cmd)
+ continue;
+
+ if ( cmd->id == -1 )
+ {
+ printf("\nList of kexwords:\n\n");
+ const KeywordTab_t *ptr;
+ for ( ptr = tab; ptr->name1; ptr++ )
+ {
+ if (ptr->name2)
+ printf(" %s = %s\n",ptr->name1,ptr->name2);
+ else
+ printf(" %s\n",ptr->name1);
+ }
+ putchar('\n');
+ return 0;
+ }
+
+ if (cmd->opt)
+ exmode &= ~cmd->opt;
+ exmode |= cmd->id;
+ }
+
+ if (!( exmode & EXPORT_FLAG_MASK ))
+ exmode |= EXPORT_FLAG_MASK;
+
+ if (!( exmode & EXPORT_CMD_MASK ))
+ {
+ ERROR0(ERR_SYNTAX,"Export: No ouput format entered!\n");
+ exmode = 0;
+ }
+
+ return exmode;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// repair magic ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[repair_mode_t]]
+
+// vars for --repair-magic
+repair_mode_t repair_magic = REPMD_OFF;
+repair_mode_t repair_magic_flags = 0;
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptRepairMagic ( ccp arg )
+{
+ if (!arg)
+ {
+ repair_magic = REPMD_REPAIR;
+ repair_magic_flags = 0;
+ return 0;
+ }
+
+ static const KeywordTab_t keytab[] =
+ {
+ { REPMD_OFF, "OFF", 0, REPMD_M_MODES },
+ { REPMD_ANALYZE, "ANALYZE", 0, REPMD_M_MODES },
+ { REPMD_REPAIR, "REPLACE", "ON", REPMD_M_MODES },
+ { REPMD_F_FNAME_FIRST, "FNAME", 0, 0 },
+
+ {0,0,0,0}
+ };
+
+ const s64 stat = ScanKeywordList(arg,keytab,0,true,0,0,
+ "Option --repair-magic",ERR_SYNTAX);
+ if ( stat == -1 )
+ return 1;
+
+ repair_magic = stat & REPMD_M_MODES;
+ repair_magic_flags = stat & REPMD_M_FLAGS;
+ PRINT("%llx => repair_magic=%x,%x\n",stat,repair_magic,repair_magic_flags);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+repair_ff_t * ResetRepairMagic ( repair_ff_t * data, repair_ff_t * fallback )
+{
+ static repair_ff_t falback2;
+ if (!data)
+ data = fallback ? fallback : &falback2;
+
+ memset(data,0,sizeof(*data));
+ data->mode = REPMD_OFF;
+ data->fform = FF_UNKNOWN;
+
+ return data;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetRepairMagicInfo ( const repair_ff_t *rep )
+{
+ if (!rep)
+ return NULL;
+
+ char buf[80];
+ int len = snprintfS(buf,sizeof(buf),
+ "smode=%u%s%s%s, fform=%s, ext=%s, magic=%s/%s",
+ rep->mode,
+ rep->flags & REPMD_F_FNAME_FIRST? "f" : "",
+ rep->flags & REPMD_R_FNAME ? "F" : "",
+ rep->flags & REPMD_R_FALLBACK ? "X" : "",
+ GetNameFF(rep->fform,0), rep->file_ext,
+ PrintID(rep->orig_magic,rep->magic_len,0),
+ PrintID(rep->magic,rep->magic_len,0)
+ ) + 1;
+
+ char *res = GetCircBuf(len);
+ memcpy(res,buf,len);
+ return res;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+file_format_t AnalyseMagic
+(
+ // returns rep->fform
+
+ repair_ff_t *rep, // not NULL: result, will be initalized
+ const void *data, // pointer to data
+ uint data_size, // size of data
+ uint file_size, // NULL or total size of file
+ file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails
+ ccp fname, // filename to analyse if GetByMagicFF() fails
+ bool fname_first // analyse the filename first.
+)
+{
+ //fprintf(stderr,">>> %s\n",fname); HexDump16(stderr,0,0,data,16);
+
+ repair_ff_t local_rep;
+ rep = ResetRepairMagic(rep,&local_rep);
+ DASSERT(rep);
+
+ memcpy( rep->magic, data,
+ data_size < sizeof(rep->magic) ? data_size : sizeof(rep->magic) );
+ memcpy( rep->orig_magic, rep->magic, sizeof(rep->orig_magic) );
+
+ if (!be32(rep->magic))
+ fname_first = true;
+ rep->flags = fname_first ? REPMD_F_FNAME_FIRST : 0;
+
+ if (fname)
+ {
+ ccp point = strrchr(fname,'.');
+ if (point)
+ {
+ ccp slash = strrchr(fname,'/');
+ if ( !slash || point > slash && strlen(point) < sizeof(rep->file_ext) )
+ StringLowerS(rep->file_ext,sizeof(rep->file_ext),point);
+ }
+ }
+
+ repair_mode_t status = 0;
+ file_format_t ff_magic = GetByMagicFF(data,data_size,file_size);
+ if ( ff_magic == FF_UNKNOWN && ff_fallback != FF_UNKNOWN )
+ {
+ ff_magic = ff_fallback;
+ status = REPMD_R_FALLBACK;
+ }
+
+ rep->magic_len = FileTypeTab[ff_magic].magic_len;
+ if ( ff_magic != FF_UNKNOWN && !fname_first )
+ {
+ rep->flags |= status;
+ return rep->fform = ff_magic;
+ }
+
+ const file_type_t *ft = GetFileTypeByExt(rep->file_ext,true);
+ if (!ft)
+ ft = GetFileTypeBySubdir(fname,true);
+ rep->flags |= ft ? REPMD_R_FNAME : status;
+ if ( !ft || ft->fform == ff_magic )
+ return rep->fform = ff_magic;
+
+ //fprintf(stderr,">>> ff: %d > %d : %s\n",ff_magic,ft->fform,fname);
+ DASSERT(ft);
+ rep->fform = ft->fform;
+ const uint mlen = ft->magic_len < sizeof(rep->magic)
+ ? ft->magic_len : sizeof(rep->magic);
+ rep->magic_len = mlen;
+ memset(rep->magic,0,sizeof(rep->magic));
+ memcpy(rep->magic,ft->magic,mlen);
+ rep->mode = REPMD_ANALYZE;
+
+ PRINT("AnalyseMagic() %s\n",GetRepairMagicInfo(rep));
+ return rep->fform;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+file_format_t RepairMagic
+(
+ // returns rep->fform
+
+ repair_ff_t *rep, // not NULL: result, will be initalized
+ void *data, // pointer to data, magic will be replaced
+ uint data_size, // size of data
+ uint file_size, // NULL or total size of file
+ file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails
+ ccp fname, // filename to analyse if GetByMagicFF() fails
+ bool fname_first // analyse the filename first.
+)
+{
+ repair_ff_t local_rep;
+ if (!rep)
+ rep = &local_rep;
+ AnalyseMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first);
+
+ if ( rep->mode == REPMD_ANALYZE && rep->magic_len )
+ {
+ if (logging>1)
+ fprintf(stderr,"REPAIR MAGIC: %s -> %s (%s) : %s\n",
+ PrintID(data,rep->magic_len,0),
+ PrintID(rep->magic,rep->magic_len,0),
+ GetNameFF(0,rep->fform), fname );
+
+ rep->mode = REPMD_REPAIR;
+ memcpy(data,rep->magic,rep->magic_len);
+ }
+
+ PRINT("RepairMagic() %s\n",GetRepairMagicInfo(rep));
+ return rep->fform;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+file_format_t AnalyseMagicByOpt
+(
+ // returns rep->fform
+ // mode & fname_first depends on --repair-magic, but is never REPMD_REPAIR
+
+ repair_ff_t *rep, // not NULL: result, will be initalized
+ const void *data, // pointer to data
+ uint data_size, // size of data
+ uint file_size, // NULL or total size of file
+ file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails
+ ccp fname // filename to analyse if GetByMagicFF() fails
+)
+{
+ repair_ff_t local_rep;
+ rep = ResetRepairMagic(rep,&local_rep);
+ DASSERT(rep);
+
+ const bool fname_first = ( repair_magic_flags & REPMD_F_FNAME_FIRST ) != 0;
+ switch(repair_magic)
+ {
+ case REPMD_ANALYZE:
+ case REPMD_REPAIR:
+ AnalyseMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first);
+ break;
+
+ default:
+ rep->fform = GetByMagicFF(data,data_size,file_size);
+ if ( rep->fform == FF_UNKNOWN && ff_fallback != FF_UNKNOWN )
+ {
+ rep->fform = ff_fallback;
+ rep->flags |= REPMD_R_FALLBACK;
+ }
+ break;
+ }
+ return rep->fform;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+file_format_t RepairMagicByOpt
+(
+ // returns rep->fform
+ // mode & fname_first depends on --repair-magic
+
+ repair_ff_t *rep, // not NULL: result, will be initalized
+ void *data, // pointer to data, magic will be replaced
+ uint data_size, // size of data
+ uint file_size, // NULL or total size of file
+ file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails
+ ccp fname // filename to analyse if GetByMagicFF() fails
+)
+{
+ repair_ff_t local_rep;
+ rep = ResetRepairMagic(rep,&local_rep);
+ DASSERT(rep);
+
+ const bool fname_first = ( repair_magic_flags & REPMD_F_FNAME_FIRST ) != 0;
+ switch(repair_magic)
+ {
+ case REPMD_ANALYZE:
+ AnalyseMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first);
+ break;
+
+ case REPMD_REPAIR:
+ RepairMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first);
+ break;
+
+ default:
+ rep->fform = GetByMagicFF(data,data_size,file_size);
+ if ( rep->fform == FF_UNKNOWN && ff_fallback != FF_UNKNOWN )
+ {
+ rep->fform = ff_fallback;
+ rep->flags |= REPMD_R_FALLBACK;
+ }
+ break;
+ }
+ return rep->fform;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// misc ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SHA1_to_ID ( sha1_id_t id, const sha1_hash_t hash )
+{
+ //--- create a smaller hash value (20->5 bytes with 3 bytes padding)
+
+ u8 buf[8];
+ u8 * dest = buf;
+ const u8 * src = hash;
+
+ uint i;
+ for ( i = 0; i < 5; i++ )
+ {
+ *dest++ = src[0] ^ src[5] ^ src[10] ^ src[15];
+ src++;
+ }
+
+ //--- now create a printable and case insenitive ID
+
+ static const u8 ctab[] = "0123456789abcdefghijklmnopqrstuv";
+ u64 val = le64(buf);
+ dest = id;
+ for ( i = 0; i < 8; i++ )
+ {
+ *dest++ = ctab[ val & 0x1f ];
+ val >>= 5;
+ }
+ *dest = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+bool IsExtractEnabled()
+{
+ static bool enabled = false;
+ if ( opt_extract && *opt_extract && !enabled )
+ {
+ if ( CreatePath(opt_extract,true) == ERR_OK )
+ enabled = true;
+ }
+ return enabled;
+}
+
+//-----------------------------------------------------------------------------
+
+bool PrepareExtract
+(
+ // return FALSE on disabled or failure, otherwise return TRUE
+
+ File_t *f, // file structure, will be initialized
+ int mode, // convert filename: <0=to lower, >0=to upper
+ ccp format, // format of filename
+ ... // arguments
+)
+{
+ DASSERT(f);
+ DASSERT(format);
+
+ if (!IsExtractEnabled())
+ {
+ InitializeFile(f);
+ return 0;
+ }
+
+ char fname[1000];
+ va_list arg;
+ va_start(arg,format);
+ vsnprintf(fname,sizeof(fname),format,arg);
+ va_end(arg);
+
+ if (mode)
+ {
+ char *ptr = fname;
+ if ( mode < 0 )
+ while ( ( *ptr = tolower((int)*ptr) ) != 0 )
+ ptr++;
+ else
+ while ( ( *ptr = toupper((int)*ptr) ) != 0 )
+ ptr++;
+ }
+
+ char pathbuf[PATH_MAX];
+ ccp path = PathCatPP(pathbuf,sizeof(pathbuf),opt_extract,fname);
+ CreateFile(f,true,path,FM_REMOVE);
+ return f->f != NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+CheckMode_t GetCheckMode
+(
+ bool no_head, // true: suppress header
+ bool no_hints, // true: suppress hints
+ bool quiet, // true: be quiet if no hint|warning is printed
+ bool verbose // true: print some more hints
+)
+{
+ CheckMode_t mode = CMOD_DEFAULT;
+ if (no_head)
+ mode &= ~(CMOD_HEADER|CMOD_FORCE_HEADER);
+ if (no_hints)
+ mode &= ~CMOD_HINT;
+ if (quiet)
+ mode &= ~(CMOD_FORCE_HEADER|CMOD_FORCE_FOOTER);
+ if (verbose)
+ mode |= CMOD_INFO|CMOD_VERBOSE;
+ return mode;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+CheckMode_t GetMainCheckMode ( CheckMode_t mode, bool reset_counter )
+{
+ if (reset_counter)
+ {
+ global_warn_count = 0;
+ global_hint_count = 0;
+ global_info_count = 0;
+ }
+
+ return mode | CMOD_FORCE_HEADER;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+CheckMode_t GetSubCheckMode ( CheckMode_t mode, bool reset_counter )
+{
+ return mode & ~(CMOD_FOOTER|CMOD_FORCE_FOOTER)
+ | CMOD_FORCE_HEADER
+ | CMOD_GLOBAL_STAT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint GetMkwMusicSlot ( uint tid )
+{
+ if ( tid < MKW_N_TRACKS )
+ return track_info[tid].music_id;
+
+ if ( tid - MKW_N_TRACKS < MKW_N_ARENAS )
+ return arena_info[tid-MKW_N_TRACKS].music_id;
+
+ switch (tid)
+ {
+ case 0x36: return 0xc9; // Galaxy Stadium
+ default: return 0x75;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// apple only ///////////////
+///////////////////////////////////////////////////////////////////////////////
+#ifdef __APPLE__
+///////////////////////////////////////////////////////////////////////////////
+
+void * memmem ( const void *l, size_t l_len, const void *s, size_t s_len )
+{
+ register char *cur, *last;
+ const char *cl = (const char *)l;
+ const char *cs = (const char *)s;
+
+ /* we need something to compare */
+ if (l_len == 0 || s_len == 0)
+ return NULL;
+
+ /* "s" must be smaller or equal to "l" */
+ if (l_len < s_len)
+ return NULL;
+
+ /* special case where s_len == 1 */
+ if (s_len == 1)
+ return memchr(l, (int)*cs, l_len);
+
+ /* the last position where its possible to find "s" in "l" */
+ last = (char *)cl + l_len - s_len;
+
+ for (cur = (char *)cl; cur <= last; cur++)
+ if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
+ return cur;
+
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#endif // __APPLE__
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ImageGeometry_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const ImageGeometry_t img_geo_I4 =
+{
+ IMG_I4, // iform
+ 0, 1, 0, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 4, 8, 8, 32, // bits_per_pixel, block: width,height,size
+ "I4" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_I8 =
+{
+ IMG_I8, // iform
+ 0, 1, 0, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 8, 8, 4, 32, // bits_per_pixel, block: width,height,size
+ "I8" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_IA4 =
+{
+ IMG_IA4, // iform
+ 0, 1, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 8, 8, 4, 32, // bits_per_pixel, block: width,height,size
+ "IA4" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_IA8 =
+{
+ IMG_IA8, // iform
+ 0, 1, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 16, 4, 4, 32, // bits_per_pixel, block: width,height,size
+ "IA8" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_RGB565 =
+{
+ IMG_RGB565, // iform
+ 0, 0, 0, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 16, 4, 4, 32, // bits_per_pixel, block: width,height,size
+ "RGB565" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_RGB5A3 =
+{
+ IMG_RGB5A3, // iform
+ 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 16, 4, 4, 32, // bits_per_pixel, block: width,height,size
+ "RGB5A3" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_RGBA32 =
+{
+ IMG_RGBA32, // iform
+ 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 32, 4, 4, 64, // bits_per_pixel, block: width,height,size
+ "RGBA32" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_C4 =
+{
+ IMG_C4, // iform
+ 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0x10, 1, 0, // max_palette, read_support, write_support
+ 4, 8, 8, 32, // bits_per_pixel, block: width,height,size
+ "C4" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_C8 =
+{
+ IMG_C8, // iform
+ 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0x100, 1, 0, // max_palette, read_support, write_support
+ 8, 8, 4, 32, // bits_per_pixel, block: width,height,size
+ "C8" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_C14X2 =
+{
+ IMG_C14X2, // iform
+ 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0x4000, 1, 0, // max_palette, read_support, write_support
+ 16, 4, 4, 32, // bits_per_pixel, block: width,height,size
+ "C14X2" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_CMPR =
+{
+ IMG_CMPR, // iform
+ 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 4, 8, 8, 32, // bits_per_pixel, block: width,height,size
+ "CMPR" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_X_GRAY =
+{
+ IMG_X_GRAY, // iform
+ 1, 1, 1, 0, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 16, 1, 1, 2, // bits_per_pixel, block: width,height,size
+ "X-GRAY" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_X_RGB =
+{
+ IMG_X_RGB, // iform
+ 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks
+ 0, 1, 1, // max_palette, read_support, write_support
+ 32, 1, 1, 4, // bits_per_pixel, block: width,height,size
+ "X-RGB" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_X_PAL_4 =
+{
+ IMG_X_PAL4, // iform
+ 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks
+ 0x10, 1, 1, // max_palette, read_support, write_support
+ 16, 1, 1, 2, // bits_per_pixel, block: width,height,size
+ "X-PAL4" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_X_PAL_8 =
+{
+ IMG_X_PAL8, // iform
+ 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks
+ 0x100, 1, 1, // max_palette, read_support, write_support
+ 16, 1, 1, 2, // bits_per_pixel, block: width,height,size
+ "X-PAL8" // name
+};
+
+//-----------------------------------------------------------------------------
+
+static const ImageGeometry_t img_geo_X_PAL_14 =
+{
+ IMG_X_PAL14, // iform
+ 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks
+ 0x4000, 1, 1, // max_palette, read_support, write_support
+ 16, 1, 1, 2, // bits_per_pixel, block: width,height,size
+ "X-PAL14" // name
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+const ImageGeometry_t * GetImageGeometry ( image_format_t iform )
+{
+ switch (iform)
+ {
+ case IMG_I4: return &img_geo_I4;
+ case IMG_I8: return &img_geo_I8;
+ case IMG_IA4: return &img_geo_IA4;
+ case IMG_IA8: return &img_geo_IA8;
+ case IMG_RGB565: return &img_geo_RGB565;
+ case IMG_RGB5A3: return &img_geo_RGB5A3;
+ case IMG_RGBA32: return &img_geo_RGBA32;
+ case IMG_C4: return &img_geo_C4;
+ case IMG_C8: return &img_geo_C8;
+ case IMG_C14X2: return &img_geo_C14X2;
+ case IMG_CMPR: return &img_geo_CMPR;
+ case IMG_X_GRAY: return &img_geo_X_GRAY;
+ case IMG_X_RGB: return &img_geo_X_RGB;
+ case IMG_X_PAL4: return &img_geo_X_PAL_4;
+ case IMG_X_PAL8: return &img_geo_X_PAL_8;
+ case IMG_X_PAL14: return &img_geo_X_PAL_14;
+ case IMG_X_PAL: return &img_geo_X_PAL_14;
+
+ case IMG_N_INTERN:
+ case IMG_INVALID:
+ case IMG_X_AUTO:
+ return 0;
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp PrintImageFormat
+(
+ image_format_t iform, // image format
+ palette_format_t pform // palette format
+)
+{
+ const ImageGeometry_t *geo = GetImageGeometry(iform);
+ if (geo)
+ {
+ if (!geo->max_palette)
+ return geo->name;
+
+ ccp pal;
+ switch (pform)
+ {
+ case PAL_IA8: pal = "IA8"; break;
+ case PAL_RGB565: pal = "RGB565"; break;
+ case PAL_RGB5A3: pal = "RGB5A3"; break;
+ case PAL_X_RGB: pal = "X-RGB"; break;
+ default: pal = 0;
+ }
+
+ const uint bufsize = 20;
+ char *buf = GetCircBuf(bufsize);
+ if (pal)
+ snprintf(buf,bufsize,"%s.%s",geo->name,pal);
+ else
+ snprintf(buf,bufsize,"%s.#%x",geo->name,pform);
+ return buf;
+ }
+
+ const uint bufsize = 20;
+ char *buf = GetCircBuf(bufsize);
+ snprintf(buf,bufsize,"#%x",iform);
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint CalcImageSize
+(
+ uint width, // width of image in pixel
+ uint height, // height of image in pixel
+
+ uint bits_per_pixel, // number of bits per pixel
+ uint block_width, // width of a single block
+ uint block_height, // height of a single block
+
+ uint * x_width, // not NULL: store extended width here
+ uint * x_height, // not NULL: store extended height here
+ uint * h_blocks, // not NULL: store number of horizontal blocks
+ uint * v_blocks // not NULL: store number of vertical blocks
+)
+{
+ const uint hblocks = ( width + block_width - 1 ) / block_width;
+ if (h_blocks)
+ *h_blocks = hblocks;
+
+ const uint xwidth = hblocks * block_width;
+ if (x_width)
+ *x_width = xwidth;
+
+ const uint vblocks = ( height + block_height - 1 ) / block_height;
+ if (v_blocks)
+ *v_blocks = vblocks;
+
+ const uint xheight = vblocks * block_height;
+ if (x_height)
+ *x_height = xheight;
+
+ return xwidth * xheight * bits_per_pixel / 8;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const ImageGeometry_t * CalcImageGeometry
+(
+ image_format_t iform, // image format
+ uint width, // width of image in pixel
+ uint height, // height of image in pixel
+
+ uint * x_width, // not NULL: store extended width here
+ uint * x_height, // not NULL: store extended height here
+ uint * h_blocks, // not NULL: store number of horizontal blocks
+ uint * v_blocks, // not NULL: store number of vertical blocks
+ uint * img_size // not NULL: return image size
+)
+{
+ const ImageGeometry_t *geo = GetImageGeometry(iform);
+ if (geo)
+ {
+ const uint size
+ = CalcImageSize( width, height,
+ geo->bits_per_pixel, geo->block_width, geo->block_height,
+ x_width, x_height, h_blocks, v_blocks );
+ if (img_size)
+ *img_size = size;
+ return geo;
+ }
+
+ if (x_width) *x_width = width;
+ if (x_height) *x_height = height;
+ if (h_blocks) *h_blocks = width;
+ if (v_blocks) *v_blocks = height;
+ if (img_size) *img_size = 0;
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// image formats ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const KeywordTab_t cmdtab_image_format[] =
+{
+ { IMG_I4, "I4", 0, CF_IMAGE },
+ { IMG_I8, "I8", 0, CF_IMAGE },
+ { IMG_IA4, "IA4", 0, CF_IMAGE },
+ { IMG_IA8, "IA8", 0, CF_IMAGE },
+ { IMG_RGB565, "RGB565", 0, CF_IMAGE },
+ { IMG_RGB5A3, "RGB5A3", 0, CF_IMAGE },
+ { IMG_RGBA32, "RGBA32", "RGBA8", CF_IMAGE },
+ { IMG_C4, "C4", "CI4", CF_IMAGE|CF_PALETTE },
+ { IMG_C8, "C8", "CI8", CF_IMAGE|CF_PALETTE },
+ { IMG_C14X2, "C14X2", "CI14X2", CF_IMAGE|CF_PALETTE },
+ { IMG_CMPR, "CMPR", 0, CF_IMAGE },
+
+ { IMG_X_AUTO, "AUTO", 0, CF_AUTO },
+ { IMG_X_GRAY, "X-GRAY", "GRAY", 0 },
+ { IMG_X_RGB, "X-RGB", "RGB", 0 },
+
+ { IMG_X_PAL4, "X-PAL4", "PAL4", 0 },
+ { IMG_X_PAL8, "X-PAL8", "PAL8", 0 },
+ { IMG_X_PAL14, "X-PAL14", "PAL14", 0 },
+ { IMG_X_PAL, "X-PAL", "PAL", CF_AUTO },
+ { IMG_X_PAL, "X-PALETTE", "PALETTE", CF_AUTO },
+
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+image_format_t ScanImageFormat ( ccp arg )
+{
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,cmdtab_image_format);
+ return cmd ? cmd->id : IMG_INVALID;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetImageFormatName ( image_format_t iform, ccp unknown_value )
+{
+ if ( iform >= 0 )
+ {
+ const KeywordTab_t * ct;
+ for ( ct = cmdtab_image_format; ct->name1; ct++ )
+ if ( ct->id == iform )
+ return ct->name1;
+ }
+ return unknown_value;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// palette formats ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const KeywordTab_t cmdtab_palette_format[] =
+{
+ { PAL_IA8, "P-IA8", "PIA8", 0 },
+ { PAL_IA8, "GRAY", "IA8", 0 },
+
+ { PAL_RGB565, "P-RGB565", "PRGB565", 0 },
+ { PAL_RGB565, "COLOR", "RGB565", 0 },
+
+ { PAL_RGB5A3, "P-RGB5A3", "PRGB5A3", 0 },
+ { PAL_RGB5A3, "ALPHA", "RGB5A3", 0 },
+
+ { PAL_X_RGB, "P-RGB", "RGB", 0 },
+
+ { PAL_AUTO, "P-AUTO", "AUTO", 0 },
+
+ { 0,0,0,0 }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+palette_format_t ScanPaletteFormat ( ccp arg )
+{
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,cmdtab_palette_format);
+ return cmd ? cmd->id : PAL_INVALID;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetPaletteFormatName ( image_format_t iform, ccp unknown_value )
+{
+ if ( iform >= 0 )
+ {
+ const KeywordTab_t * ct;
+ for ( ct = cmdtab_palette_format; ct->name1; ct++ )
+ if ( ct->id == iform )
+ return ct->name1;
+ }
+ return unknown_value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int PaletteToImageFormat ( int pform, int return_if_invalid )
+{
+ switch (pform)
+ {
+ case PAL_IA8: return IMG_IA8;
+ case PAL_RGB565: return IMG_RGB565;
+ case PAL_RGB5A3: return IMG_RGB5A3;
+ case PAL_X_RGB: return IMG_X_RGB;
+ }
+ return return_if_invalid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint GetPaletteCountIF
+(
+ image_format_t iform // image format
+)
+{
+ switch (iform)
+ {
+ case IMG_C4:
+ case IMG_X_PAL4:
+ return 0x10;
+
+ case IMG_C8:
+ case IMG_X_PAL8:
+ return 0x100;
+
+ case IMG_C14X2:
+ case IMG_X_PAL14:
+ case IMG_X_PAL:
+ return 0x4000;
+
+ default:
+ return 0;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// scan patch options ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const KeywordTab_t cmdtab_patch_image[] =
+{
+ // order is important for back calculations
+
+ { 0, "DEFAULT", "D", PIM_MASK },
+ { PIM_CENTER, "CENTER", "C", PIM_M_V | PIM_M_H },
+
+ { PIM_VCENTER, "VCENTER", "V", PIM_M_V },
+ { PIM_TOP, "TOP", "T", PIM_M_V },
+ { PIM_BOTTOM, "BOTTOM", "B", PIM_M_V },
+ { PIM_INS_TOP, "INS-TOP", "IT", PIM_M_V },
+ { PIM_INS_BOTTOM, "INS-BOTTOM", "IB", PIM_M_V },
+
+ { PIM_HCENTER, "HCENTER", "H", PIM_M_H },
+ { PIM_LEFT, "LEFT", "L", PIM_M_H },
+ { PIM_RIGHT, "RIGHT", "R", PIM_M_H },
+ { PIM_INS_LEFT, "INS-LEFT", "IL", PIM_M_H },
+ { PIM_INS_RIGHT, "INS-RIGHT", "IR", PIM_M_H },
+
+ { PIM_MIX, "MIX", "MX", PIM_M_COPY },
+ { PIM_FOREGROUND, "FOREGROUND", "FG", PIM_M_COPY },
+ { PIM_BACKGROUND, "BACKGROUND", "BG", PIM_M_COPY },
+ { PIM_COPY, "COPY", "CP", PIM_M_COPY },
+
+ { PIM_LEAVE, "LEAVE", "LV", PIM_M_SIZE },
+ { PIM_SHRINK, "SHRINK", "SH", PIM_M_SIZE },
+ { PIM_GROW, "GROW", "GR", PIM_M_SIZE },
+
+ {0,0,0,0}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool ScanPatchImage ( ccp arg, PatchImage_t * mode )
+{
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,cmdtab_patch_image);
+ if (!cmd)
+ return false;
+
+ if (mode)
+ *mode = *mode & ~cmd->opt | cmd->id;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptPatchImage ( ccp arg )
+{
+ ccp eq = strchr(arg,'=');
+ if (!eq)
+ {
+ ERROR0(ERR_SYNTAX,"Missing equal sign: %s\n",arg);
+ return 1;
+ }
+
+ int stat = 0;
+ if ( eq > arg )
+ {
+ char keyword[1000];
+ StringCopyS(keyword,sizeof(keyword),arg);
+ if ( eq-arg < sizeof(keyword) )
+ keyword[eq-arg] = 0;
+
+ stat = ScanKeywordList(keyword,cmdtab_patch_image,0,true,0,0,
+ "Option --patch",ERR_SYNTAX);
+ if ( stat == -1 )
+ return 1;
+ }
+
+ PRINT("ADD PATCH: %02x %s\n",stat,eq+1);
+ FormatFieldItem_t * ffi = AppendFormatField(&patch_image_list,eq+1,false,false);
+ DASSERT(ffi);
+ ffi->patch_mode = stat;
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SZS file list ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int IterateTrackFiles
+(
+ FileList_t mode, // bit field with requested files
+ IterateTrackFilesFunc
+ func, // call back function
+ void *param // user defined parameter
+)
+{
+ int count = 0;
+ char idbuf[10], fbuf[50];
+
+ if ( mode & FLT_TRACK )
+ {
+ int i;
+ for ( i = 0; i < MKW_N_TRACKS; i++ )
+ {
+ const TrackInfo_t *ti = track_info + track_pos[i];
+ if ( mode & FLT_STD )
+ {
+ snprintf(idbuf,sizeof(idbuf),"T%u%u",i/4+1,i%4+1);
+ int stat = func(ti->track_fname,idbuf,FLT_TRACK|FLT_STD,ti,param);
+ if ( stat < 0 )
+ return stat;
+ count += stat;
+ }
+ if ( mode & FLT_D )
+ {
+ snprintf(idbuf,sizeof(idbuf),"T%u%ud",i/4+1,i%4+1);
+ StringCat2S(fbuf,sizeof(fbuf),ti->track_fname,"_d");
+ int stat = func(fbuf,idbuf,FLT_TRACK|FLT_D,ti,param);
+ if ( stat < 0 )
+ return stat;
+ count += stat;
+ }
+ }
+ }
+
+ if ( mode & FLT_ARENA )
+ {
+ int i;
+ for ( i = 0; i < MKW_N_ARENAS; i++ )
+ {
+ const TrackInfo_t *ti = arena_info + arena_pos[i];
+ if ( mode & FLT_STD )
+ {
+ snprintf(idbuf,sizeof(idbuf),"A%u%u",i/5+1,i%5+1);
+ int stat = func(ti->track_fname,idbuf,FLT_ARENA|FLT_STD,ti,param);
+ if ( stat < 0 )
+ return stat;
+ count += stat;
+ }
+ if ( mode & FLT_D )
+ {
+ snprintf(idbuf,sizeof(idbuf),"A%u%ud",i/5+1,i%5+1);
+ StringCat2S(fbuf,sizeof(fbuf),ti->track_fname,"_d");
+ int stat = func(fbuf,idbuf,FLT_ARENA|FLT_D,ti,param);
+ if ( stat < 0 )
+ return stat;
+ count += stat;
+ }
+ }
+ }
+
+ if ( mode & (FLT_OTHER|FLT_OLD) )
+ {
+ struct tab_t { bool have_d; uint mode; char id[4]; ccp fname; };
+ static const struct tab_t tab[] =
+ {
+ { 1, FLT_OTHER, "WIN", "winningrun_demo" },
+ { 1, FLT_OTHER, "LOS", "loser_demo" },
+ { 1, FLT_OTHER, "END", "ending_demo" },
+ { 1, FLT_OTHER, "MAR", "old_mario_gc_b" },
+ { 1, FLT_OTHER, "RNG", "ring_mission" },
+
+ { 1, FLT_OLD, "DRW", "draw_demo" },
+ { 0, FLT_OLD, "HAY", "old_mario_gc_hayasi" },
+ { 0, FLT_OLD, "NAR", "old_mario_gc_narita" },
+ { 0, FLT_OLD, "YAB", "old_mario_gc_yabuki" },
+
+ {0,0,"",0}
+ };
+
+ const struct tab_t *ptr;
+ for ( ptr = tab; ptr->fname; ptr++ )
+ {
+ if (!(mode&ptr->mode))
+ continue;
+
+ if ( mode & FLT_STD )
+ {
+ int stat = func(ptr->fname,ptr->id,ptr->mode|FLT_STD,0,param);
+ if ( stat < 0 )
+ return stat;
+ count += stat;
+ }
+ if ( mode & FLT_D && ptr->have_d )
+ {
+ snprintf(idbuf,sizeof(idbuf),"%sd",ptr->id);
+ StringCat2S(fbuf,sizeof(fbuf),ptr->fname,"_d");
+ int stat = func(fbuf,idbuf,ptr->mode|FLT_D,0,param);
+ if ( stat < 0 )
+ return stat;
+ count += stat;
+ }
+ }
+ }
+
+ return count;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// IsValid() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ccp valid_text[VALID__N] =
+{
+ "ok+", // VALID_OK
+ "ok", // VALID_NO_TEST
+ "warn", // VALID_WARNING
+ "fail", // VALID_ERROR
+ "type", // VALID_WRONG_FF
+ "-", // VALID_UNKNOWN_FF
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidSZS
+(
+ struct szs_file_t *szs, // valid szs archive
+ bool warnings // true: print warnings
+)
+{
+ DASSERT(szs);
+ return IsValid( szs->data, szs->size, szs->file_size,
+ szs, szs->fform_current, warnings ? szs->fname : 0 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValid
+(
+ const void *data, // valid data to test
+ uint data_size, // size of 'data'
+ uint file_size, // not NULL: size of complete file
+ szs_file_t *szs, // not NULL: base archive
+ file_format_t fform, // known file format
+ // if FF_UNKNOWN: call GetByMagicFF()
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ DASSERT( !szs || data_size <= szs->size );
+
+// [[analyse-magic]]
+ file_format_t real_fform = GetByMagicFF(data,data_size,file_size);
+ if ( fform == FF_UNKNOWN )
+ fform = real_fform;
+ else if ( fform == FF_DIRECTORY )
+ return VALID_UNKNOWN_FF;
+ else if ( fform != real_fform )
+ return VALID_WRONG_FF;
+
+ switch (fform)
+ {
+ case FF_UNKNOWN:
+ return VALID_UNKNOWN_FF;
+
+ case FF_BTI:
+ return IsValidBTI(data,data_size,file_size,fname);
+
+ case FF_KCL:
+ return IsValidKCL(0,data,data_size,file_size,fname);
+
+ case FF_KMP:
+ return IsValidKMP(data,data_size,file_size,fname);
+
+ case FF_LEX:
+ return IsValidLEX(data,data_size,file_size,fname);
+
+ case FF_MDL:
+ return IsValidMDL(data,data_size,file_size,szs,fname,0);
+
+ case FF_PAT:
+ return IsValidPAT(data,data_size,file_size,szs,fname,0,0);
+
+ case FF_STATICR:
+ return IsValidSTATICR(data,data_size,file_size,fname);
+
+ case FF_TEX_CT:
+ return IsValidTEXCT(data,data_size,file_size,szs,fname);
+
+ case FF_CT1_DATA:
+ return IsValidCTCODE(data,data_size,file_size,fname);
+
+ default:
+ break;
+ }
+
+ const ff_attrib_t fa = GetAttribFF(fform);
+ if ( fa & FFT_BRSUB )
+ return IsValidBRSUB(data,data_size,file_size,szs,fform,true,fname,0);
+
+ if ( fa & FFT_TEXT )
+ return VALID_OK;
+
+ return VALID_NO_TEST;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidBRSUB
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ struct szs_file_t *szs, // not NULL: base archive
+ file_format_t fform, // known file format
+ // if FF_UNKNOWN: call GetByMagicFF()
+ bool check_fform, // true: confirm 'fform' by calling GetByMagicFF()
+ ccp fname, // not NULL: print warnings with file ref
+ const endian_func_t *endian // not NULL: use this endian functions
+)
+{
+ DASSERT( !szs || szs->file_size >= szs->size );
+
+ if ( !data || data_size < sizeof(brsub_header_t) )
+ return VALID_WRONG_FF;
+
+ if (!endian)
+ endian = szs ? szs->endian : &be_func;
+ DASSERT(endian);
+
+ if ( fform == FF_UNKNOWN || check_fform )
+ {
+// [[analyse-magic]]
+ file_format_t real_fform = GetByMagicFF(data,data_size,file_size);
+ if ( fform == FF_UNKNOWN )
+ fform = real_fform;
+ else if ( fform != real_fform )
+ return VALID_WRONG_FF;
+ }
+
+ const brsub_header_t * bh = data;
+ const u32 size = endian->rd32(&bh->size);
+
+ if ( data_size < sizeof(*bh)
+ //|| data_size < size
+ || file_size && file_size < size )
+ {
+ PRINT("INVALID! data_size=%x, size=%x, version=%u\n",
+ data_size, size, endian->rd32(&bh->version) );
+ return VALID_ERROR;
+ }
+
+ if (szs)
+ {
+ if (!szs->data)
+ {
+ PRINT("INVALID BRSUB: no data\n");
+ return VALID_ERROR;
+ }
+
+ if ( (u8*)data + data_size > szs->data + szs->file_size )
+ {
+ PRINT("INVALID BRSUB: invalid data size: %zx > %zx\n",
+ (u8*)data + data_size - szs->data, szs->file_size );
+ return VALID_ERROR;
+ }
+
+ const u32 brres_offset = endian->rd32(&bh->brres_offset);
+ if ( brres_offset != (u32)( szs->data - (u8*)data )
+ && ( !szs->parent || brres_offset != (u32)( szs->parent->data - (u8*)data ) ))
+ {
+ PRINT("INVALID BRSUB: Invalid back offset: %zx != %x\n",
+ szs->data - (u8*)data,
+ endian->rd32(&bh->brres_offset) );
+ #ifdef TEST // [[2do]]
+ ERROR0(ERR_WARNING,
+ "Invalid subfile back reference to BRRES archive file: Offset %x @%s\n",
+ (u32)( (u8*)data - szs->data ), szs->fname );
+ #else
+ return VALID_ERROR;
+ #endif
+ }
+ }
+ else
+ {
+ const u32 off = endian->rd32(&bh->brres_offset);
+ if ( off && off < 0xc0000000 ) // 1GB limit
+ {
+ PRINT("INVALID BRSUB: limit %u\n",off);
+ return VALID_ERROR;
+ }
+ }
+
+ noPRINT("BRSUB: %d.%d.%d.%d\n",
+ isalnum( (int)(bh->magic[0]) ),
+ isalnum( (int)(bh->magic[1]) ),
+ isalnum( (int)(bh->magic[2]) ),
+ isalnum( (int)(bh->magic[3]) ) );
+
+ return isalnum( (int)(bh->magic[0]) )
+ && isalnum( (int)(bh->magic[1]) )
+ && isalnum( (int)(bh->magic[2]) )
+ && isalnum( (int)(bh->magic[3]) )
+ ? VALID_OK : VALID_ERROR;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidBTI
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ //--- First make a plausibility checks and return VALID_WRONG_FF on fail.
+ //--- This phase is done because of missing a magic
+
+ if ( !data || data_size < sizeof(bti_header_t) )
+ return VALID_WRONG_FF;
+
+ const bti_header_t *bti = (bti_header_t*)data;
+ u32 data_off = be32(&bti->data_off);
+ if ( !bti->n_image || data_off < sizeof(bti_header_t) || data_off & 3 )
+ return VALID_WRONG_FF;
+
+ const image_format_t iform = bti->iform;
+ const ImageGeometry_t *geo = GetImageGeometry(iform);
+ if ( !geo || geo->is_x )
+ return VALID_WRONG_FF;
+
+ const u32 pal_off = be32(&bti->pal_off);
+ const palette_format_t pform = be16(&bti->pform);
+ const uint n_pal = be16(&bti->n_pal);
+ if ( geo->max_palette > 0 )
+ {
+ if ( pal_off < sizeof(bti_header_t) || pal_off & 3 )
+ return VALID_WRONG_FF;
+ if (PaletteToImageFormat(pform,IMG_X_RGB) == IMG_X_RGB )
+ return VALID_WRONG_FF;
+ const uint max_pal = GetPaletteCountIF(iform);
+ if ( !n_pal || n_pal > max_pal )
+ return VALID_WRONG_FF;
+ }
+ else
+ {
+ if ( pform || n_pal || pal_off )
+ return VALID_WRONG_FF;
+ }
+
+ const uint width = be16(&bti->width);
+ const uint height = be16(&bti->height);
+ if ( !width || !height )
+ return VALID_WRONG_FF;
+
+ const uint image_size = geo->bits_per_pixel * width * height / 8;
+ if ( data_off + image_size > data_size )
+ return VALID_WRONG_FF;
+
+
+ //--- Now we think it's a BTI => make content checks
+
+ if (file_size)
+ {
+ // [[2do]]
+ // return VALID_ERROR;
+ // return VALID_OK;
+ }
+
+ return VALID_NO_TEST; // [[2do]] or VALID_OK?
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidTEXCT
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ szs_file_t *szs, // not NULL: base archive
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ const valid_t brsub_valid
+ = IsValidBRSUB(data,data_size,file_size,szs,FF_TEX,false,fname,0);
+ if ( brsub_valid >= VALID_ERROR )
+ return brsub_valid;
+
+ const brsub_header_t *bh = (brsub_header_t*)data;
+ if ( be32(&bh->version) != 3 )
+ return VALID_WRONG_FF;
+
+ const u32 img_off = be32(bh->grp_offset);
+ if ( img_off <= CT_CODE_TEX_OFFSET || file_size && img_off >= file_size )
+ return VALID_WRONG_FF;
+
+ return IsValidCTCODE( data + CT_CODE_TEX_OFFSET,
+ img_off - CT_CODE_TEX_OFFSET,
+ file_size ? file_size - CT_CODE_TEX_OFFSET : 0,
+ fname );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidCTCODE
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ if ( data_size < sizeof(ctcode_header_t) )
+ return VALID_WRONG_FF;
+
+ const ctcode_header_t *ch = (ctcode_header_t*)data;
+ const uint n_sect = be32(&ch->n_sect);
+ const u32 min_data_off = sizeof(ctcode_header_t) + n_sect * sizeof(ctcode_sect_info_t);
+ if ( n_sect < 2
+ || data_size < min_data_off
+ || be32(&ch->magic) != CT1_DATA_MAGIC_NUM
+ || memcmp(ch->sect_info[0].name,"CUP1",4)
+ || memcmp(ch->sect_info[1].name,"CRS1",4)
+ )
+ {
+ return VALID_WRONG_FF;
+ }
+
+ // ??? [[2do]] [[ctcode]]
+ return VALID_NO_TEST;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidKCL
+(
+ kcl_analyze_t * ka, // not NULL: init and store stats
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ kcl_analyze_t local_ka;
+ if (!ka)
+ ka = &local_ka;
+ memset(ka,0,sizeof(*ka));
+
+ noPRINT("IsValidKCL(%u,%u,%s)\n",data_size,file_size,fname);
+ if ( !data || data_size < sizeof(kcl_head_t) )
+ return ka->valid = VALID_WRONG_FF;
+
+ ka->head_size = sizeof(kcl_head_t);
+
+
+ //--- setup memory map
+
+ MemMap_t mm;
+ InitializeMemMap(&mm);
+ if (file_size)
+ {
+ ka->file_size = file_size;
+ MemMapItem_t *mi = InsertMemMap(&mm,file_size,0);
+ mi->index = N_KCL_SECT;
+ StringCopyS(mi->info,sizeof(mi->info),"End of file");
+ }
+
+ uint sect;
+ const kcl_head_t * kcl = data;
+
+ for ( sect = 0; sect < N_KCL_SECT; sect++ )
+ {
+ u32 offset = be32(kcl->sect_off+sect);
+ if ( sect == 2 )
+ offset += 0x10;
+
+ if ( offset == sizeof(kcl_head_t) - 4 )
+ ka->head_size = offset;
+
+ ka->off[sect] = offset;
+ if ( offset & 3
+ || offset < sizeof(kcl_head_t) - 4 // unknown_0x38 is optional
+ || file_size && offset > file_size
+ )
+ {
+ noPRINT("INVALID KCL: sect=%d, off=%x, headsize=%zx, filesize=%x\n",
+ sect, offset, sizeof(kcl_head_t), file_size );
+ return ka->valid = VALID_ERROR;
+ }
+
+ MemMapItem_t *mi = InsertMemMap(&mm,offset,0);
+ DASSERT(mi);
+ mi->index = sect;
+ snprintf(mi->info,sizeof(mi->info),"Section #%u",sect+1);
+ }
+
+
+ //--- calculate section sizes
+
+ static const uint elem_size[N_KCL_SECT]
+ = { sizeof(float3), sizeof(float3), sizeof(kcl_triangle_t) };
+
+ uint i, err = 0, order = 0;
+ for ( i = 0; i < mm.used - 1; i++ )
+ {
+ MemMapItem_t *p1 = mm.field[i];
+ MemMapItem_t *p2 = mm.field[i+1];
+ p1->size = p2->off - p1->off;
+ order = order * 10 + p1->index + 1;
+
+ const uint esize = elem_size[p1->index];
+ if (esize)
+ {
+ const uint n = p1->size / esize;
+ ka->n[p1->index] = n;
+ if ( p1->size != n * esize )
+ {
+ p1->size = n * esize;
+ err++;
+ }
+ }
+ ka->size[p1->index] = p1->size;
+ }
+ ka->order_value = order;
+ ka->order_ok = order == 1234;
+
+
+ //--- logging
+
+ if ( logging > 1 )
+ {
+ fprintf(stdlog,"\nMemory map of KCL sections: %s\n", fname ? fname : "" );
+ PrintMemMap(&mm,stdlog,3,"info");
+ fputc('\n',stdlog);
+ }
+
+ // [[2do]] [[kcl]] more tests?
+
+ noPRINT_IF(err,"INVALID KCL: %s\n",GetValidInfoKCL(ka));
+ return ka->valid = err ? VALID_ERROR : VALID_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+ccp GetValidInfoKCL ( kcl_analyze_t * ka )
+{
+ if (!ka)
+ return 0;
+
+ char order[20];
+ if (!ka->order_ok)
+ snprintf(order,sizeof(order),"order=%u / ",ka->order_value);
+ else
+ *order = 0;
+
+ char buf[150];
+ uint len = snprintfS(buf,sizeof(buf),
+ "%ssect=%x+%x, %x+%x, %x+%x, %x+%x, %x / N=%d,%d,%d,%d",
+ order,
+ ka->off[0], ka->size[0],
+ ka->off[1], ka->size[1],
+ ka->off[2], ka->size[2],
+ ka->off[3], ka->size[3],
+ ka->file_size,
+ ka->n[0], ka->n[1], ka->n[2], ka->n[3] ) + 1;
+
+ char *res = GetCircBuf(len);
+ memcpy(res,buf,len);
+ return res;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ScanHeadInfoKMP ( kmp_head_info_t *hi, cvp data, uint data_size )
+{
+ DASSERT(hi);
+ DASSERT(data||!data_size);
+
+ memset(hi,0,sizeof(*hi));
+ hi->data = data;
+ hi->end = hi->data + data_size;
+ hi->data_size = data_size;
+
+ const kmp_file_gen_t * kfile = data;
+ hi->file_size = ntohl(kfile->file_size);
+ hi->max_size = hi->file_size < data_size ? hi->file_size : data_size;
+
+ hi->head_size = ntohs(kfile->head_size);
+ hi->max_off = data_size - hi->head_size - 4;
+ hi->n_sect = ntohs(kfile->n_sect);
+
+ const uint max_sect = ( hi->head_size - 0x10 ) / 4;
+ if ( hi->n_sect > max_sect )
+ hi->n_sect = 0;
+ hi->sect_off = (u32*) ((u8*)data + hi->head_size - 4*hi->n_sect);
+}
+
+//-----------------------------------------------------------------------------
+
+valid_t IsValidKMP
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ if ( !data || data_size < sizeof(kmp_file_gen_t) )
+ return VALID_WRONG_FF;
+
+ kmp_head_info_t hi;
+ ScanHeadInfoKMP(&hi,data,data_size);
+ if (memcmp(data,"RKMD",4))
+ return VALID_WRONG_FF;
+
+ PRINT("M=%.4s n-sect=%d, h-size=%u,%u,%zu f-size=%u,%u\n",
+ (ccp)data, hi.n_sect,
+ hi.head_size, GetFileHeadSizeKMP(hi.n_sect), sizeof(kmp_file_mkw_t),
+ hi.file_size, file_size );
+
+ if ( force_kmp && hi.file_size < file_size )
+ hi.file_size = file_size;
+ if ( hi.head_size < GetFileHeadSizeKMP(hi.n_sect) || hi.head_size >= hi.file_size )
+ return VALID_ERROR;
+
+ valid_t stat = VALID_OK;
+ if ( file_size && hi.file_size > file_size )
+ {
+ if (!force_kmp)
+ return VALID_ERROR;
+
+ stat = VALID_WARNING;
+ if (fname)
+ ERROR0(ERR_WARNING,
+ "KMP header declares file size as %u bytes, but it has only %u bytes: %s",
+ hi.file_size, file_size, fname );
+ hi.file_size = file_size;
+
+ kmp_file_gen_t *kfile = (kmp_file_gen_t*)data;
+ write_be32(&kfile->file_size,hi.file_size);
+ }
+
+ const uint max_off = hi.file_size - hi.head_size;
+ uint warn_count = 0, sect;
+ for ( sect = 0; sect < hi.n_sect; sect++ )
+ {
+ const u32 off = ntohl(hi.sect_off[sect]);
+ noPRINT("OFF #%02u: %4x/%4x/%4x\n", sect, off, max_off, file_size );
+ if ( off >= max_off || file_size && off > file_size )
+ warn_count++;
+ }
+
+ if ( warn_count > 0 && fname && *fname )
+ ERROR0(ERR_WARNING,"KMP: %u segment%s behind end of file marker: %s\n",
+ warn_count, warn_count == 1 ? "" : "s", fname );
+
+ return warn_count ? VALID_WARNING : stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidLEX
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ if ( !data || data_size < sizeof(lex_header_t) )
+ return VALID_WRONG_FF;
+
+ const lex_header_t * lex = data;
+ if ( memcmp(lex->magic,"LE-X",sizeof(lex->magic)))
+ return VALID_WRONG_FF;
+
+ // [[todo]] [[lex]] ???
+ return VALID_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidMDL
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ struct szs_file_t *szs, // not NULL: base archive
+ ccp fname, // not NULL: print warnings with file ref
+ const endian_func_t *endian // not NULL: use this endian functions
+)
+{
+ valid_t brsub_valid
+ = IsValidBRSUB(data,data_size,file_size,szs,FF_MDL,true,fname,endian);
+ if ( brsub_valid >= VALID_ERROR )
+ return brsub_valid;
+
+ // [[2do]] [[mdl]] ???
+ return brsub_valid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidPAT
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ struct szs_file_t *szs, // not NULL: base archive
+ ccp fname, // not NULL: print warnings with file ref
+ const endian_func_t *endian, // not NULL: use this endian functions
+ struct pat_analyse_t *ana // not NULL: store analysis data here
+)
+{
+ pat_analyse_t ana0;
+ if (!ana)
+ ana = &ana0;
+ memset(ana,0,sizeof(*ana));
+ ana->data = data;
+ ana->data_size = data_size;
+
+ ana->valid = IsValidBRSUB(data,data_size,file_size,szs,FF_PAT,true,fname,endian);
+ if ( ana->valid >= VALID_ERROR )
+ return ana->valid;
+
+
+ //--- start with PAT analysis
+
+ //HEXDUMP16(0,0,data,data_size);
+
+ if (!file_size)
+ file_size = szs ? szs->size : data_size;
+
+
+ //--- check header
+
+ if ( file_size < PAT_HEAD_OFFSET + sizeof(pat_head_t) )
+ return ana->valid = VALID_ERROR;
+ if ( data_size < PAT_HEAD_OFFSET + sizeof(pat_head_t) )
+ return ana->valid = VALID_WARNING;
+
+ ana->head = (pat_head_t*)(data + PAT_HEAD_OFFSET);
+ ana->n_sect0 = be16(&ana->head->n_sect0);
+ ana->n_sect1 = be16(&ana->head->n_sect1);
+ ana->data_complete = true;
+
+
+ //--- check existence of sections 1 and 3
+
+ const brsub_header_t *bh = data;
+ u32 off = be32(bh->grp_offset+1);
+ u32 end_off = off + 4*ana->n_sect1;
+ if (!off)
+ ana->data_complete = false;
+ else if ( end_off > file_size )
+ {
+ ana->data_complete = false;
+ ana->valid = VALID_ERROR;
+ }
+ else if ( end_off <= data_size )
+ ana->s1_list = (u32*)(ana->data + off);
+
+ off = be32(bh->grp_offset+3);
+ end_off = off + 4*ana->n_sect1;
+ if (!off)
+ ana->data_complete = false;
+ else if ( end_off > file_size )
+ {
+ ana->data_complete = false;
+ ana->valid = VALID_ERROR;
+ }
+ else if ( end_off <= data_size )
+ ana->s3_list = (u32*)(ana->data + off);
+
+
+ //--- now analyse section 0
+
+ off = be32(bh->grp_offset+0);
+ if ( off + sizeof(pat_s0_bhead_t) > data_size )
+ {
+ invalid:
+ ana->data_complete = false;
+ ana->valid = VALID_ERROR;
+ }
+ else
+ {
+ pat_s0_bhead_t *bhead = (pat_s0_bhead_t*)(ana->data + off);
+ u32 bhead_end = off + be32(&bhead->size);
+ if ( bhead_end > data_size )
+ goto invalid;
+ ana->s0_base = bhead;
+ if ( be16(&bhead->n_elem) != ana->n_sect0 )
+ {
+ PRINT("N-SECT: %d %d %d\n",
+ ana->n_sect0, be16(&bhead->n_elem), be16(&bhead->n_unknown) );
+ ana->valid = VALID_WARNING;
+ }
+
+ uint i;
+ for ( i = 0; i < ana->n_sect0; i++ )
+ {
+ pat_s0_belem_t *belem = bhead->elem + i;
+ const u32 ref_off = off + be32(&belem->offset_strref);
+ if ( ref_off + sizeof(pat_s0_sref_t) > data_size )
+ goto invalid;
+
+ pat_s0_sref_t *sref = (pat_s0_sref_t*)( ana->data + ref_off );
+ const u32 str_off = ref_off + be32(&sref->offset_strlist);
+ if ( str_off + 4 > data_size )
+ goto invalid;
+
+ pat_s0_shead_t *shead = (pat_s0_shead_t*)( ana->data + str_off );
+ uint n_slist = be16(&shead->n_elem);
+ if ( str_off + 4 * (n_slist+1) > data_size )
+ goto invalid;
+
+ if ( i < PAT_MAX_ELEM )
+ {
+ ana->n_s0_list[i] = n_slist;
+ ana->s0_sref[i] = sref;
+ ana->s0_shead[i] = shead;
+ }
+ }
+ }
+
+ //--- 'sref' order
+
+ const uint max = ana->n_sect0 < PAT_MAX_ELEM ? ana->n_sect0 : PAT_MAX_ELEM;
+ pat_s0_sref_t *last_sr = 0;
+ u8 *dest_sr = ana->s0_sref_order;
+ for(;;)
+ {
+ uint i;
+ pat_s0_sref_t *found_sr = 0, **sr = ana->s0_sref;
+ for ( i = 0; i < max; i++, sr++ )
+ if ( *sr > last_sr && ( !found_sr || *sr < found_sr ))
+ found_sr = *sr;
+ if (!found_sr)
+ break;
+
+ sr = ana->s0_sref;
+ for ( i = 0; i < max; i++, sr++ )
+ if ( *sr == found_sr )
+ *dest_sr++ = i;
+ last_sr = found_sr;
+ }
+
+
+ //--- 's0_shead' order
+
+ pat_s0_shead_t *last_sh = 0;
+ u8 *dest_sh = ana->s0_shead_order;
+ for(;;)
+ {
+ uint i;
+ pat_s0_shead_t *found_sh = 0, **sh = ana->s0_shead;
+ for ( i = 0; i < max; i++, sh++ )
+ if ( *sh > last_sh && ( !found_sh || *sh < found_sh ))
+ found_sh = *sh;
+ if (!found_sh)
+ break;
+
+ sh = ana->s0_shead;
+ for ( i = 0; i < max; i++, sh++ )
+ if ( *sh == found_sh )
+ *dest_sh++ = i;
+ last_sh = found_sh;
+ }
+
+
+ //--- terminate
+
+ PRINT("ORDER:\n");
+ HEXDUMP16(10,0,ana->s0_sref_order,sizeof(ana->s0_sref_order));
+ HEXDUMP16(10,0,ana->s0_shead_order,sizeof(ana->s0_shead_order));
+
+ DASSERT( PAT_MAX_ELEM >= 5 );
+ PRINT("ANA(PAT): valid=%d,%d, N=%d,%d,[%d,%d,%d,%d,%d], "
+ " ptr=%zx,%zx,[%zx,%zx,%zx,%zx,%zx],[%zx,%zx,%zx,%zx,%zx],%zx,%zx\n",
+ ana->valid, ana->data_complete,
+ ana->n_sect0, ana->n_sect1,
+
+ ana->n_s0_list[0],
+ ana->n_s0_list[1],
+ ana->n_s0_list[2],
+ ana->n_s0_list[3],
+ ana->n_s0_list[4],
+
+ ana->head ? (u8*)ana->head - ana->data : 0,
+ ana->s0_base ? (u8*)ana->s0_base - ana->data : 0,
+
+ ana->s0_sref[0] ? (u8*)ana->s0_sref[0] - ana->data : 0,
+ ana->s0_sref[1] ? (u8*)ana->s0_sref[1] - ana->data : 0,
+ ana->s0_sref[2] ? (u8*)ana->s0_sref[2] - ana->data : 0,
+ ana->s0_sref[3] ? (u8*)ana->s0_sref[3] - ana->data : 0,
+ ana->s0_sref[4] ? (u8*)ana->s0_sref[4] - ana->data : 0,
+
+ ana->s0_shead[0]? (u8*)ana->s0_shead[0] - ana->data : 0,
+ ana->s0_shead[1]? (u8*)ana->s0_shead[1] - ana->data : 0,
+ ana->s0_shead[2]? (u8*)ana->s0_shead[2] - ana->data : 0,
+ ana->s0_shead[3]? (u8*)ana->s0_shead[3] - ana->data : 0,
+ ana->s0_shead[4]? (u8*)ana->s0_shead[4] - ana->data : 0,
+
+ ana->s1_list ? (u8*)ana->s1_list - ana->data : 0,
+ ana->s3_list ? (u8*)ana->s3_list - ana->data : 0 );
+
+ return ana->valid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+valid_t IsValidSTATICR
+(
+ const void * data, // data
+ uint data_size, // size of 'data'
+ uint file_size, // NULL or size of complete file
+ ccp fname // not NULL: print warnings with file ref
+)
+{
+ //--- First make a plausibility checks and return VALID_WRONG_FF on fail.
+ //--- This phase is done because of missing a magic
+
+ if ( !data || data_size < 0x100 )
+ return VALID_WRONG_FF;
+
+ static const u8 identification[] =
+ {
+ /* 80 */ 0x00, 0x00, 0x78, 0xb0, 0x00, 0x00, 0x00, 0x00,
+ /* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* a0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* a8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* b0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* b8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* c0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* c8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* d0 */ 0x00, 0x00, 0x00, 0x00, 0x80, 0xa3, 0x00, 0x04,
+ /* d8 */ 0x80, 0x04, 0x00, 0x04, 0x7c, 0x65, 0x00, 0x50,
+ /* e0 */ 0x7c, 0x00, 0x28, 0x50, 0x7c, 0x60, 0x03, 0x78,
+ /* e8 */ 0x54, 0x03, 0x0f, 0xfe, 0x4e, 0x80, 0x00, 0x20,
+ /* f0 */ 0x94, 0x21, 0xff, 0xf0, 0x7c, 0x08, 0x02, 0xa6,
+ /* f8 */ 0x2c, 0x03, 0x00, 0x00, 0x90, 0x01, 0x00, 0x14,
+ };
+
+ if (memcmp(data+0x80,identification,sizeof(identification)))
+ return VALID_WRONG_FF;
+
+ // ??? [[2do]]
+ return VALID_NO_TEST; // [[2do]] or VALID_OK?
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// xsrc/x-lib ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#if HAVE_XSRC
+ #include "xsrc/x-std.c"
+#endif
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+