/*************************************************************************** * _______ _______ _______ * * | ___ |____ | ___ | * * | | |_| / /| | |_| * * | |_____ / / | |_____ * * |_____ | / / |_____ | * * _ | | / / _ | | * * | |___| |/ /____| |___| | * * |_______|_______|_______| * * * * 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-2024 by Dirk Clemens * * * *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * * * ***************************************************************************/ #include "dclib-utf8.h" #include "dclib-regex.h" #include "lib-ledis.h" #include "lib-analyze.h" #include "lib-szs.h" #include "lib-image.h" #include "db-mkw.h" #include "crypt.h" #include "ledis.inc" #include "distrib.inc" #include // /////////////////////////////////////////////////////////////////////////////// /////////////// global vars /////////////// /////////////////////////////////////////////////////////////////////////////// ccp opt_create_dump = 0; // NULL of filename le_distrib_t ledis_dump = {0}; // data collector for 'opt_create_dump' int ledis_dump_enabled = 0; // -1:done, 0:disabled, 1:enabled ccp opt_le_define = 0; // NULL or last defined filename StringField_t le_define_list = {0}; // list with all filenames /////////////////////////////////////////////////////////////////////////////// static const le_cup_ref_t standard_bt_ref[MKW_N_ARENAS] = { 33,32,35,34,36, 39,40,41,37,38 }; static const le_cup_ref_t standard_vs_ref[MKW_N_TRACKS] = { 8, 1, 2, 4, 16,20,25,26, 0, 5, 6, 7, 27,31,23,18, 9,15,11, 3, 21,30,29,17, 14,10,12,13, 24,22,19,28 }; static const le_cup_ref_t mkwfun_random_ref[MKW_N_LE_RANDOM] = { 62, 63, 64, 65 }; // /////////////////////////////////////////////////////////////////////////////// /////////////// helpers /////////////// /////////////////////////////////////////////////////////////////////////////// #define DUMP_CUP_SLOTS_ENABLED 0 #if DUMP_CUP_SLOTS_ENABLED static void DumpCupSlots ( const le_distrib_t *ld, ccp info ) { fflush(stdout); fflush(stderr); const int min = 68; const int max = min + 15; fprintf(stdlog,"CUP-SLOT %d..%d:",min,max); for ( int slot = min; slot <= max; slot++ ) { le_track_t *lt = GetTrackLD((le_distrib_t*)ld,slot); if (lt) fprintf(stdlog," %d",lt->cup_slot); else fputs(" -",stdlog); } if (info) fprintf(stdlog," # %s\n",info); else fputs("\n",stdlog); fflush(stdlog); } #else static inline void DumpCupSlots ( le_distrib_t const *ld, ccp info ) {} #endif /////////////////////////////////////////////////////////////////////////////// static int scan_source_opt ( // return : -1:error + abort, 0:ignored, 1 processed + next_arg KeyListParam_t *par, // parameters and current result ccp name, // name of option as written uint name_len, // length of 'name' const KeywordTab_t *key_tab // valid pointer to command table ) { ASSERT(par); ASSERT(par->user_param); ASSERT(name); ASSERT(key_tab); // not needed here le_strpar_t *lepar = (le_strpar_t*)par->user_param; if ( !lepar || name_len < 1 || *name != '@' && *name != '[' ) return 0; enumError err; ccp res = ScanLSP(lepar,name,name_len,&err); if ( !err && res == name + name_len ) return 1; par->valid = false; par->result = ~0; ERROR0(err,"Invalid string selector: %.*s\n",name_len,name); return -1; } // /////////////////////////////////////////////////////////////////////////////// /////////////// enum le_track_type_t /////////////// /////////////////////////////////////////////////////////////////////////////// ccp GetNameLTTY ( le_track_type_t track_type ) { return track_type & LTTY_TRACK ? "vs" : track_type & LTTY_ARENA ? "bt" : track_type & LTTY_RANDOM ? "rd" : "xx"; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_options_t /////////////// /////////////////////////////////////////////////////////////////////////////// le_options_t defaultLEO = LEO__DEFAULT; //----------------------------------------------------------------------------- static KeywordTab_t keywords_leo[] = { { LEO__DEFAULT, "RESET", 0, ~0 }, { 0, "CLEAR", 0, ~0 }, { LTT_SHA1, "SHA1", 0, LEO_LTT_SELECTOR }, { LTT_SHA1_D, "D-SHA1", "DSHA1", LEO_LTT_SELECTOR }, { LTT_IDENT, "IDENT", 0, LEO_LTT_SELECTOR }, { LTT_IDENT_D, "D-IDENT", "DIDENT", LEO_LTT_SELECTOR }, { LTT_FILE, "FILES", "FILE", LEO_LTT_SELECTOR }, { LTT_PATH, "PATHS", "PATH", LEO_LTT_SELECTOR }, { LTT_NAME, "NAMES", "NAME", LEO_LTT_SELECTOR }, { LTT_XNAME, "XNAMES", "XNAME", LEO_LTT_SELECTOR }, { LTT_XNAME2, "X2NAMES", "X2NAME", LEO_LTT_SELECTOR }, #if LE_STRING_LIST_ENABLED { LTT_TEMP1, "TEMP1", 0, LEO_LTT_SELECTOR }, { LTT_TEMP2, "TEMP2", 0, LEO_LTT_SELECTOR }, #endif { LEO_MKW, "MKW", 0, 0 }, { LEO_MKW1, "MKW1", 0, 0 }, { LEO_MKW2, "MKW2", 0, 0 }, { LEO_CTCODE, "CT-CODE", "CTCODE", 0 }, { LEO_LECODE, "LE-CODE", "LECODE", 0 }, { LEO_IN_EMPTY, "IN-EMPTY", "INEMPTY", 0 }, { LEO_IN_EMPTY, "IEMPTY", 0, 0 }, { LEO_IN_MINUS, "IN-MINUS", "INMINUS", 0 }, { LEO_IN_MINUS, "IMINUS", 0, 0 }, { LEO_IN_ALL, "IN-ALL", "INALL", 0 }, { LEO_IN_ALL, "IALL", 0, 0 }, { LEO_OVERWRITE, "OVERWRITE", 0, LEO_M_HOW }, { LEO_INSERT, "INSERT", 0, LEO_M_HOW }, { LEO_REPLACE, "REPLACE", 0, LEO_M_HOW }, { LEO_VERSUS, "VERSUS", "VS", 0 }, { LEO_BATTLE, "BATTLE", "BT", 0 }, { LEO_CUSTOM, "CUSTOM", 0, 0 }, { LEO_ORIGINAL, "ORIGINAL", 0, 0 }, { LEO_NO_D_FILES, "NO-D", "NOD", 0 }, { LEO_OUT_EMPTY, "OUT-EMPTY", "OUTEMPTY", 0 }, { LEO_OUT_EMPTY, "OEMPTY", 0, 0 }, { LEO_OUT_MINUS, "OUT-MINUS", "OUTMINUS", 0 }, { LEO_OUT_MINUS, "OMINUS", 0, 0 }, { LEO_OUT_DUMMY, "OUT-DUMMY", "OUTDUMMY", 0 }, { LEO_OUT_DUMMY, "ODUMMY", 0, 0 }, { LEO_OUT_ALL, "OUT-ALL", "OUTALL", 0 }, { LEO_OUT_ALL, "OALL", 0, 0 }, { LEO_IN_LECODE, "IN-LECODE", "INLECODE", 0 }, { LEO_IN_LECODE, "ILECODE", 0, 0 }, { LEO_NO_SLOT, "NO-SLOT", "NOSLOT", 0 }, { LEO_HEX2SLOT, "HEX2SLOT", 0, 0 }, { LEO_NAME2SLOT, "NAME2SLOT", 0, 0 }, { LEO_CT_SLOTS, "CT-SLOTS", "CTSLOTS", 0 }, { LEO_BRIEF, "BRIEF", 0, 0 }, { LEO_AUTO_PATH, "AUTO-PATH", "AUTOPATH", 0 }, { LEO_PROGRESS0, "PR0", 0, LEO_M_PROGRESS }, { LEO_PROGRESS1, "PR1", 0, LEO_M_PROGRESS }, { LEO_PROGRESS2, "PR2", 0, LEO_M_PROGRESS }, { LEO_PROGRESS5, "PR5", 0, LEO_M_PROGRESS }, { LEO_PROGRESS10, "PR10", "PROGRESS", LEO_M_PROGRESS }, { LEO_PROGRESS20, "PR20", 0, LEO_M_PROGRESS }, { LEO_PROGRESS30, "PR30", 0, LEO_M_PROGRESS }, { LEO_PROGRESS60, "PR60", 0, LEO_M_PROGRESS }, { LEO_HELP, "HELP", "H", 0 }, { LEO_CUT_ALL, "CUT-ALL", "CUTALL", 0 }, { LEO_CUT_STD, "CUT-STD", "CUTSTD", 0 }, { LEO_CUT_CTCODE, "CUT-CTCODE", "CUTCTCODE", 0 }, {0,0,0,0} }; /////////////////////////////////////////////////////////////////////////////// le_options_t SetupDefaultLEO(void) { defaultLEO = LEO__DEFAULT; if ( !print_header || brief_count ) defaultLEO |= LEO_BRIEF; return defaultLEO; } /////////////////////////////////////////////////////////////////////////////// le_options_t ScanLEO ( le_options_t current, ccp arg, ccp err_info ) { if (!arg) return current; keywords_leo[0].id = SetupDefaultLEO(); s64 stat = ScanKeywordList(arg,keywords_leo,0,true,0,current,err_info,ERR_SYNTAX); PRINT0("ScanLEO(%#lx,%s) => %#llx, progress=%lld\n",current,arg,stat,GetProgressMSecLEO(stat)); return stat != -1 ? stat : current; } /////////////////////////////////////////////////////////////////////////////// u_msec_t GetProgressMSecLEO ( le_options_t opt ) { switch ( (uint)opt & LEO_M_PROGRESS ) { case LEO_PROGRESS1: return 1000; case LEO_PROGRESS2: return 2000; case LEO_PROGRESS5: return 5000; case LEO_PROGRESS10: return 10000; case LEO_PROGRESS20: return 20000; case LEO_PROGRESS30: return 30000; case LEO_PROGRESS60: return 60000; } return 0; } /////////////////////////////////////////////////////////////////////////////// bool IsInputNameValidLEO ( ccp name, le_options_t opt ) { return !name || !*name ? ( opt & LEO_IN_EMPTY ) != 0 : *name == '-' && !name[1] ? ( opt & LEO_IN_MINUS ) != 0 : true; } /////////////////////////////////////////////////////////////////////////////// bool IsOutputNameValidLEO ( ccp name, le_options_t opt ) { if ( name && name[0] == '_' && strlen(name) == 4 && ( isdigit(name[1]) || name[1] >= 'a' && name[1] <= 'f' ) && ( isdigit(name[2]) || name[2] >= 'a' && name[2] <= 'f' ) && ( isdigit(name[3]) || name[3] >= 'a' && name[3] <= 'f' ) ) { return ( opt & LEO_OUT_DUMMY ) != 0; } return !name || !*name ? ( opt & LEO_OUT_EMPTY ) != 0 : *name == '-' && !name[1] ? ( opt & LEO_OUT_MINUS ) != 0 : true; } /////////////////////////////////////////////////////////////////////////////// bool IsAssignmentAllowedLEO ( ccp cur_name, ccp new_name, le_options_t opt ) { if (!IsInputNameValidLEO(new_name,opt)) return false; switch ( opt & LEO_M_HOW ) { case 0: case LEO_OVERWRITE: return true; case LEO_INSERT: return !IsOutputNameValidLEO(cur_name,opt); case LEO_REPLACE: return IsOutputNameValidLEO(cur_name,opt); default: return false; } } /////////////////////////////////////////////////////////////////////////////// ccp GetUpperNameLTT ( le_track_text_t ltt ) { switch ( ltt & LEO_LTT_SELECTOR ) { case LTT_SHA1: return "SHA1"; case LTT_SHA1_D: return "D-SHA1"; case LTT_IDENT: return "IDENT"; case LTT_IDENT_D: return "D-IDENT"; case LTT_FILE: return "FILE"; case LTT_PATH: return "PATH"; case LTT_NAME: return "NAME"; case LTT_XNAME: return "XNAME"; case LTT_XNAME2: return "XNAME2"; #if LE_STRING_LIST_ENABLED case LTT_TEMP1: return "TEMP1"; case LTT_TEMP2: return "TEMP2"; #endif } return 0; } //----------------------------------------------------------------------------- ccp GetLowerNameLTT ( le_track_text_t ltt ) { switch ( ltt & LEO_LTT_SELECTOR ) { case LTT_SHA1: return "sha1"; case LTT_SHA1_D: return "d-sha1"; case LTT_IDENT: return "ident"; case LTT_IDENT_D: return "d-ident"; case LTT_FILE: return "file"; case LTT_PATH: return "path"; case LTT_NAME: return "name"; case LTT_XNAME: return "xname"; case LTT_XNAME2: return "xname2"; #if LE_STRING_LIST_ENABLED case LTT_TEMP1: return "temp1"; case LTT_TEMP2: return "temp2"; #endif } return 0; } //----------------------------------------------------------------------------- int GetDModeLTT ( le_track_text_t ltt ) { // returns 0:unknown ltt, 1:std string, 2:_d string, 3:both switch ( ltt & LEO_LTT_SELECTOR ) { case LTT_SHA1: case LTT_IDENT: return 1; case LTT_SHA1_D: case LTT_IDENT_D: return 2; case LTT_FILE: case LTT_PATH: case LTT_NAME: case LTT_XNAME: case LTT_XNAME2: #if LE_STRING_LIST_ENABLED case LTT_TEMP1: case LTT_TEMP2: #endif return 3; } return 0; } /////////////////////////////////////////////////////////////////////////////// ccp GetTextLEO ( le_options_t opt ) { char buf[300], *dest = buf, *end = buf + sizeof(buf) - 1; static const le_options_t ctab[] = { LEO_IN_ALL, LEO_M_ARENA, LEO_M_ORIGINAL, LEO_OUT_ALL, 0 }; for ( const le_options_t *cptr = ctab; *cptr; cptr++ ) if ( ( opt & *cptr ) == *cptr ) opt &= ~*cptr; *dest = 0; PrintKeywordList(dest,end-dest,0,keywords_leo,opt,0,INT64_MIN); if ( dest > buf && !*dest ) *--dest = 0; return CopyCircBuf0(buf,strlen(buf)); } /////////////////////////////////////////////////////////////////////////////// ccp GetInputFilterLEO ( le_options_t opt ) { static const le_options_t mask = LEO_M_BMG | LEO_IN_ALL | LEO_M_HOW ; return GetTextLEO(opt&mask); } /////////////////////////////////////////////////////////////////////////////// ccp GetOutputFilterLEO ( le_options_t opt ) { static const le_options_t mask = LEO_M_ARENA | LEO_M_ORIGINAL | LEO_NO_D_FILES | LEO_OUT_ALL ; return GetTextLEO(opt&mask); } /////////////////////////////////////////////////////////////////////////////// bool GetBmgBySlotLEO ( char *buf, uint bufsize, const bmg_t *bmg, int slot, le_options_t opt ) { if ( !bmg || slot < 0 ) return 0; if (!(opt&LEO_M_BMG)) opt |= LEO_M_BMG; //-- LE-CODE if ( opt & LEO_LECODE && slot < BMG_N_LE_TRACK ) { const bmg_item_t *bi = FindItemBMG(bmg,slot+MID_LE_TRACK_BEG); if ( bi && bi->text ) { PrintString16BMG(buf,bufsize,bi->text,bi->len,BMG_UTF8_MAX,0,1); if (IsInputNameValidLEO(buf,opt)) return true; } } //-- CT-CODE if ( opt & LEO_CTCODE && slot < BMG_N_CT_TRACK ) { const bmg_item_t *bi = FindItemBMG(bmg,slot+MID_CT_TRACK_BEG); if ( bi && bi->text ) { PrintString16BMG(buf,bufsize,bi->text,bi->len,BMG_UTF8_MAX,0,1); if (IsInputNameValidLEO(buf,opt)) return true; } } //-- MKW2 if ( opt & LEO_MKW2 ) { const bmg_item_t *bi = IsMkwTrack(slot) ? FindItemBMG(bmg,slot+MID_TRACK1_BEG) : IsMkwArena(slot) ? FindItemBMG(bmg,slot-BMG_N_TRACK+MID_ARENA1_BEG) : 0; if ( bi && bi->text ) { PrintString16BMG(buf,bufsize,bi->text,bi->len,BMG_UTF8_MAX,0,1); if (IsInputNameValidLEO(buf,opt)) return true; } } //-- MKW1 if ( opt & LEO_MKW1 ) { const bmg_item_t *bi = IsMkwTrack(slot) ? FindItemBMG(bmg,slot+MID_TRACK2_BEG) : IsMkwArena(slot) ? FindItemBMG(bmg,slot+MID_ARENA2_BEG) : 0; if ( bi && bi->text ) { PrintString16BMG(buf,bufsize,bi->text,bi->len,BMG_UTF8_MAX,0,1); if (IsInputNameValidLEO(buf,opt)) return true; } } return false; } // /////////////////////////////////////////////////////////////////////////////// /////////////// struct le_strpar_t /////////////// /////////////////////////////////////////////////////////////////////////////// const le_strpar_t NullLSP = {0}; const le_strpar_t DefaultLSP = { .opt = LEO__DEFAULT }; /////////////////////////////////////////////////////////////////////////////// void ResetLSP ( le_strpar_t *par ) { if (par) { #if LE_STRING_SET_ENABLED FreeString(par->key); #endif InitializeLSP(par,par->opt); } } /////////////////////////////////////////////////////////////////////////////// char * ScanLSP ( le_strpar_t *par, ccp arg, int arg_len, enumError *ret_err ) { static const KeywordTab_t keytab[] = { { LTT_SHA1, "SHA1", 0, 0 }, { LTT_SHA1_D, "D-SHA1", "DSHA1", 0 }, { LTT_IDENT, "IDENT", 0, 0 }, { LTT_IDENT_D, "D-IDENT", "DIDENT", 0 }, { LTT_FILE, "FILES", 0, 0 }, { LTT_PATH, "PATHS", 0, 0 }, { LTT_NAME, "NAMES", 0, 0 }, { LTT_XNAME, "XNAMES", 0, 0 }, { LTT_XNAME2, "X2NAMES", 0, 0 }, #if LE_STRING_LIST_ENABLED { LTT_TEMP1, "TEMP1", 0, 0 }, { LTT_TEMP2, "TEMP2", 0, 0 }, #endif {0,0,0,0} }; //-------------- if (ret_err) *ret_err = ERR_NOT_EXISTS; if ( !par || !arg ) return (char*)arg; ccp arg_end = arg + ( arg_len < 0 ? strlen(arg) : arg_len ); ccp src = arg; while ( src < arg_end && (uchar)*src <= ' ' ) src++; if ( *src == '@' ) { src++; while ( src < arg_end && (uchar)*src <= ' ' ) src++; } enumError err = ERR_OK; #if LE_STRING_SET_ENABLED if ( *src == '[' ) { src++; ccp end = strchr(src,']'); if (!end) err = ERR_SYNTAX; else { SetKeyNameLSP(par,src,end-src); src = end+1; } } else #endif { char name[50], *nptr = name; while ( isalnum((int)*src) && nptr < name + sizeof(name) -2 ) *nptr++ = *src++; *nptr = 0; int abbrev_count; const KeywordTab_t *cmd = ScanKeywordEx(&abbrev_count,name,nptr-name,LOUP_UPPER,keytab); if (!cmd) err = ERR_SEMANTIC; else par->opt = par->opt & ~LEO_LTT_SELECTOR | cmd->id; } //--- terminate if (ret_err) *ret_err = err; if ( err == ERR_OK ) { while ( src < arg_end && (uchar)*src <= ' ' ) src++; arg = src; } return (char*)arg; } /////////////////////////////////////////////////////////////////////////////// char * ScanOptionalLSP ( le_strpar_t *par, ccp arg, int arg_len, le_track_text_t fallback, enumError *ret_err ) { if (ret_err) *ret_err = ERR_NOT_EXISTS; par->opt = par->opt & ~LEO_LTT_SELECTOR | fallback & LEO_LTT_SELECTOR; if ( !par || !arg ) return (char*)arg; ccp arg_end = arg + ( arg_len < 0 ? strlen(arg) : arg_len ); ccp src = arg; while ( src < arg_end && (uchar)*src <= ' ' ) src++; if ( *src != '[' && *src != '@' ) return (char*)src; src = ScanLSP(par,arg,arg_len,ret_err); while ( src < arg_end && ( *src <= ' ' || *src == ',' )) src++; return (char*)src; } /////////////////////////////////////////////////////////////////////////////// ccp GetNameLSP ( const le_strpar_t *par ) { if (par) { ccp name = GetLowerNameLEO(par->opt); if (name) return name; #if LE_STRING_SET_ENABLED if (IsValidSetLSP(par)) return PrintCircBuf("[%s]",par->key); #endif } return "?"; } /////////////////////////////////////////////////////////////////////////////// ccp GetOptionsLSP ( const le_strpar_t *par, le_options_t mask ) { if (!par) return 0; #if LE_STRING_SET_ENABLED if (IsValidSetLSP(par)) return PrintCircBuf("[%s],%s", par->key, GetTextLEO(par->opt&~LEO_LTT_SELECTOR&mask) ); #endif return GetTextLEO(par->opt&mask); } /////////////////////////////////////////////////////////////////////////////// enumError ScanOptionsLSP ( le_strpar_t *par, ccp arg, ccp err_info ) { if ( !par || !arg ) return ERR_MISSING_PARAM; #if LE_STRING_SET_ENABLED while ( *arg > 0 && *arg <= ' ' ) arg++; if ( *arg == '[' ) { enumError err; arg = ScanLSP(par,arg,-1,&err); if (err) return err; } #endif par->opt = ScanLEO(par->opt,arg,err_info); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// #if LE_STRING_SET_ENABLED void SetKeyNameLSP ( le_strpar_t *par, ccp key, int key_len ) { if ( par && par->key != key ) { FreeString(par->key); if ( key && *key ) { if ( key_len < 0 ) key_len = strlen(key); par->key = MEMDUP(key,key_len); par->opt = par->opt & ~LEO_LTT_SELECTOR | LTT_STRSET; } else par->key = 0; } } #endif // /////////////////////////////////////////////////////////////////////////////// /////////////// struct le_cup_t /////////////// /////////////////////////////////////////////////////////////////////////////// void InitializeLECUP ( le_cup_t *lc, uint max_cups, uint tracks_per_cup ) { DASSERT(lc); memset(lc,0,sizeof(*lc)); lc->max = max_cups; lc->tracks = tracks_per_cup; lc->first_custom = tracks_per_cup == 5 ? 2 : 8; lc->fill_slot = -1; lc->fill_src = -1; lc->dirty = true; } /////////////////////////////////////////////////////////////////////////////// void ResetLECUP ( le_cup_t *lc ) { if (lc) { FREE(lc->list); InitializeLECUP(lc,lc->max,lc->tracks); } } /////////////////////////////////////////////////////////////////////////////// le_cup_ref_t * GetLECUP ( const le_cup_t *lc, uint cup_index ) { return lc && cup_index < lc->used ? lc->list + cup_index * lc->tracks : 0; } /////////////////////////////////////////////////////////////////////////////// le_cup_ref_t * DefineLECUP ( le_cup_t *lc, uint cup_index ) { if ( !lc || cup_index >= lc->max ) return 0; const uint size_per_cup = sizeof(*lc->list) * lc->tracks; if ( cup_index >= lc->size ) { uint n_cups = cup_index + lc->size/4 + 100; if ( n_cups > lc->max ) n_cups = lc->max; lc->list = REALLOC( lc->list, n_cups * size_per_cup ); lc->size = n_cups; ASSERT( lc->size <= lc->max ); } ASSERT( cup_index < lc->size ); if ( cup_index >= lc->used ) { lc->dirty = true; le_cup_ref_t *beg = lc->list + lc->used * lc->tracks; lc->used = cup_index + 1; ASSERT( lc->used <= lc->size ); le_cup_ref_t *end = lc->list + lc->used * lc->tracks; memset( beg, ~0, (u8*)end-(u8*)beg ); } ASSERT( cup_index < lc->used ); return lc->list + cup_index * lc->tracks; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// int GetIndexByRefLECUP ( const le_cup_t *lc, int cup_ref ) { if ( !lc || cup_ref <= 0 ) return -1; const uint cup = cup_ref / 10 - 1; const uint idx = cup_ref % 10 - 1; return cup < lc->max && idx < lc->tracks ? cup*lc->tracks + idx : -1; } /////////////////////////////////////////////////////////////////////////////// uint GetRefByIndexLECUP ( const le_cup_t *lc, uint cup_index ) { if ( !lc || cup_index >= lc->max * lc->tracks ) return 0; return cup_index / lc->tracks * 10 + cup_index % lc->tracks + 11; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// uint GetCupSlotLECUP ( const le_cup_t *lc, const le_cup_ref_t *ptr ) { if ( !lc || !lc->list || !ptr ) return 0; const uint idx = ptr - lc->list; const uint cup = idx / lc->tracks; return cup < lc->used ? 10 * cup + (idx % lc->tracks) + 11 : 0; } /////////////////////////////////////////////////////////////////////////////// void AppendLECUP ( le_cup_t *lc, le_cup_ref_t ref ) { if ( lc && ref >= 0 ) { if ( lc->cur_index >= lc->tracks ) { lc->cur_index = 0; lc->cur_cup++; } le_cup_ref_t *cr = DefineLECUP(lc,lc->cur_cup); if (cr) { lc->dirty = true; cr += lc->cur_index++; *cr = ref; } } } /////////////////////////////////////////////////////////////////////////////// void CloseLECUP ( le_cup_t *lc ) { if (lc) { lc->close_used = true; if ( lc->cur_index ) lc->cur_index = lc->tracks; } } /////////////////////////////////////////////////////////////////////////////// void PackLECUP ( le_cup_t *lc ) { if ( !lc || !lc->used ) return; PRINT("PackLECUP(%d) < N=%d\n",lc->tracks,lc->used); le_cup_ref_t *dest = lc->list; le_cup_ref_t *end = lc->list + lc->used * lc->tracks; for ( le_cup_ref_t *src = lc->list; src < end; src++ ) if ( *src >= 0 ) *dest++ = *src; lc->used = ( dest - lc->list + lc->tracks - 1 ) / lc->tracks; end = lc->list + lc->used * lc->tracks; while ( dest < end ) *dest++ = -1; PRINT("PackLECUP(%d) > N=%d\n",lc->tracks,lc->used); } /////////////////////////////////////////////////////////////////////////////// void EvenLECUP ( le_cup_t *lc ) { if (lc) { DefineLECUP( lc, lc->tracks == 4 ? 5 : 1 ); if ( lc->used & 1 ) DefineLECUP(lc,lc->used); } } /////////////////////////////////////////////////////////////////////////////// bool HaveInvalidLECUP ( le_cup_t *lc ) { if (lc) { le_cup_ref_t *end = lc->list + lc->used * lc->tracks; for ( le_cup_ref_t *ptr = lc->list; ptr < end; ptr++ ) if ( *ptr < 0 ) return true; } return false; } /////////////////////////////////////////////////////////////////////////////// void FillLECUP ( le_cup_t *lc, le_cup_ref_t fallback ) { if (!lc) return; le_cup_ref_t *end = lc->list + lc->used * lc->tracks; for ( le_cup_ref_t *ptr = lc->list; ptr < end; ptr++ ) if ( *ptr < 0 ) *ptr = fallback; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_track_t /////////////// /////////////////////////////////////////////////////////////////////////////// void InitializeLT ( le_track_t *lt ) { DASSERT(lt); memset(lt,0,sizeof(*lt)); lt->track_status = LTS_VALID; #if LE_STRING_SET_ENABLED InitializeParamField(<->str_set); lt->str_set.free_data = true; #endif } /////////////////////////////////////////////////////////////////////////////// void ClearStringsLT ( le_track_t *lt ) { if (lt) { FreeString(lt->file); FreeString(lt->path); FreeString(lt->name); FreeString(lt->xname); FreeString(lt->lti[0].ident); FreeString(lt->lti[1].ident); lt->file = 0; lt->name = 0; lt->xname = 0; memset(lt->lti,0,sizeof(*lt->lti)); #if LE_STRING_LIST_ENABLED for ( int idx = 0; idx < LTT__LIST_SIZE; idx++ ) FreeString(lt->str_list[idx]); memset(lt->str_list,0,sizeof(lt->str_list)); #endif #if LE_STRING_SET_ENABLED ResetParamField(<->str_set); #endif } } /////////////////////////////////////////////////////////////////////////////// void ClearLT ( le_track_t *lt, le_track_type_t ltty ) // auto-ltty if 0 { if (lt) { ClearStringsLT(lt); const int slot = lt->track_slot; memset(lt,0,sizeof(*lt)); lt->track_slot = slot; lt->track_type = ltty ? ltty : IsMkwArena(slot) ? LTTY_ARENA : LTTY_TRACK; } } /////////////////////////////////////////////////////////////////////////////// void MoveLT ( le_track_t *dest, le_track_t *src ) { if ( dest && src && dest != src ) { ClearLT(dest,0); memcpy(dest,src,sizeof(*dest)); src->file = 0; src->name = 0; src->xname = 0; src->lti[0].ident = 0; src->lti[1].ident = 0; ClearLT(src,0); } } /////////////////////////////////////////////////////////////////////////////// void SetupStandardArenaLT ( le_track_t *lt, uint setup_slot ) { if (IsMkwArena(setup_slot)) setup_slot -= MKW_ARENA_BEG; if ( setup_slot < MKW_N_ARENAS ) SetupByTrackInfoLT(lt,arena_info+setup_slot); } /////////////////////////////////////////////////////////////////////////////// void SetupStandardArenasLD ( le_distrib_t *ld, bool if_no_arnea_defined ) { DASSERT(ld); if ( if_no_arnea_defined ) { le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) if ( lt->track_status >= LTS_EXPORT && lt->track_type == LTTY_ARENA ) return; } for ( int slot = MKW_ARENA_BEG; slot < MKW_ARENA_END; slot++ ) SetupStandardArenaLT(DefineFreeTrackLD(ld,LTTY_ARENA,true),slot); } /////////////////////////////////////////////////////////////////////////////// void SetupStandardTrackLT ( le_track_t *lt, uint setup_slot ) { if (IsMkwTrack(setup_slot)) SetupByTrackInfoLT(lt,track_info+setup_slot); else if (IsMkwArena(setup_slot)) { setup_slot -= MKW_ARENA_BEG; SetupByTrackInfoLT(lt,arena_info+setup_slot); } } /////////////////////////////////////////////////////////////////////////////// void SetupStandardTracksLD ( le_distrib_t *ld, bool if_no_track_defined ) { DASSERT(ld); if ( if_no_track_defined ) { le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) if ( lt->track_status >= LTS_EXPORT && lt->track_type == LTTY_TRACK ) return; } for ( int slot = MKW_TRACK_BEG; slot < MKW_TRACK_END; slot++ ) SetupStandardTrackLT(DefineFreeTrackLD(ld,LTTY_TRACK,true),slot); } /////////////////////////////////////////////////////////////////////////////// void SetupByTrackInfoLT ( le_track_t *lt, const TrackInfo_t *ti ) { if ( !lt || !ti ) return; lt->cup_slot = ti->def_slot; lt->track_type = IsMkwArena(ti->track_id) ? LTTY_ARENA : LTTY_TRACK; lt->property = ti->track_id; lt->music = ti->music_id; le_strpar_t spar = { .opt = LEO__DEFAULT|LEO_IN_ALL }; SetFileLT(lt,&spar,ti->track_fname); SetNameLT(lt,&spar,ti->name_en); sha1_hex_t hex; Sha1Bin2Hex(hex,ti->sha1[0]); SetIdentOptLT(lt,hex,false,false,LEO_IN_ALL); const u32 have_d = be32(ti->sha1[TI_SHA1_IS_D]); if (have_d) { Sha1Bin2Hex(hex,ti->sha1[TI_SHA1_IS_D]); SetIdentOptLT(lt,hex,true,false,LEO_IN_ALL); } // last action because SetIdent*LT() clears 'is_original' lt->is_original = lt->lti[0].orig_sha1 = lt->lti[1].orig_sha1 = lt->track_slot == ti->track_id; } /////////////////////////////////////////////////////////////////////////////// void SetupMkwExtraTrackLT ( le_track_t *lt, uint setup_slot ) { ccp info = GetMkwExtraInfo(setup_slot,0); if (info) { lt->track_status = LTS_ACTIVE; lt->track_type = LTTY_EXTRA; lt->property = 0; lt->music = MKW_MUSIC_MIN_ID; SetNameLT(lt,0,info); } } /////////////////////////////////////////////////////////////////////////////// void SetupLecodeRandomTrackLT ( le_track_t *lt, uint setup_slot ) { ccp info = GetLecodeRandomInfo(setup_slot,0); if (info) { lt->track_status = LTS_ACTIVE; lt->track_type = LTTY_RANDOM|LTTY_TRACK|LTTY_ARENA; lt->cup_slot = setup_slot - MKW_LE_RANDOM_BEG + 91; lt->property = 0; lt->music = MKW_MUSIC_MIN_ID; SetNameLT(lt,0,info); } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // -1:wrong slot or not a battle, 0:correct slots, // 1:wrong property, 2:wrong music, 3:both wrong int HaveUsualBattleSlots ( le_track_t *lt ) { if ( !lt || !IsArenaLTTY(lt->track_type) || !IsMkwArena(lt->track_slot) ) return -1; const TrackInfo_t *ai = arena_info + lt->track_slot - MKW_ARENA_BEG; return ( lt->property == lt->track_slot ? 0 : 1 ) | ( lt->music == ai->music_id ? 0 : 2 ); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ClearIdentLT ( le_track_t *lt ) { FreeString(lt->lti[0].ident); FreeString(lt->lti[1].ident); lt->lti[0].ident = lt->lti[1].ident = 0; lt->lti[0].have_sha1 = lt->lti[1].have_sha1 = 0; lt->lti[0].orig_sha1 = lt->lti[1].orig_sha1 = 0; lt->is_original = 0; } // /////////////////////////////////////////////////////////////////////////////// /////////////// Set*LT() functions /////////////// /////////////////////////////////////////////////////////////////////////////// void SetIdentOptLT ( le_track_t *lt, ccp ident, bool use_d, bool csum_only, le_options_t opt ) { le_strpar_t spar = { .opt = opt, .use_d = use_d, .csum_only = csum_only }; SetIdentLT(lt,&spar,ident); } //----------------------------------------------------------------------------- void SetIdentLT ( le_track_t *lt, const le_strpar_t *par, ccp ident ) { // par: opt, use_d, csum_only if (!lt) return; if (!par) par = &DefaultLSP; le_track_id_t *lti = lt->lti + (par->use_d>0); if ( IsAssignmentAllowedLEO(lti->ident,ident,par->opt) ) { if (!par->use_d) ClearIdentLT(lt); if (ident) { while ( *ident == ' ' || *ident == '\t' ) ident++; sha1_size_hash_t sha1size; const int stat = IsSSChecksum(&sha1size,ident,-1); if (stat) { lti->have_sha1 = true; memcpy(lti->sha1bin,sha1size.hash,sizeof(lti->sha1bin)); FreeString(lti->ident); lti->ident = stat == 2 ? STRDUP(ident) : 0; } else { lti->have_sha1 = false; memset(lti->sha1bin,0,sizeof(lti->sha1bin)); FreeString(lti->ident); lti->ident = par->csum_only ? 0 : STRDUP(ident); } //-- calculate is_orig bool is_orig = false; if (lti->have_sha1) { const TrackInfo_t *ti = IsMkwTrack(lt->track_slot) ? track_info + lt->track_slot : IsMkwArena(lt->track_slot) ? arena_info + lt->track_slot - MKW_ARENA_BEG : 0; if (ti) { const u8 *sptr = ti->sha1[ par->use_d ? TI_SHA1_IS_D : 0 ]; if (lti->have_sha1) is_orig = !memcmp(lti->sha1bin,sptr,sizeof(lti->sha1bin)) || !memcmp(lti->sha1bin,sptr+TI_SHA1_NORM,sizeof(lti->sha1bin)); else is_orig = !*(u32*)sptr; } } lti->orig_sha1 = is_orig; } lt->is_original = lt->lti[0].orig_sha1 && lt->lti[1].orig_sha1; } } /////////////////////////////////////////////////////////////////////////////// void SetFileLT ( le_track_t *lt, const le_strpar_t *par, ccp fname ) { // par: opt if (!par) par = &DefaultLSP; if ( lt && IsAssignmentAllowedLEO(lt->file,fname,par->opt) ) { FreeString(lt->file); lt->file = STRDUP(fname); } } /////////////////////////////////////////////////////////////////////////////// void SetPathLT ( le_track_t *lt, const le_strpar_t *par, ccp path ) { // par: opt if (!par) par = &DefaultLSP; if ( lt && IsAssignmentAllowedLEO(lt->path,path,par->opt) ) { FreeString(lt->path); lt->path = STRDUP(path); } } /////////////////////////////////////////////////////////////////////////////// void SetNameLT ( le_track_t *lt, const le_strpar_t *par, ccp name ) { // par: opt if (!par) par = &DefaultLSP; if ( lt && IsAssignmentAllowedLEO(lt->name,name,par->opt) ) { FreeString(lt->name); if ( lt->xname && !strcmp(lt->xname,name) ) { lt->name = lt->xname; lt->xname = 0; } else lt->name = STRDUP(name); } } /////////////////////////////////////////////////////////////////////////////// void SetXNameLT ( le_track_t *lt, const le_strpar_t *par, ccp xname ) { // par: opt if (!par) par = &DefaultLSP; if ( lt && IsAssignmentAllowedLEO(lt->xname,xname,par->opt) ) { FreeString(lt->xname); if ( !lt->name || strcmp(lt->name,xname) ) lt->xname = STRDUP(xname); } } /////////////////////////////////////////////////////////////////////////////// #if LE_STRING_LIST_ENABLED void SetListLT ( le_track_t *lt, const le_strpar_t *par, ccp text ) { // par: ltt, opt if (!par) par = &DefaultLSP; const le_track_text_t ltt = par->opt & LEO_LTT_SELECTOR; if ( lt && ltt >= LTT__LIST_BEG && ltt < LTT__LIST_END ) { ccp *ptr = lt->str_list + ltt - LTT__LIST_BEG; if (IsAssignmentAllowedLEO(*ptr,text,par->opt)) { FreeString(*ptr); *ptr = STRDUP(text); } } } #endif // LE_STRING_LIST_ENABLED /////////////////////////////////////////////////////////////////////////////// #if LE_STRING_SET_ENABLED void SetSetLT ( le_track_t *lt, const le_strpar_t *par, ccp text ) { // par: ltt, key, opt if ( lt && par && par->key ) { PRINT0(" %u [%s] = %s\n",lt->track_slot,par->key,text); ReplaceParamField(<->str_set,par->key,false,0,STRDUP(text)); } } //---------------------------------------------------------------------------- void SetByKeyLT ( le_track_t *lt, ccp key, ccp text ) { le_strpar_t par = { .key = key }; SetSetLT(lt,&par,text); } #endif // LE_STRING_SET_ENABLED /////////////////////////////////////////////////////////////////////////////// void SetTextOptLT ( le_track_t *lt, ccp text, le_options_t opt ) { le_strpar_t par = { .opt = opt }; SetTextLT(lt,&par,text); } //----------------------------------------------------------------------------- void SetTextLT ( le_track_t *lt, const le_strpar_t *par, ccp text ) { // par: ltt, * le_strpar_t mypar = {0}; if (par) mypar = *par; const le_track_text_t ltt = mypar.opt & LEO_LTT_SELECTOR; switch (ltt) { case LTT_SHA1: mypar.use_d = false; mypar.csum_only = true; SetIdentLT(lt,&mypar,text); break; case LTT_IDENT: mypar.use_d = false; mypar.csum_only = false; SetIdentLT(lt,&mypar,text); break; case LTT_SHA1_D: mypar.use_d = true; mypar.csum_only = true; SetIdentLT(lt,&mypar,text); break; case LTT_IDENT_D: mypar.use_d = true; mypar.csum_only = false; SetIdentLT(lt,&mypar,text); break; case LTT_FILE: SetFileLT(lt,&mypar,text); break; case LTT_PATH: SetPathLT(lt,&mypar,text); break; case LTT_NAME: SetNameLT(lt,&mypar,text); break; case LTT_XNAME: case LTT_XNAME2: SetXNameLT(lt,&mypar,text); break; #if LE_STRING_SET_ENABLED case LTT_STRSET: SetSetLT(lt,&mypar,text); break; #endif default: #if LE_STRING_LIST_ENABLED if ( ltt >= LTT__LIST_BEG && ltt < LTT__LIST_END ) SetListLT(lt,&mypar,text); #endif break; } } // /////////////////////////////////////////////////////////////////////////////// /////////////// Get*LT() functions /////////////// /////////////////////////////////////////////////////////////////////////////// const u8 * GetSha1BinLT ( const le_track_t *lt, bool use_d ) { const le_track_id_t *lti = lt->lti + (use_d>0); return lt && lt->track_status >= LTS_EXPORT && lti->have_sha1 ? lti->sha1bin : 0; } /////////////////////////////////////////////////////////////////////////////// ccp GetSha1LT ( const le_track_t *lt, bool use_d, ccp return_on_empty ) { const le_track_id_t *lti = lt->lti + (use_d>0); if ( !lt || lt->track_status < LTS_EXPORT || !lti->have_sha1 ) return return_on_empty; char *buf = GetCircBuf(sizeof(sha1_hex_t)); Sha1Bin2Hex(buf,lti->sha1bin); return buf; } /////////////////////////////////////////////////////////////////////////////// ccp GetIdentLT ( const le_track_t *lt, bool use_d, bool use_sha1, ccp return_on_empty ) { const le_track_id_t *lti = lt->lti + (use_d>0); if ( !lt || lt->track_status < LTS_EXPORT ) return return_on_empty; return lti->ident ? ( *lti->ident ? lti->ident : return_on_empty ) : use_sha1 ? GetSha1LT(lt,use_d,return_on_empty) : return_on_empty; } /////////////////////////////////////////////////////////////////////////////// ccp GetFileLT ( const le_track_t *lt, ccp return_on_empty ) { return lt && lt->track_status >= LTS_EXPORT && lt->file && *lt->file ? lt->file : return_on_empty; } /////////////////////////////////////////////////////////////////////////////// ccp GetPathL2 ( le_distrib_t *ld, const le_track_t *lt, le_options_t opt, ccp return_on_empty ) { ccp path = lt && lt->track_status >= LTS_EXPORT && lt->path && *lt->path ? lt->path : return_on_empty; char dname[PATH_MAX]; if ( lt && opt & LEO_AUTO_PATH ) { le_strpar_t par = { .opt = opt }; if ( !IsOutputNameValidLEO(path,opt) && opt_track_source.used ) { ccp fname = GetFileLT(lt,0); if (IsOutputNameValidLEO(fname,opt)) { TransferMode_t tfer_mode; ccp found = FindTrackFile(fname,&tfer_mode); if (found) { PRINT0("ld=%p, tfer_mode:%x, move:%x\n",ld,tfer_mode,tfer_mode & (TFMD_J_MOVE|TFMD_J_MOVE1)); SetPathLT((le_track_t*)lt,&par,found); if ( ld && tfer_mode & (TFMD_J_MOVE|TFMD_J_MOVE1) ) { Insert_d(dname,sizeof(dname),found); InsertStringField(&ld->remove_files,found,true); InsertStringField(&ld->remove_files,dname,false); } else FreeString(found); return lt->path; } } char buf[20]; snprintf(buf,sizeof(buf), lecode_04x ? "%04x" : "%03x", lt->track_slot ); TransferMode_t tfer_mode; ccp found = FindTrackFile(buf,&tfer_mode); if (found) { PRINT0("ld=%p, tfer_mode:%x, move:%x\n",ld,tfer_mode,tfer_mode & (TFMD_J_MOVE|TFMD_J_MOVE1)); SetPathLT((le_track_t*)lt,&par,found); if ( ld && tfer_mode & (TFMD_J_MOVE|TFMD_J_MOVE1) ) { Insert_d(dname,sizeof(dname),found); InsertStringField(&ld->remove_files,found,true); InsertStringField(&ld->remove_files,dname,false); } else FreeString(found); return lt->path; } } if (path) { const int len = strlen(path); if ( len > 0 && path[len-1] == '/' ) { char newpath[PATH_MAX]; ccp format = lecode_04x ? "%s%04x.szs" : "%s%03x.szs"; snprintf(newpath,sizeof(newpath),format,path,lt->track_slot); SetPathLT((le_track_t*)lt,&par,newpath); return lt->path; } } } return path; } //----------------------------------------------------------------------------- ccp GetPathLT ( const le_track_t *lt, le_options_t opt, ccp return_on_empty ) { return GetPathL2(0,lt,opt,return_on_empty); } /////////////////////////////////////////////////////////////////////////////// ccp GetNameLT ( const le_track_t *lt, ccp return_on_empty ) { return lt && lt->track_status >= LTS_EXPORT && IsOutputNameValidLEO(lt->name,0) ? lt->name : return_on_empty; } //----------------------------------------------------------------------------- ccp GetXNameLT ( const le_track_t *lt, ccp return_on_empty ) { return lt && lt->track_status >= LTS_EXPORT && IsOutputNameValidLEO(lt->xname,0) ? lt->xname : return_on_empty; } //----------------------------------------------------------------------------- ccp GetXName2LT ( const le_track_t *lt, ccp return_on_empty ) { if ( !lt || lt->track_status < LTS_EXPORT ) return return_on_empty; return IsOutputNameValidLEO(lt->xname,0) ? lt->xname : IsOutputNameValidLEO(lt->name,0) ? lt->name : return_on_empty; } /////////////////////////////////////////////////////////////////////////////// #if LE_STRING_LIST_ENABLED ccp GetListLT ( const le_track_t *lt, le_options_t opt, ccp return_on_empty ) { const le_track_text_t ltt = opt & LEO_LTT_SELECTOR; if ( lt && lt->track_status >= LTS_EXPORT && ltt >= LTT__LIST_BEG && ltt < LTT__LIST_END ) { ccp text = lt->str_list[ ltt - LTT__LIST_BEG ]; if ( text && *text ) return text; } return return_on_empty; } #endif // LE_STRING_LIST_ENABLED /////////////////////////////////////////////////////////////////////////////// #if LE_STRING_SET_ENABLED ccp GetSetLT ( const le_track_t *lt, const le_strpar_t *par, ccp return_on_empty ) { if ( lt && lt->track_status >= LTS_EXPORT && IsValidSetLSP(par) ) { const ParamFieldItem_t *it = FindParamField(<->str_set,par->key); if (it) return it->data; } return return_on_empty; } #endif // LE_STRING_SET_ENABLED /////////////////////////////////////////////////////////////////////////////// ccp GetTextLT ( const le_track_t *lt, const le_strpar_t *par, ccp return_on_empty ) { if (!par) return return_on_empty; #if LE_STRING_SET_ENABLED if (IsValidSetLSP(par)) return GetSetLT(lt,par,return_on_empty); #endif return GetTextOptLT(lt,par->opt,return_on_empty); } //----------------------------------------------------------------------------- ccp GetTextOptLT ( const le_track_t *lt, le_options_t opt, ccp return_on_empty ) { const le_track_text_t ltt = opt & LEO_LTT_SELECTOR; switch (ltt) { case LTT_SHA1: return GetSha1LT(lt,false,return_on_empty); case LTT_SHA1_D: return GetSha1LT(lt,true, return_on_empty); case LTT_IDENT: return GetIdentLT(lt,false,false,return_on_empty); case LTT_IDENT_D: return GetIdentLT(lt,true, false,return_on_empty); case LTT_FILE: return GetFileLT(lt,return_on_empty); case LTT_PATH: return GetPathLT(lt,opt,return_on_empty); case LTT_NAME: return GetNameLT(lt,return_on_empty); case LTT_XNAME: return GetXNameLT(lt,return_on_empty); case LTT_XNAME2: return GetXName2LT(lt,return_on_empty); default: #if LE_STRING_LIST_ENABLED if ( ltt >= LTT__LIST_BEG && ltt < LTT__LIST_END ) return GetListLT(lt,opt,return_on_empty); #endif break; } return return_on_empty; } /////////////////////////////////////////////////////////////////////////////// ccp * GetPtrLT ( const le_track_t *lt, const le_strpar_t *par ) { #if LE_STRING_SET_ENABLED if (IsValidSetLSP(par)) { ParamFieldItem_t *it = FindParamField(<->str_set,par->key); return it ? (ccp*)&it->data : 0; } #endif return par ? GetPtrOptLT(lt,par->opt,par->allow_d) : 0; } //----------------------------------------------------------------------------- ccp * GetPtrOptLT ( const le_track_t *lt, le_options_t opt, bool allow_d ) { const le_track_text_t ltt = opt & LEO_LTT_SELECTOR; switch (ltt) { case LTT_IDENT_D: if (allow_d) return (ccp*)<->lti[1].ident; break; case LTT_IDENT: return (ccp*)<->lti[0].ident; case LTT_FILE: return (ccp*)<->file; case LTT_PATH: return (ccp*)<->path; case LTT_NAME: return (ccp*)<->name; case LTT_XNAME: return (ccp*)<->xname; default: #if LE_STRING_LIST_ENABLED if ( ltt >= LTT__LIST_BEG && ltt < LTT__LIST_END ) return (ccp*)lt->str_list + ltt - LTT__LIST_BEG; #endif break; } return 0; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// ccp GetSlotNameLT ( const le_track_t *lt ) { if (!lt) return 0; ccp name = GetLecodeRandomName(lt->track_slot,0); return name ? name : PrintCircBuf( "%s%u", GetNameLTTY(lt->track_type), lt->track_slot ); } /////////////////////////////////////////////////////////////////////////////// ccp GetCupLT ( const le_track_t *lt, ccp return_on_empty ) { if (!lt) return return_on_empty; const bool hidden = IsHiddenLEFL(lt->flags); if (!lt->cup_slot) return hidden ? "H" : "-"; return PrintCircBuf("%s%s%u.%u", hidden ? "H" : "", IsArenaLTTY(lt->track_type) ? "A" : IsRandomLTTY(lt->track_type) ? "R" : "", lt->cup_slot / 10, lt->cup_slot % 10 ); } //----------------------------------------------------------------------------- ccp GetCupAlignLT ( const le_track_t *lt ) { if (!lt) return "? "; if (!lt->cup_slot) return IsHiddenLEFL(lt->flags) ? "hidden " : "no-cup "; ccp res = GetCupLT(lt,"-"); return IsHiddenLEFL(lt->flags) ? PrintCircBuf("%8s",res) : PrintCircBuf("%6s ",res); } /////////////////////////////////////////////////////////////////////////////// DistribFlags_t GetDistribFlagsLT ( const le_track_t *lt, bool use_d ) { if (!lt) return 0; DistribFlags_t dta = lt->distrib_flags & ~(G_DTA_IS_D|G_DTA_TITLE|G_DTA_HIDDEN|G_DTA_ORIGINAL); if (!(dta&G_DTA_F_CALCULATED)) { ccp text = GetXName2LT(lt,0); if (text) { split_filename_t spf; AnalyseSPF(&spf,true,text,0,CPM_LINK,opt_plus); dta = spf.distrib_flags | G_DTA_F_CALCULATED; ResetSPF(&spf); } } const le_track_id_t *lti = lt->lti; if (use_d) { dta |= G_DTA_IS_D; lti++; } if (IsTitleLEFL(lt->flags)) dta |= G_DTA_TITLE; if (IsHiddenLEFL(lt->flags)) dta |= G_DTA_HIDDEN; if (lti->orig_sha1) dta |= G_DTA_ORIGINAL; ((le_track_t*)lt)->distrib_flags = dta; return dta; } // /////////////////////////////////////////////////////////////////////////////// /////////////// Quote*LT() functions /////////////// /////////////////////////////////////////////////////////////////////////////// ccp QuoteSha1LT ( const le_track_t *lt, bool use_d, ccp return_on_empty ) { ccp res = GetSha1LT(lt,use_d,return_on_empty); return QuoteStringCircS(res,"\"\"",CHMD__MODERN); } /////////////////////////////////////////////////////////////////////////////// ccp QuoteIdentLT ( const le_track_t *lt, bool use_d, bool use_sha1, ccp return_on_empty ) { ccp res = GetIdentLT(lt,use_d,use_sha1,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } /////////////////////////////////////////////////////////////////////////////// ccp QuoteFileLT ( const le_track_t *lt, ccp return_on_empty ) { ccp res = GetFileLT(lt,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } /////////////////////////////////////////////////////////////////////////////// ccp QuotePathLT ( const le_track_t *lt, le_options_t opt, ccp return_on_empty ) { ccp res = GetPathLT(lt,opt,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } /////////////////////////////////////////////////////////////////////////////// ccp QuoteNameLT ( const le_track_t *lt, ccp return_on_empty ) { ccp res = GetNameLT(lt,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } /////////////////////////////////////////////////////////////////////////////// ccp QuoteXNameLT ( const le_track_t *lt, ccp return_on_empty ) { ccp res = GetXNameLT(lt,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } /////////////////////////////////////////////////////////////////////////////// ccp QuoteXName2LT ( const le_track_t *lt, ccp return_on_empty ) { ccp res = GetXName2LT(lt,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } /////////////////////////////////////////////////////////////////////////////// #if LE_STRING_LIST_ENABLED ccp QuoteListLT ( const le_track_t *lt, le_options_t opt, ccp return_on_empty ) { ccp res = GetListLT(lt,opt,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } #endif // LE_STRING_LIST_ENABLED /////////////////////////////////////////////////////////////////////////////// #if LE_STRING_SET_ENABLED ccp QuoteSetLT ( const le_track_t *lt, const le_strpar_t *par, ccp return_on_empty ) { ccp res = GetSetLT(lt,par,return_on_empty); return QuoteStringCircS(res,EmptyQuote,CHMD__MODERN); } #endif // LE_STRING_SET_ENABLED /////////////////////////////////////////////////////////////////////////////// ccp QuoteTextLT ( const le_track_t *lt, const le_strpar_t *par, ccp return_on_empty ) { if (!par) return return_on_empty; #if LE_STRING_SET_ENABLED if (IsValidSetLSP(par)) return QuoteSetLT(lt,par,return_on_empty); #endif return QuoteTextOptLT(lt,par->opt,return_on_empty); } //----------------------------------------------------------------------------- ccp QuoteTextOptLT ( const le_track_t *lt, le_options_t opt, ccp return_on_empty ) { const le_track_text_t ltt = opt & LEO_LTT_SELECTOR; switch (ltt) { case LTT_SHA1: return QuoteSha1LT(lt,false,return_on_empty); break; case LTT_SHA1_D: return QuoteSha1LT(lt,true, return_on_empty); break; case LTT_IDENT: return QuoteIdentLT(lt,false,false,return_on_empty); break; case LTT_IDENT_D: return QuoteIdentLT(lt,true, false,return_on_empty); break; case LTT_NAME: return QuoteNameLT(lt,return_on_empty); break; case LTT_XNAME: return QuoteXNameLT(lt,return_on_empty); break; case LTT_XNAME2: return QuoteXName2LT(lt,return_on_empty); break; case LTT_FILE: return QuoteFileLT(lt,return_on_empty); break; case LTT_PATH: return QuotePathLT(lt,opt,return_on_empty); break; default: #if LE_STRING_LIST_ENABLED if ( ltt >= LTT__LIST_BEG && ltt < LTT__LIST_END ) return QuoteListLT(lt,opt,return_on_empty); #endif break; } return return_on_empty; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: management /////////////// /////////////////////////////////////////////////////////////////////////////// void InitializeLD ( le_distrib_t *ld ) { DASSERT(ld); memset(ld,0,sizeof(*ld)); ld->is_initialized = true; ld->scan.bt.add_unused = true; ld->scan.vs.add_unused = true; ld->auto_setup = LAS_DEFAULT; InitializeLSP(&ld->spar,SetupDefaultLEO()); InitializeLECUP(&ld->cup_battle,BMG_MAX_BCUP,BMG_BCUP_TRACKS); InitializeLECUP(&ld->cup_versus,BMG_MAX_RCUP,BMG_RCUP_TRACKS); InitializeStringField(&ld->remove_files); InitializeParamField(&ld->dis_param); ld->dis_param.free_data = true; InitializeEML(&ld->group); } /////////////////////////////////////////////////////////////////////////////// void ResetLD ( le_distrib_t *ld ) { if (ld) { ccp *ptr = ld->remove_files.field, *end; for ( end = ptr + ld->remove_files.used; ptr < end; ptr++ ) { PRINT0("RM %s\n",*ptr); unlink(*ptr); } ResetStringField(&ld->remove_files); ResetArchLD(ld); ResetTracksAndCupsLD(ld); ResetParamField(&ld->dis_param); FREE(ld->lpar); ResetLSP(&ld->spar); FreeString(ld->separator); if (ld->ctcode) { ResetCTCODE(ld->ctcode); FREE(ld->ctcode); } for ( int reg = 0; reg < LEREG__N; reg++ ) if (ld->lecode_alloced[reg]) FREE((u8*)ld->lecode[reg].ptr); InitializeLD(ld); } } /////////////////////////////////////////////////////////////////////////////// void ResetTracksAndCupsLD ( le_distrib_t *ld ) { if (ld) { le_track_t *lt = ld->tlist; le_track_t *end = lt + ld->tlist_used; for ( ; lt < end; lt++ ) ResetLT(lt); FREE(ld->tlist); ld->tlist = 0; ld->tlist_used = ld->tlist_size = 0; FREE(ld->reflist); ld->reflist = 0; ld->reflist_used = ld->reflist_size = 0; ResetLECUP(&ld->cup_battle); ResetLECUP(&ld->cup_versus); } } /////////////////////////////////////////////////////////////////////////////// void AutoSetupLD ( le_distrib_t *ld, le_auto_setup_t auto_setup ) { if (!ld) return; ld->auto_setup = auto_setup; for ( int slot = MKW_TRACK_BEG; slot < MKW_ARENA_END; slot++ ) DefineTrackLD(ld,slot,true); for ( int slot = MKW_EXTRA_BEG; slot < MKW_EXTRA_END; slot++ ) DefineTrackLD(ld,slot,true); for ( int slot = MKW_LE_RANDOM_BEG; slot < MKW_LE_RANDOM_END; slot++ ) DefineTrackLD(ld,slot,true); if ( auto_setup & LAS_ARENA ) { DefineLECUP(&ld->cup_battle,1); memcpy(ld->cup_battle.list,standard_bt_ref,sizeof(standard_bt_ref)); } if ( auto_setup & LAS_TRACK ) { DefineLECUP(&ld->cup_versus,7); memcpy(ld->cup_versus.list,standard_vs_ref,sizeof(standard_vs_ref)); } if ( auto_setup & LAS_TEMPLATE ) { le_cup_ref_t *ref = DefineLECUP(&ld->cup_versus,8); if (ref) memcpy(ref,mkwfun_random_ref,sizeof(mkwfun_random_ref)); ref = DefineLECUP(&ld->cup_versus,9); if (ref) { for ( int i = 1; i <= 6; i++ ) { le_track_t *lt = DefineFreeTrackLD(ld,LTTY_TRACK,true); if (lt) { lt->property = 10+i; lt->music = NormalizeMusicID(20+i); char buf[30]; snprintf(buf,sizeof(buf),"file_%03u",lt->track_slot); SetFileLT(lt,0,buf); if ( i == 3 ) { lt->flags = G_LEFL_RND_HEAD; SetNameLT(lt,0,"Head of a group"); *ref++ = lt->track_slot; } else if ( i == 4 || i == 5 ) { lt->flags = G_LEFL_RND_GROUP; snprintf(buf,sizeof(buf),"Group Track #%u (hidden)",i-3); SetNameLT(lt,0,buf); SetIdentOptLT(lt,"SHA1 of SZS",false,false,0); } else { snprintf(buf,sizeof(buf),"Test Track #%u",i); SetNameLT(lt,0,buf); SetIdentOptLT(lt,"SHA1 of SZS",false,false,0); *ref++ = lt->track_slot; } snprintf(buf,sizeof(buf),"Extended Name #%u",i); SetXNameLT(lt,0,buf); } } } } } /////////////////////////////////////////////////////////////////////////////// void AnalyzeLD ( le_distrib_t *ld, bool force_new_analysis ) { if ( !ld || ld->ana.valid && !force_new_analysis ) return; memset(&ld->ana,0,sizeof(ld->ana)); ld->ana.valid = true; ld->ana.bt.orig_tracks = true; ld->ana.vs.orig_tracks = true; ld->ana.bt.add_unused = !ld->scan.valid || ld->scan.bt.add_unused; ld->ana.vs.add_unused = !ld->scan.valid || ld->scan.vs.add_unused; for ( int slot = MKW_ARENA_BEG; slot < MKW_ARENA_END; slot++ ) { le_track_t *lt = GetTrackLD(ld,slot); if ( !lt || lt->track_status < LTS_EXPORT || !lt->is_original || lt->property != slot ) { ld->ana.bt.orig_tracks = false; break; } } for ( int slot = MKW_TRACK_BEG; slot < MKW_TRACK_END; slot++ ) { le_track_t *lt = GetTrackLD(ld,slot); if ( !lt || lt->track_status < LTS_EXPORT || !lt->is_original || lt->property != slot ) { ld->ana.vs.orig_tracks = false; break; } } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void CutLD ( le_distrib_t *ld, le_options_t opt ) { if (!ld) return; if ( opt & LEO_CUT_ALL ) ClearTracksLD( ld, 0, INT_MAX ); else { if ( opt & LEO_CUT_STD ) ClearTracksLD( ld, BMG_N_TRACK + BMG_N_ARENA, INT_MAX ); if ( opt & LEO_CUT_CTCODE ) { ClearTracksLD( ld, CT_CODE_MAX_TRACKS, INT_MAX ); ClearTracksLD( ld, BMG_N_TRACK, BMG_N_TRACK + BMG_N_ARENA ); } } } //----------------------------------------------------------------------------- void ClearTracksLD ( le_distrib_t *ld, int from, int to ) { if (!ld) return; if ( from < 0 ) from = 0; if ( to > ld->tlist_used ) to = ld->tlist_used; le_track_t *lt = ld->tlist + from; le_track_t *end = ld->tlist + to; for ( ; lt < end; lt++ ) { FreeString(lt->lti[0].ident); FreeString(lt->lti[1].ident); FreeString(lt->name); FreeString(lt->file); memset(lt,0,sizeof(*lt)); } if ( ld->tlist_used == to ) ld->tlist_used = from; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// uint PackTracksLD ( const le_distrib_t *ld ) { if (!ld) return 0; if ( ld->tlist_used < LE_FIRST_CT_SLOT ) return LE_FIRST_CT_SLOT; int slot; for ( slot = ld->tlist_used - 1; slot >= LE_FIRST_CT_SLOT; slot-- ) { le_track_t *lt = ld->tlist + slot; if ( lt->track_status >= LTS_ACTIVE ) break; ClearLT(lt,0); } return ((le_distrib_t*)ld)->tlist_used = slot+1; } /////////////////////////////////////////////////////////////////////////////// void CheckTracksLD ( const le_distrib_t *ld ) { if (!ld) return; le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) { if ( lt->track_status < LTS_ACTIVE ) continue; if ( lt->track_type & LTTY_TRACK ) { if (!IsMkwTrack(lt->property)) lt->track_status = LTS_FAIL; } else { if (!IsMkwArena(lt->property)) lt->track_status = LTS_FAIL; } } PackTracksLD(ld); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void DefineExtraTracksLD ( le_distrib_t *ld ) { for ( int slot = MKW_EXTRA_BEG; slot < MKW_EXTRA_END; slot++ ) DefineTrackLD(ld,slot,false); } /////////////////////////////////////////////////////////////////////////////// void DefineRandomTracksLD ( le_distrib_t *ld ) { for ( int slot = MKW_LE_RANDOM_BEG; slot < MKW_LE_RANDOM_END; slot++ ) DefineTrackLD(ld,slot,false); } /////////////////////////////////////////////////////////////////////////////// le_track_t * GetTrackLD ( le_distrib_t *ld, int slot ) { if ( !ld || !IsValidLecodeSlot(slot) || slot >= ld->tlist_used ) return 0; le_track_t *lt = ld->tlist + slot; // extra && random slots are defined automatically on access if ( lt->track_status < LTS_ACTIVE && ( IsMkwExtra(slot) || IsLecodeRandom(slot) )) DefineTrackLD(ld,slot,false); return lt->track_status < LTS_ACTIVE ? 0 : lt; } /////////////////////////////////////////////////////////////////////////////// le_track_t * DefineTrackLD ( le_distrib_t *ld, int slot, bool mark_export ) { if ( !ld || !IsValidLecodeSlot(slot) ) return 0; if (!ld->is_initialized) InitializeLD(ld); uint need = slot + 1; if ( need > ld->tlist_size ) { static const int tab[] = { 512, 1024, 2048, LE_MAX_TRACKS, 0 }; for ( const int *ptr = tab; *ptr; ptr++ ) if ( need <= *ptr ) { need = *ptr; break; } ld->tlist = REALLOC( ld->tlist, need * sizeof(*ld->tlist) ); memset( ld->tlist + ld->tlist_size, 0, ( need - ld->tlist_size) * sizeof(*ld->tlist) ); ld->tlist_size = need; FREE(ld->reflist); ld->reflist = 0; ld->reflist_used = ld->reflist_size = 0; } DASSERT( slot >= 0 && slot <= MKW_MAX_TRACK_SLOT ); ASSERT( slot < ld->tlist_size ); if ( ld->tlist_used <= slot ) ld->tlist_used = slot + 1; if ( ld->tlist_used < LE_FIRST_CT_SLOT ) ld->tlist_used = LE_FIRST_CT_SLOT; ASSERT( ld->tlist_used <= ld->tlist_size ); le_track_t *lt = ld->tlist + slot; if ( lt->track_status == LTS_INVALID ) { // initialize InitializeLT(lt); lt->track_slot = slot; if ( IsMkwArena(slot) ) { lt->track_type = LTTY_ARENA; if ( ld->auto_setup & LAS_ARENA ) SetupStandardArenaLT(lt,slot); } else if ( IsLecodeRandom(slot) ) { lt->track_status = LTS_ACTIVE; lt->track_type = LTTY_RANDOM|LTTY_TRACK|LTTY_ARENA; if ( ld->auto_setup & LAS_RANDOM ) SetupLecodeRandomTrackLT(lt,slot); } else if ( IsMkwExtra(slot) ) { lt->track_status = LTS_ACTIVE; lt->track_type = LTTY_EXTRA; if ( ld->auto_setup & LAS_EXTRA ) SetupMkwExtraTrackLT(lt,slot); } else { lt->track_type = LTTY_TRACK; if ( IsMkwTrack(slot) && ld->auto_setup & LAS_TRACK ) SetupStandardTrackLT(lt,slot); } DirtyCupLD(ld,lt->track_type); } if ( mark_export && lt->track_status < LTS_ACTIVE ) lt->track_status = LTS_EXPORT; return lt; } /////////////////////////////////////////////////////////////////////////////// le_track_t * DefineFreeTrackLD ( le_distrib_t *ld, le_track_type_t ltty, bool mark_export ) { if (!ld) return 0; if (!ltty) ltty = LTTY_TRACK | LTTY_ARENA; const int slot_beg = ltty == LTTY_TRACK ? MKW_TRACK_BEG : ltty == LTTY_ARENA ? MKW_ARENA_BEG : ltty & (LTTY_TRACK|LTTY_ARENA) ? LE_FIRST_CT_SLOT : -1; if ( slot_beg < 0 ) return 0; for ( int slot = slot_beg; slot <= LE_LAST_CT_SLOT; slot++ ) { if ( slot == MKW_TRACK_END && !(ltty<TY_ARENA) || slot == MKW_ARENA_END ) slot = LE_FIRST_CT_SLOT; le_track_t *lt = DefineTrackLD(ld,slot,false); if ( lt && lt->track_status < LTS_ACTIVE ) { if (mark_export) lt->track_status = LTS_EXPORT; lt->track_type = ltty; DirtyCupLD(ld,ltty); return lt; } } return 0; } /////////////////////////////////////////////////////////////////////////////// le_track_t * DefineGroupTrackLD ( le_distrib_t *ld, le_track_type_t ltty, bool mark_export ) { if (!ld) return 0; if (!ltty) ltty = LTTY_TRACK | LTTY_ARENA; DirtyCupLD(ld,ltty); int slot = PackTracksLD(ld); ASSERT ( slot >= LE_FIRST_CT_SLOT ); return DefineTrackLD(ld,slot,mark_export); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// int FindFreeTracksLD ( le_distrib_t *ld, le_track_type_t ltty, int n ) { int beg, end; if ( ltty == LTTY_TRACK ) { beg = MKW_TRACK_BEG; end = MKW_TRACK_END; } else if ( ltty == LTTY_ARENA ) { beg = MKW_ARENA_BEG; end = MKW_ARENA_END; } else { beg = LE_FIRST_CT_SLOT; end = 0; } int prev = -10, start = -10; for ( int slot = beg; slot < LE_LAST_CT_SLOT; slot++ ) { if ( slot == end ) slot = LE_FIRST_CT_SLOT; if ( slot < ld->tlist_used && IsActiveLT(ld->tlist+slot) ) continue; if ( prev+1 != slot ) start = slot; prev = slot; if ( start + n == slot ) return start; } return -1; } /////////////////////////////////////////////////////////////////////////////// le_track_t * ReserveTracksLD ( le_distrib_t *ld, le_track_type_t ltty, int n, bool mark_export ) { const int first = FindFreeTracksLD(ld,ltty,n); if ( first < 0 ) return 0; for ( int slot = first; slot < first + n; slot++ ) { le_track_t *lt = DefineTrackLD(ld,slot,mark_export); lt->track_type = ltty; lt->track_status = mark_export ? LTS_EXPORT : LTS_ACTIVE; } return ld->tlist + first; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ClearUserFlagsAndCupsLD ( le_distrib_t *ld, bool purge_le_flags ) { if (ld) { le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) { lt->user_flags = 0; lt->cup_slot = 0; if (purge_le_flags) { lt->flags &= G_LEFL__ALL; if (IsMkwTrack(lt->property)) lt->flags |= G_LEFL_VERSUS; else if (IsMkwArena(lt->property)) lt->flags |= G_LEFL_BATTLE; } } } } /////////////////////////////////////////////////////////////////////////////// bool HaveWiimmCupLD ( const le_distrib_t *ld ) { if (!ld) return false; le_cup_ref_t *ref = GetLECUP(&ld->cup_versus,8); return ref && ref[0] == 62 && ref[1] == 63 && ref[2] == 64 && ref[3] == 65; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: scan text files /////////////// /////////////////////////////////////////////////////////////////////////////// static ccp xstring ( char *buf, int bufsize, mem_t src ) { if (!src.len) return 0; StringCopySM(buf,bufsize,src.ptr,src.len); ScanEscapedStringPipe(buf,bufsize,buf,0); return buf; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ScanDumpLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' const void * data, // data to scan uint data_size // size of 'data' ) { ScanText_t st; SetupScanText(&st,data,data_size); ScanDumpSTLD(ld,init_ld,&st); ResetScanText(&st); } //----------------------------------------------------------------------------- void ScanDumpSTLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' ScanText_t *st // initialized source ) { if ( !ld || !st ) return; st->detect_sections++; for(;;) { if (!st->is_section) NextLineScanText(st); if (st->is_eot) break; if (!st->is_section) continue; ccp sect = st->line + 1; int slen = st->eol - 1 - sect; PRINT0("SECTION: %.*s\n",slen,sect); if (!strncasecmp(sect,"TOOL",4)) { PRINT0(">> TOOL: ignore\n"); st->is_section = false; } else if (!strncasecmp(sect,"SETTINGS",8)) { PRINT0(">> SETTINGS: 2do\n"); // [[2do]] ??? st->is_section = false; } else if (!strncasecmp(sect,"DISTRIBUTION-PARAM",18)) { ImportDistribSTLD(ld,st); } else if ( !strncasecmp(sect,"LECODE-PARAMETERS",17) || !strncasecmp(sect,"CHAT-MESSAGE-MODES",18) || !strncasecmp(sect,"DEBUG-DOCU",10) || !strncasecmp(sect,"DEBUG-1",7) || !strncasecmp(sect,"DEBUG-2",7) || !strncasecmp(sect,"DEBUG-3",7) || !strncasecmp(sect,"DEBUG-4",7) ) { SetupLparLD(ld,false); ccp start = st->line; while ( NextLineScanText(st) && !st->is_section ) ; ScanTextLPAR(ld->lpar,false,0,start,st->line-start); } else if (!strncasecmp(sect,"CUP-REF",7)) { PRINT0(">> CUP-REF: 2do\n"); ScanRefSTLD(ld,false,st); } else if (!strncasecmp(sect,"TRACK-REF",9)) { PRINT0(">> TRACK-REF: 2do\n"); ScanRefSTLD(ld,false,st); } else if (!strncasecmp(sect,"TRACK-STRINGS",13)) { PRINT0(">> TRACK-STRINGS: 2do\n"); ScanStrSTLD(ld,false,st); } else if ( !strncasecmp(sect,"END",3) || !strncasecmp(sect,"LECODE-INFO",6) ) { // simply ignore st->is_section = false; } else { ERROR0(ERR_WARNING,"Unknown section will be ignored: %.*s\n",slen,sect); st->is_section = false; } } st->detect_sections--; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ScanRefLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' const void * data, // data to scan uint data_size // size of 'data' ) { ScanText_t st; SetupScanText(&st,data,data_size); ScanRefSTLD(ld,init_ld,&st); ResetScanText(&st); } /////////////////////////////////////////////////////////////////////////////// void ScanRefSTLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' ScanText_t *st // initialized source ) { if ( !ld || !st ) return; if ( init_ld || !ld->is_initialized ) InitializeLD(ld); enum { IDX_TSLOT, // track_slot IDX_TYPE, // 'vs' | 'bt' IDX_ORIGINAL, IDX_CUP_SLOT, // cup slot IDX_CUP_NAME, // cup name IDX_PROPERTY, IDX_MUSIC, IDX_FLAGS, IDX_HIDDEN, IDX_LAPS, IDX_SPEED, IDX_SHA1, IDX_SHA1_D, IDX_IDENT, IDX_IDENT_D, IDX_FILE, IDX_FILE_D, IDX_NAME, IDX_NAME_D, IDX_XNAME, IDX_XNAME_D, IDX_PATH, IDX_PATH_D, IDX__N }; while (NextLineScanText(st)) { if ( *st->line == '@' ) continue; mem_t srcline = { .ptr = st->line, .len = st->eol - st->line }; mem_t src[IDX__N+2]; const uint n = SplitByCharMem(src,IDX__N+1,srcline,'|'); if ( n < IDX_FLAGS ) continue; const int tslot = str2l(src[IDX_TSLOT].ptr,0,10); le_track_t *lt = DefineTrackLD(ld,tslot,true); if (!lt) continue; PRINT0("LINE[%d]: %.*s|\n",tslot,(int)(ptr-line),line); lt->cup_slot = str2l(src[IDX_CUP_SLOT].ptr,0,10); lt->property = str2l(src[IDX_PROPERTY].ptr,0,10); lt->music = str2l(src[IDX_MUSIC].ptr,0,10); lt->flags = str2l(src[IDX_FLAGS].ptr,0,10); lt->lap_count = str2l(src[IDX_LAPS].ptr,0,10); lt->speed_factor= strtof(src[IDX_SPEED].ptr,0); le_cup_t *cup = &ld->cup_versus; if (IsMkwArena(lt->property)) { lt->track_type = LTTY_ARENA; cup = &ld->cup_battle; } int cup_index = GetIndexByRefLECUP(cup,lt->cup_slot); if ( cup_index >= 0 ) { le_cup_ref_t *ref = DefineLECUP(cup,cup_index); PRINT0("tslot=%d, cup_slot=%d, cup_index=%d, ref=%p\n",tslot,lt->cup_slot,cup_index,ref); if (ref) *ref = tslot; } char buf[LE_TRACK_STRING_MAX+1]; if ( n > IDX_SHA1 ) SetIdentOptLT(lt,xstring(buf,sizeof(buf),src[IDX_SHA1]), 0,1,ld->spar.opt ); if ( n > IDX_IDENT ) SetIdentOptLT(lt,xstring(buf,sizeof(buf),src[IDX_IDENT]), 0,0,ld->spar.opt ); if ( n > IDX_SHA1_D ) SetIdentOptLT(lt,xstring(buf,sizeof(buf),src[IDX_SHA1_D]), 1,1,ld->spar.opt ); if ( n > IDX_IDENT_D ) SetIdentOptLT(lt,xstring(buf,sizeof(buf),src[IDX_IDENT_D]),1,0,ld->spar.opt ); if ( n > IDX_FILE ) SetFileLT (lt,&ld->spar,xstring(buf,sizeof(buf),src[IDX_FILE] ) ); if ( n > IDX_PATH ) SetPathLT (lt,&ld->spar,xstring(buf,sizeof(buf),src[IDX_PATH] ) ); if ( n > IDX_NAME ) SetNameLT (lt,&ld->spar,xstring(buf,sizeof(buf),src[IDX_NAME] ) ); if ( n > IDX_XNAME ) SetXNameLT(lt,&ld->spar,xstring(buf,sizeof(buf),src[IDX_XNAME]) ); #if 0 // this is the last action to overwrite previous changes if (src[IDX_ORIGINAL].len) lt->is_original = str2l(src[IDX_ORIGINAL].ptr,0,10); #endif } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ScanStrLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' const void * data, // data to scan uint data_size // size of 'data' ) { ScanText_t st; SetupScanText(&st,data,data_size); ScanStrSTLD(ld,init_ld,&st); ResetScanText(&st); } /////////////////////////////////////////////////////////////////////////////// void ScanStrSTLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' ScanText_t *st // initialized source ) { if ( !ld || !st ) return; if ( init_ld || !ld->is_initialized ) InitializeLD(ld); enum { IDX_TSLOT, // track_slot IDX_D_MODE, // bit fields: 1: set std, 2: set _d, 3: set both IDX_IS_SET, IDX_NAME, IDX_STRING, IDX__N }; while (NextLineScanText(st)) { if ( *st->line == '@' ) continue; mem_t srcline = { .ptr = st->line, .len = st->eol - st->line }; mem_t src[IDX__N+2]; const uint n = SplitByCharMem(src,IDX__N+1,srcline,'|'); if ( n < IDX__N ) continue; const int tslot = str2l(src[IDX_TSLOT].ptr,0,10); le_track_t *lt = DefineTrackLD(ld,tslot,true); if (!lt) continue; le_strpar_t spar = { .opt = LEO__DEFAULT|LEO_IN_ALL }; if ( str2l(src[IDX_IS_SET].ptr,0,10) > 0 ) #if LE_STRING_SET_ENABLED SetKeyNameLSP(&spar,src[IDX_NAME].ptr,src[IDX_NAME].len); #else continue; #endif else ScanLSP(&spar,src[IDX_NAME].ptr,src[IDX_NAME].len,0); char buf[LE_TRACK_STRING_MAX+1]; StringCopySM(buf,sizeof(buf),src[IDX_STRING].ptr,src[IDX_STRING].len); ScanEscapedStringPipe(buf,sizeof(buf),buf,0); PRINT0(">>>> %s = %s\n",GetNameLSP(&spar),buf); const int dmode = str2l(src[IDX_D_MODE].ptr,0,10); if ( dmode == 2 ) spar.use_d = true; SetTextLT(lt,&spar,buf); } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ScanSha1LD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' const void * data, // data to scan uint data_size // size of 'data' ) { ScanText_t st; SetupScanText(&st,data,data_size); ScanSha1STLD(ld,init_ld,&st); ResetScanText(&st); } /////////////////////////////////////////////////////////////////////////////// void ScanSha1STLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' ScanText_t *st // initialized source ) { if ( !ld || !st ) return; if ( init_ld || !ld->is_initialized ) InitializeLD(ld); while (NextLineScanText(st)) ScanSha1LineLD(ld,st->line,st->eol,true); } /////////////////////////////////////////////////////////////////////////////// bool ScanSha1LineLD ( le_distrib_t *ld, // valid structure ccp src, // NULL or start of line ccp end, // end of line bool ident_only // TRUE: SetIdent*LT() only ) { if ( !ld || !src || *src == '@' || !end ) return false; bool is_arena = false; bool is_d = false; uint tslot = -1; uint cup = 0; int pi, le_flags = -1; char buf[300]; int valid_sha1 = false; sha1_size_hash_t bin; enum { P_TYPE, P_FLAGS, P_LEFLAGS, P_SHA1, P_TSLOT, P_CUP, P_NAME }; for ( pi = P_TYPE; pi <= P_NAME; pi++ ) { while ( src < end && (uchar)*src <= ' ' ) src++; ccp par = src; while ( src < end && (uchar)*src > ' ' ) src++; switch (pi) { case P_TYPE: PRINT0("TYPE: %.*s\n",(int)(src-par),par); if ( src - par != 2 ) return false; if (!strncasecmp(par,"bt",2)) is_arena = true; else if (strncasecmp(par,"vs",2)) return false; break; case P_FLAGS: PRINT0("FLAGS: %.*s\n",(int)(src-par),par); StringCopySM(buf,sizeof(buf),par,src-par); is_d = strchr(buf,'d') != 0; break; case P_LEFLAGS: if ( src - par < 40 ) { PRINT0("LEFLAGS: %.*s\n",(int)(src-par),par); StringCopySM(buf,sizeof(buf),par,src-par); le_flags = ScanLEFL(buf); break; } pi++; // fall through case P_SHA1: PRINT0("SHA1: %.*s\n",(int)(src-par),par); valid_sha1 = IsSSChecksum(&bin,par,src-par); break; case P_TSLOT: PRINT0("TSLOT: %.*s\n",(int)(src-par),par); { char *scan_end; tslot = str2ul(par,&scan_end,10); if ( tslot > MKW_MAX_TRACK_SLOT || scan_end != src ) return false; } break; case P_CUP: PRINT0("CUP: %.*s\n",(int)(src-par),par); { if ( *par == 'a' || *par == 'A' ) par++; if ( par < src ) { ccp pt = strchr(par,'.'); if ( pt && pt < src ) cup = str2ul(par,0,10) * 10 + str2ul(pt+1,0,10); } } break; case P_NAME: src = end-1; while ( src >= par && (uchar)*src <= ' ' ) src--; src++; PRINT0("XNAME: %.*s\n",(int)(src-par),par); StringCopySM(buf,sizeof(buf),par,src-par); break; } } if (!valid_sha1) return false; sha1_hex_t hex; Sha1Bin2Hex(hex,bin.hash); PRINT0(">>> arena=%d, d=%d, tslot=%4d, cup=%2d : %s : |%s|\n", is_arena, is_d, tslot, cup, hex, buf ); le_track_t *lt = DefineTrackLD(ld,tslot,true); if (!ident_only) { lt->track_type = is_arena ? LTTY_ARENA : LTTY_TRACK; lt->cup_slot = cup; if ( le_flags >= 0 ) lt->flags = le_flags; SetXNameLT(lt,0,buf); } SetIdentOptLT(lt,hex,is_d,true,0); return true; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: scan ledef /////////////// /////////////////////////////////////////////////////////////////////////////// static const KeywordTab_t * scan_cmd ( ScanInfo_t *si, // valid structure const KeywordTab_t *commands, // command table ccp section, // name of section enumError *max_err // NULL or max_err variable ) { DASSERT(si); DASSERT(commands); char buf[100]; if (!ScanNameSI(si,buf,sizeof(buf),true,true,0)) { if (!si->no_warn) { ccp eol = FindNextLineFeedSI(si,true); ScanFile_t *sf = si->cur_file; DASSERT(sf); ERROR0(ERR_WARNING, "File %s, section [%s], line #%u:\nMissing command: %.*s\n", sf->name, section, sf->line, (int)(eol - sf->prev_ptr), sf->prev_ptr ); } if ( max_err && *max_err < ERR_WARNING ) *max_err = ERR_WARNING; GotoEolSI(si); return 0; } int abbrev_count; const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,buf,commands); if (!cmd) { if (!si->no_warn) { ScanFile_t *sf = si->cur_file; ccp msg = GetKeywordError(commands,buf,abbrev_count,0); ERROR0(ERR_WARNING, "File %s, section [%s], line #%u:\n%s\n", sf->name, section, sf->line, msg ); } if ( max_err && *max_err < ERR_WARNING ) *max_err = ERR_WARNING; GotoEolSI(si); return 0; } return cmd; } /////////////////////////////////////////////////////////////////////////////// static enumError scan_track ( le_track_t *lt, ScanInfo_t *si ) { if ( !lt || !si || !si->ld ) return ERR_MISSING_PARAM; le_distrib_t *ld = si->ld; memset(lt,0,sizeof(*lt)); lt->track_status = LTS_EXPORT; enumError err; ScanIndexSI(si,&err,0,0,ld->is_pass2?0:2); if (err) return err; uint uval; err = ScanUIntValueSI(si,&uval); if (err) return err; lt->track_type = uval; err = ScanUIntValueSI(si,&uval); if (err) return err; lt->property = uval; err = ScanUIntValueSI(si,&uval); if (err) return err; lt->music = NormalizeMusicID(uval); DEFINE_VAR(var); err = ScanValueSI(si,&var); if (!err) lt->flags = var.mode == VAR_STRING ? ScanLEFL(var.str) : GetIntV(&var); ResetV(&var); PRINT0("TRACK %s: t=%d, p=%d, m=%d, f:%x\n", last_index_name, lt->track_type, lt->property, lt->music, lt->flags ); if ( lt->track_type == LTTY_TRACK ) { if (!IsMkwTrack(lt->property)) { if (!si->no_warn) { ScanFile_t *sf = si->cur_file; ERROR0(ERR_WARNING, "File %s, section [TRACK-LIST], line #%u:\n" "Versus track with invalid property %d.\n", sf->name, sf->line, lt->property ); } return ERR_WARNING; } } else if ( lt->track_type == LTTY_ARENA ) { if (!IsMkwArena(lt->property)) { if (!si->no_warn) { ScanFile_t *sf = si->cur_file; ERROR0(ERR_WARNING, "File %s, section [TRACK-LIST], line #%u:\n" "Battle arena with invalid property %d.\n", sf->name, sf->line, lt->property ); } return ERR_WARNING; } } else { if (!si->no_warn) { ScanFile_t *sf = si->cur_file; ERROR0(ERR_WARNING, "File %s, section [TRACK-LIST], line #%u:\n" "Invalid track type (not bt, not vs).\n", sf->name, sf->line ); } return ERR_WARNING; } return err; } //----------------------------------------------------------------------------- static enumError ScanLeDefTRACKS ( le_distrib_t *ld, // valid structure ScanInfo_t *si, // valid structure int pass // 1|2 ) { if ( !ld || !si ) return ERR_MISSING_PARAM; enum { C_SLOT, C_IGNORE_SLOT, C_TRACK, C_LAPS, C_SPEED, C_IDENT, C_FILE, C_PATH, C_NAME, C_XNAME, C_LIST, C_STD_ARENAS, C_STD_VERSUS }; enum { O_CURRENT = 0x100, O_STRING = 0x200 }; static const KeywordTab_t commands[] = { { C_SLOT, "SLOT", 0, 0 }, { C_IGNORE_SLOT,"IGNORE-SLOT", "IGNORESLOT", 0 }, { C_TRACK, "TRACK", 0, 0 }, { C_LAPS, "LAPS", "LAP", O_CURRENT }, { C_SPEED, "SPEED", 0, O_CURRENT }, { C_IDENT, "IDENT", 0, O_STRING }, { C_FILE, "FILE", 0, O_STRING }, { C_PATH, "PATH", 0, O_STRING }, { C_NAME, "NAME", 0, O_STRING }, { C_XNAME, "XNAME", 0, O_STRING }, #if LE_STRING_LIST_ENABLED { C_LIST, "TEMP1", 0, O_STRING|LTT_TEMP1 }, { C_LIST, "TEMP2", 0, O_STRING|LTT_TEMP2 }, #endif { C_STD_ARENAS, "STANDARD-BATTLE-ARENAS", "STANDARDBATTLEARENAS", 0 }, { C_STD_VERSUS, "STANDARD-VERSUS-TRACKS", "STANDARDVERSUSTRACKS", 0 }, { C_STD_VERSUS, "STD_VERSUS", 0, 0 }, { 0,0,0,0 } }; s_nsec_t total_nsec = -GetTimerNSec(), get_track_nsec = 0; enumError max_err = ERR_OK; int next_slot = -1, ignore_slot = 0, count = 0; DEFINE_VAR(string_par); le_track_t *current_lt = 0; le_strpar_t spar = { .opt = ld->spar.opt }; for (;;) { if (IsSectionOrEotSI(si,true)) break; bool define_set = false; #if LE_STRING_SET_ENABLED char ch = NextCharSI(si,true); if ( ch == '[' ) { ccp eol = GetEolSI(si); if (eol) { ScanFile_t *sf = si->cur_file; DASSERT(sf); ccp beg = sf->ptr+1; ccp ptr = memchr(beg,']',eol-beg); if ( ptr && ptr < eol ) { SetKeyNameLSP(&spar,beg,ptr-beg); sf->ptr = ptr+1; PRINT0("[%s] = %.*s\n",buf,(int)(eol-sf->ptr),sf->ptr); define_set = true; } } } #endif const KeywordTab_t *cmd = 0; if (!define_set) { cmd = scan_cmd(si,commands,"TRACK-LIST",&max_err); if (!cmd) continue; } const le_options_t opt = cmd ? cmd->opt : O_CURRENT; if ( !current_lt && opt & (O_CURRENT|O_STRING) ) { if (!si->no_warn) { ccp eol = FindNextLineFeedSI(si,true); ScanFile_t *sf = si->cur_file; DASSERT(sf); ERROR0(ERR_WARNING, "File %s, section [TRACK-LIST], line #%u:\nNo current track: %.*s\n", sf->name, sf->line, (int)(eol - sf->prev_ptr), sf->prev_ptr ); } if ( max_err < ERR_WARNING ) max_err = ERR_WARNING; GotoEolSI(si); continue; } if ( opt & O_STRING || define_set ) { DASSERT(current_lt); enumError err = ScanStringSI(si,&string_par); if ( max_err < err ) max_err = err; #if LE_STRING_SET_ENABLED if (define_set) { SetSetLT(current_lt,&spar,string_par.str); continue; } #endif } ASSERT(cmd); switch(cmd->id) { case C_SLOT: ScanIntValueSI(si,&next_slot); PRINT0("NEXT-SLOT: %d\n",next_slot); CheckEolSI(si); continue; // skip 'next_slot=-1' case C_IGNORE_SLOT: ScanIntValueSI(si,&ignore_slot); break; case C_TRACK: { current_lt = 0; le_track_t temp; if (!scan_track(&temp,si)) { count++; get_track_nsec -= GetTimerNSec(); le_track_t *lt = next_slot >= 0 && !ignore_slot ? DefineTrackLD(ld,next_slot,true) // SLOT has highest priority : IsRandomLEFL(temp.flags) ? DefineGroupTrackLD(ld,temp.track_type,true) // append random tracks always : DefineFreeTrackLD(ld,temp.track_type,true); // fallback: search a free slot get_track_nsec += GetTimerNSec(); if (lt) { temp.track_slot = lt->track_slot; MoveLT(lt,&temp); if (*last_index_name) DefineIntVar(&si->gvar,last_index_name,lt->track_slot); current_lt = lt; } } ClearLT(&temp,0); } break; case C_LAPS: ASSERT(current_lt); { uint uval; if ( !ScanUIntValueSI(si,&uval) && uval > 0 && uval < 10 ) current_lt->lap_count = uval; } break; case C_SPEED: ASSERT(current_lt); { double dval; if ( !ScanDValueSI(si,&dval) && dval > 0.0 ) current_lt->speed_factor = dval; } break; case C_IDENT: SetIdentOptLT(current_lt,string_par.str,false,true,ld->spar.opt); break; case C_FILE: SetFileLT(current_lt,&spar,string_par.str); break; case C_PATH: SetPathLT(current_lt,&spar,string_par.str); break; case C_NAME: SetNameLT(current_lt,&spar,string_par.str); break; case C_XNAME: SetXNameLT(current_lt,&spar,string_par.str); break; #if LE_STRING_LIST_ENABLED case C_LIST: SetListLT(current_lt,&spar,string_par.str); break; #endif // LE_STRING_LIST_ENABLED case C_STD_ARENAS: SetupStandardArenasLD(ld,false); current_lt = 0; break; case C_STD_VERSUS: SetupStandardTracksLD(ld,false); current_lt = 0; break; } CheckEolSI(si); next_slot = -1; } ResetV(&string_par); if ( log_timing >= 1 ) { total_nsec += GetTimerNSec(); fprintf(stdlog, "%sLE-DEF: %d tracks scanned in %s," " of which %s are searching free slots.\n", verbose >= 1 ? " > " : "", count, PrintTimerNSec6(0,0,total_nsec,0), PrintTimerNSec6(0,0,get_track_nsec,0) ); } return max_err; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void add_reflist_to_cup ( le_distrib_t *ld, const le_cup_ref_t *slot_list, int n_slots, le_track_type_t force_ltty ) { if ( !ld || !slot_list || n_slots < 1 ) return; while ( n_slots-- > 0 ) { le_cup_ref_t slot = *slot_list++; PRINT0("APPEND %d %s\n",slot,IsValidLecodeSlot(slot)?"":" INVALID!"); if (!IsValidLecodeSlot(slot)) continue; if (IsLecodeRandom(slot)) { if ( force_ltty == LTTY_ARENA ) AppendLECUP(&ld->cup_battle,slot); else AppendLECUP(&ld->cup_versus,slot); continue; } le_track_t *lt = GetTrackLD(ld,slot); if ( !IsVisibleLT(lt) ) continue; if ( lt->track_type & LTTY_TRACK && force_ltty != LTTY_ARENA ) { lt->user_flags |= LTTY_TRACK; AppendLECUP(&ld->cup_versus,slot); } if ( lt->track_type & LTTY_ARENA && force_ltty != LTTY_TRACK ) { lt->user_flags |= LTTY_ARENA; AppendLECUP(&ld->cup_battle,slot); } } } //----------------------------------------------------------------------------- static void append_to_cup ( ScanInfo_t *si, le_track_type_t ltty ) { if ( !si || !si->ld ) return; for(;;) { char ch = NextCharSI(si,false); if (!ch) break; int val; const enumError err = ScanIntValueSI(si,&val); if (err) return; le_cup_ref_t ref = val; add_reflist_to_cup(si->ld,&ref,1,ltty); SkipCharSI(si,','); } } //----------------------------------------------------------------------------- static void close_cup ( ScanInfo_t *si ) { if ( !si || !si->ld ) return; for(;;) { char ch = NextCharSI(si,false); if (!ch) break; int val; const enumError err = ScanIntValueSI(si,&val); if (err) return; if ( val == LTTY_ARENA ) CloseLECUP(&si->ld->cup_battle); else if ( val == LTTY_TRACK ) CloseLECUP(&si->ld->cup_versus); SkipCharSI(si,','); } } //----------------------------------------------------------------------------- static enumError ScanLeDefCUPS ( le_distrib_t *ld, // valid structure ScanInfo_t *si, // valid structure int pass // 1|2 ) { if ( !ld || !si ) return ERR_MISSING_PARAM; enum { C_APPEND, C_APPEND_BT, C_APPEND_VS, C_NEW_CUP, C_STD_CUPS, C_MKWFUN, C_UNUSED }; enum { O_BOOL_VAL = 1, O_BT = 2, O_VS = 4 }; static const KeywordTab_t commands[] = { { C_APPEND, "APPEND", 0, 0 }, { C_APPEND_BT, "APPEND-BT", "APPEND-BT", 0 }, { C_APPEND_VS, "APPEND-VS", "APPEND-VS", 0 }, { C_NEW_CUP, "NEW-CUP", "NEWCUP", 0 }, { C_STD_CUPS, "STANDARD-BATTLE-CUPS", "STANDARDBATTLECUPS", O_BT }, { C_STD_CUPS, "STANDARD-VERSUS-CUPS", "STANDARDVERSUSCUPS", O_VS }, { C_STD_CUPS, "STANDARD-CUPS", "STANDARDCUPS", O_BT|O_VS }, { C_MKWFUN, "MKWFUN-RANDOM-CUP", "MKWFUNRANDOMCUP", 0 }, { C_UNUSED, "ADD-UNUSED-ARENAS", "ADDUNUSEDARENAS", O_BOOL_VAL|O_BT }, { C_UNUSED, "ADD-UNUSED-TRACKS", "ADDUNUSEDTRACKS", O_BOOL_VAL|O_VS }, { C_UNUSED, "ADD-UNUSED", "ADDUNUSED", O_BOOL_VAL|O_BT|O_VS }, { 0,0,0,0 } }; enumError max_err = ERR_OK; bool bool_val = false; for (;;) { char ch = NextCharSI(si,true); if ( !ch || ch == '[' ) break; const KeywordTab_t *cmd = scan_cmd(si,commands,"TRACK-LIST",&max_err); if (!cmd) continue; if ( cmd->opt & O_BOOL_VAL ) { SkipCharSI(si,'='); int val; const enumError err = ScanIntValueSI(si,&val); if (!err) bool_val = val > 0; else if ( max_err < err ) max_err = err; } switch(cmd->id) { case C_APPEND: append_to_cup(si,0); break; case C_APPEND_BT: append_to_cup(si,LTTY_ARENA); break; case C_APPEND_VS: append_to_cup(si,LTTY_TRACK); break; case C_NEW_CUP: close_cup(si); break; case C_STD_CUPS: if (cmd->opt&O_BT) add_reflist_to_cup(ld,standard_bt_ref,MKW_N_ARENAS,LTTY_ARENA); if (cmd->opt&O_VS) add_reflist_to_cup(ld,standard_vs_ref,MKW_N_TRACKS,LTTY_TRACK); break; case C_MKWFUN: for ( int slot = MKW_LE_RANDOM_BEG; slot <= MKW_LE_RANDOM_END; slot++ ) DefineTrackLD(ld,slot,true); add_reflist_to_cup(ld,mkwfun_random_ref,MKW_N_LE_RANDOM,LTTY_TRACK); break; case C_UNUSED: if (cmd->opt&O_BT) ld->scan.bt.add_unused = bool_val; if (cmd->opt&O_VS) ld->scan.vs.add_unused = bool_val; break; } CheckEolSI(si); } #if 0 xBINGO; // keep HexDump16(stdout,0,0,ld->cup_versus.list, sizeof(*ld->cup_versus.list)*ld->cup_versus.used*ld->cup_versus.tracks); #endif return max_err; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// enumError ScanLeDefLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' const void * data, // data to scan uint data_size, // size of 'data' ccp fname, // NULL or filename for error messages CopyMode_t cm_fname // copy mode of 'fname' ) { ScanText_t st; SetupScanText(&st,data,data_size); SetFilenameScanText(&st,fname,cm_fname); const enumError err = ScanLeDefSTLD(ld,init_ld,&st); ResetScanText(&st); return err; } /////////////////////////////////////////////////////////////////////////////// enumError ScanLeDefSTLD ( le_distrib_t *ld, // valid structure bool init_ld, // TRUE: initialze 'ld' ScanText_t *st // initialized source ) { enum { SECT_TOOL, SECT_TRACKS, SECT_CUPS, SECT_END }; static const KeywordTab_t sections[] = { { SECT_TOOL, "TOOL", 0, 1 }, { SECT_TRACKS, "TRACK-LIST", "TRACKLIST", 1 }, { SECT_CUPS, "CUP-LIST", "CUPLIST", 2 }, { SECT_END, "END", 0, 0 }, { 0,0,0,0 } }; if ( !ld || !st ) return ERR_MISSING_PARAM; if ( init_ld || !ld->is_initialized ) InitializeLD(ld); if (!ld->second_ledef) { ld->second_ledef = true; ResetTracksAndCupsLD(ld); memset(&ld->scan,0,sizeof(ld->scan)); ld->scan.valid = true; ld->scan.bt.add_unused = true; ld->scan.vs.add_unused = true; ClearUserFlagsAndCupsLD(ld,false); } ScanInfo_t si; InitializeSI(&si,st->data,st->data_size,st->fname,0); si.predef = SetupVarsLECODE(); si.ld = ld; enumError max_err = ERR_OK; for ( int pass = 1; pass <= 2; pass++ ) { if ( pass > 1 ) RestartSI(&si); PRINT("----- pass %u\n",pass); max_err = ERR_OK; ld->is_pass2 = pass == 2; si.no_warn = pass == 1; si.total_err = 0; DefineIntVar(&si.gvar, "$PASS", pass ); for(;;) { char ch = NextCharSI(&si,true); if (!ch) break; if ( ch != '[' ) { NextLineSI(&si,true,false); continue; } ResetLocalVarsSI(&si,0); si.cur_file->ptr++; char name[40]; ScanNameSI(&si,name,sizeof(name),true,true,0); PRINT0("--> pass=%u: #%04u: %s\n",pass,si.cur_file->line,name); int abbrev_count; const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,sections); if ( !cmd || abbrev_count ) continue; NextLineSI(&si,false,false); PRINT0("Path %d, section %s\n",pass,cmd->name1); PRINT0("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); // check pass if ( cmd->opt == pass ) si.no_warn = false; else if ( cmd->opt ) continue; // wrong pass else si.no_warn = pass == 1; enumError err = ERR_OK; switch (cmd->id) { case SECT_TRACKS: err = ScanLeDefTRACKS(ld,&si,pass); break; case SECT_CUPS: err = ScanLeDefCUPS(ld,&si,pass); break; default: // ignore all other sections without any warnings break; } if ( max_err < err ) max_err = err; } #if HAVE_PRINT0 if ( logging >= 1 ) { printf("VAR DUMP/GLOBAL PASS %u:\n",pass); DumpVarMap(stdout,3,&si.gvar,false); } #endif } //--- terminate CheckLevelSI(&si); if ( max_err < ERR_WARNING && si.total_err ) max_err = ERR_WARNING; PRINT("ERR(ScanTextKMP) = %u (errcount=%u)\n", max_err, si.total_err ); ResetSI(&si); return max_err; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: cup helpers /////////////// /////////////////////////////////////////////////////////////////////////////// void DirtyCupLD ( le_distrib_t *ld, le_track_type_t ltty ) { if (ld) { if ( ltty & LTTY_ARENA ) ld->cup_battle.dirty = true; if ( ltty & LTTY_TRACK ) ld->cup_versus.dirty = true; } } /////////////////////////////////////////////////////////////////////////////// le_track_t * FindFillTrackLD ( le_distrib_t *ld, le_cup_t *lc, bool allow_candidate ) { if ( !ld || !lc ) return 0; le_track_type_t ltty = GetLttyLECUP(lc); le_track_t *fill = GetTrackLD(ld,lc->fill_slot); if ( !fill || fill->track_status != LTS_FILL || !(fill->track_type<ty) ) { fill = 0; le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used ; slot++, lt++ ) if ( lt->track_status >= LTS_FILL && lt->track_type & ltty ) { if ( lt->track_status == LTS_FILL ) { // we have already a fill track fill = lt; lc->fill_slot = fill->track_slot; break; } if ( allow_candidate && !fill ) fill = lt; } } return fill; } /////////////////////////////////////////////////////////////////////////////// bool PrintCupRefLD ( FILE *f, le_distrib_t *ld, le_cup_t *lc ) { if ( !f || !lc || !lc->used ) return false; le_track_t *fill = FindFillTrackLD(ld,lc,true); const int fill_slot = fill ? fill->track_slot : -999; ccp type = lc->tracks == BMG_BCUP_TRACKS ? "BT" : "VS"; #if 1 le_cup_ref_t *cup = lc->list; for ( int i = 0; i< lc->used; i++ ) { fprintf(f,"%s|%u|",type,i); for ( int nt = lc->tracks; nt > 0; nt--, cup++ ) fprintf(f,"%d|", *cup == fill_slot ? -1 : *cup ); fputs("\r\n",f); } return true; #else if ( lc->tracks == BMG_BCUP_TRACKS ) { le_cup_ref_t *cup = lc->list; for ( int i = 0; i< lc->used; i++, cup += BMG_BCUP_TRACKS ) fprintf(f,"bt|%u|%d|%d|%d|%d|%d|\r\n", i, cup[0], cup[1], cup[2], cup[3], cup[4] ); return true; } if ( lc->tracks == BMG_RCUP_TRACKS ) { le_cup_ref_t *cup = lc->list; for ( int i = 0; i< lc->used; i++, cup += BMG_RCUP_TRACKS ) fprintf(f,"vs|%u|%d|%d|%d|%d|\r\n", i, cup[0], cup[1], cup[2], cup[3] ); return true; } ASSERT(0); return false; #endif } /////////////////////////////////////////////////////////////////////////////// static void update_cup_helper ( le_distrib_t *ld, // valid distrib le_cup_t *lc, // cup to update le_track_type_t ltty, // LTTY_ARENA | LTTY_TRACK le_cup_ref_t p_fallback, // fall back if no track is found bool add_missed // true: add missed files (user_flag==0) ) { DASSERT(ld); DASSERT(lc); PRINT("update_cup_helper(%d,fb=%d,add=%d) #%u: used=%d, dirty=%d\n", lc->tracks, p_fallback, add_missed, __LINE__, lc->used, lc->dirty ); //--- remove invalid cup references & mark used le_cup_ref_t *end = lc->list + lc->used * lc->tracks; for ( le_cup_ref_t *ptr = lc->list; ptr < end; ptr++ ) { if ( *ptr >= 0 ) { le_track_t *lt = GetTrackLD(ld,*ptr); if ( IsVisibleLT(lt) && lt->track_type & ltty ) lt->user_flags |= ltty; else *ptr = -1; } } //--- append not used tracks if (add_missed) { le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) if ( IsVisibleLT(lt) && lt->track_type == ltty && !(lt->user_flags<ty) ) AppendLECUP(lc,slot); } //--- pack, even num of cups if (!lc->close_used) PackLECUP(lc); EvenLECUP(lc); //--- fill cups if (HaveInvalidLECUP(lc)) { le_strpar_t spar = { .opt = LEO__DEFAULT|LEO_IN_ALL }; le_track_t *fill = FindFillTrackLD(ld,lc,true); if (!fill) { fill = DefineFreeTrackLD(ld,ltty,true); SetupStandardTrackLT(fill,p_fallback); fill->track_status = LTS_FILL; lc->fill_slot = fill->track_slot; lc->fill_src = -1; } else if ( fill->track_status != LTS_FILL ) { const int fill_slot = fill->track_slot; le_track_t *next = DefineTrackLD(ld,ld->tlist_used,true); fill = ld->tlist + fill_slot; // maybe REALLOC() if (next) { next->track_status = LTS_FILL; next->property = fill->property; next->music = fill->music; lc->fill_slot = next->track_slot; lc->fill_src = fill->track_slot; SetFileLT(next,&spar,GetFileLT(fill,0)); SetIdentOptLT(next,GetIdentLT(fill,false,true,0),false,false,LEO_IN_ALL); SetIdentOptLT(next,GetIdentLT(fill,true, true,0),true, false,LEO_IN_ALL); fill = next; } } if (fill) { if ( fill->track_status == LTS_FILL ) { fill->flags = 0; fill->track_type = ltty; SetNameLT(fill,&spar,"-"); } FillLECUP(lc,fill->track_slot); } } //--- assign cup_slot's if ( lc->tracks == 4 ) DumpCupSlots(ld,"before assign"); const uint first_custom = lc->first_custom * lc->tracks; end = lc->list + lc->used * lc->tracks; for ( le_cup_ref_t *ptr = lc->list; ptr < end; ptr++ ) { if ( *ptr >= 0 ) { le_track_t *lt = GetTrackLD(ld,*ptr); if ( lt && lt->track_type & ltty ) { if ( ptr - lc->list < first_custom ) lt->flags |= G_LEFL_CUP|G_LEFL_ORIG_CUP; else lt->flags |= G_LEFL_CUP|G_LEFL_CUSTOM_CUP; if (!lt->cup_slot) { lt->cup_slot = GetCupSlotLECUP(lc,ptr); if ( lt->flags & G_LEFL_RND_HEAD ) { for ( int hslot = lt->track_slot+1; hslot < ld->tlist_used; hslot++ ) { le_track_t *hlt = GetTrackLD(ld,hslot); if ( IsActiveLT(hlt) && IsHiddenLEFL(hlt->flags) ) hlt->cup_slot = lt->cup_slot; else break; } } } } } } if ( lc->tracks == 4 ) DumpCupSlots(ld,"behind assign"); //--- terminate lc->dirty = false; PRINT("update_cup_helper(%d) #%u: used=%d, dirty=%d\n",lc->tracks,__LINE__,lc->used,lc->dirty); } //----------------------------------------------------------------------------- void UpdateCupsLD ( le_distrib_t *ld ) { if (!ld) return; CloseArchLD(ld); if ( ld->cup_battle.used && !ld->cup_battle.dirty && ld->cup_versus.used && !ld->cup_versus.dirty ) return; PRINT0("UpdateCupsLD() N=%d,%d, dirty=%d,%d\n", ld->cup_battle.used, ld->cup_versus.used, ld->cup_battle.dirty, ld->cup_versus.dirty ); s_nsec_t dur_nsec = -GetTimerNSec(); //--- clear user flags CheckTracksLD(ld); ClearUserFlagsAndCupsLD(ld,true); //--- battle cups if (!ld->cup_battle.used) { SetupStandardArenasLD(ld,true); add_reflist_to_cup(ld,standard_bt_ref,MKW_N_ARENAS,LTTY_ARENA); } update_cup_helper( ld, &ld->cup_battle, LTTY_ARENA, MKW_ARENA_BEG, !ld->scan.valid || ld->scan.bt.add_unused ); //--- versus cups if (!ld->cup_versus.used) { SetupStandardTracksLD(ld,true); add_reflist_to_cup(ld,standard_vs_ref,MKW_N_TRACKS,LTTY_TRACK); } update_cup_helper( ld, &ld->cup_versus, LTTY_TRACK, MKW_TRACK_BEG, !ld->scan.valid || ld->scan.vs.add_unused ); //--- terminate if ( log_timing >= 1 ) { dur_nsec += GetTimerNSec(); fprintf(stdlog,"%sUpdateCupsLD() N=%d,%d, done in %s\n", verbose >= 1 ? " > " : "", ld->cup_battle.used, ld->cup_versus.used, PrintTimerNSec6(0,0,dur_nsec,0) ); } } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: progress /////////////// /////////////////////////////////////////////////////////////////////////////// void ClearProgressLD ( le_distrib_t *ld ) { DASSERT(ld); ld->show_progress = 0; ld->progress_count = 0; ld->first_progress_msec = ld->last_progress_msec = GetTimerMSec(); } /////////////////////////////////////////////////////////////////////////////// void SetupProgressLD ( le_distrib_t *ld ) { DASSERT(ld); ClearProgressLD(ld); ld->show_progress = GetProgressMSecLEO(ld->spar.opt); PRINT0("ld.show_progress = %lld\n",ld->show_progress); } /////////////////////////////////////////////////////////////////////////////// static void ShowProgressTracksLD ( le_distrib_t *ld, bool force ) { DASSERT(ld); if ( ld->show_progress > 0 && ld->progress_count > 0 ) { const u_msec_t now = GetTimerMSec(); if ( now >= ld->last_progress_msec + ld->show_progress || force && now >= ld->last_progress_msec + 1000 ) { ld->last_progress_msec = now; fprintf(stdlog, "%s%u file%s scanned in %llu seconds.\n", verbose >= 1 ? " > " : " - ", ld->progress_count, ld->progress_count == 1 ? "" : "s", ( now - ld->first_progress_msec ) / 1000 ); fflush(stdlog); } } } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: manage archive /////////////// /////////////////////////////////////////////////////////////////////////////// // [[track-arch]] void ResetLTA ( le_track_arch_t *ta, bool reset_lt ) { if (ta) { if (reset_lt) ResetLT(&ta->lt); FreeString(ta->name_order); FreeString(ta->version); memset(ta,0,sizeof(*ta)); } } /////////////////////////////////////////////////////////////////////////////// void ResetArchLD ( le_distrib_t *ld ) { if (ld) { le_track_arch_t *ta = ld->arch; le_track_arch_t *end = ta + ld->arch_used; for ( ; ta < end; ta++ ) ResetLTA(ta,true); ld->arch = 0; ld->arch_used = 0; ld->arch_size = 0; exmem_key_t *ek = ld->group.list; exmem_key_t *gend = ek + ld->group.used; for ( ; ek < gend; ek++ ) { le_group_info_t *gi = (le_group_info_t*)ek->data.data.ptr; FREE(gi->list); } ResetEML(&ld->group); } } /////////////////////////////////////////////////////////////////////////////// static void AddToGroup ( le_distrib_t *ld, le_track_arch_t *ta, mem_t grp ) { if ( !ld || !ta || !grp.len ) return; mem_t pre = { .ptr = ta->lt.track_type == LTTY_ARENA ? "A" : "T", .len = 1 }; mem_t key = MemCat2A(pre,grp); PRINT0("+GRP[%.*s] += %d\n",key.len,key.ptr,ta->lt.track_slot); exmem_key_t *ek = FindInsertEML(&ld->group,key.ptr,CPM_MOVE,0); if (!ek->data.data.ptr) { ek->data.data.ptr = CALLOC(1,sizeof(le_group_info_t)); ek->data.data.len = sizeof(le_group_info_t); ek->data.is_alloced = true; le_group_info_t *gi = (le_group_info_t*)ek->data.data.ptr; gi->group = ld->group.used; } le_group_info_t *gi = (le_group_info_t*)ek->data.data.ptr; ta->group = gi->group; #if 0 char xbuf[30]; snprintf(xbuf,sizeof(xbuf),"%d %s",ta->group,ek->key); SetByKeyLT(&ta->lt,"grp",xbuf); #endif //-- append index to list; if ( ta->lt.flags & G_LEFL_RND_HEAD && !gi->n_head++ ) gi->head1 = ta->lt.track_slot; if ( ta->lt.flags & G_LEFL_RND_GROUP ) gi->n_group++; if ( gi->used >= gi->size ) { gi->size = gi->used + 10; gi->list = REALLOC( gi->list, gi->size * sizeof(*gi->list) ); } ASSERT( gi->used < gi->size ); gi->list[gi->used++] = ta->lt.track_slot; } /////////////////////////////////////////////////////////////////////////////// le_track_arch_t * GetNextArchLD ( le_distrib_t *ld ) { if (!ld) return 0; if ( ld->arch_used >= ld->arch_size ) { static const int tab[] = { 100, 200, 500, 100, 2000, LE_MAX_TRACKS, 0 }; uint need = ld->arch_used + 1; for ( const int *ptr = tab; *ptr; ptr++ ) if ( need <= *ptr ) { need = *ptr; break; } ld->arch = REALLOC( ld->arch, need * sizeof(*ld->arch) ); memset( ld->arch + ld->arch_size, 0, ( need - ld->arch_size ) * sizeof(*ld->arch) ); ld->arch_size = need; } DASSERT( ld->arch_used < ld->tlist_size ); le_track_arch_t *ta = ld->arch + ld->arch_used; memset(ta,0,sizeof(*ta)); InitializeLT(&ta->lt); ta->lt.track_slot = ld->arch_used++; // index of 'arch' ta->lt.track_status = LTS_EXPORT; ta->force_slot = -1; ta->attr_order = 1000; ta->plus_order = 10000000; ta->game_order = 10000000; return ta; } // /////////////////////////////////////////////////////////////////////////////// enumError AddToArchLD ( le_distrib_t *ld, raw_data_t *raw ) { if ( !ld || !raw ) return ERR_MISSING_PARAM; ShowProgressTracksLD(ld,false); ld->progress_count++; char buf[LE_TRACK_STRING_MAX+1], fname_buf[LE_TRACK_STRING_MAX+1]; exmem_dest_t exdest = { .buf = buf, .buf_size = sizeof(buf), .try_circ = true }; // [[split-le-flags]] split_filename_t spf; AnalyseSPF(&spf,true,raw->fname,0,CPM_LINK,opt_plus); int force_slot = -1; if ( ld->spar.opt & (LEO_HEX2SLOT|LEO_NAME2SLOT) ) { if (!spf.f_d.len) { if ( ld->spar.opt & LEO_HEX2SLOT && ( spf.f_name.len == 3 || spf.f_name.len == 4 )) { const uint slot = str2ul(spf.f_name.ptr,0,16); snprintf(fname_buf,sizeof(fname_buf),"%03x",slot); if (!strncasecmp(spf.f_name.ptr,fname_buf,3)) { PRINT0("HEX: %.3s\n",spf.f_name.ptr); force_slot = slot; } else { snprintf(fname_buf,sizeof(fname_buf),"%04x",slot); if (!strncasecmp(spf.f_name.ptr,fname_buf,4)) { PRINT0("HEX: %.4s\n",spf.f_name.ptr); force_slot = slot; } } } if ( ld->spar.opt & LEO_NAME2SLOT ) { StringCopySM(fname_buf,sizeof(fname_buf),spf.f_name.ptr,spf.f_name.len); const int slot = FindTrackSlot(fname_buf,true); if ( slot >= 0 ) { PRINT0("NAME: 0x%02x = %s\n",slot,fname_buf); force_slot = slot; } } } if ( !IsUsableXcodeSlot(force_slot,ld->spar.opt&LEO_CT_SLOTS) ) { ResetSPF(&spf); return ERR_JOB_IGNORED; } } szs_file_t szs; AssignSZS(&szs,true,raw->data,raw->data_size,false,raw->fform,raw->fname); analyze_szs_t as; AnalyzeSZS(&as,true,&szs,raw->fname); PRINT0("SHA1: %s, slots: r=%d a=%d m=%d\n", as.sha1_szs, as.slotinfo.race_slot, as.slotinfo.arena_slot, as.slotinfo.music_index ); enumError err = ERR_WARNING; if ( as.valid_track && !spf.f_d.len ) { ccp slash = strrchr(raw->fname,'/'); if (slash) ScanOptTrackSource(raw->fname,slash-raw->fname,opt_szs_mode); else ScanOptTrackSource(".",1,opt_szs_mode); err = ERR_OK; le_track_arch_t *ta = GetNextArchLD(ld); ta->lt.lap_count = as.lap_count; ta->lt.speed_factor = as.speed_factor; ta->force_slot = force_slot; if (szs.is_arena) { ta->lt.track_type = LTTY_ARENA; ta->lt.property = MKW_ARENA_BEG; if (as.slotinfo.arena_slot) { int slot = as.slotinfo.arena_slot - 11; slot = slot/10*5 + slot%10; if ( (uint)slot < MKW_N_ARENAS ) ta->lt.property = arena_pos_default[slot] + MKW_ARENA_BEG; } } else { ta->lt.track_type = LTTY_TRACK; ta->lt.property = MKW_TRACK_BEG; if (as.slotinfo.race_slot) { int slot = as.slotinfo.race_slot - 11; slot = slot/10*4 + slot%10; if ( (uint)slot < MKW_N_TRACKS ) ta->lt.property = track_pos_default[slot]; } } ta->lt.music = as.slotinfo.music_index ? as.slotinfo.music_index : NormalizeMusicID(ta->lt.property); // [[split-le-flags]] if (spf.plus.len) ta->lt.flags |= G_LEFL_NEW; //-- setup basic strings mem_t space = { .ptr = " ", .len = 1 }; le_strpar_t spar = { .opt = LEO__DEFAULT | LEO_IN_ALL | LEO_OUT_ALL }; SetIdentLT(&ta->lt,&spar,as.sha1_szs); StringCopySM(buf,sizeof(buf),spf.f_name.ptr,spf.f_name.len); SetFileLT(&ta->lt,&spar,buf); SetPathLT(&ta->lt,&spar,raw->fname); mem_t list[] = { spf.boost, spf.game, spf.name, spf.extra }; mem_src_t msrc = { .src = list, .n_src = sizeof(list)/sizeof(*list) }; exmem_t exsum = ExMemCat(&exdest,space,&msrc); SetNameLT(&ta->lt,&spar,exsum.data.ptr); FreeExMem(&exsum); StringCopySM(buf,sizeof(buf),spf.norm.ptr,spf.norm.len); SetXNameLT(&ta->lt,&spar,buf); //-- setup arch extensions mem_t sum = MemCatSep3A(space,spf.name,spf.version,spf.extra); ta->name_order = sum.ptr; ta->version = MEMDUP(spf.version.ptr,spf.version.len); //-- setup by spf ta->lt.flags = spf.le_flags; ta->attr_order = spf.attrib_order; ta->plus_order = spf.plus_order; ta->game_order = spf.game_order; // ta->game_color = spf.game1_color; ta->lt.distrib_flags = spf.distrib_flags | G_DTA_F_CALCULATED; if (spf.le_group.len) AddToGroup(ld,ta,spf.le_group); //-- clean PRINT0("order=%d,%d,%d, grp=%d \"%.*s\"\n", ta->attr_order, ta->plus_order, ta->game_order, ta->group, grp.len, grp.ptr ); } //-- clean & return ResetSPF(&spf); ResetAnalyzeSZS(&as); ResetSZS(&szs); return err; } // setfilelt ->file // lti /////////////////////////////////////////////////////////////////////////////// static int cmp_order ( const le_track_arch_t **pa, const le_track_arch_t **pb ) { const le_track_arch_t *a = *pa; const le_track_arch_t *b = *pb; ASSERT(a&&b); int res = ( a->lt.track_type & LTTY_TRACK ) - ( b->lt.track_type & LTTY_TRACK ); if (!res) { res = a->attr_order - b->attr_order; if (!res) { res = a->plus_order - b->plus_order; if (!res) { res = strcasecmp(a->name_order,b->name_order); if (!res) { res = a->game_order - b->game_order; if (!res) res = (int)a->lt.track_slot - (int)b->lt.track_slot; } } } } return res; } //----------------------------------------------------------------------------- bool CloseArchLD ( le_distrib_t *ld ) { if ( !ld || !ld->arch ) return false; //-- manage groups exmem_key_t *ek = ld->group.list; exmem_key_t *gend = ek + ld->group.used; for ( ; ek < gend; ek++ ) { le_group_info_t *gi = (le_group_info_t*)ek->data.data.ptr; const bool clear_group = !gi->n_group || gi->n_group == 1 && !gi->n_head; if ( !clear_group && !gi->n_head && gi->used ) { // first member becomes head gi->n_head++; gi->head1 = *gi->list; } for ( int i = 0; i < gi->used; i++ ) { int index = gi->list[i]; PRINT0("A: %zd/%d : %d/%d/%d : %d/%d/%d\n", ek-ld->group.list, ld->group.used, i, gi->used, gi->size, index, ld->arch_used, ld->arch_size ); ASSERT( index < ld->arch_used ); le_track_arch_t *ta = ld->arch + index; if (clear_group) { if ( ta->lt.flags & G_LEFL_RND_GROUP ) { ta->lt.flags &= ~G_LEFL__RND; ta->group = 0; } else ta->lt.track_status = LTS_VALID; // deactivate track } else if ( index == gi->head1 ) ta->lt.flags |= G_LEFL_RND_HEAD; else if ( ta->lt.flags & G_LEFL_RND_GROUP ) ta->lt.flags &= ~G_LEFL_RND_HEAD; else ta->lt.track_status = LTS_VALID; // deactivate track } } //--- [[2do]] order le_track_arch_t **order = MALLOC( (ld->arch_used+1) * sizeof(*order) ); le_track_arch_t **ptr = order; le_track_arch_t *ta = ld->arch; le_track_arch_t *end = ta + ld->arch_used; for ( ; ta < end; ta++ ) if ( ta->lt.track_status >= LTS_EXPORT && !IsHiddenLEFL(ta->lt.flags) ) *ptr++ = ta; *ptr = 0; int n_order = ptr - order; if ( n_order > 1 ) qsort( order, n_order, sizeof(*order), (qsort_func)cmp_order ); //--- assign tracks for ( ptr = order; *ptr; ptr++ ) { le_track_arch_t *ta = *ptr; le_track_t *lt = ta->force_slot >= 0 ? DefineTrackLD(ld,ta->force_slot,false) : IsRandomLEFL(ta->lt.flags) ? DefineGroupTrackLD(ld,ta->lt.track_type,true) : DefineFreeTrackLD(ld,ta->lt.track_type,true); if (!lt) continue; ta->lt.track_slot = lt->track_slot; ResetLT(lt); *lt = ta->lt; if (ta->group) { for ( exmem_key_t *ek = ld->group.list; ek < gend; ek++ ) { le_group_info_t *gi = (le_group_info_t*)ek->data.data.ptr; if ( gi->group == ta->group ) { for ( int i = 0; i < gi->used; i++ ) { int index = gi->list[i]; PRINT0("B: %zd/%d : %d/%d/%d : %d/%d/%d\n", ek-ld->group.list, ld->group.used, i, gi->used, gi->size, index, ld->arch_used, ld->arch_size ); ASSERT( index < ld->arch_used ); le_track_arch_t *ta2 = ld->arch + index; if ( ta2 != ta && IsHiddenLEFL(ta2->lt.flags) ) { le_track_t *lt2 = DefineFreeTrackLD(ld,0,true); ta2->lt.track_slot = lt2->track_slot; *lt2 = ta2->lt; ResetLTA(ta2,false); } } break; } } } ResetLTA(ta,false); } ld->cup_battle.dirty = true; ld->cup_versus.dirty = true; FREE(order); ResetArchLD(ld); return true; } //----------------------------------------------------------------------------- bool CloseArchLogLD ( le_distrib_t *ld ) { if (IsActiveArchLD(ld)) { const u_nsec_t start_nsec = GetTimerNSec(); if ( verbose >= 1 ) PrintDistribHead("Close Track Archive"); if ( CloseArchLD(ld) && verbose >= 2 ) { const u_nsec_t dur = GetTimerNSec() - start_nsec; fprintf(stdlog,"%s >> Track archive closed in %s%s\n", collog->status, PrintTimerNSec6(0,0,dur,0), collog->reset ); } return true; } return false; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: import /////////////// /////////////////////////////////////////////////////////////////////////////// #if 0 void ImportLD ( le_distrib_t *ld, const le_distrib_t *src ) { // [[disabled]] ??? } #endif /////////////////////////////////////////////////////////////////////////////// void SetupLparLD ( le_distrib_t *ld, bool load_lpar ) { if ( !ld || ld->lpar ) return; ld->lpar = MALLOC(sizeof(*ld->lpar)); InitializeLPAR(ld->lpar,load_lpar); PatchLPAR(ld->lpar); ld->lpar->limit_mode = LPM_FORCE_AUTOMATIC; } /////////////////////////////////////////////////////////////////////////////// void ImportLparLD ( le_distrib_t *ld, const le_lpar_t *lpar ) { if ( !ld || !lpar ) return; SetupLparLD(ld,false); ASSERT(ld->lpar); memcpy(ld->lpar,lpar,sizeof(*ld->lpar)); PatchLPAR(ld->lpar); ld->lpar->limit_mode = LPM_FORCE_AUTOMATIC; } /////////////////////////////////////////////////////////////////////////////// void ImportAnaLD ( le_distrib_t *ld, const le_analyze_t *ana ) { if ( !ld || !ana || !ana->valid ) return; PRINT0("ImportAnaLD() nslot=%d, ncup=%d+%d\n",ana->n_slot,ana->n_cup_arena,ana->n_cup_track); //--- import lecode binary + lpar if ( ld->spar.opt & LEO_IN_LECODE ) AssignLecodeLD(ld,GetLERegion(ana),ana->data,ana->data_size); ImportLparLD(ld,&ana->lpar); //--- import tracks DefineTrackLD(ld,ana->n_slot-1,false); // alloc reference list of good size for ( int slot = 0; slot < ana->n_slot; slot++ ) { if ( slot == MKW_ARENA_END ) slot = LE_FIRST_CT_SLOT; if (!IsLESlotUsed(ana,slot)) continue; le_track_t *lt = DefineTrackLD(ld,slot,true); if (lt) { if (ana->property) lt->property = ana->property[slot]; if (ana->music) lt->music = ana->music[slot]; if (ana->flags) lt->flags = ana->flags[slot]; char name[30]; snprintf(name,sizeof(name), lecode_04x ? "_%04x" : "_%03x", slot ); SetNameLT(lt,0,name); } } //--- arena cups and slots if ( ana->n_cup_arena && ana->cup_arena ) { const uint *ref_src = ana->cup_arena; for ( int cup_idx = 0; cup_idx < ana->n_cup_arena; cup_idx++ ) { le_cup_ref_t *ref_dest = DefineLECUP(&ld->cup_battle,cup_idx); for ( int i = 0; i < BMG_BCUP_TRACKS; i++ ) { int slot = htonl( ana->cup_arena[ BMG_BCUP_TRACKS*cup_idx + i ] ); le_track_t *lt = DefineTrackLD(ld,slot,false); if ( lt && IsArenaLTTY(lt->track_type) ) { lt->cup_slot = 10*cup_idx + i + 11; if ( ref_dest && ref_src ) *ref_dest++ = ntohl(*ref_src++); } } } } //--- racing cups and slots if ( ana->n_cup_track && ana->cup_track ) { const uint *ref_src = ana->cup_track; for ( int cup_idx = 0; cup_idx < ana->n_cup_track; cup_idx++ ) { le_cup_ref_t *ref_dest = DefineLECUP(&ld->cup_versus,cup_idx); for ( int i = 0; i < BMG_RCUP_TRACKS; i++ ) { int slot = htonl( ana->cup_track[ BMG_RCUP_TRACKS*cup_idx + i ] ); le_track_t *lt = DefineTrackLD(ld,slot,false); if ( lt && IsTrackLTTY(lt->track_type) ) { lt->cup_slot = 10*cup_idx + i + 11; if ( ref_dest && ref_src ) *ref_dest++ = ntohl(*ref_src++); } } } } PackTracksLD(ld); } /////////////////////////////////////////////////////////////////////////////// void ImportCtcodeLD ( le_distrib_t *ld, const ctcode_t *ctcode ) { if ( !ld || !ctcode ) return; //--- setup lower slots with defaults for ( int slot = 0; slot < MKW_ARENA_END; slot++ ) { le_track_t *lt = DefineTrackLD(ld,slot,true); lt->property = slot; lt->music = GetMkwMusicSlot(slot); } DefineExtraTracksLD(ld); DefineRandomTracksLD(ld); //--- copy track properties le_strpar_t spar = { .opt = LEO__DEFAULT|LEO_IN_ALL }; const ctcode_crs1_data_t *td = ctcode->crs->data; if (td) { const int max = ctcode->n_tracks; for ( int slot = 0; slot < max; slot++, td++ ) { if ( slot == MKW_ARENA_END ) { slot = LE_FIRST_CT_SLOT; td = ctcode->crs->data + slot; } le_track_t *lt = DefineTrackLD(ld,slot,true); if (lt) { if (IsMkwArena(slot)) { const u8 prop = ctcode->arena.prop[slot-MKW_ARENA_BEG]; if (prop) // invalid if 0 { lt->property = prop; lt->music = ctcode->arena.music[slot-MKW_ARENA_BEG]; } } else { lt->property = ntohl(td->property_id); lt->music = ntohl(td->music_id); lt->flags = ctcode->le_flags[slot]; } if (IsMkwArena(lt->property)) lt->track_type = LTTY_ARENA; char buf[200]; const int str_idx = ctcode->ctb.track_name1.beg + slot; const bmg_item_t *bi; bi = FindItemBMG(&ctcode->track_ident,str_idx); if ( bi && bi->text ) { PrintString16BMG(buf,sizeof(buf),bi->text,bi->len,BMG_UTF8_MAX,0,1); SetIdentLT(lt,&spar,buf); } bi = FindItemBMG(&ctcode->track_file,str_idx); if ( bi && bi->text ) { PrintString16BMG(buf,sizeof(buf),bi->text,bi->len,BMG_UTF8_MAX,0,1); SetFileLT(lt,&spar,buf); } bi = FindItemBMG(&ctcode->track_string,str_idx); if ( bi && bi->text ) { PrintString16BMG(buf,sizeof(buf),bi->text,bi->len,BMG_UTF8_MAX,0,1); SetNameLT(lt,&spar,buf); } bi = FindItemBMG(&ctcode->track_xstring,str_idx); if ( bi && bi->text ) { PrintString16BMG(buf,sizeof(buf),bi->text,bi->len,BMG_UTF8_MAX,0,1); SetXNameLT(lt,&spar,buf); } } } } //--- battle cups and tracks const ctcode_cup1_data_t *cup; cup = ctcode->cup_battle; if (cup) { uint max = ctcode->n_battle_cups; if ( max > ld->cup_battle.max ) max = ld->cup_battle.max; for ( int cup_idx = 0; cup_idx < max; cup_idx++, cup++ ) { le_cup_ref_t *dest = DefineLECUP(&ld->cup_battle,cup_idx); for ( int i = 0; i < BMG_BCUP_TRACKS; i++ ) { const int slot = ntohl(cup->track_id[i]); le_track_t *lt = DefineTrackLD(ld,slot,false); if ( lt && IsArenaLTTY(lt->track_type) ) { lt->cup_slot = 10*cup_idx + i + 11; if (dest) *dest++ = slot; } } } } //--- racing cups and tracks cup = ctcode->cup_racing; if (cup) { uint max = ctcode->n_racing_cups; if ( max > ld->cup_versus.max ) max = ld->cup_versus.max; for ( int cup_idx = 0; cup_idx < max; cup_idx++, cup++ ) { le_cup_ref_t *dest = DefineLECUP(&ld->cup_versus,cup_idx); for ( int i = 0; i < BMG_RCUP_TRACKS; i++ ) { const int slot = ntohl(cup->track_id[i]); le_track_t *lt = DefineTrackLD(ld,slot,false); if ( lt && IsTrackLTTY(lt->track_type) ) { lt->cup_slot = 10*cup_idx + i + 11; if (dest) *dest++ = slot; } } } } ld->ana.bt.add_unused = false; ld->ana.vs.add_unused = false; ld->scan.valid = true; ld->scan.bt.add_unused = false; ld->scan.vs.add_unused = false; #if 0 // [[DEBUG]] for ( int slot = 68; slot < 76; slot++ ) { le_track_t *lt = DefineTrackLD(ld,slot,false); if (lt) { printf("%2d %3d %x %x %2d %04x %3d %s\n", lt->track_slot, lt->cup_slot, lt->track_status, lt->track_type, lt->property, lt->music, lt->flags, lt->name ); } } #endif #if 0 xBINGO; // keep HexDump16(stdout,0,0,ld->cup_versus.list, sizeof(*ld->cup_versus.list)*ld->cup_versus.used*ld->cup_versus.tracks); #endif } /////////////////////////////////////////////////////////////////////////////// void ImportBmgLD ( le_distrib_t *ld, const bmg_t *bmg, const le_strpar_t *par ) { if ( !ld || !bmg ) return; if (!par) par = &ld->spar; char buf[LE_TRACK_STRING_MAX+1]; for ( int slot = 0; slot < ld->tlist_used; slot++ ) { le_track_t *lt = GetTrackLD(ld,slot); if ( lt && GetBmgBySlotLEO(buf,sizeof(buf),bmg,slot,ld->spar.opt) ) SetTextLT(lt,par,buf); } } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: import files and options /////////////// /////////////////////////////////////////////////////////////////////////////// enumError ImportRawDataLD ( le_distrib_t *ld, // destination raw_data_t *raw // source ) { enumError err = ERR_MISSING_PARAM; if ( ld && raw ) { if (!ld->is_initialized) InitializeLD(ld); if ( raw->fform != FF_U8 && raw->fform != FF_WU8 ) CloseArchLogLD(ld); if ( logging >= 2 && raw->fname ) fprintf(stdlog,"Import data to LD: %s\n",raw->fname); err = ERR_OK; switch(raw->fform) { case FF_LEDEF: err = ScanLeDefLD(ld,false,raw->data,raw->data_size,raw->fname,CPM_LINK); break; case FF_LEDIS: ScanDumpLD(ld,false,raw->data,raw->data_size); break; case FF_LEREF: ScanRefLD(ld,false,raw->data,raw->data_size); break; case FF_LESTR: ScanStrLD(ld,false,raw->data,raw->data_size); break; case FF_SHA1REF: ScanSha1LD(ld,false,raw->data,raw->data_size); break; case FF_LE_BIN: { le_analyze_t ana; err = AnalyzeLEBinary(&ana,raw->data,raw->data_size); if (!err) ImportAnaLD(ld,&ana); ResetLEAnalyze(&ana); } break; case FF_PREFIX: DefinePrefixTable(raw->data,raw->data_size); break; case FF_MTCAT: DefineCategoryList(raw->data,raw->data_size); break; case FF_CT_SHA1: DefineSha1Ref(raw->data,raw->data_size); break; case FF_LPAR: { le_lpar_t lpar; InitializeLPAR(&lpar,true); err = ScanTextLPAR(&lpar,false,raw->fname,raw->data,raw->data_size); if (!err) ImportLparLD(ld,&lpar); ResetLPAR(&lpar); } break; case FF_BMG: case FF_BMG_TXT: { bmg_t bmg; err = ScanBMG(&bmg,true,raw->fname,raw->data,raw->data_size); if (!err) ImportBmgLD(ld,&bmg,&ld->spar); ResetBMG(&bmg); } break; case FF_DISTRIB: err = ImportDistribLD(ld,raw->data,raw->data_size); break; case FF_U8: case FF_WU8: err = AddToArchLD(ld,raw); break; case FF_BRRES: case FF_TEX_CT: case FF_CT1_DATA: case FF_CTDEF: if (!ld->ctcode) ld->ctcode = MALLOC(sizeof(*ld->ctcode)); InitializeCTCODE(ld->ctcode,CTM_LECODE2); err = ScanRawDataCTCODE(ld->ctcode,CTM_NO_INIT,raw,0); if (!err) ImportCtcodeLD(ld,ld->ctcode); ResetCTCODE(ld->ctcode); break; case FF_UNKNOWN: err = ERROR0(ERR_INVALID_DATA, "Need LE-CODE compatible file, but have unknown file format: %s\n", raw->fname); break; //case FF_FTA: default: err = ERROR0(ERR_INVALID_DATA, "Need LE-CODE compatible file, but file format is %s: %s", GetNameFF(raw->fform_file,raw->fform),raw->fname); } } return err; } /////////////////////////////////////////////////////////////////////////////// struct search_t { le_distrib_t *ld; int count; enumError max_err; }; //----------------------------------------------------------------------------- static enumError search_func ( mem_t path, // full path of existing file, never NULL uint st_mode, // copy of struct stat.st_mode, see "man 2 stat" void *param // user defined parameter ) { if (!S_ISREG(st_mode)) return ERR_JOB_IGNORED; struct search_t *s = (struct search_t*)param; DASSERT(s); DASSERT(s->ld); raw_data_t raw; enumError err = LoadRawData(&raw,true,path.ptr,0,opt_ignore>0,0); if (!err) err = ImportRawDataLD(s->ld,&raw); ResetRawData(&raw); s->count++; if ( s->max_err < err ) s->max_err = err; return err; } //----------------------------------------------------------------------------- enumError ImportFileLD ( le_distrib_t *ld, // destination ccp fname, // file name of source bool use_wildcard, // true & file not exist: search files bool support_options // support options (leading '+' and no slash ) ) { PRINT0("ImportFileLD(,%s,%d,%d)\n",fname,use_wildcard,support_options); enumError err = ERR_MISSING_PARAM; if ( ld && fname ) { if (!ld->is_initialized) InitializeLD(ld); if ( support_options && *fname == '+' ) ld->spar.opt = ScanLEO(ld->spar.opt,fname+1,"Filter option"); else { struct stat st; if ( use_wildcard && HaveWildcards(MemByString(fname)) && stat(fname,&st) ) { SetupProgressLD(ld); PRINT0(" > SEARCH %s\n",fname); struct search_t param = { .ld = ld }; SearchPaths(fname,0,WM__DEFAULT,search_func,¶m); ShowProgressTracksLD(ld,true); ClearProgressLD(ld); return param.count ? param.max_err : ERROR0(ERR_CANT_OPEN,"File not found: %s\n",fname); } raw_data_t raw; err = LoadRawData(&raw,true,fname,0,opt_ignore>0,0); if (!err) err = ImportRawDataLD(ld,&raw); ResetRawData(&raw); } } return err; } /////////////////////////////////////////////////////////////////////////////// void ImportOptionsLD ( le_distrib_t *ld ) { if (ld) { if (!ld->is_initialized) InitializeLD(ld); ccp *ptr = le_define_list.field, *end; for ( end = ptr + le_define_list.used; ptr < end; ptr++ ) ImportFileLD(ld,*ptr,true,false); } } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: export /////////////// /////////////////////////////////////////////////////////////////////////////// bool ExportAnaLD ( const le_distrib_t *ld, le_analyze_t *ana ) { if ( !ld && !ana ) return false; //--- assign LPAR if (ld->lpar) ana->lpar = *ld->lpar; //--- slots and flags ana->n_slot = 0; int max = PackTracksLD(ld); le_property_t *prop = ana->property; le_music_t *music = ana->music; le_flags_t *flags = ana->flags; if ( prop && music && flags ) { if ( max > ana->max_slot ) max = ana->max_slot; ana->n_slot = max; const le_track_t *lt = ld->tlist; for ( int slot = 0; slot < max; slot++, lt++ ) { if ( lt->track_status < LTS_EXPORT ) continue; prop[slot] = lt->property; music[slot] = lt->music; flags[slot] = lt->flags; } } //--- copy battle cups ana->n_cup_arena = 0; le_cup_track_t *cup_dest = ana->cup_arena; if (cup_dest) { uint max = ld->cup_battle.used; if ( max > ana->max_cup_arena ) max = ana->max_cup_arena; for ( int cup_idx = 0; cup_idx < max; cup_idx++ ) { const le_cup_ref_t *ref = GetLECUP(&ld->cup_battle,cup_idx); if (ref) { *cup_dest++ = htonl(*ref++); *cup_dest++ = htonl(*ref++); *cup_dest++ = htonl(*ref++); *cup_dest++ = htonl(*ref++); *cup_dest++ = htonl(*ref++); ana->n_cup_arena++; } } } //--- copy racing cups ana->n_cup_track = 0; cup_dest = ana->cup_track; if (cup_dest) { uint max = ld->cup_versus.used; if ( max > ana->max_cup_track ) max = ana->max_cup_track; for ( int cup_idx = 0; cup_idx < max; cup_idx++ ) { const le_cup_ref_t *ref = GetLECUP(&ld->cup_versus,cup_idx); if (ref) { *cup_dest++ = htonl(*ref++); *cup_dest++ = htonl(*ref++); *cup_dest++ = htonl(*ref++); *cup_dest++ = htonl(*ref++); ana->n_cup_track++; } } } PRINT("%s() #%u: n_cups=%d+%d\n",__FUNCTION__,__LINE__,ana->n_cup_arena,ana->n_cup_track); //--- setup by version switch (ana->param_vers) { case 1: ana->cup_par->n_racing_cups = htonl(ana->n_cup_track); ana->cup_par->n_battle_cups = htonl(ana->n_cup_arena); ana->course_par->n_slot = htonl(ana->n_slot); break; } //--- finalize PatchLECODE(ana); return false; } /////////////////////////////////////////////////////////////////////////////// #if 0 bool ExportCtCodeLD ( const le_distrib_t *ld, ctcode_t *ctc ) { //[[disabled]] ??? return false; } #endif // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: reference list /////////////// /////////////////////////////////////////////////////////////////////////////// static void PrintSectionTool ( FILE *f, bool be_verbose ) { DASSERT(f); if (be_verbose) fprintf(f,text_ledis_tool_cr, tool_name, SYSTEM2, VERSION, REVISION_NUM, DATE ); else fprintf(f, "\r\n[TOOL]\r\n" "TOOL = %s\r\n" "SYSTEM = %s\r\n" "VERSION = %s\r\n" "REVISION = %u\r\n" "DATE = %s\r\n" "\r\n", tool_name, SYSTEM2, VERSION, REVISION_NUM, DATE ); } /////////////////////////////////////////////////////////////////////////////// int SetupReferenceList ( le_distrib_t *ld, FilterFuncLD filter, le_options_t opt ) { const uint need = ld->tlist_used + 1; if ( ld->reflist_size < need ) { FREE(ld->reflist); ld->reflist = CALLOC(need,sizeof(*ld->reflist)); ld->reflist_size = need; } le_track_t *lt = ld->tlist, **dest = ld->reflist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) if ( filter ? filter(lt,opt) : lt->track_status >= LTS_EXPORT ) *dest++ = lt; *dest = 0; ld->reflist_used = dest - ld->reflist; ASSERT( ld->reflist_used < ld->reflist_size ); return ld->reflist_used ; } /////////////////////////////////////////////////////////////////////////////// static bool check_filter ( const le_track_t *lt, le_options_t opt ) { const le_options_t a = opt & LEO_M_ARENA; if ( a == LEO_VERSUS && !IsTrackLTTY(lt->track_type) || a == LEO_BATTLE && !IsArenaLTTY(lt->track_type) ) return false; const le_options_t o = opt & LEO_M_ORIGINAL; if ( o == LEO_CUSTOM && lt->is_original || o == LEO_ORIGINAL && !lt->is_original ) return false; return true; } //----------------------------------------------------------------------------- static bool filter_options_td ( const le_track_t *lt, le_options_t opt ) { return lt->track_status >= LTS_EXPORT && check_filter(lt,opt); } //----------------------------------------------------------------------------- static bool filter_name_td ( const le_track_t *lt, le_options_t opt ) { return lt->track_status >= LTS_EXPORT && check_filter(lt,opt) && IsOutputNameValidLEO(lt->name,opt); } //----------------------------------------------------------------------------- static bool filter_xname2_td ( const le_track_t *lt, le_options_t opt ) { return lt->track_status >= LTS_EXPORT && check_filter(lt,opt) && ( IsOutputNameValidLEO(lt->name,opt) || IsOutputNameValidLEO(lt->xname,opt) ); } /////////////////////////////////////////////////////////////////////////////// static int cmp_cup_lt ( const le_track_t **pa, const le_track_t **pb ) { const le_track_t *a = *pa; const le_track_t *b = *pb; ASSERT(a&&b); int res = a->track_type - b->track_type; if (!res) { res = a->cup_slot - b->cup_slot; if (!res) res = a->track_slot - b->track_slot; else if ( !a->cup_slot || !b->cup_slot ) // no cup => show last res = -1; } return res; } //----------------------------------------------------------------------------- int SortByCupLD ( le_distrib_t *ld, FilterFuncLD filter, le_options_t opt ) { DASSERT(ld); UpdateCupsLD(ld); int n = SetupReferenceList(ld,filter,opt); if ( n > 1 ) qsort( ld->reflist, n, sizeof(*ld->reflist), (qsort_func)cmp_cup_lt ); return n; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: listings /////////////// /////////////////////////////////////////////////////////////////////////////// enumError CreateDumpLD ( FILE *f, le_distrib_t *ld ) { if ( !f || !ld ) return ERR_MISSING_PARAM; UpdateCupsLD(ld); export_count++; brief_count++; fprintf(f, "%s : LE-CODE distribution\r\n" "%s[TOOL]\r\n\r\n" "TOOL = %s\r\n" "SYSTEM = %s\r\n" "VERSION = %s\r\n" "REVISION = %u\r\n" "DATE = %s\r\n" "\r\n", LE_DISTRIB_MAGIC8, section_sep, tool_name, SYSTEM2, VERSION, REVISION_NUM, DATE ); #if 0 fprintf(f, "%s[SETTINGS]\r\n\r\n" "\r\n", section_sep ); #endif //--- distribution params if (ld->dis_param.used) { int count = 0; ParamFieldItem_t *it = ld->dis_param.field, *end; for ( end = it + ld->dis_param.used; it < end; it++ ) { ccp data = (ccp)it->data; if ( data && *data ) { if (!count++) fprintf(f,"%s[DISTRIBUTION-PARAM]\r\n\r\n",section_sep); int len = strlen(it->key); ccp sep = len > 14 ? " " : len > 6 ? "\t" : "\t\t"; fprintf(f,"@%s%s= %s\r\n", it->key, sep, data ); } } } //-- lecode info fprintf(f,"%s[LECODE-INFO]\r\n# Info about current lecode binaries:\r\n\r\n", section_sep ); CreateLeInfoLD(f,ld,true); //-- LPAR if (ld->lpar) CreateLparLD(f,ld,false); //-- cup reference fprintf(f,"%s[CUP-REF]\r\n\r\n",section_sep); if (PrintCupRefLD(f,ld,&ld->cup_battle)) fputs("\r\n",f); if (PrintCupRefLD(f,ld,&ld->cup_versus)) fputs("\r\n",f); //-- track reference fprintf(f,"%s[TRACK-REF]\r\n\r\n",section_sep); const enumError err1 = CreateRefLD(f,ld,false); //-- track strings fprintf(f,"%s[TRACK-STRINGS]\r\n\r\n",section_sep); const enumError err2 = CreateStringsLD(f,ld); //-- end fprintf(f, "%s[END]\r\n" "# This section is ignored.\r\n" "\r\n", section_sep ); export_count--; brief_count--; return err1 > err2 ? err1 : err2; } /////////////////////////////////////////////////////////////////////////////// enumError CreateRefLD ( FILE *f, le_distrib_t *ld, bool add_strings ) { if ( !f || !ld ) return ERR_MISSING_PARAM; UpdateCupsLD(ld); SetupReferenceList(ld,filter_options_td,ld->spar.opt); fprintf(f,"%s : LE-CODE track reference v1.%u\r\n@VERSION=%u\r\n", LE_REFERENCE_MAGIC8, LE_REFERENCE_VERSION, LE_REFERENCE_VERSION ); ccp filter = GetOutputFilterLEO(ld->spar.opt&LEO_M_OUTPUT); if (filter) fprintf(f,"# Active output filter: %s\r\n",filter); fprintf(f, "@CONTENT=track_slot|type|orig|cup_slot|cup_slot_name|property|music|flags|hidden|laps|speed|sha1|sha1_d|%s\r\n" "#-----\r\n", add_strings ? "ident|ident_d|file|file_d|name|name_d|xname|xname_d|path|path_d|" : "" ); const bool no_d = ( ld->spar.opt & LEO_NO_D_FILES ) != 0; int count = 0; for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { count++; const le_track_t *lt = *plt; fprintf(f,"%u|%s|%u|%u|%s|%u|%u|%u|%u|%u|%5.3f|%s|%s|", lt->track_slot, GetNameLTTY(lt->track_type), lt->is_original, lt->cup_slot, GetCupLT(lt,""), lt->property, lt->music, lt->flags, IsHiddenLEFL(lt->flags), lt->lap_count, lt->speed_factor, GetSha1LT(lt,0,""), no_d ? "" : GetSha1LT(lt,1,"") ); if (add_strings) { exmem_t id1 = EscapeStringPipeCircS(GetIdentLT(lt,0,false,0)); exmem_t id2 = EscapeStringPipeCircS( no_d ? 0 : GetIdentLT(lt,1,false,0) ); exmem_t file = EscapeStringPipeCircS(GetFileLT(lt,0)); exmem_t name = EscapeStringPipeCircS(GetNameLT(lt,0)); exmem_t xname = EscapeStringPipeCircS(GetXNameLT(lt,0)); exmem_t path = EscapeStringPipeCircS(GetPathLT(lt,0,0)); fprintf(f,"%s|%s|%s||%s||%s||%s||\r\n", id1.data.ptr, id2.data.ptr, file.data.ptr, name.data.ptr, xname.data.ptr, path.data.ptr ); FreeExMem(&id1); FreeExMem(&id2); FreeExMem(&file); FreeExMem(&name); FreeExMem(&xname); FreeExMem(&path); } else fputs("\r\n",f); } if ( count > 1 ) fprintf(f,"#-----\r\n# %u records total\r\n",count); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateStringsLD ( FILE *f, le_distrib_t *ld ) { if ( !f || !ld ) return ERR_MISSING_PARAM; SetupReferenceList(ld,filter_options_td,ld->spar.opt); fprintf(f,"%s : LE-CODE track strings v1.%u\r\n@VERSION=%u\r\n", LE_STRINGS_MAGIC8, LE_STRINGS_VERSION, LE_STRINGS_VERSION ); ccp filter = GetOutputFilterLEO(ld->spar.opt&LEO_M_OUTPUT); if (filter) fprintf(f,"# Active output filter: %s\r\n",filter); fputs("#> track_slot|d_mode|is_set|name|string|\r\n",f); const bool allow_d = !( ld->spar.opt & LEO_NO_D_FILES ); int n_strings = 0, n_tracks = 0; for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { n_tracks++; const le_track_t *lt = *plt; for ( int ltt = 0; ltt < LTT__N; ltt++ ) { ccp *ptr = GetPtrOptLT(lt,ltt,allow_d); if ( ptr && *ptr ) { n_strings++; exmem_t text = EscapeStringPipeCircS(*ptr); fprintf(f,"%u|%u|0|%s|%s|\r\n", lt->track_slot, GetDModeLTT(ltt), GetLowerNameLTT(ltt), text.data.ptr ); FreeExMem(&text); } } #if LE_STRING_SET_ENABLED ParamFieldItem_t *end = lt->str_set.field + lt->str_set.used; for ( ParamFieldItem_t *ptr = lt->str_set.field; ptr < end; ptr++ ) { n_strings++; exmem_t name = EscapeStringPipeCircS(ptr->key); exmem_t text = EscapeStringPipeCircS(ptr->data); fprintf(f,"%u|3|1|%s|%s|\r\n", lt->track_slot, name.data.ptr, text.data.ptr ); FreeExMem(&name); FreeExMem(&text); } #endif } if ( n_tracks > 1 ) fprintf(f,"#-----\r\n# %u strings for %u tracks total\r\n",n_strings,n_tracks); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// // [[mtcat]] static u32 GetPrefixIdByName ( ccp name, bool is_arena ) { intx_t res; res.u32[0] = 0; if ( name && *name == '+' ) for ( int i = 0; i < 4 && (u8)*name > ' ' ; i++ ) res.s8[i] = *name++; return is_arena ? ~res.u32[0] : res.u32[0]; } //----------------------------------------------------------------------------- uint GetNameFw ( le_distrib_t *ld, bool use_xname ) { uint fw = 4; if (ld->reflist) { for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { le_track_t *lt = *plt; ccp name = use_xname ? GetXName2LT(lt,"") : lt->name; uint nlen = strlen8(name) + GetEscapeLen(name,-1,CHMD__MODERN,'"'); if ( fw < nlen ) fw = nlen; } } return fw; } //----------------------------------------------------------------------------- enumError CreateNamesLD ( FILE *f, le_distrib_t *ld, bool use_xname ) { if ( !f || !ld ) return ERR_MISSING_PARAM; SortByCupLD( ld, use_xname ? filter_xname2_td : filter_name_td, ld->spar.opt ); uint fw = GetNameFw(ld,use_xname) + 11; fprintf(f,"\r\n%.*s\r\n Cup Name\r\n%.*s\r\n", fw, Minus300, fw, Minus300 ); u32 prev_id = 0; int count = 0, prev_cup = -1; for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { le_track_t *lt = *plt; ccp name = use_xname ? GetXName2LT(lt,"") : lt->name; u32 cur_id = GetPrefixIdByName(name,IsArenaLTTY(lt->track_type)); if (!count++) prev_id = cur_id; int cur_cup = lt->cup_slot / 10; if ( prev_id != cur_id ) { prev_id = cur_id; prev_cup = cur_cup; fprintf(f,"\r\n%.*s\r\n\r\n",fw,Minus300); } else if ( prev_cup != cur_cup ) { prev_cup = cur_cup; fputs("\r\n",f); } exmem_t esc = EscapeStringEx(name,-1,EmptyString,EmptyString,CHMD__MODERN,0,false); fprintf(f,"%s %s\r\n", GetCupAlignLT(lt), esc.data.ptr); FreeExMem(&esc); } if ( count > 1 ) fprintf(f,"\r\n%.*s\r\n\r\n",fw,Minus300); else fputs("\r\n",f); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateInfoLD ( FILE *f, le_distrib_t *ld, bool use_xname, bool add_rating ) { if ( !f || !ld ) return ERR_MISSING_PARAM; DumpCupSlots(ld,"before sort"); SortByCupLD( ld, use_xname ? filter_xname2_td : filter_name_td, ld->spar.opt ); DumpCupSlots(ld,"behind sort"); const uint fw = GetNameFw(ld,use_xname) + 33 + ( add_rating ? 8 : 0 ); fprintf(f,"\r\n%.*s\r\n%s Cup Prop\tMusic\tFlags\tName\r\n%.*s\r\n", fw, Minus300, add_rating ? "()\t" : "", fw, Minus300 ); u32 prev_id = 0; int count = 0, prev_cup = -1; for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { le_track_t *lt = *plt; ccp name = use_xname ? GetXName2LT(lt,"") : lt->name; u32 cur_id = GetPrefixIdByName(name,IsArenaLTTY(lt->track_type)); if (!count++) prev_id = cur_id; int cur_cup = lt->cup_slot / 10; if ( prev_id != cur_id ) { prev_id = cur_id; prev_cup = cur_cup; fprintf(f,"\r\n%.*s\r\n\r\n",fw,Minus300); } else if ( prev_cup != cur_cup ) { prev_cup = cur_cup; fputs("\r\n",f); } exmem_t esc = EscapeStringEx(name,-1,EmptyString,EmptyString,CHMD__MODERN,0,false); fprintf(f,"%s%s %s\t%s\t%s\t%s\r\n", add_rating ? "()\t" : "", GetCupAlignLT(lt), PrintPropertyID(lt->property,false), PrintMusicID(lt->music,false), PrintLEFL16(lt->flags,true), esc.data.ptr ); FreeExMem(&esc); } if ( count > 1 ) fprintf(f,"\r\n%.*s\r\n\r\n",fw,Minus300); else fputs("\r\n",f); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateSha1LD ( FILE *f, le_distrib_t *ld, bool use_xname, bool add_comments ) { if ( !f || !ld ) return ERR_MISSING_PARAM; SortByCupLD(ld,filter_options_td,ld->spar.opt); static char sep[] = "#----------\r\n"; if (add_comments) { fprintf(f, "%s\r\n" "@VERSION=%u\r\n" "# A SHA1 list, that can be used for example @ Wiimmfi.de\r\n" ,LE_SHA1REF_MAGIC8 ,LE_SHA1REF_VERSION ); ccp filter = GetOutputFilterLEO(ld->spar.opt&LEO_M_OUTPUT); if ( filter && *filter ) fprintf(f,"# Active output filter: %s\r\n",filter); fputs( "# Columns: TYPE DISTRIB_FLAGS LE_FLAGS SHA1 TRACK_SLOT CUP NAME\r\n" //X "# During the import, only the first 4 columns are evaluated.\r\n" "# Distribution Flags:\r\n" "# B:boost, N:new, A:again, U:update, F:fill\r\n" "# d:is _d file, t:title only, h:hidden, o:original track\r\n" ,f ); } int count = 0, prev_type = 99, prev_cup = -1; for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { const le_track_t *lt = *plt; if ( prev_type != lt->track_type || prev_cup != !lt->cup_slot ) { prev_type = lt->track_type; prev_cup = !lt->cup_slot; fputs( add_comments ? sep : "\r\n", f ); } const int max = ld->spar.opt & LEO_NO_D_FILES ? 0 : 1; for ( int is_d = 0; is_d <= max; is_d++ ) { const le_track_id_t *lti = lt->lti + is_d; if ( lti->have_sha1 ) { count++; sha1_hex_t hex; Sha1Bin2Hex(hex,lti->sha1bin); ccp name = use_xname ? GetXName2LT(lt,0) : GetNameLT(lt,0); exmem_t esc = EscapeStringEx(name,-1,EmptyString,EmptyString,CHMD__MODERN,0,false); fprintf(f,"%s %s %s %s %4d %s %s\r\n", GetNameLTTY(lt->track_type), PrintDTA(GetDistribFlagsLT(lt,is_d),true), PrintLEFL16(lt->flags,true), hex, lt->track_slot, GetCupAlignLT(lt), esc.data.ptr ); FreeExMem(&esc); } } } if ( count > 3 && add_comments ) fprintf(f,"%s# %u records total\r\n",sep,count); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateCtDefLD ( FILE *f, le_distrib_t *ld ) { if ( !f || !ld ) return ERR_MISSING_PARAM; UpdateCupsLD(ld); const bool have_wiimm_cup = HaveWiimmCupLD(ld); fprintf(f,text_ledis_ctdef_header_cr,CT_DEF_MAGIC8,have_wiimm_cup); int prev_cup = 0, tidx = (8+have_wiimm_cup)*4; for ( int slot = BMG_LE_FIRST_CTRACK; slot < ld->tlist_used; slot++ ) { le_track_t *lt = GetTrackLD(ld,slot); if ( !lt || lt->track_status < LTS_EXPORT ) continue; char type; if (IsHiddenLEFL(lt->flags)) type = 'H'; else { type = 'T'; const int cur_cup = tidx++/4; if ( prev_cup != cur_cup ) { prev_cup = cur_cup; fprintf(f,"\r\nC \"Cup %u\"\r\n",cur_cup); } } fprintf(f,"%c %s\t; %s\t; 0x%02x\t; %s; %s; %s; %s\r\n", type, PrintMusicID(lt->music,false), PrintPropertyID(lt->property,false), lt->flags, QuoteFileLT(lt,""), QuoteNameLT(lt,""), QuoteIdentLT(lt,false,true,""), QuoteXNameLT(lt,"") ); } fputs("\r\n",f); extern const char text_ctcode_setup_arena_cr[]; int count = 0; for ( int slot = MKW_ARENA_BEG; slot < MKW_ARENA_END; slot++ ) { le_track_t *lt = GetTrackLD(ld,slot); if ( !lt || lt->track_status < LTS_EXPORT || HaveUsualBattleSlots(lt) <= 0 ) continue; if (!count++) fprintf(f,"\r\n%s",text_ctcode_setup_arena_cr); fprintf(f,"%s\t%s\t%s\t# A%u\r\n", PrintPropertyID(lt->track_slot,false), PrintPropertyID(lt->property,false), PrintMusicID(lt->music,false), lt->cup_slot ); } if (count) fputs("\r\n",f); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// static void print_ledef_text ( FILE *f, const le_distrib_t *ld, int max_len, ccp name, ccp text ) { DASSERT(f); DASSERT(ld); DASSERT(name); if (IsOutputNameValidLEO(text,ld->spar.opt)) { ccp esc = EscapeStringS(text,"",CHMD__MODERN); const int n_tabs = ( max_len - strlen8(name) ) / 8; if ( n_tabs > 0 ) fprintf(f,"\t%s%.*s\"%s\"\r\n",name,n_tabs,Tabs20,esc); else fprintf(f,"\t%s \"%s\"\r\n",name,esc); FreeString(esc); } } //----------------------------------------------------------------------------- static void print_ledef_track ( FILE *f, const le_distrib_t *ld, const le_track_t *lt ) { DASSERT(f); DASSERT(ld); DASSERT(lt); char comment[30], *dest = comment; if ( lt->lti[0].orig_sha1 ) dest = StringCopyE(dest,comment+sizeof(comment),", original"); if ( IsHiddenLEFL(lt->flags) ) dest = StringCopyE(dest,comment+sizeof(comment),", hidden"); *dest = 0; if (ld->spar.opt&LEO_NO_SLOT) fputs("\r\n",f); else fprintf(f,"\r\nSLOT %u\r\n",lt->track_slot); ccp type = GetNameLTTY(lt->track_type); fprintf(f,"TRACK %s%u\t%s\t%s\t%s\t\"%s\"%s%s\r\n", type, lt->track_slot, type, PrintPropertyID(lt->property,false), PrintMusicID(lt->music,false), PrintLEFL16(lt->flags,true), *comment ? "\t#" : "", *comment ? comment+1 : "" ); #if LE_STRING_SET_ENABLED int max_len = 0; ParamFieldItem_t *set_end = lt->str_set.field + lt->str_set.used; if (lt->str_set.used) { for ( ParamFieldItem_t *ptr = lt->str_set.field; ptr < set_end; ptr++ ) if (IsOutputNameValidLEO(ptr->data,ld->spar.opt)) { const int slen = strlen8(ptr->key); if ( max_len < slen ) max_len = slen; } } #else int max_len = 7; #endif max_len = ( max_len + 18 ) / 8 * 8 - 1; const int n_tabs = ( max_len - 7 ) / 8; if ( lt->lap_count > 0 && lt->track_type & LTTY_TRACK ) fprintf(f,"\tlaps%.*s%u\r\n",n_tabs,Tabs20,lt->lap_count); if ( lt->speed_factor > 0.0 ) fprintf(f,"\tspeed%.*s%5.3f\r\n",n_tabs,Tabs20,lt->speed_factor); print_ledef_text(f,ld,max_len,"ident",GetIdentLT(lt,false,true,0)); print_ledef_text(f,ld,max_len,"file",GetFileLT(lt,0)); print_ledef_text(f,ld,max_len,"path",GetPathLT(lt,ld->spar.opt,0)); print_ledef_text(f,ld,max_len,"name",GetNameLT(lt,0)); print_ledef_text(f,ld,max_len,"xname",GetXNameLT(lt,0)); #if LE_STRING_LIST_ENABLED for ( int ltt = LTT__LIST_BEG; ltt < LTT__LIST_END; ltt++ ) { ccp *ptr = GetPtrOptLT(lt,ltt,true); if ( ptr && *ptr ) print_ledef_text(f,ld,max_len,GetLowerNameLTT(ltt),GetListLT(lt,ltt,0)); } #endif #if LE_STRING_SET_ENABLED if (lt->str_set.used) { max_len -= 2; for ( ParamFieldItem_t *ptr = lt->str_set.field; ptr < set_end; ptr++ ) if (IsOutputNameValidLEO(ptr->data,ld->spar.opt)) { ccp esc = EscapeStringS(ptr->data,"",CHMD__MODERN); const int n_tabs = ( max_len - strlen8(ptr->key) ) / 8; if ( n_tabs > 0 ) fprintf(f,"\t[%s]%.*s\"%s\"\r\n",ptr->key,n_tabs,Tabs20,esc); else fprintf(f,"\t[%s] \"%s\"\r\n",ptr->key,esc); FreeString(esc); } } #endif } //----------------------------------------------------------------------------- static void print_ledef_cup ( FILE *f, le_distrib_t *ld, const le_cup_t *lc, int sep_index, ccp suffix ) { DASSERT(f); DASSERT(ld); DASSERT(lc); if (!lc->used) return; //--- get field widths int max = LE_FIRST_CT_SLOT; for ( int i = 0; i < lc->used; i++ ) { le_cup_ref_t *ref = GetLECUP(lc,i); if (ref) for ( int t = 0; t < lc->tracks; t++ ) if ( max < ref[t] ) max = ref[t]; } char buf[20]; const int fw_cup = snprintf(buf,sizeof(buf),"%d",lc->used-1); const int fw_track = snprintf(buf,sizeof(buf),"%d",max) + 2; //--- print cups ccp sep = ""; int count = 0, next_sep = -1; const int cup_end = lc->used; for ( int i = 0; i < cup_end; i++ ) { le_cup_ref_t *ref = GetLECUP(lc,i); if ( i+2 <= cup_end && !memcmp(standard_bt_ref,ref,sizeof(standard_bt_ref)) ) { fputs(sep,f); fputs("# 2 battle cups with standard layout (STANDARD-BATTLE-CUPS):\r\n",f); next_sep = count+2; } else if ( i+8 <= cup_end && !memcmp(standard_vs_ref,ref,sizeof(standard_vs_ref)) ) { fputs(sep,f); fputs("# 8 versus cups with standard layout (STANDARD-VERSUS-CUPS):\r\n",f); next_sep = count+8; } else if ( !memcmp(mkwfun_random_ref,ref,sizeof(mkwfun_random_ref)) ) { fputs(sep,f); fputs("# 1 versus cup with standard MKW-Fun random slots (MKWFUN-RANDOM-CUP):\r\n",f); next_sep = count+1; } if (ref) { if ( count == next_sep || count >= next_sep && count == sep_index ) fputs(sep,f); count++; sep = "\r\n"; fprintf(f,"APPEND%s",suffix); for ( int t = 0; t < lc->tracks; t++ ) { const int slot = ref[t]; ccp info = "-1"; if ( slot >= 0 ) { const le_track_t *lt = GetTrackLD(ld,slot); if ( IsActiveLT(lt) ) { if ( lt->track_status == LTS_FILL ) info = "fill"; else info = GetSlotNameLT(lt); } } fprintf(f,"\t%*s",fw_track,info); //fprintf(f," [%u]",slot); } fprintf(f,"\t# %*d\r\n",fw_cup,i); } } fputs(sep,f); } //----------------------------------------------------------------------------- enumError CreateLeDefLD ( FILE *f, le_distrib_t *ld ) { if ( !f || !ld ) return ERR_MISSING_PARAM; UpdateCupsLD(ld); AnalyzeLD(ld,true); const bool be_verbose = !(ld->spar.opt & LEO_BRIEF); if (be_verbose) fprintf(f,text_ledis_ledef_header_cr,LE_DEFINE_MAGIC8); else fprintf(f,"%s\r\n",LE_DEFINE_MAGIC8); PrintSectionTool(f,be_verbose); //-- TRACK-LIST if (be_verbose) fprintf(f,text_ledis_ledef_track_list_cr); else fprintf(f,"\r\n[TRACK-LIST]\r\n"); // battle arenas ccp info = "\r\n#--- battle arenas\r\n"; int first = MKW_ARENA_BEG; if (ld->ana.bt.orig_tracks) { first = MKW_ARENA_END; fprintf(f,"%s\r\nSTANDARD-BATTLE-ARENAS\r\n",info); info = 0; } for ( int slot = first; slot < ld->tlist_used; slot++ ) { const le_track_t *lt = GetTrackLD(ld,slot); if ( !lt || lt->track_status < LTS_EXPORT || !IsArenaLTTY(lt->track_type) ) continue; if (info) { fputs(info,f); info = 0; } print_ledef_track(f,ld,lt); } // versus tracks info = "\r\n#--- versus tracks\r\n"; first = MKW_TRACK_BEG; if (ld->ana.vs.orig_tracks) { first = MKW_TRACK_END; fprintf(f,"%s\r\nSTANDARD-VERSUS-TRACKS\r\n",info); info = 0; } for ( int slot = first; slot < ld->tlist_used; slot++ ) { const le_track_t *lt = GetTrackLD(ld,slot); if ( !lt || lt->track_status < LTS_EXPORT || IsArenaLTTY(lt->track_type) ) continue; if (info) { fputs(info,f); info = 0; } print_ledef_track(f,ld,lt); } fputs("\r\n",f); //-- CUP-LIST if (be_verbose) fprintf(f,text_ledis_ledef_cup_list_cr); else fprintf(f,"\r\n\r\n[CUP-LIST]\r\n\r\n"); // battle cups fputs("#--- battle cups\r\n\r\n",f); print_ledef_cup(f,ld,&ld->cup_battle,2,"-BT"); fprintf(f, "# Append unused battle arenas automatically (0=NO/1=YES):\r\n" "ADD-UNUSED-ARENAS = %s\r\n" "\r\n" ,ld->ana.bt.add_unused ? "yes" : "no" ); // versus cups fputs("#--- versus cups\r\n\r\n",f); print_ledef_cup(f,ld,&ld->cup_versus,8,"-VS"); fprintf(f, "# Append unused versus tracks automatically (0=NO/1=YES):\r\n" "ADD-UNUSED-TRACKS = %s\r\n" "\r\n" ,ld->ana.vs.add_unused ? "yes" : "no" ); //-- END if (be_verbose) fputs("\r\n#\f\r\n###########\r\n [END]\r\n###########\r\n",f); else fputs("\r\n[END]\r\n",f); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateLecode4LD ( ccp fname, le_distrib_t *ld ) { if ( !fname || !ld ) return ERR_MISSING_PARAM; ccp at = strrchr(fname,'@'); if (!at) return ERROR0(ERR_WARNING,"Missing '@' in filename: %s\n",fname); char path[PATH_MAX]; if ( strlen(fname) > sizeof(path) - 4 ) return ERROR0(ERR_WARNING,"Filename too long: %s\n",fname); char *tag = path + (at-fname); memcpy(path,fname,tag-path); strncpy(tag+3,at+1,path+sizeof(path)-tag-3); for ( le_region_t reg = LEREG__BEG; reg <= LEREG__END; reg++ ) { memcpy(tag,GetNameLEREG(reg,"@@@"),3); File_t F; enumError err = CreateFileOpt(&F,true,path,false,0); if (!err) { err = CreateLecodeLD(F.f,ld,reg); CloseFile(&F,0); } if (err) return err; } return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateLecodeLD ( FILE *f, le_distrib_t *ld, le_region_t region ) { if ( !f || !ld ) return ERR_MISSING_PARAM; UpdateCupsLD(ld); mem_t lecode = GetLecodeLD(ld,region); if (!lecode.ptr) return ERR_MISSING_PARAM; le_analyze_t ana; u8 *data = MEMDUP(lecode.ptr,lecode.len); if (!AnalyzeLEBinary(&ana,data,lecode.len)) { ExportAnaLD(ld,&ana); fwrite(data,lecode.len,1,f); } FREE(data); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateLeInfoLD ( FILE *f, const le_distrib_t *ld, bool list_only ) { if ( !f || !ld ) return ERR_MISSING_PARAM; if (!list_only) { const ColorSet_t *colset = GetFileColorSet(f); fprintf(f,"\r\n%sCurrent LE-CODE binaries:%s\r\n\r\n",colset->caption,colset->reset); } for ( le_region_t reg = LEREG__BEG; reg <= LEREG__END; reg++ ) { mem_t builtin = GetLecodeLEREG(reg); mem_t current = GetLecodeLD(ld,reg); if ( current.ptr ) fprintf(f," %s, %s\r\n", GetInfoLECODE(current), current.ptr == builtin.ptr ? "built-in" : "loaded" ); } fputs("\r\n",f); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateLparLD ( FILE *f, le_distrib_t *ld, bool print_full ) { if ( !f || !ld ) return ERR_MISSING_PARAM; SetupLparLD(ld,true); return SaveTextFileLPAR(ld->lpar,f,print_full); } /////////////////////////////////////////////////////////////////////////////// enumError CreateBmgLD ( FILE *f, le_distrib_t *ld, bool bmg_text ) { if ( !f || !ld ) return ERR_MISSING_PARAM; bmg_t bmg; InitializeBMG(&bmg); le_options_t source = ld->spar.opt & LEO_M_BMG; if (!source) source = LEO_DEFAULT_BMG; for ( int slot = 0; slot < ld->tlist_used; slot++ ) { le_track_t *lt = GetTrackLD(ld,slot); if ( !lt || lt->track_status < LTS_EXPORT ) continue; ccp text = GetTextLT(lt,&ld->spar,0); if (!IsOutputNameValidLEO(text,ld->spar.opt)) continue; PRINT0("SLOT %d: %s\n",slot,text); if ( source & LEO_MKW1 ) { const u32 mid = IsMkwTrack(slot) ? slot+MID_TRACK1_BEG : IsMkwArena(slot) ? slot-BMG_N_TRACK+MID_ARENA1_BEG : 0; if (mid) { bmg_item_t *bi = InsertItemBMG(&bmg,mid,0,0,0); ASSERT(bi); AssignItemScanTextBMG(bi,text,-1); } } if ( source & LEO_MKW2 ) { const u32 mid = IsMkwTrack(slot) ? slot+MID_TRACK2_BEG : IsMkwArena(slot) ? slot-BMG_N_TRACK+MID_ARENA2_BEG : 0; if (mid) { bmg_item_t *bi = InsertItemBMG(&bmg,mid,0,0,0); ASSERT(bi); AssignItemScanTextBMG(bi,text,-1); } } if ( source & LEO_CTCODE && slot < BMG_N_CT_TRACK ) { bmg_item_t *bi = InsertItemBMG(&bmg,slot+MID_CT_TRACK_BEG,0,0,0); ASSERT(bi); AssignItemScanTextBMG(bi,text,-1); } if ( source & LEO_LECODE ) { bmg_item_t *bi = InsertItemBMG(&bmg,slot+MID_LE_TRACK_BEG,0,0,0); ASSERT(bi); AssignItemScanTextBMG(bi,text,-1); } } enumError err = bmg_text || isatty(fileno(f)) ? SaveTextFileBMG( &bmg, f, 0, long_count > 0, brief_count > 0 ? brief_count : !print_header ) : SaveRawFileBMG(&bmg,f,0); ResetBMG(&bmg); return err; } /////////////////////////////////////////////////////////////////////////////// enumError CreateCupIconsLD ( ld_out_param_t *lop, mem_t mem_opt, bool print_info ) { if (!lop) return ERR_MISSING_PARAM; //--- setup buffer char namebuf[50], *nameend = namebuf + sizeof(namebuf) - 3; const uint bufsize = lop->ld->cup_versus.used * 21 + 200; char *buf = MALLOC(bufsize), *dest = buf, *bufend = buf + bufsize - 1; dest = StringCopyE(dest,bufend,":cup-icon="); char *cuplist = dest; //--- scan options enum { O_ORIG = 0x0001, O_SWAPPED = 0x0002, O_1WIIMM = 0x0004, O_9WIIMM = 0x0008, O_XWIIMM = 0x0010, O_PLUS = 0x0020, O_XPLUS = 0x0040, O_GAME = 0x0080, O_XGAME = 0x0100, O_SPACE = 0x0200, O_TEST = 0x0400, O_CHARS = 0x1000, O_M_CHARS = 0xf000, O_S_CHARS = 12, O_PIXEL = 0x010000, // N(pixel) = ( (val&O_M_PIXEL) >> O_S_PIXEL ) * O_PIXEL_FACTOR O_M_PIXEL = 0x1f0000, O_S_PIXEL = 16, O_PIXEL_FACTOR = 8, O__DEFAULT = 5*O_CHARS, }; static const KeywordTab_t keytab[] = { { O_ORIG, "ORIGINAL", 0, 0 }, { O_SWAPPED, "SWAPPED", 0, 0 }, { O_1WIIMM, "1WIIMM", 0, 0 }, { O_9WIIMM, "9WIIMM", "WIIMM", 0 }, { O_XWIIMM, "XWIIMM", 0, 0 }, { O_PLUS, "PLUS", 0, 0 }, { O_XPLUS, "XPLUS", 0, 0 }, { O_GAME, "GAME", 0, 0 }, { O_XGAME, "XGAME", 0, 0 }, { O_SPACE, "SPACE", 0, 0 }, { O_TEST, "TEST", 0, 0 }, { 0*O_CHARS, "0", 0, O_M_CHARS }, { 1*O_CHARS, "1", 0, O_M_CHARS }, { 2*O_CHARS, "2", 0, O_M_CHARS }, { 3*O_CHARS, "3", 0, O_M_CHARS }, { 4*O_CHARS, "4", 0, O_M_CHARS }, { 5*O_CHARS, "5", 0, O_M_CHARS }, { 6*O_CHARS, "6", 0, O_M_CHARS }, { 7*O_CHARS, "7", 0, O_M_CHARS }, { 8*O_CHARS, "8", 0, O_M_CHARS }, { 9*O_CHARS, "9", 0, O_M_CHARS }, { 10*O_CHARS, "10", 0, O_M_CHARS }, { 11*O_CHARS, "11", 0, O_M_CHARS }, { 12*O_CHARS, "12", 0, O_M_CHARS }, { 13*O_CHARS, "13", 0, O_M_CHARS }, { 14*O_CHARS, "14", 0, O_M_CHARS }, { 15*O_CHARS, "15", 0, O_M_CHARS }, { 1*O_PIXEL, "8PX", 0, O_M_PIXEL }, { 2*O_PIXEL, "16PX", 0, O_M_PIXEL }, { 3*O_PIXEL, "24PX", 0, O_M_PIXEL }, { 4*O_PIXEL, "32PX", 0, O_M_PIXEL }, { 5*O_PIXEL, "40PX", 0, O_M_PIXEL }, { 6*O_PIXEL, "48PX", 0, O_M_PIXEL }, { 7*O_PIXEL, "56PX", 0, O_M_PIXEL }, { 8*O_PIXEL, "64PX", 0, O_M_PIXEL }, { 9*O_PIXEL, "72PX", 0, O_M_PIXEL }, { 10*O_PIXEL, "80PX", 0, O_M_PIXEL }, { 11*O_PIXEL, "88PX", 0, O_M_PIXEL }, { 12*O_PIXEL, "96PX", 0, O_M_PIXEL }, { 13*O_PIXEL, "104PX", 0, O_M_PIXEL }, { 14*O_PIXEL, "112PX", 0, O_M_PIXEL }, { 15*O_PIXEL, "120PX", 0, O_M_PIXEL }, { 16*O_PIXEL, "128PX", 0, O_M_PIXEL }, { 17*O_PIXEL, "136PX", 0, O_M_PIXEL }, { 18*O_PIXEL, "144PX", 0, O_M_PIXEL }, { 19*O_PIXEL, "152PX", 0, O_M_PIXEL }, { 20*O_PIXEL, "160PX", 0, O_M_PIXEL }, { 21*O_PIXEL, "168PX", 0, O_M_PIXEL }, { 22*O_PIXEL, "176PX", 0, O_M_PIXEL }, { 23*O_PIXEL, "184PX", 0, O_M_PIXEL }, { 24*O_PIXEL, "192PX", 0, O_M_PIXEL }, { 25*O_PIXEL, "200PX", 0, O_M_PIXEL }, { 26*O_PIXEL, "208PX", 0, O_M_PIXEL }, { 27*O_PIXEL, "216PX", 0, O_M_PIXEL }, { 28*O_PIXEL, "224PX", 0, O_M_PIXEL }, { 29*O_PIXEL, "232PX", 0, O_M_PIXEL }, { 30*O_PIXEL, "240PX", 0, O_M_PIXEL }, { 31*O_PIXEL, "248PX", 0, O_M_PIXEL }, {0,0,0,0} }; le_strpar_t lepar = { .opt = LEO_NAME }; KeyListParam_t keypar; InitializeKeyListParam(&keypar); keypar.func_arg = scan_source_opt; keypar.user_param = &lepar; uint opt = 0; if (mem_opt.len) { s64 res = ScanKeywordListP(&keypar,mem_opt.ptr,mem_opt.len,O__DEFAULT,keytab); if ( res < 0 ) return ERR_SYNTAX; if ( res >= 0 ) opt = res; } if (!opt) opt = O__DEFAULT; if ( opt & O_TEST ) dest = StringCopyE(dest,bufend,":test\n"); const uint pix = (( opt & O_M_PIXEL ) >> O_S_PIXEL ) * O_PIXEL_FACTOR; if ( pix && pix != 128 ) dest = snprintfE(dest,bufend,":%upx\n",pix); dest = StringCopyE(dest,bufend,":arrows\n"); enumError err = ERR_OK; int cup_index = 0; if ( opt & O_SWAPPED ) { dest = StringCopyE(dest,bufend,":swapped\n"); cup_index = 8; } else if ( opt & O_ORIG ) { dest = StringCopyE(dest,bufend,":original\n"); cup_index = 8; } const int wiimm1 = opt & O_1WIIMM ? 0 : -1; const int wiimm9 = opt & O_9WIIMM ? 8 : -1; const int wiimmx = opt & O_XWIIMM ? lop->ld->cup_versus.used - 1 : -1; const bool use_plus_prefix = (opt & (O_PLUS|O_XPLUS)) != 0; const bool use_game_prefix = (opt & (O_GAME|O_XGAME)) != 0; const ccp append_plus = (opt & O_XPLUS) ? "_" : " "; const ccp append_game = (opt & O_XGAME) ? "_" : " "; const int max_chars = ( opt & O_M_CHARS ) >> O_S_CHARS; //--- add cup names for ( ; cup_index < lop->ld->cup_versus.used; cup_index++ ) { if ( cup_index == wiimm1 || cup_index == wiimm9 || cup_index == wiimmx ) { dest = StringCopyE(dest,bufend,":wiimm\n"); continue; } dest = snprintfE(dest,bufend,"%u-",cup_index+1); le_cup_ref_t *ref = GetLECUP(&lop->ld->cup_versus,cup_index); if (ref) { le_track_t *lt = GetTrackLD(lop->ld,*ref); ccp text = GetTextLT(lt,&lepar,0); if ( text && *text ) { split_filename_t spf; AnalyseSPF(&spf,true,text,0,CPM_LINK,opt_plus); if ( spf.name.len && dest < bufend ) { char *ndest = namebuf; if ( use_plus_prefix && spf.plus.len ) ndest = StringCat2LE(ndest,nameend,spf.plus.ptr,spf.plus.len,append_plus,1); if ( use_game_prefix && spf.game.len ) ndest = StringCat2LE(ndest,nameend,spf.game.ptr,spf.game.len,append_game,1); ndest = StringCopyE(ndest,nameend,spf.name.ptr); if ( ndest > namebuf + max_chars ) ndest = namebuf + max_chars; while ( ndest > namebuf && ( ndest[-1] == ' ' || ndest[-1] == '\t' || ndest[-1] == '_' )) *--ndest = 0; *ndest = 0; if ( opt & O_SPACE ) for ( ndest = namebuf; *ndest; ndest++ ) if ( *ndest == ' ' ) { *ndest = 0; break; } dest = StringCopyE(dest,bufend,namebuf); } ResetSPF(&spf); } } if ( dest < bufend ) *dest++ = '\n'; } //--- terminate *dest = 0; if (print_info) fwrite(cuplist,dest-cuplist,1,lop->f); else { GenericImgParam_t genpar; if (CheckGenericIMG(&genpar,buf,true)) { Image_t img; err = CreateGenericIMG(&genpar,&img,true); if (!err) { // [[tpl-ex+]] const file_format_t fform = GetImageFFByFName(lop->fname,FF_CUPICON,true); err = SaveIMG(&img,fform,0,lop->f,lop->fname,false); } } } FREE(buf); return err; } /////////////////////////////////////////////////////////////////////////////// static void debug_cups ( FILE *f, const le_cup_t *lc ) { fprintf(f,"%6u (0x%x) cups used, %u (0x%x) alloced (%s)\r\n", lc->used, lc->used, lc->size, lc->size, PrintSize1000(0,0,lc->size*sizeof(*lc->list)*lc->tracks,0) ); if (!lc->used) return; u16 count[LE_MAX_TRACKS] = {0}; int slots_used = 0, different = 0, max_use = 0; const le_cup_ref_t *end = lc->list + lc->used * lc->tracks; for ( const le_cup_ref_t *ptr = lc->list; ptr < end; ptr++ ) { uint idx = *ptr; if ( idx < LE_MAX_TRACKS ) { slots_used++; if (!count[idx]++) different++; if ( max_use < count[idx] ) max_use = count[idx]; } } fprintf(f,"%6u slots total, %u slots used.\r\n", lc->used*lc->tracks, slots_used ); fprintf(f,"%6u different track references, max %u for one track.\r\n", different, max_use ); } //----------------------------------------------------------------------------- enumError CreateDebugLD ( FILE *f, const le_distrib_t *ld ) { if ( !f || !ld ) return ERR_MISSING_PARAM; const ColorSet_t *colset = GetFileColorSet(f); //--- basics fprintf(f,"\r\n%sBasic settings:%s\r\n",colset->caption,colset->reset); fprintf(f, " Current auto-setup: 0x%x\r\n" " Current options: %s\r\n" ,ld->auto_setup ,GetOptionsLSP(&ld->spar,LEO__ALL) ); //--- tracks fprintf(f,"\r\n%sTracks:%s\r\n",colset->caption,colset->reset); fprintf(f," %u (0x%x) tracks used, %u (0x%x) alloced (%s)\r\n", ld->tlist_used, ld->tlist_used, ld->tlist_size, ld->tlist_size, PrintSize1000(0,0,ld->tlist_size*sizeof(*ld->tlist),0) ); uint n_status[LTS__N] = {0}; uint n_arena = 0, n_track = 0, n_random = 0; uint n_text[LTT__N] = {0}, size_text[LTT__N] = {0}; uint n_sha1 = 0, n_sha1_d = 0; #if LE_STRING_SET_ENABLED typedef struct count_t { uint n; uint size; } count_t; exmem_list_t str_count; InitializeEML(&str_count); #endif le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) { if ( lt->track_status < LTS__N ) n_status[lt->track_status]++; if ( lt->track_status < LTS_ACTIVE ) continue; if ( lt->track_type & LTTY_ARENA ) n_arena++; if ( lt->track_type & LTTY_TRACK ) n_track++; if ( lt->track_type & LTTY_RANDOM ) n_random++; if (lt->lti[0].have_sha1) n_sha1++; if (lt->lti[1].have_sha1) n_sha1_d++; for ( int ltt = 0; ltt < LTT__N; ltt++ ) { ccp *ptr = GetPtrOptLT(lt,ltt,true); if ( ptr && *ptr ) { n_text[ltt]++; size_text[ltt] += GetGoodAllocSize(strlen(*ptr) + 1); } } #if LE_STRING_SET_ENABLED ParamFieldItem_t *end = lt->str_set.field + lt->str_set.used; for ( ParamFieldItem_t *ptr = lt->str_set.field; ptr < end; ptr++ ) { exmem_key_t *ek = FindInsertEML(&str_count,ptr->key,CPM_LINK,0); if (!ek->data.data.ptr) { ek->data.data.ptr = CALLOC(1,sizeof(count_t)); ek->data.data.len = sizeof(count_t); ek->data.is_alloced = true; } count_t *cnt = (count_t*)ek->data.data.ptr; cnt->n++; cnt->size += GetGoodAllocSize(strlen(ptr->data)+1); } #endif } fprintf(f,"%s Summaries by track status:%s\r\n",colset->heading,colset->reset); for ( int i = 0; i < LTS__N; i++ ) if (n_status[i]) fprintf(f,"%8u of status %s\r\n",n_status[i],GetNameLTS(i)); fprintf(f,"%s Summaries by track type:%s\r\n",colset->heading,colset->reset); if (n_arena) fprintf(f,"%8u of type BATTLE ARENA\r\n",n_arena); if (n_track) fprintf(f,"%8u of type VERSUS TRACK\r\n",n_track); if (n_random) fprintf(f,"%8u of type RANDOM\r\n",n_random); fprintf(f,"%s String usage:%s\r\n",colset->heading,colset->reset); if (n_sha1) fprintf(f,"%8u string%c of type SHA1 (not alloced)\r\n", n_sha1, n_sha1 == 1 ? ' ' : 's' ); if (n_sha1_d) fprintf(f,"%8u string%c of type D-SHA1 (not alloced)\r\n", n_sha1_d, n_sha1_d == 1 ? ' ' : 's' ); for ( int ltt = 0; ltt < LTT__N; ltt++ ) { if (n_text[ltt]) { char type[20]; snprintf(type,sizeof(type),"%s,",GetUpperNameLTT(ltt)); fprintf(f,"%8u string%c of type %-8s %s\r\n", n_text[ltt], n_text[ltt] == 1 ? ' ' : 's', type, PrintSize1000(0,0,size_text[ltt],DC_SFORM_ALIGN)); } } #if LE_STRING_SET_ENABLED if (str_count.used) { fprintf(f,"%s String set usage:%s\r\n",colset->heading,colset->reset); exmem_key_t *end = str_count.list + str_count.used; for ( exmem_key_t *ek = str_count.list; ek < end; ek++ ) { count_t *cnt = (count_t*)ek->data.data.ptr; fprintf(f,"%8u, %s : %s\r\n", cnt->n, PrintSize1000(0,0,cnt->size,DC_SFORM_ALIGN), ek->key ); } } #endif // LE_STRING_SET_ENABLED //--- cups fprintf(f,"\r\n%sBattle Cups:%s\r\n",colset->caption,colset->reset); debug_cups(f,&ld->cup_battle); fprintf(f,"\r\n%sVersus Cups:%s\r\n",colset->caption,colset->reset); debug_cups(f,&ld->cup_versus); //--- misc fprintf(f,"\r\n%sCurrent LE-CODE binaries:%s\r\n",colset->caption,colset->reset); CreateLeInfoLD(f,ld,true); //--- terminate #if LE_STRING_SET_ENABLED ResetEML(&str_count); #endif return ERR_OK; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: create report /////////////// /////////////////////////////////////////////////////////////////////////////// typedef struct report_counter_t { uint visible; // visible tracks uint hidden; // hidden tracks uint title; // title tracks uint g_header; // group header uint g_member; // group member uint g_both; // group header+member uint le_new; // tracks marked as 'new' by LE-FLAGS uint le_texture; // tracks marked as 'texture' by LE-FLAGS uint boost; // tracks marked as 'boost' by DTA uint new; // tracks marked as 'new' by DTA uint again; // tracks marked as 'again' by DTA uint update; // tracks marked as 'update' by DTA uint fill; // tracks marked as 'fill' by DTA StringField_t sha1; // distinct SHA1 } report_counter_t; /////////////////////////////////////////////////////////////////////////////// static void report_counters ( FILE *f, const report_counter_t *cnt, ccp type ) { DASSERT(cnt); DASSERT(type); const uint total = cnt->visible + cnt->hidden; if ( cnt->visible && cnt->hidden ) fprintf(f,"\r\n" "%6u %s%s total\r\n" "%8u visible %s%s\r\n" "%8u hidden %s%s\r\n" ,total ,type, total==1?"":"s" ,cnt->visible ,type ,cnt->visible==1?"":"s" ,cnt->hidden ,type ,cnt->hidden==1?"":"s" ); else if ( total ) fprintf(f,"\r\n" "%6u %s %s%s total\r\n" ,total ,cnt->visible ? "visible" : "hidden" ,type, total==1?"":"s" ); else fprintf(f," - no %ss found\r\n",type); const uint distinct = cnt->sha1.used; if (distinct) fprintf(f,"%8u distinct %s%s detected\r\n" ,distinct ,type, distinct==1?"":"s" ); if (cnt->title) fprintf(f,"%8u %s%s used as title only\r\n" ,cnt->title ,type, cnt->title==1?" ":"s" ); if ( cnt->g_header ) fprintf(f,"%8u %s%s used as group header\r\n" ,cnt->g_header ,type, cnt->g_header==1?" ":"s" ); if ( cnt->g_member ) fprintf(f,"%8u %s%s used as group member\r\n" ,cnt->g_member ,type, cnt->g_member==1?" ":"s" ); if ( cnt->g_both ) fprintf(f,"%8u %s%s used as group header + member\r\n" ,cnt->g_both ,type, cnt->g_both==1?" ":"s" ); if ( cnt->le_new ) fprintf(f,"%8u %s%s marked by le-flags as 'new'\r\n" ,cnt->le_new ,type, cnt->le_new==1?" ":"s" ); if ( cnt->le_texture ) fprintf(f,"%8u %s%s marked by le-flags as 'texture'\r\n" ,cnt->le_texture ,type, cnt->le_texture==1?" ":"s" ); if ( cnt->boost ) fprintf(f,"%8u %s%s marked by distribution info as 'boost'\r\n" ,cnt->boost ,type, cnt->boost==1?" ":"s" ); if ( cnt->new ) fprintf(f,"%8u %s%s marked by distribution info as 'new'\r\n" ,cnt->new ,type, cnt->new==1?" ":"s" ); if ( cnt->again ) fprintf(f,"%8u %s%s marked by distribution info as 'again'\r\n" ,cnt->again ,type, cnt->again==1?" ":"s" ); if ( cnt->update ) fprintf(f,"%8u %s%s marked by distribution info as 'update'\r\n" ,cnt->update ,type, cnt->update==1?" ":"s" ); if ( cnt->fill ) fprintf(f,"%8u %s%s marked by distribution info as 'fill'\r\n" ,cnt->fill ,type, cnt->fill==1?" ":"s" ); } /////////////////////////////////////////////////////////////////////////////// static void report_duplicates ( ld_out_param_t *lop, int idx, ccp singular, ccp plural, int dup_count, u16 *list, int n_list ) { if (!dup_count) return; fprintf(lop->f,"\r\n\r\n%s%u %s that have been used two or more times (tid,fid,cid,flags,slot,cup,name):%s\r\n", lop->colset->caption, dup_count, dup_count == 1 ? singular : plural, lop->colset->reset ); for ( int il = 1; il < n_list; il++ ) { if ( list[il] < 2 ) continue; fputs("\r\n",lop->f); le_track_t *lt = lop->ld->tlist; for ( int slot = 0; slot < lop->ld->tlist_used; slot++, lt++ ) { if ( IsTitleLT(lt) ) continue; const u8 *sha1 = GetSha1BinLT(lt,false); if (sha1) { const sha1_reference_t *ref = FindSha1RefBin((void*)sha1); if ( ref && ref->ref[idx] == il ) fprintf(lop->f,"%7u %5u %5u %s %4u %s %s\r\n", ref->track, ref->family, ref->clan, PrintLEFL16(lt->flags,true), lt->track_slot, GetCupAlignLT(lt), GetXName2LT(lt,"-") ); } } } } /////////////////////////////////////////////////////////////////////////////// static TrackClassIndex_t get_track_class_index ( const le_track_t *lt ) { ccp sha1 = GetSha1LT(lt,false,0); if (sha1) { const sha1_reference_t *ref = FindSha1RefHex(sha1,0); if (ref) return GetTrackClassIndex(ref->tclass); } return -1; } /////////////////////////////////////////////////////////////////////////////// enumError CreateReportLD ( ld_out_param_t *lop, mem_t mem_opt ) { if (!lop) return ERR_MISSING_PARAM; enum { O_COUNTERS = 0x0001, O_CLASSES = 0x0002, O_XCLASSES = 0x0004, O_NAMES = 0x0008, O_TRACKS = 0x0010, O_FAMILIES = 0x0020, O_CLANS = 0x0040, O_ALL = 0x007f, O_M_DUPLICATES = O_NAMES | O_TRACKS | O_FAMILIES | O_CLANS, O_NEED_SHA1_REF = O_CLASSES | O_XCLASSES | O_TRACKS | O_FAMILIES | O_CLANS, O_DEFAULT = O_ALL & ~O_XCLASSES, }; static const KeywordTab_t keytab[] = { { O_COUNTERS, "COUNTERS", 0, 0 }, { O_CLASSES, "CLASSES", 0, 0 }, { O_XCLASSES, "XCLASSES", 0, 0 }, { O_NAMES, "NAMES", 0, 0 }, { O_TRACKS, "TRACKS", 0, 0 }, { O_FAMILIES, "FAMILIES", "FAMILY", 0 }, { O_CLANS, "CLANS", 0, 0 }, { O_M_DUPLICATES, "DUPLICATES", 0, O_M_DUPLICATES }, { O_DEFAULT, "DEFAULT", 0, O_DEFAULT }, { O_ALL, "ALL", 0, O_ALL }, {0,0,0,0} }; le_strpar_t lepar = { .opt = LEO_NAME }; KeyListParam_t keypar; InitializeKeyListParam(&keypar); keypar.func_arg = scan_source_opt; keypar.user_param = &lepar; u32 opt = 0; if (mem_opt.len) { const s64 def = *mem_opt.ptr == '-' ? O_ALL : 0; s64 res = ScanKeywordListP(&keypar,mem_opt.ptr,mem_opt.len,def,keytab); if ( res < 0 ) return ERR_SYNTAX; if ( res >= 0 ) opt = res; } if (!opt) opt = O_DEFAULT; PRINT0("SOURCE: %s\n",GetNameLSP(&lepar)); UpdateCupsLD(lop->ld); const int n_tracks = lop->ld->tlist_used; //--- check need of sha1 reference if ( opt & O_NEED_SHA1_REF && !s1ref_used ) { static char msg[] = "\r\n" "{warn|Without a SHA1 reference file" " (see https://ct.wiimm.de/export/sha1ref/view)" " the analysis of classes and a search for duplicate tracks," " families or clans is not possible.}"; PutColoredLines(lop->f,lop->colset,0,GetTermWidth(80,40)-1,0,0,msg); opt &= ~O_NEED_SHA1_REF; } //--- counters if ( opt & O_COUNTERS ) { fprintf(lop->f,"\r\n%sVersus Track & Battle Arena counters:%s\r\n", lop->colset->caption, lop->colset->reset ); report_counter_t versus = {0}, battle = {0}; le_track_t *lt = lop->ld->tlist; for ( int slot = 0; slot < n_tracks; slot++, lt++ ) { if ( lt->track_status < LTS_ACTIVE ) continue; report_counter_t *cnt = lt->track_type & LTTY_TRACK ? &versus : &battle; if ( lt->flags & G_LEFL_CUP ) cnt->visible++; else cnt->hidden++; if (IsTitleLT(lt)) cnt->title++; switch ( lt->flags & G_LEFL__RND ) { case G_LEFL_RND_HEAD: cnt->g_header++; break; case G_LEFL_RND_GROUP: cnt->g_member++; break; case G_LEFL__RND: cnt->g_both++; break; } if ( lt->flags & G_LEFL_NEW ) cnt->le_new++; if ( lt->flags & G_LEFL_TEXTURE) cnt->le_texture++; const DistribFlags_t dta = GetDistribFlagsLT(lt,false); if ( dta & G_DTA_BOOST ) cnt->boost++; if ( dta & G_DTA_NEW ) cnt->new++; if ( dta & G_DTA_AGAIN ) cnt->again++; if ( dta & G_DTA_UPDATE ) cnt->update++; if ( dta & G_DTA_FILL ) cnt->fill++; ccp sha1 = GetSha1LT(lt,false,0); if (sha1) InsertStringField(&cnt->sha1,sha1,false); } report_counters(lop->f,&versus,"versus track"); report_counters(lop->f,&battle,"battle arena"); ResetStringField(&versus.sha1); ResetStringField(&battle.sha1); } //--- find classes if ( opt & (O_CLASSES|O_XCLASSES) ) { uint *counter = CALLOC(G_TCLSI__N,sizeof(*counter)); TrackClassIndex_t *list = CALLOC(n_tracks,sizeof(*list)); le_track_t *lt = lop->ld->tlist; for ( int slot = 0; slot < n_tracks; slot++, lt++ ) { if (IsTitleLT(lt)) continue; const TrackClassIndex_t tidx = get_track_class_index(lt); list[slot] = tidx; if ( tidx >= 0 ) counter[tidx]++; } if ( !(opt&O_XCLASSES) ) { counter[G_TCLSI_NINTENDO] = 0; counter[G_TCLSI_SELECT] = 0; } for ( int ci = 0; ci < G_TCLSI__N; ci++ ) { const uint n = counter[ci]; if (!n) continue; fprintf(lop->f,"\r\n\r\n%s%u track%s of class %s%s\r\n\r\n", lop->colset->caption, n, n == 1 ? "" : "s", GetTrackClassName(ci,0), lop->colset->reset ); le_track_t *lt = lop->ld->tlist; for ( int slot = 0; slot < n_tracks; slot++, lt++ ) { if ( !IsTitleLT(lt) && get_track_class_index(lt) == ci ) fprintf(lop->f," %s %4u %s %s\r\n", PrintLEFL16(lt->flags,true), lt->track_slot, GetCupAlignLT(lt), GetXName2LT(lt,"-") ); } } FREE(list); FREE(counter); } //--- find duplicate names if ( opt & O_NAMES ) { StringField_t sha1_list; InitializeStringField(&sha1_list); ParamField_t pf; InitializeParamField(&pf); // collect data uint n = 0; le_track_t *lt = lop->ld->tlist; for ( int slot = 0; slot < n_tracks; slot++, lt++ ) { if (IsTitleLT(lt)) continue; ccp text = GetTextLT(lt,&lepar,0); if (text) { ccp sha1 = GetSha1LT(lt,false,0); if ( sha1 && !InsertStringField(&sha1_list,sha1,false) ) continue; ParamFieldItem_t *it = FindInsertParamField(&pf,text,false,0,0); if ( ++it->num == 2 ) n++; } } if (n) { fprintf(lop->f, "\r\n\r\n%s%u name%s are used by two or more distinct tracks" " (flags,slot,cup,name):%s\r\n", lop->colset->caption, n, n == 1 ? "" : "s", lop->colset->reset ); ParamFieldItem_t *ptr = pf.field, *end; for ( end = ptr + pf.used; ptr < end; ptr++ ) { if ( ptr->num < 2 ) continue; fputs("\r\n",lop->f); le_track_t *lt = lop->ld->tlist; for ( int slot = 0; slot < n_tracks; slot++, lt++ ) { if (IsTitleLT(lt)) continue; ccp name = GetNameLT(lt,0); if (!name || strcmp(name,ptr->key )) continue; fprintf(lop->f," %s %4u %s %s\r\n", PrintLEFL16(lt->flags,true), lt->track_slot, GetCupAlignLT(lt), GetXName2LT(lt,"-") ); } } } ResetParamField(&pf); ResetStringField(&sha1_list); } //--- find duplicate tracks, families and clans if ( opt & O_M_DUPLICATES ) { //-- search duplicates u16 *track_count = CALLOC(s1ref_max1_track,sizeof(*track_count)); u16 *family_count = CALLOC(s1ref_max1_family,sizeof(*family_count)); u16 *clan_count = CALLOC(s1ref_max1_clan,sizeof(*clan_count)); int have_dup_track = 0; int have_dup_family = 0; int have_dup_clan = 0; le_track_t *lt = lop->ld->tlist; for ( int slot = 0; slot < n_tracks; slot++, lt++ ) { if ( IsTitleLT(lt) ) continue; const u8 *sha1 = GetSha1BinLT(lt,false); if (sha1) { const sha1_reference_t *ref = FindSha1RefBin((void*)sha1); if (ref) { if ( track_count[ref->track]++ ) { if ( track_count[ref->track] == 2 ) have_dup_track++; } else if ( family_count[ref->family]++ ) { if ( family_count[ref->family] == 2 ) have_dup_family++; } else if ( clan_count[ref->clan]++ ) { if ( clan_count[ref->clan] == 2 ) have_dup_clan++; } } } } if ( track_count[0] >= 2 ) have_dup_track--; if ( family_count[0] >= 2 ) have_dup_family--; if ( clan_count[0] >= 2 ) have_dup_clan--; //-- output duplicates if ( opt & O_TRACKS ) report_duplicates(lop,0,"track","tracks",have_dup_track,track_count,s1ref_max1_track); if ( opt & O_FAMILIES ) report_duplicates(lop,1,"family","families",have_dup_family,family_count,s1ref_max1_family); if ( opt & O_CLANS ) report_duplicates(lop,2,"clan","clans",have_dup_clan,clan_count,s1ref_max1_clan); //-- clean data FREE(track_count); FREE(family_count); FREE(clan_count); } //--- terminate fputs("\r\n",lop->f); return ERR_OK; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: patch szs files /////////////// /////////////////////////////////////////////////////////////////////////////// #if LE_DIS_PATCH_ENABLED int patch_ld ( szs_iterator_t *it, // iterator struct with all infos bool term // true: termination hint ) { if (!term) { ccp path = it->path; if ( path[0] == '.' && path[1] == '/' ) path += 2; printf(" >> %s\n",path); } return 0; } /////////////////////////////////////////////////////////////////////////////// enumError PatchLD ( le_distrib_t *ld, mem_t mem_opt, StringField_t *filelist ) { if ( !ld || !filelist ) return ERR_MISSING_PARAM; enum { O_MENU = 0x0001, O_ALL = 0x0001, O_DEFAULT = O_ALL, O_F_TESTMODE = 0x0002, }; static const KeywordTab_t keytab[] = { { O_MENU, "MENUS", 0, 0 }, { O_DEFAULT, "DEFAULT", 0, O_DEFAULT }, { O_ALL, "ALL", 0, O_ALL }, { O_F_TESTMODE, "TESTMODE", 0, 0 }, {0,0,0,0} }; le_strpar_t lepar = { .opt = LEO_NAME }; KeyListParam_t keypar; InitializeKeyListParam(&keypar); keypar.func_arg = scan_source_opt; keypar.user_param = &lepar; u32 opt = 0; if (mem_opt.len) { const s64 def = *mem_opt.ptr == '-' ? O_ALL : 0; s64 res = ScanKeywordListP(&keypar,mem_opt.ptr,mem_opt.len,def,keytab); if ( res < 0 ) return ERR_SYNTAX; if ( res >= 0 ) opt = res; } if (!opt) opt = O_DEFAULT; PRINT0("SOURCE: %s\n",GetNameLSP(&lepar)); PRINT0("DEST: %s\n",opt_dest); const int testmode = opt & O_F_TESTMODE; ccp mydest = opt_dest && *opt_dest && strcmp(opt_dest,"-") ? opt_dest : 0; //--- main loop raw_data_t raw; InitializeRawData(&raw); ccp *ptr = filelist->field, *end = ptr + filelist->used; for ( ; ptr < end; ptr++ ) { #ifdef __CYGWIN__ ccp fname = AllocNormalizedFilenameCygwin(*ptr,false); #else ccp fname = *ptr; #endif enumError err = LoadRawData(&raw,false,fname,0,false,0); if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) continue; if ( err > ERR_WARNING ) return err; char dest[PATH_MAX]; SubstDest(dest,sizeof(dest),fname,mydest,"\1P/\1F", GetExtFF(raw.fform_file,raw.fform),false); const bool dest_is_source = IsSameFilename(dest,fname); if ( verbose >= 1 || testmode ) { ccp ff_name = GetNameFF(raw.fform_file,raw.fform); if (dest_is_source) fprintf(stdlog," > %satch %s:%s\n", testmode ? "Would p" : "P", ff_name, dest ); else fprintf(stdlog," > %satch %s:%s -> %s:%s\n", testmode ? "Would p" : "P", ff_name, raw.fname, ff_name, dest ); fflush(stdlog); } szs_file_t szs; AssignSZS(&szs,true,raw.data,raw.data_size,false,raw.fform,raw.fname); int count = 0; int stat = IterateFilesSZS(&szs,patch_ld,&count,0,0); MARK_USED(stat,count); ResetSZS(&szs); if ( fname != *ptr ) FreeString(fname); } ResetRawData(&raw); return ERR_OK; } #endif // LE_DIS_PATCH_ENABLED // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: text jobs /////////////// /////////////////////////////////////////////////////////////////////////////// enumError SeparatorLD ( le_distrib_t *ld, ccp arg ) { if (!ld) return ERR_MISSING_PARAM; FreeString(ld->separator); ld->separator = !arg ? 0 : !*arg ? EmptyString : STRDUP(arg); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CopyLD ( le_distrib_t *ld, const le_strpar_t *dest, const le_strpar_t *src, int n_src ) { if ( !ld || !dest || !src || n_src < 1 ) return ERR_MISSING_PARAM; char buf[LE_TRACK_STRING_MAX+10]; ccp sep = ld->separator ? ld->separator : " "; SetupReferenceList(ld,filter_options_td,dest->opt); for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { le_track_t *lt = *plt; char *bufptr = buf; for ( int idx = 0; idx < n_src; idx++ ) { if ( bufptr > buf ) bufptr = StringCopyE(bufptr,buf+sizeof(buf),sep); ccp text = GetTextLT(lt,src+idx,0); if (IsInputNameValidLEO(text,src[idx].opt)) bufptr = StringCopyE(bufptr,buf+sizeof(buf),text); if ( bufptr >= buf + LE_TRACK_STRING_MAX ) { buf[LE_TRACK_STRING_MAX-1] = 0; break; } } ccp *ptr = GetPtrLT(lt,dest); if (IsAssignmentAllowedLEO( ptr ? *ptr : 0,buf,dest->opt)) SetTextLT(lt,dest,buf); } return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError SplitLD ( le_distrib_t *ld, const le_strpar_t *dest, const le_strpar_t *src, ccp arg ) { if ( !ld || !dest || !src || !arg ) return ERR_MISSING_PARAM; PRINT0("SPLIT: %s := %s / %s\n",GetNameLSP(dest),GetNameLSP(src),arg); typedef struct { le_track_t *first; bool done; } storage_t; char destbuf[LE_TRACK_STRING_MAX+10]; exmem_dest_t exdest = { .buf = destbuf, .buf_size = sizeof(destbuf), .try_circ = true }; print_split_par_t psp = { .format = arg, .plus_mode = opt_plus, .u_use_list = true }; SetupReferenceList(ld,filter_options_td,dest->opt); for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { le_track_t *lt = *plt; ccp text = GetTextLT(lt,src,0); exmem_t res = PrintNameSPF(&exdest,text,0,&psp); SetTextLT(lt,dest,res.data.ptr); if (psp.u_item) { exmem_key_t *ek = psp.u_item; if (!ek->data.data.ptr) { ek->data.data.ptr = CALLOC(1,sizeof(storage_t)); ek->data.data.len = sizeof(storage_t); ek->data.is_alloced = true; storage_t *st = (storage_t*)ek->data.data.ptr; st->first = lt; } else { storage_t *st = (storage_t*)ek->data.data.ptr; ASSERT(st); ASSERT(st->first); ASSERT(st->first!=lt); if (!st->done) { st->done = true; ccp text2 = GetTextLT(st->first,src,0); exmem_t res2 = PrintNameSPF(&exdest,text2,0,&psp); SetTextLT(st->first,dest,res2.data.ptr); FreeExMem(&res2); } } } FreeExMem(&res); } ResetPSP(&psp); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError SubstLD ( le_distrib_t *ld, const le_strpar_t *dest, ccp arg ) { if ( !ld || !dest || !arg || !*arg ) return ERR_MISSING_PARAM; //--- setup Regex_t re; enumError err = ScanRegex(&re,true,arg); PRINT0("err=%d, valid=%d\n",err,re.valid); if ( err || !re.valid ) { ResetRegex(&re); return err ? err : ERR_INVALID_DATA; } struct { FastBuf_t b; char space[1000]; } result; InitializeFastBuf(&result,sizeof(result)); SetupReferenceList(ld,filter_options_td,dest->opt); //--- main loop for ( le_track_t **plt = ld->reflist; *plt; plt++ ) { le_track_t *lt = *plt; if ( lt->track_status < LTS_EXPORT ) continue; ccp text = GetTextLT(lt,dest,0); if ( text && *text ) { ReplaceRegex(&re,&result.b,text,-1); SetTextLT(lt,dest,result.b.buf); } } //--- terminate ResetRegex(&re); ResetFastBuf(&result.b); return ERR_OK; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: copy files /////////////// /////////////////////////////////////////////////////////////////////////////// enumError TransferFilesLD ( le_distrib_t *ld, bool logit, bool testmode ) { LogFile_t log0 = { .log = stdlog }; LogFile_t *log = logit ? &log0 : 0; TransferMode_t flags = testmode ? TFMD_F_TEST : 0; le_track_t *lt = ld->tlist; for ( int slot = 0; slot < ld->tlist_used; slot++, lt++ ) if ( lt->track_status >= LTS_EXPORT ) { ccp file = GetFileLT(lt,0); if (file) TransferTrackFile(log,lt->track_slot,file,flags); } // support for fill-tracks TransferTrackBySlot(log,ld->cup_battle.fill_slot,ld->cup_battle.fill_src,flags); TransferTrackBySlot(log,ld->cup_versus.fill_slot,ld->cup_versus.fill_src,flags); return ERR_OK; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: sort tracks /////////////// /////////////////////////////////////////////////////////////////////////////// enum // sort track options { // sort tracks STO_NONE = 0x0000, STO_NAME = 0x0001, STO_ID = 0x0002, STO_SHA1 = 0x0003, STO_M_JOBS = STO_NAME | STO_ID | STO_SHA1, STO_KEEP_VS = 0x0010, STO_KEEP_BT = 0x0020, STO_M_KEEP = STO_KEEP_VS | STO_KEEP_BT, STO_DEFAULT = 0, }; /////////////////////////////////////////////////////////////////////////////// #define MAX_SORT_MEMBERS 4 typedef struct sort_track_t { track_slot_t src_slot; // ? track_slot_t grp_used; // number of used elements in 'grp_member' track_slot_t grp_size; // number of alloced elements in 'grp_member' track_slot_t *grp_member; // either 'grp_buf' or alloced track_slot_t grp_buf[MAX_SORT_MEMBERS]; // initial buffer for 'grp_member' ccp key; // NULL or allocaed key for sorting } sort_track_t; /////////////////////////////////////////////////////////////////////////////// static void sort_tracks ( le_distrib_t *ld, const le_strpar_t *src, uint opt ) { DASSERT(ld); //-- setup data sort_track_t *slist = CALLOC(ld->tlist_used,sizeof(*slist)); sort_track_t *slist_end = slist + ld->tlist_used; sort_track_t *xlist = CALLOC(ld->tlist_used,sizeof(sort_track_t*)); sort_track_t *xptr = xlist; // [[2do]] ??? MARK_USED(xptr); //-- free data for ( sort_track_t *p = slist; p < slist_end; p++ ) { FreeString(p->key); if ( p->grp_member != p->grp_buf ) FREE(p->grp_member); } FREE(slist); FREE(xlist); } /////////////////////////////////////////////////////////////////////////////// bool SortTracksLD ( le_distrib_t *ld, const le_strpar_t *src, mem_t mem_opt ) { if (!ld) return false; //--- check options static const KeywordTab_t keytab[] = { { STO_NONE, "NONE", 0, STO_M_JOBS }, { STO_NAME, "NAME", 0, STO_M_JOBS }, { STO_ID, "ID", 0, STO_M_JOBS }, { STO_SHA1, "SHA1", 0, STO_M_JOBS }, { STO_KEEP_VS, "KEEP-VS", "KEEPVS", 0 }, { STO_KEEP_BT, "KEEP-BT", "KEEPBT", 0 }, { STO_M_KEEP, "KEEP-ALL", "KEEPALL", 0 }, {0,0,0,0} }; uint opt = 0; if (mem_opt.len) { s64 res = ScanKeywordListP(0,mem_opt.ptr,mem_opt.len,0,keytab); if ( res < 0 ) return false; opt = res; } if (!(opt&STO_M_JOBS)) { // [[2do]] warning return false; } //--- sort tracks UpdateCupsLD(ld); sort_tracks(ld,src,opt); //--- sort cups return true; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: sort cups /////////////// /////////////////////////////////////////////////////////////////////////////// enum // sort cup options { // sort cups SCO_VS_ORIG = 0x0001, SCO_VS_CUSTOM = 0x0002, SCO_VS_ALL = SCO_VS_ORIG | SCO_VS_CUSTOM, SCO_BT_ORIG = 0x0004, SCO_BT_CUSTOM = 0x0008, SCO_BT_ALL = SCO_BT_ORIG | SCO_BT_CUSTOM, SCO_M_JOB = SCO_VS_ALL | SCO_BT_ALL, SCO_USE_PLUS = 0x0010, SCO_USE_GAME = 0x0020, SCO_DEFAULT = 0, }; /////////////////////////////////////////////////////////////////////////////// #define MAX_SORT_MEMBERS 4 typedef struct sort_cup_t { int dummy; ccp key; // allocaed key for sorting } sort_cup_t; /////////////////////////////////////////////////////////////////////////////// #if 0 static void sort_cupss ( le_distrib_t *ld, const le_strpar_t *src, uint opt ) { DASSERT(ld); //-- setup data sort_track_t *slist = CALLOC(ld->tlist_used,sizeof(*slist)); sort_track_t *slist_end = slist + ld->tlist_used; sort_track_t *xlist = CALLOC(ld->tlist_used,sizeof(sort_track_t*)); sort_track_t *xptr = xlist; // [[2do]] ??? MARK_USED(xptr); //-- free data for ( sort_track_t *p = slist; p < slist_end; p++ ) { FreeString(p->key); if ( p->grp_member != p->grp_buf ) FREE(p->grp_member); } FREE(slist); FREE(xlist); } #endif /////////////////////////////////////////////////////////////////////////////// bool SortCupsLD ( le_distrib_t *ld, const le_strpar_t *src, mem_t mem_opt ) { if (!ld) return false; //--- check options static const KeywordTab_t keytab[] = { { SCO_VS_ORIG, "VS-ORIGINAL", "VSORIGINAL", 0 }, { SCO_VS_CUSTOM, "VS-CUSTOM", "VSCUSTOM", 0 }, { SCO_VS_ALL, "VS-ALL", "VSALL", 0 }, { SCO_BT_ORIG, "BT-ORIGINAL", "BTORIGINAL", 0 }, { SCO_BT_CUSTOM, "BT-CUSTOM", "BTCUSTOM", 0 }, { SCO_BT_ALL, "BT-ALL", "BTALL", 0 }, { SCO_USE_PLUS, "PLUS", 0, 0 }, { SCO_USE_GAME, "GAME", 0, 0 }, {0,0,0,0} }; uint opt = 0; if (mem_opt.len) { s64 res = ScanKeywordListP(0,mem_opt.ptr,mem_opt.len,0,keytab); if ( res < 0 ) return false; opt = res; } if (!(opt&SCO_M_JOB)) { // [[2do]] warning return false; } //--- sort cups UpdateCupsLD(ld); // [[2do]] ??? //--- sort cups return true; } // /////////////////////////////////////////////////////////////////////////////// /////////////// le_distrib_t: distribution /////////////// /////////////////////////////////////////////////////////////////////////////// void UpdateDistribLD ( le_distrib_t *ld, bool overwrite ) { DASSERT(ld); char buf[100]; UpdateCupsLD(ld); //-- UUID uuid_buf_t uuid; bool create_uuid = overwrite; if (!create_uuid) { const ParamFieldItem_t *it = FindParamField(&ld->dis_param,"UUID"); if ( !it || !it->data || ScanUUID(uuid,(ccp)it->data) == (ccp)it->data ) create_uuid = true; } if (create_uuid) { CreateTextUUID(buf,sizeof(buf)); ReplaceParamField(&ld->dis_param,"UUID",false,0,STRDUP(buf)); } //-- time stamps ccp tim = PrintTimeByFormat("%F %T %z",GetTimeSec(false)); if ( overwrite || !FindParamField(&ld->dis_param,"FIRST-CREATION") ) ReplaceParamField(&ld->dis_param,"FIRST-CREATION",false,0,STRDUP(tim)); ReplaceParamField(&ld->dis_param,"LAST-UPDATE",false,0,STRDUP(tim)); } /////////////////////////////////////////////////////////////////////////////// bool AddDistribParamLD ( le_distrib_t *ld, ccp src, ccp end ) { if ( !src || *src != '@' ) return false; char name_buf[100], *name = name_buf; src++; //-- scan name for(;;) { const char ch = *src; if ( !isalnum(ch) && ch != '_' && ch != '-' && ch != '.' ) break; src++; if ( name < name_buf + sizeof(name_buf) - 1 ) *name++ = toupper(ch); } //-- skip spaces while ( src < end && (uchar)*src <= ' ' ) src++; //--- check for '=' if ( src < end && *src == '=' ) { src++; while ( src < end && (uchar)*src <= ' ' ) src++; } if ( name > name_buf ) { PRINT0("@%.*s = |%.*s|\n",(int)(name-name_buf),name_buf,(int)(end-src),src); ReplaceParamField( &ld->dis_param, MEMDUP(name_buf,name-name_buf), true, 0, MEMDUP(src,end-src) ); } return true; } /////////////////////////////////////////////////////////////////////////////// enumError ImportDistribLD ( le_distrib_t *ld, cvp data, uint size ) { if ( !ld || !data || !size ) return ERR_MISSING_PARAM; ScanText_t st; SetupScanText(&st,data,size); st.ignore_values = false; const enumError err = ImportDistribSTLD(ld,&st); ResetScanText(&st); return err; } //----------------------------------------------------------------------------- enumError ImportDistribSTLD ( le_distrib_t *ld, ScanText_t *st ) { if ( !ld || !st ) return ERR_MISSING_PARAM; while (NextLineScanText(st)) { PRINT0("LINE: |%.*s|\n",(int)(st->eol-st->line),st->line); if ( *st->line == '@' ) AddDistribParamLD(ld,st->line,st->eol); else ScanSha1LineLD(ld,st->line,st->eol,false); } return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// enumError CreateDistribLD ( FILE *f, le_distrib_t *ld, bool use_xname ) { if ( !f || !ld ) return ERR_MISSING_PARAM; UpdateDistribLD(ld,false); ccp uuid = GetParamFieldStr(&ld->dis_param,1,"UUID",""); fprintf(f,text_distrib_head_cr ,GetParamFieldStr(&ld->dis_param,1,"NAME","?") ,GetParamFieldStr(&ld->dis_param,1,"VERSION","?") ,GetParamFieldStr(&ld->dis_param,1,"AUTHORS","?") ,GetParamFieldStr(&ld->dis_param,1,"RELEASE-DATE","") ,GetParamFieldStr(&ld->dis_param,1,"KEYWORDS","") ,GetParamFieldStr(&ld->dis_param,1,"PREDECESSOR","") ,GetParamFieldStr(&ld->dis_param,1,"WIIMMFI-REGION","") ,GetParamFieldStr(&ld->dis_param,1,"INFO-TEXT","") ,GetParamFieldStr(&ld->dis_param,1,"INFO-URL","") ,uuid ,GetParamFieldIntMM(&ld->dis_param,1,"DISPLAY-MODE",3,0,3) ,GetParamFieldIntMM(&ld->dis_param,1,"DATABASE-NAME",1,0,1) ,GetParamFieldIntMM(&ld->dis_param,1,"VIEW-COMMENT",0,0,1) ,GetParamFieldIntMM(&ld->dis_param,1,"ENABLE-NEW",0,0,1) ,GetParamFieldIntMM(&ld->dis_param,1,"ENABLE-AGAIN",0,0,1) ,GetParamFieldIntMM(&ld->dis_param,1,"ENABLE-FILL",0,0,1) ,GetParamFieldIntMM(&ld->dis_param,1,"ENABLE-UPDATE",0,0,1) ,GetParamFieldIntMM(&ld->dis_param,1,"ENABLE-BOOST",0,0,1) ,GetParamFieldStr(&ld->dis_param,1,"COMMENT1","") ,GetParamFieldStr(&ld->dis_param,1,"COMMENT2","") ,GetParamFieldStr(&ld->dis_param,1,"COMMENT3","") ,GetParamFieldStr(&ld->dis_param,1,"COMMENT4","") ,GetParamFieldStr(&ld->dis_param,1,"COMMENT5","") ,GetParamFieldStr(&ld->dis_param,1,"USER-CT-WIIMM","") ,GetParamFieldStr(&ld->dis_param,1,"USER-WIIMMFI","") ,GetParamFieldStr(&ld->dis_param,1,"USER-CT-WIIKI","") ,GetParamFieldStr(&ld->dis_param,1,"USER-MISC","") ,GetParamFieldStr(&ld->dis_param,1,"MAIL","") ,GetParamFieldStr(&ld->dis_param,1,"NOTE-FOR-WIMMM","") ,uuid ,VERSION ,REVISION_NUM ,GetParamFieldStr(&ld->dis_param,1,"FIRST-CREATION","") ,GetParamFieldStr(&ld->dis_param,1,"LAST-UPDATE","") ); //--- mark some more params as 'used' GetParamFieldStr(&ld->dis_param,1,"REFERENCE-NAME",""); GetParamFieldStr(&ld->dis_param,1,"WSZST-VERSION",""); GetParamFieldStr(&ld->dis_param,1,"WSZST-REVISION",""); //--- additional parameters ccp info = text_distrib_param_cr; const ParamFieldItem_t *it = ld->dis_param.field; for ( int i = 0; i < ld->dis_param.used; i++, it++ ) if (!it->num) { int len = strlen(it->key); ccp sep = len > 14 ? " " : len > 6 ? "\t" : "\t\t"; fprintf(f,"%s@%s%s= %s\r\n", info, it->key, sep, (ccp)it->data ); info = ""; } //--- sha1 list of tracks and arenas fputs(text_distrib_tracks_cr,f); CreateSha1LD(f,ld,use_xname,false); fputs("\r\n",f); return ERR_OK; } // /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void progress_lta ( lta_manager_t *lm, bool force ) { DASSERT(lm); if ( lm->show_progress > 0 && lm->base_slot >= 0 ) { const u_msec_t now = GetTimerMSec(); if ( now >= lm->last_log_msec + lm->show_progress || force && now >= lm->last_log_msec + 1000 ) { lm->last_log_msec = now; fprintf(stdlog, "%s%u node%s with %u track%s processed in %llu seconds.\n", verbose >= 1 ? " > " : " - ", lm->progress_nodes, lm->progress_nodes == 1 ? "" : "s", lm->progress_tracks, lm->progress_tracks == 1 ? "" : "s", ( now - lm->start_msec ) / 1000 ); fflush(stdlog); } } } //----------------------------------------------------------------------------- static enumError close_lta ( lta_manager_t *lm ) { enumError err = ERR_OK; if ( lm->base_slot >= 0 && lm->F.f ) { progress_lta(lm,true); const int n_slots = lm->last_slot + 1 - lm->base_slot; const uint node_size = ALIGN32( n_slots * sizeof(lta_node_t), opt_align_lta ); const uint ext_off = lm->data_offset + node_size; const uint ext_size = lm->append_ext ? GetFastBufLen(&lm->ext) : 0; const uint file_size = ALIGN32( ext_off + ext_size, opt_align_lta ); lta_header_t head = { .magic = hton64(LTA_MAGIC_NUM), .version = htonl(LTA_VERSION), .head_size = htonl(sizeof(lta_header_t)), .file_size = htonl(file_size), .node_off = htonl(lm->data_offset), .node_size = htonl(sizeof(lta_node_t)), .ext_off = htonl(ext_off), .ext_size = htonl(ext_size), .base_slot = htonl(lm->base_slot), .n_slots = htonl(n_slots), }; WriteFileAt(&lm->F,&lm->current_offset,0,&head,sizeof(head)); write_be32n((u32*)lm->node,(u32*)lm->node,sizeof(lm->node)/4); WriteFileAt(&lm->F,&lm->current_offset,lm->data_offset,lm->node,node_size); lm->data_offset += node_size; if (ext_size) { WriteFileAt(&lm->F,&lm->current_offset,ext_off,lm->ext.buf,ext_size); lm->data_offset = ext_off + ext_size; } const int fill_size = file_size - lm->current_offset; if ( fill_size > 0 ) { memset(lm->node,0,sizeof(lm->node)); // use as temporary buffer WriteFileAt(&lm->F,&lm->current_offset,lm->current_offset,lm->node,fill_size); } SetFileSize(&lm->F,&lm->current_offset,file_size); if ( verbose >= 0 ) { CloseFile(&lm->F,0); char d_buf[20], lfl_buf[20], cache_buf[20], conv_buf[20], dup_buf[30]; *d_buf = *lfl_buf = *conv_buf = *cache_buf = *dup_buf = 0; if (lm->d_count) snprintf(d_buf,sizeof(d_buf),", %u _d",lm->d_count); if (lm->lfl_count) snprintf(lfl_buf,sizeof(lfl_buf),", %u LFL",lm->lfl_count); if (lm->cache_count) snprintf(cache_buf,sizeof(cache_buf),", %u cache",lm->cache_count); if (lm->convert_count) snprintf(conv_buf,sizeof(conv_buf),", %u conv",lm->convert_count); if (lm->lfl_dup_count) snprintf(dup_buf,sizeof(dup_buf),", %u+%u dup",lm->szs_dup_count,lm->lfl_dup_count); else if (lm->szs_dup_count) snprintf(dup_buf,sizeof(dup_buf),", %u dup",lm->szs_dup_count); fprintf(stdlog, "%sCreated LTA:%s (%s), slots %u..%u (%u std%s%s%s%s%s), %s\n", verbose >= 1 ? " > " : " - ", lm->F.fname, PrintSize1000(0,0,lm->data_offset,0), lm->base_slot, lm->last_slot, lm->std_count, d_buf, lfl_buf, cache_buf, conv_buf, dup_buf, PrintTimerNSec6(0,0,GetTimerNSec()-lm->start_nsec,0) ); fflush(stdlog); } } //--- reset data ResetFastBuf(&lm->ext); ResetFile(&lm->F,0); ResetEML(&lm->stored_szs); ResetEML(&lm->stored_lfl); lm->start_nsec = GetTimerNSec(); lm->base_slot = -1; lm->last_slot = 0; lm->std_count = 0; lm->d_count = 0; lm->lfl_count = 0; lm->cache_count = 0; lm->convert_count = 0; lm->szs_dup_count = 0; lm->lfl_dup_count = 0; lm->progress_nodes = 0; lm->progress_tracks = 0; lm->start_msec = lm->last_log_msec = GetTimerMSec(); memset(lm->node,0,sizeof(lm->node)); return err; } //----------------------------------------------------------------------------- static void load_file_lta ( lta_manager_t *lm, // lta manager lta_node_record_t *rec, // record to process ccp fname, // file name bool is_d // TRUE: is _d file ) { DASSERT(lm); DASSERT(rec); DASSERT(fname); progress_lta(lm,false); uint fsize = rec->szs_size; if (!fsize) return; if ( lm->buf_size < fsize ) { FREE(lm->buf); lm->buf = MALLOC(fsize); lm->buf_size = fsize; } u8 *fdata = lm->buf; if (!LoadFILE(fname,0,0,fdata,fsize,0,0,false)) { lm->progress_tracks++; if (lm->rm_source) InsertStringField(&lm->ld->remove_files,fname,false); szs_file_t szs; AssignSZS(&szs,true,fdata,fsize,false,FF_UNKNOWN,fname); PRINT0("data=%d,%zu, cdata=data=%d,%zu, ff=%s\n", szs.data!=0, szs.size,szs.cdata!=0, szs.csize, GetNameFF_SZS(&szs) ); if ( lm->force_ff != FF_UNKNOWN && lm->force_ff != szs.fform_file ) { enumError err = CompressWith(&szs,COMPR_DEFAULT,false,lm->force_ff,FF_UNKNOWN); if ( !err && szs.cdata ) { fdata = szs.cdata; fsize = szs.csize; if (szs.cache_used) lm->cache_count++; else { lm->convert_count++; #if 0 if ( !err && szs.cache_fname ) { File_t F; CreateFile(&F,true,szs.cache_fname,FM_SILENT|FM_REMOVE); if (F.f) fwrite(fdata,1,fsize,F.f); ResetFile(&F,false); } #endif } } } ccp ext = GetExtFF(szs.fform_file,szs.fform_arch); if (ext) AppendFastBuf(&lm->ext,ext+1,strlen(ext)); else AppendFastBuf(&lm->ext,EmptyString,1); if (is_d) lm->d_count++; else lm->std_count++; sha1_size_b64_t sha1_szs; CreateSS64(fdata,fsize,sha1_szs); bool old_found; exmem_key_t *eml_szs = FindInsertEML(&lm->stored_szs,sha1_szs,CPM_COPY,&old_found); if (old_found) { lm->szs_dup_count++; *rec = *(lta_node_record_t*)eml_szs->data.data.ptr; return; } rec->szs_off = lm->data_offset; rec->szs_size = fsize; WriteFileAt(&lm->F,&lm->current_offset,lm->data_offset,fdata,fsize); lm->data_offset += ALIGN32(fsize,opt_align_lta); DecodeWU8(&szs); CollectCommonFilesSZS(&szs,true,SORT_NONE); CreateLFL(&szs,0,true,true); // sorting is defined here PRINT0("LFL: %p %zu\n",szs.data,szs.size); if (szs.size) { sha1_size_b64_t sha1_lfl; CreateSS64(szs.data,szs.size,sha1_lfl); bool old_found; exmem_key_t *eml_lfl = FindInsertEML(&lm->stored_lfl,sha1_lfl,CPM_COPY,&old_found); if (old_found) { lm->lfl_dup_count++; lta_node_par_t *temp = (lta_node_par_t*)eml_lfl->data.data.ptr; rec->lfl_off = temp->offset; rec->lfl_size = temp->size; } else { rec->lfl_off = lm->data_offset; rec->lfl_size = szs.size; WriteFileAt(&lm->F,&lm->current_offset,lm->data_offset,szs.data,szs.size); lm->data_offset += ALIGN32(szs.size,opt_align_lta); lm->lfl_count++; lta_node_par_t par = { .offset = rec->lfl_off, .size = rec->lfl_size }; eml_lfl->data = ExMemDup(&par,sizeof(par)); } szs.fname = 0; } ResetSZS(&szs); eml_szs->data = ExMemDup(rec,sizeof(*rec)); } } //----------------------------------------------------------------------------- enumError CreateTrackArchivesLD ( le_distrib_t *ld, ccp destdir, mem_t mem_opt ) { if ( !ld || !destdir ) return ERR_MISSING_PARAM; enum { O_YAZ = 0x001, // force FF_YAZ0 compression O_BZ = 0x002, // force FF_BZ compression O_YBZ = 0x003, // force FF_YBZ compression O_BZIP2 = 0x004, // force FF_BZIP2 compression O_LZ = 0x005, // force FF_LZ compression O_YLZ = 0x006, // force FF_YLZ compression O_LZMA = 0x007, // force FF_LZMA compression O_M_METHOD = 0x007, // mask for above O_RM = 0x008, O_EXT = 0x010, O_REDIR = 0x020, O_ALL = 0x1ff, O_DEFAULT = 0 }; static const KeywordTab_t keytab[] = { { O_YAZ, "YAZ0", "SZS", O_M_METHOD }, { O_BZ, "WBZ", "BZ", O_M_METHOD }, { O_YBZ, "YBZ", 0, O_M_METHOD }, { O_BZIP2, "BZIP2", "BZ2", O_M_METHOD }, { O_LZ, "WLZ", "LZ", O_M_METHOD }, { O_YLZ, "YLZ", 0, O_M_METHOD }, { O_LZMA, "LZMA", 0, O_M_METHOD }, { O_RM, "REMOVE", "RM", 0 }, { O_EXT, "EXT", 0, 0 }, { O_REDIR, "REDIR", 0, 0 }, { LEO_PROGRESS0, "PR0", 0, LEO_M_PROGRESS }, { LEO_PROGRESS1, "PR1", 0, LEO_M_PROGRESS }, { LEO_PROGRESS2, "PR2", 0, LEO_M_PROGRESS }, { LEO_PROGRESS5, "PR5", 0, LEO_M_PROGRESS }, { LEO_PROGRESS10, "PR10", "PROGRESS", LEO_M_PROGRESS }, { LEO_PROGRESS20, "PR20", 0, LEO_M_PROGRESS }, { LEO_PROGRESS30, "PR30", 0, LEO_M_PROGRESS }, { LEO_PROGRESS60, "PR60", 0, LEO_M_PROGRESS }, {0,0,0,0} }; static file_format_t ff_tab[] = { FF_UNKNOWN, FF_YAZ0, FF_BZ, FF_YBZ, FF_BZIP2, FF_LZ, FF_YLZ, FF_LZMA, }; uint opt = O_DEFAULT | ( ld->spar.opt & LEO_M_PROGRESS ); if (mem_opt.len) { s64 res = ScanKeywordListP(0,mem_opt.ptr,mem_opt.len,opt,keytab); if ( res < 0 ) return ERR_SYNTAX; if ( res >= 0 ) opt = res; } PRINT0("opt:%x, dir=%s\n",opt,destdir); //--------------- char tname[30], path[PATH_MAX]; lta_manager_t lm = { .ld = ld, .destdir = destdir, .rm_source = (opt&O_RM) != 0, .force_ff = ff_tab[opt&O_M_METHOD], .base_slot = -1, .append_ext = ( opt & O_EXT ) != 0, .show_progress = GetProgressMSecLEO(opt), }; close_lta(&lm); // initialize more vars if ( lm.force_ff != FF_UNKNOWN ) LoadSZSCache(); //--- optimization: find last used slot for ( int slot = ld->tlist_used -1 ; slot >= 0; slot-- ) { le_track_t *lt = GetTrackLD(ld,slot); if (!IsActiveLT(lt)) continue; ccp fname = GetPathL2(ld,lt,ld->spar.opt,0); if (!fname) continue; struct stat st; if ( stat(fname,&st) || !S_ISREG(st.st_mode) ) continue; lm.distrib_end_slot = slot + 1; PRINT0("distrib_end_slot = %d\n",lm.distrib_end_slot); break; } //--- main loop for ( int slot = 0; slot < lm.distrib_end_slot; slot++ ) { restart:; lm.progress_nodes++; le_track_t *lt = GetTrackLD(ld,slot); if (!IsActiveLT(lt)) { cont: AppendFastBuf(&lm.ext,EmptyString,2); // std + _d continue; } ccp fname = GetPathL2(ld,lt,ld->spar.opt,0); if (!fname) goto cont; struct stat st; if ( stat(fname,&st) || !S_ISREG(st.st_mode) ) goto cont; if ( slot >= lm.base_slot + LTA_MAX_NODES ) close_lta(&lm); if ( lm.base_slot < 0 ) { InitializeFastBufAlloc(&lm.ext,10000); lm.base_slot = slot; lm.max_slots = lm.distrib_end_slot - lm.base_slot; if ( lm.max_slots > LTA_MAX_NODES ) lm.max_slots = LTA_MAX_NODES; PRINT0("last_slot=%d, distrib_last_slot=%d => max_slots=%d\n",lm->last_slot,lm->distrib_last_slot,max_slots); lm.data_offset = ALIGN32(sizeof(lta_header_t),opt_align_lta); snprintf(tname,sizeof(tname),"tracks-%u",++lm.track_index); PathCatBufPPE(path,sizeof(path),lm.destdir,tname,".lta"); enumError err = CreateFileOpt(&lm.F,true,path,false,0); if (err) return err; } const uint saved_data_offset = lm.data_offset; const uint saved_ext_size = GetFastBufLen(&lm.ext); lta_node_t node = { .std.szs_size = st.st_size }; load_file_lta(&lm,&node.std,fname,false); //--- _d file Insert_d(path,sizeof(path),fname); const bool have_d = !stat(path,&st) && S_ISREG(st.st_mode); if (have_d) { node.d.szs_size = st.st_size; load_file_lta(&lm,&node.d,path,true); } else AppendFastBuf(&lm.ext,EmptyString,1); if (!node.d.szs_off) node.d = node.std; const uint node_size = ALIGN32( ( slot - lm.base_slot + 1 ) * sizeof(lta_node_t), opt_align_lta ); const uint ext_size = lm.append_ext ? ALIGN32(GetFastBufLen(&lm.ext),opt_align_lta) : 0; if ( lm.data_offset + node_size + ext_size> LTA_MAX_SIZE ) { lm.data_offset = saved_data_offset; SetFastBufLen(&lm.ext,saved_ext_size); close_lta(&lm); goto restart; } lm.node[ slot - lm.base_slot ] = node; lm.last_slot = slot; } close_lta(&lm); //--- create lta.szs if ( lm.track_index && opt & O_REDIR ) { PathCatBufPPE(path,sizeof(path),lm.destdir,"lta",".szs"); File_t F; enumError err = CreateFileOpt(&F,true,path,false,0); if (err) return err; fputs("#USE-LTA\r\n",F.f); CloseFile(&F,0); } //--- clean FREE(lm.buf); return ERR_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // lecode support void FreeLecodeLD ( le_distrib_t *ld, le_region_t reg ) { if ( !ld || reg < LEREG__BEG || reg > LEREG__END ) return; const int idx = reg - LEREG__BEG; if (ld->lecode_alloced[idx]) FREE((u8*)ld->lecode[idx].ptr); ld->lecode[idx].ptr = 0; ld->lecode[idx].len = 0; ld->lecode_alloced[idx] = false; } /////////////////////////////////////////////////////////////////////////////// mem_t AssignLecodeLD ( le_distrib_t *ld, le_region_t reg, cvp data, uint size ) { if ( !ld || reg < LEREG__BEG || reg > LEREG__END ) return NullMem; FreeLecodeLD(ld,reg); const int idx = reg - LEREG__BEG; mem_t *lec = ld->lecode + idx; if ( data && size ) { lec->ptr = MEMDUP(data,size); lec->len = size; ld->lecode_alloced[idx] = true; } return *lec; } /////////////////////////////////////////////////////////////////////////////// mem_t GetLecodeLD ( const le_distrib_t *ld, le_region_t reg ) { if ( !ld || reg < LEREG__BEG || reg > LEREG__END ) return NullMem; const int idx = reg - LEREG__BEG; mem_t lecode = ld->lecode[idx]; if (!lecode.ptr) lecode = GetLecodeLEREG(reg); return lecode; } // /////////////////////////////////////////////////////////////////////////////// /////////////// ledis_dump support /////////////// /////////////////////////////////////////////////////////////////////////////// int EnableLDUMP ( ccp filename ) { if ( ledis_dump_enabled >= 0 ) { ledis_dump_enabled = 1; if (filename) { FreeString(opt_create_dump); opt_create_dump = STRDUP(filename); } } return ledis_dump_enabled; } /////////////////////////////////////////////////////////////////////////////// enumError CloseLDUMP() { enumError err = ERR_NOTHING_TO_DO; if ( ledis_dump_enabled > 0 ) { if ( opt_create_dump && *opt_create_dump && ledis_dump.tlist_used ) { File_t F; err = CreateFile(&F,true,opt_create_dump,FM_OVERWRITE|FM_STDIO|FM_TOUCH); if (!err) err = CreateRefLD(F.f,&ledis_dump,true); CloseFile(&F,0); } ResetLD(&ledis_dump); ledis_dump_enabled = -1; } return err; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ImportAnaLDUMP ( const le_analyze_t *ana ) { if ( ledis_dump_enabled > 0 ) ImportAnaLD(&ledis_dump,ana); } /////////////////////////////////////////////////////////////////////////////// void ImportCtcodeLDUMP ( const ctcode_t *ctcode ) { if ( ledis_dump_enabled > 0 ) ImportCtcodeLD(&ledis_dump,ctcode); } // /////////////////////////////////////////////////////////////////////////////// /////////////// struct DistributionInfo_t /////////////// /////////////////////////////////////////////////////////////////////////////// void InitializeDistributionInfo ( DistributionInfo_t * dinf, bool add_default_param ) { DASSERT(dinf); memset(dinf,0,sizeof(*dinf)); InitializeLD(&dinf->ld); InitializeParamField(&dinf->translate); dinf->translate.free_data = true; uint slot; for ( slot = 0; slot < MAX_DISTRIBUTION_ARENA; slot++ ) { ParamField_t *pf = dinf->arena + slot; InitializeParamField(pf); pf->free_data = true; } for ( slot = 0; slot < MAX_DISTRIBUTION_TRACK; slot++ ) { ParamField_t *pf = dinf->track + slot; InitializeParamField(pf); pf->free_data = true; } if (add_default_param) AddParamDistributionInfo(dinf,true); } /////////////////////////////////////////////////////////////////////////////// void ResetDistributionInfo ( DistributionInfo_t * dinf ) { if (dinf) { ResetParamField(&dinf->translate); ResetLD(&dinf->ld); uint slot; for ( slot = 0; slot < MAX_DISTRIBUTION_ARENA; slot++ ) ResetParamField(dinf->arena+slot); for ( slot = 0; slot < MAX_DISTRIBUTION_TRACK; slot++ ) ResetParamField(dinf->track+slot); } } /////////////////////////////////////////////////////////////////////////////// void AddParamDistributionInfo ( DistributionInfo_t * dinf, bool overwrite ) { DASSERT(dinf); UpdateDistribLD(&dinf->ld,overwrite); } // /////////////////////////////////////////////////////////////////////////////// /////////////// slot translation /////////////// /////////////////////////////////////////////////////////////////////////////// char * ScanSlot ( uint *res_slot, ccp source, bool need_point ) { DASSERT(res_slot); DASSERT(source); *res_slot = 0; char *end; uint slot = strtoul(source,&end,10); if ( !need_point && slot >= MIN_DISTRIBUTION_SLOT && slot < MAX_DISTRIBUTION_TRACK && (uchar)*end <= ' ' && ( end-source == 2 || end-source == 3 )) { *res_slot = slot; return end; } slot *= 10; if ( slot < MIN_DISTRIBUTION_SLOT || slot >= MAX_DISTRIBUTION_TRACK || *end != '.' ) return (char*)source; uint track = strtoul(end+1,&end,10); slot += track; if ( track < 1 || track > 9 || slot >= MAX_DISTRIBUTION_TRACK ) return (char*)source; *res_slot = slot; while ( *end == ' ' || *end == '\t' ) end++; return end; } /////////////////////////////////////////////////////////////////////////////// void NormalizeSlotTranslation ( char *buf, uint bufsize, ccp name, char ** p_first_para, char ** p_first_brack ) { DASSERT(buf); DASSERT(bufsize>10); if (!name) name = ""; char *dest = buf, *buf_end = buf + bufsize - 4; char *first_para = 0; char *first_brack = 0; uint have_space = 0; while ( *name && dest < buf_end ) { int ch = (uchar)*name++; switch(ch) { case ' ': case '_': case '-': case '+': have_space++; break; case '\'': break; // ignore case '[': if (!first_brack) first_brack = dest; goto bracket; case '(': if (!first_para) first_para = dest; // fall through case '{': bracket: ch = '('; // fall through case '.': if (have_space) { have_space = 0; *dest++ = ' '; } *dest++ = ch; break; case '}': case ')': case ']': *dest++ = ')'; break; default: ch = tolower(ch); if ( ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' ) { if (have_space) { have_space = 0; *dest++ = ' '; } *dest++ = ch; } break; } } if ( dest > buf ) { if ( dest > buf+4 && !memcmp(dest-4,".szs",4) || !memcmp(dest-4,".wbz",4) ) dest -= 4; if ( dest > buf+2 && !memcmp(dest-2,"_d",2) ) dest -= 2; *dest = 0; if ( first_brack) { if ( first_brack >= dest ) first_brack = 0; else dest = first_brack; } if ( first_para >= dest ) first_para = 0; } else first_para = first_brack = 0; if (p_first_para) *p_first_para = first_para; if (p_first_brack) *p_first_brack = first_brack; } /////////////////////////////////////////////////////////////////////////////// uint DefineSlotTranslation ( ParamField_t *translate, bool is_arena, uint slot, ccp name ) { DASSERT(translate); if (!name) return 0; if (is_arena) { if ( slot >= MAX_DISTRIBUTION_ARENA ) slot = 0; else slot += DISTRIBUTION_ARENA_DELTA; } else { if ( slot >= MAX_DISTRIBUTION_TRACK ) slot = 0; } char buf[1000]; char *first_para = 0, *first_brack = 0; NormalizeSlotTranslation(buf,sizeof(buf),name,&first_para,&first_brack); uint count = 0; if (*buf) { ReplaceParamField(translate,buf,false,slot,0); count++; if (first_brack) { *first_brack = 0; ReplaceParamField(translate,buf,false,slot,0); count++; } if (first_para) { *first_para = 0; ReplaceParamField(translate,buf,false,slot,0); count++; } } return count; } /////////////////////////////////////////////////////////////////////////////// #undef EARLY_NUM_SLOT #define EARLY_NUM_SLOT 1 int FindSlotByTranslation ( const ParamField_t *translate, ccp fname, ccp sha1 ) { DASSERT(translate); ccp slash = strrchr(fname,'/'); if (slash) fname = slash+1; #if EARLY_NUM_SLOT uint slot; ScanSlot(&slot,fname,false); if (slot) return slot; #endif char buf[1000]; char *first_para = 0, *first_brack = 0; NormalizeSlotTranslation(buf,sizeof(buf),fname,&first_para,&first_brack); const ParamFieldItem_t *it = FindParamField(translate,buf); if ( !it && first_brack ) { *first_brack = 0; it = FindParamField(translate,buf); } if ( !it && first_para ) { *first_para = 0; it = FindParamField(translate,buf); } if ( !it && sha1 ) it = FindParamField(translate,sha1); #if EARLY_NUM_SLOT return it ? it->num : 0; #else if (it) return it->num; uint slot; ScanSlot(&slot,fname,false); return slot; #endif } #undef EARLY_NUM_SLOT /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void ScanSlotTranslation ( ParamField_t *translate, le_distrib_t *ld, ccp fname, bool ignore ) { DASSERT(translate); DASSERT(fname); PRINT(" - scan translation%s: %s\n", ld ? "+ld" : "", fname); szs_file_t szs; InitializeSZS(&szs); enumError err = LoadSZS(&szs,fname,true,ignore,true); if (err) return; //--- support for special file formats switch((int)szs.fform_arch) { case FF_BMG: if (verbose>1) printf(" - scan translation [BMG]: %s\n",fname); ResetSZS(&szs); return; } //--- standard text file if ( verbose > 1 ) printf("Scan translation%s[TEXT/%s]: %s\n", ld ? "+ld" : "", GetExtFF(szs.fform_file,szs.fform_arch), fname ); else if ( verbose >= 0 ) printf("Scan %s\n",fname); char *eol = (char*)szs.data; char *end = eol + szs.size; for(;;) { while ( eol < end && (uchar)*eol <= ' ' ) eol++; if ( eol == end ) break; char *src = eol; while ( eol < end && *eol != 0 && *eol != '\r' && *eol != '\n' ) eol++; *eol = 0; if ( *src == '#' || *src == '!' ) continue; //--- manage parameters if ( *src == '@' ) { if (ld) AddDistribParamLD(ld,src,eol); continue; } PRINT0("LINE: |%.*s|\n",(int)(eol-src),src); //--- scan new sha1 list if (ScanSha1LineLD(ld,src,eol,false)) continue; //--- skip "()" | detect SHA1 checksum char *sha1 = 0; if ( *src == '(' ) { while ( src < eol && *src != ')' ) src++; if ( src++ == eol ) continue; } else { // detect sha1 checksum char *x = src; while ( x < eol && isalnum((int)*x) ) x++; if ( x < eol && x - src == 40 ) { sha1 = src; src = x; } } while ( src < eol && (uchar)*src <= ' ' ) src++; const bool is_arena = *src == 'a' || *src == 'A'; if (is_arena) src++; if (!isdigit((int)*src)) continue; uint slot; src = ScanSlot(&slot,src,true); // ??? if (!slot) continue; char *tab = strrchr(src,'\t'); if (tab) src = tab; while ( src < eol && (uchar)*src <= ' ' ) src++; DefineSlotTranslation(translate,is_arena,slot,src); if (sha1) { sha1[40] = 0; DefineSlotTranslation(translate,is_arena,slot,sha1); } } ResetSZS(&szs); } // /////////////////////////////////////////////////////////////////////////////// /////////////// misc /////////////// /////////////////////////////////////////////////////////////////////////////// void ScanOptLeDefine ( ccp arg ) { opt_le_define = arg; if ( arg && *arg ) AppendStringField(&le_define_list,arg,false); } /////////////////////////////////////////////////////////////////////////////// void PrintDistribHead ( ccp format, ... ) { if (stdlog) { char buf[500]; va_list arg; va_start(arg,format); vsnprintf(buf,sizeof(buf),format,arg); va_end(arg); ASSERT(collog); fprintf(stdlog,"%s▼ %s%s\n",collog->caption,buf,collog->reset); fflush(stdlog); } } /////////////////////////////////////////////////////////////////////////////// ccp GetNameLTS ( le_track_status_t lts ) { switch(lts) { case LTS_INVALID: return "INVALID"; case LTS_VALID: return "VALID"; case LTS_FAIL: return "FAIL"; case LTS_ACTIVE: return "ACTIVE"; case LTS_FILL: return "FILL"; case LTS_EXPORT: return "EXPORT"; case LTS__N: break; } return 0; } // /////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// ///////////////////////////////////////////////////////////////////////////////