aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-brres.c
diff options
context:
space:
mode:
Diffstat (limited to 'project/src/lib-brres.c')
-rw-r--r--project/src/lib-brres.c2125
1 files changed, 2125 insertions, 0 deletions
diff --git a/project/src/lib-brres.c b/project/src/lib-brres.c
new file mode 100644
index 0000000..ea18724
--- /dev/null
+++ b/project/src/lib-brres.c
@@ -0,0 +1,2125 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * Wiimms SZS Tools *
+ * https://szs.wiimm.de/ *
+ * *
+ ***************************************************************************
+ * *
+ * This file is part of the SZS project. *
+ * Visit https://szs.wiimm.de/ for project details and sources. *
+ * *
+ * Copyright (c) 2011-2020 by Dirk Clemens <wiimm@wiimm.de> *
+ * *
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
+ * *
+ ***************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "lib-brres.h"
+#include "lib-mdl.h"
+#include "lib-pat.h"
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// brres helper ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static u16 get_highest_bit ( u8 val )
+{
+ // return bit number of highest set bit, or 0 if no bit is set.
+
+ return val & 0xf0
+ ? ( val & 0x80 ? 7
+ : val & 0x40 ? 6
+ : val & 0x20 ? 5
+ : 4 )
+ : ( val & 0x08 ? 3
+ : val & 0x04 ? 2
+ : val & 0x02 ? 1
+ : 0 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static u16 calc_brres_id
+(
+ ccp prev_name,
+ uint prev_len,
+ ccp cur_name,
+ uint cur_len
+)
+{
+ if ( prev_len < cur_len )
+ return cur_len - 1 << 3 | get_highest_bit(cur_name[cur_len-1]);
+
+ while ( cur_len-- > 0 )
+ {
+ const u8 ch = prev_name[cur_len] ^ cur_name[cur_len];
+ if (ch)
+ return cur_len << 3 | get_highest_bit(ch);
+ }
+
+ return ~(u16)0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void dump_brres_group
+(
+ const brres_root_t * root,
+ const endian_func_t * endian
+)
+{
+ const uint root_size = endian->rd32(&root->size);
+ const u8 * root_end = (u8*)root + root_size;
+
+ int grp_idx = 0;
+ const brres_group_t * grp = BRRES_ROOT_GROUP(root);
+ while ( (u8*)grp < root_end )
+ {
+ const uint grp_size = endian->rd32(&grp->size);
+ const uint n_entries = endian->rd32(&grp->n_entries);
+ const u8 * next_grp = (u8*)grp + grp_size;
+ noPRINT("GRP %2u: n=%02u, size=%04x\n",
+ grp_idx, n_entries, grp_size );
+ if ( next_grp > root_end )
+ break;
+
+ printf( "\n"
+ " idx ID ? left right name data magic name\n"
+ "%.79s\n",Minus300);
+
+ const brres_entry_t * entry = grp->entry;
+ const brres_entry_t * entry_end = entry + 1 + n_entries;
+ int entry_idx;
+ for ( entry_idx = 0; entry < entry_end; entry++, entry_idx++ )
+ {
+ ccp name = "";
+ uint name_len = 0;
+ const uint name_off = endian->rd32(&entry->name_off);
+ if (name_off)
+ {
+ name = (ccp)grp + name_off;
+ name_len = endian->rd32(name-4);
+ }
+
+ const uint data_off = endian->rd32(&entry->data_off);
+ const u8 * data = (u8*)grp + data_off;
+
+ printf("%4x: %4x %4x %4x %4x %8x %8x %-4s %.*s\n",
+ entry_idx,
+ endian->rd16(&entry->id),
+ endian->rd16(&entry->unknown),
+ endian->rd16(&entry->left_idx),
+ endian->rd16(&entry->right_idx),
+ endian->rd32(&entry->name_off),
+ endian->rd32(&entry->data_off),
+ data_off ? PrintID(data,4,0) : "-",
+ name_len, name );
+ }
+
+ grp = (brres_group_t*)next_grp;
+ grp_idx++;
+ }
+ putchar('\n');
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// get BRSUB info ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// http://wiki.tockdom.com/wiki/BRRES_Sub_Files_(File_Format)#Common_Format
+
+const brsub_info_t brsub_info[] =
+{
+
+ //-----------------------------------------------------------------------
+ // fform1+2, vers nsect warn // comment
+ //-----------------------------------------------------------------------
+
+ { FF_CHR, 0, 3,5, 1,2, BIMD_FATAL },
+ { FF_CHR, 0, 5,5, 2,2, BIMD_OK }, // [[2do]] verify hexdump
+
+ { FF_CLR, 0, 4,4, 2,2, BIMD_OK }, // [[2do]] verify hexdump
+
+ { FF_MDL, 0, 8,11, 11,14, BIMD_FATAL },
+ { FF_MDL, 0, 9,11, 11,14, BIMD_FATAL }, // found in SSBB
+ { FF_MDL, 0, 11,11, 14,14, BIMD_OK },
+
+ { FF_PAT, 0, 4,4, 6,6, BIMD_OK },
+
+ { FF_SCN, 0, 4,5, 6,7, BIMD_FATAL },
+ { FF_SCN, 0, 5,5, 7,7, BIMD_OK }, // [[2do]] verify hexdump
+
+ { FF_SHP, 0, 4,4, 3,3, BIMD_OK }, // [[2do]] verify hexdump
+
+ { FF_SRT, 0, 4,5, 1,2, BIMD_FATAL },
+ { FF_SRT, 0, 5,5, 2,2, BIMD_OK }, // [[2do]] verify hexdump
+
+ { FF_TEX,
+ FF_TEX_CT, 1,3, 1,1, BIMD_HINT },
+ { FF_TEX,
+ FF_TEX_CT, 2,3, 2,1, BIMD_FAIL },
+ { FF_TEX,
+ FF_TEX_CT, 3,3, 1,1, BIMD_OK },
+
+ //--- list terminated with fform1 == 0 (FF_UNKNOWN)
+ {0,0,0,0,0,0}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+const brsub_info_t * GetInfoBRSUB
+(
+ file_format_t fform, // file format
+ u32 version // sub version
+)
+{
+ if (fform)
+ {
+ const brsub_info_t *bi;
+ for ( bi = brsub_info; bi->fform1; bi++ )
+ if ( ( bi->fform1 == fform || bi->fform2 == fform )
+ && bi->version == version )
+ {
+ return bi;
+ }
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const brsub_info_t * GetCorrectBRSUB
+(
+ file_format_t fform // file format
+)
+{
+ if (fform)
+ {
+ const brsub_info_t *bi;
+ for ( bi = brsub_info; bi->fform1; bi++ )
+ if ( ( bi->fform1 == fform || bi->fform2 == fform ) && !bi->warn )
+ return bi;
+ }
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// get num of sections ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int GetKnownSectionNumBRSUB
+(
+ file_format_t fform, // file format
+ u32 version // sub version
+)
+{
+ noPRINT("GetKnownSectionNumBRSUB(%d,%d) ff=%s\n",
+ fform, version, GetNameFF(0,fform) );
+
+ const brsub_info_t *bi = GetInfoBRSUB(fform,version);
+ return bi ? bi->n_sect : -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int GetGenericSectionNumBRSUB
+(
+ const u8 * data, // valid data pointer
+ uint data_size, // size of 'data'
+ const endian_func_t * endian // NULL or endian functions
+)
+{
+ DASSERT(data);
+ if ( data_size < 0x20 )
+ return -1;
+
+ if (!endian)
+ endian = &be_func;
+ u32 max_offset = endian->rd32(data+4);
+ if ( max_offset > data_size )
+ max_offset = data_size;
+ const u32 body_size = max_offset;
+
+ int n_sect;
+ for ( n_sect = 0; ; n_sect++ )
+ {
+ u32 offset = 0x10 + n_sect * 4;
+ if ( offset >= max_offset )
+ break;
+
+ u32 grp_offset = endian->rd32(data+offset);
+ noPRINT("n=%2u, off=%5x, goff=%5x, maxoff=%5x\n",
+ n_sect, offset, grp_offset, max_offset );
+ if (!grp_offset)
+ continue;
+ if ( grp_offset <= offset || grp_offset > body_size || grp_offset&3 )
+ break;
+
+ if ( data[grp_offset]
+ && endian->rd32(data+grp_offset-4) == strlen((ccp)data+grp_offset) )
+ break;
+
+ if ( max_offset > grp_offset )
+ max_offset = grp_offset;
+ }
+
+ return n_sect;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int GetSectionNumBRSUB
+(
+ const u8 * data, // valid data pointer
+ uint data_size, // size of 'data'
+ const endian_func_t * endian // NULL or endian functions
+)
+{
+ DASSERT(data);
+
+// [[analyse-magic]]
+ const file_format_t fform = GetByMagicFF(data,data_size,data_size);
+ if ( fform && data_size >= 0x0c )
+ {
+ if (!endian)
+ endian = &be_func;
+ const u32 version = endian->rd32(data+8);
+ const int n = GetKnownSectionNumBRSUB(fform,version);
+ if ( n >= 0 )
+ return n;
+ }
+
+ return GetGenericSectionNumBRSUB(data,data_size,endian);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp PrintHeaderNameBRSUB
+(
+ char * buf, // result buffer
+ // If NULL, a local circulary static buffer is used
+ size_t buf_size, // size of 'buf', ignored if buf==NULL
+ const brsub_header_t * bhead, // valid brsub header
+ uint n_grp, // number of groups
+ const endian_func_t * endian // NULL or endian
+)
+{
+ DASSERT(bhead);
+
+ if (!buf)
+ buf = GetCircBuf( buf_size = 40 );
+
+ if (endian)
+ endian = &be_func;
+
+ snprintf( buf, buf_size,
+ ".BRSUB.header.%s-v%u.n%u.bin",
+ PrintID(bhead->magic,4,0),
+ endian->rd32(&bhead->version),
+ n_grp );
+
+ return buf;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// create brres ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool get_brres_id_bit
+(
+ brres_info_t * info,
+ u16 id
+)
+{
+ DASSERT(info);
+ const uint char_idx = id >> 3;
+ noPRINT("get_brres_id_bit(%x,%.*s) -> %d+%d/%d -> %d\n",
+ id, info->nlen, info->name,
+ char_idx, id&7, info->nlen,
+ char_idx < info->nlen
+ && info->name[char_idx] >> ( id & 7 ) & 1 );
+ return char_idx < info->nlen
+ && info->name[char_idx] >> ( id & 7 ) & 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CalcEntryBRRES
+(
+ brres_info_t * info,
+ uint entry_idx
+)
+{
+ DASSERT(info);
+
+ // setup 'entry' : item to insert
+ brres_info_t * entry = info + entry_idx;
+ entry->id = calc_brres_id(0,0,entry->name,entry->nlen);
+ entry->left_idx = entry->right_idx = entry_idx;
+
+ // setup 'prev' : the previuos 'current' item
+ brres_info_t * prev = info;
+
+ // setup 'current' : the current item while walking through the tree
+ uint current_idx = prev->left_idx;
+ brres_info_t * current = info + current_idx;
+
+ // last direction
+ bool is_right = false;
+
+ noPRINT("++ID: e=%zx,%x, p=%zx,%x, c=%zx,%x\n",
+ entry-info, entry->id,
+ prev-info, prev->id,
+ current-info, current->id ) ;
+
+ while ( entry->id <= current->id && current->id < prev->id )
+ {
+ if ( entry->id == current->id )
+ {
+ // calculate a new entry id
+ entry->id = calc_brres_id( current->name, current->nlen,
+ entry->name, entry->nlen );
+ if (get_brres_id_bit(current,entry->id))
+ {
+ entry->left_idx = entry_idx;
+ entry->right_idx = current_idx;
+ }
+ else
+ {
+ entry->left_idx = current_idx;
+ entry->right_idx = entry_idx;
+ }
+ }
+
+ prev = current;
+
+ is_right = get_brres_id_bit(entry,current->id);
+
+ current_idx = is_right ? current->right_idx : current->left_idx;
+ current = info + current_idx;
+ noPRINT("ID: e=%zx,%x, p=%zx,%x, c=%zx,%x\n",
+ entry-info, entry->id,
+ prev-info, prev->id,
+ current-info, current->id ) ;
+ }
+
+ if ( current->nlen == entry->nlen && get_brres_id_bit(current,entry->id) )
+ entry->right_idx = current_idx;
+ else
+ entry->left_idx = current_idx;
+
+ if (is_right)
+ prev->right_idx = entry_idx;
+ else
+ prev->left_idx = entry_idx;
+}
+
+//-----------------------------------------------------------------------------
+
+enumError CreateBRRES
+(
+ szs_file_t * szs, // valid szs
+ ccp source_dir, // NULL or path to source dir
+ u8 * source_data, // NULL or source data
+ u32 total_size // total file size
+)
+{
+ DASSERT(szs);
+ PRINT("CreateBRRES() ndir=%d, totsize=%x, align=%x\n",
+ szs->subfile.used, total_size, opt_align_brres );
+
+ enumError max_err = ERR_OK;
+ char path_buf[PATH_MAX];
+ const endian_func_t * endian = &be_func;
+ szs->endian = endian;
+
+
+ //--- sort
+
+ SortSubFilesSZS(szs,SORT_BRRES);
+
+
+ //--- load and analyze data
+
+ total_size = ALIGN32(total_size,opt_align_brres);
+ u8 * file_data = CALLOC(1,total_size);
+
+ uint n_directories = 1;
+ uint total_data_size = 0;
+ uint min_string_pool_size = 0;
+
+ {
+ // find all strings
+
+ u8 * data_ptr = file_data;
+ szs_subfile_t *f_ptr, *f_end = szs->subfile.list + szs->subfile.used;
+ for ( f_ptr = szs->subfile.list; f_ptr < f_end; f_ptr++ )
+ {
+ noPRINT_IF(f_ptr->is_dir, "DIR-ID=%d, |%s|\n",f_ptr->dir_id,f_ptr->path);
+
+ if (f_ptr->is_dir)
+ {
+ PRINT("DIR #%u FOUND: %s\n",n_directories,f_ptr->path);
+ IsolateFilename(path_buf,sizeof(path_buf),f_ptr->path,0);
+ InsertStringPool(&szs->string_pool,path_buf,false,0);
+ n_directories++;
+ }
+ else
+ {
+ f_ptr->offset = data_ptr - file_data;
+ u32 data_size = f_ptr->size;
+ if (f_ptr->data)
+ memcpy(data_ptr,f_ptr->data,data_size);
+ else
+ {
+ enumError err = LoadFILE(source_dir,f_ptr->path,0,
+ data_ptr,data_size,0,&szs->fatt,true);
+ if ( max_err < err )
+ max_err = err;
+ }
+
+// [[analyse-magic]]
+ f_ptr->fform = GetByMagicFF(data_ptr,data_size,data_size);
+ IsolateFilename(path_buf,sizeof(path_buf),f_ptr->path,f_ptr->fform);
+ InsertStringPool(&szs->string_pool,path_buf,false,0);
+
+ if (IsValidBRSUB(data_ptr,data_size,data_size,
+ 0,f_ptr->fform,true,f_ptr->path,endian) < VALID_ERROR )
+ {
+ noPRINT("----- %s -> %s -----\n",f_ptr->path,path_buf);
+ szs_file_t sub;
+ AssignSZS(&sub,true,data_ptr,data_size,false,FF_UNKNOWN,f_ptr->path);
+ if ( CollectStringsBRSUB(&szs->string_pool,false,&sub,false) > 0 )
+ {
+ // fix file size for later copy operation
+ const u32 new_data_size = endian->rd32(data_ptr+4);
+ if ( data_size > new_data_size )
+ data_size = new_data_size;
+
+ brsub_list_t * list = CALLOC(1,sizeof(*list));
+ list->data = data_ptr;
+ list->size = data_size;
+ list->next = szs->brsub_list;
+ szs->brsub_list = list;
+ }
+ ResetSZS(&sub);
+
+ const int string_pool_size = f_ptr->size - data_size;
+ if ( (int)min_string_pool_size < string_pool_size )
+ min_string_pool_size = string_pool_size;
+ }
+
+ total_data_size += ALIGN32(data_size,opt_align_brres);
+ data_ptr += ALIGN32(f_ptr->size,opt_align_brres);
+ DASSERT( data_ptr <= file_data + total_size );
+ }
+ }
+ DASSERT( data_ptr == file_data + total_size );
+
+ #if defined(TEST) && 0
+ ccp * ptr = szs->string_pool.sf.field;
+ ccp * end = ptr + szs->string_pool.sf.used;
+ while ( ptr < end )
+ PRINT(">%s<\n",*ptr++);
+ #endif
+ }
+
+
+ //--- size calculations
+
+ u32 grp_offset = sizeof(brres_header_t) + sizeof(brres_root_t);
+ u32 grp_end = grp_offset
+ + sizeof(brres_group_t) * n_directories
+ + sizeof(brres_entry_t) * ( n_directories + szs->subfile.used );
+
+ u32 data_offset = grp_end <= szs->min_data_off
+ ? ALIGN32(szs->min_data_off,opt_align_brres)
+ : ALIGN32(grp_end,
+ opt_align_brres > 0x20 ? opt_align_brres : 0x20 );
+ u32 str_offset = ALIGN32(data_offset + total_data_size, opt_align_brres );
+ CalcStringPool(&szs->string_pool,str_offset,endian);
+ u32 data_size = ALIGN32( str_offset + szs->string_pool.size,
+ opt_align_brres > 0x80 ? opt_align_brres : 0x80 );
+
+ PRINT("* GRP=%x..%x, DATA=%x, STR=%x, END=%x\n",
+ grp_offset, grp_end, data_offset, str_offset, data_size );
+
+
+ szs->fform_arch = FF_BRRES;
+ szs->ff_attrib = GetAttribFF(szs->fform_arch);
+ szs->ff_version = -1;
+ szs->data_alloced = true;
+ szs->size = data_size;
+ szs->file_size = data_size;
+
+ // alloc a little bit more temporary space if needed
+ if ( min_string_pool_size < szs->string_pool.size )
+ min_string_pool_size = szs->string_pool.size;
+ szs->data = CALLOC(1,szs->size - szs->string_pool.size + min_string_pool_size );
+
+
+ //--- setup BRRES header
+
+ brres_header_t * bhead = (brres_header_t*)szs->data;
+ memcpy(bhead->magic,BRRES_MAGIC,sizeof(bhead->magic));
+ endian->wr16(&bhead->bom,0xfeff);
+ endian->wr32(&bhead->size,data_size);
+ endian->wr16(&bhead->root_off,sizeof(brres_header_t));
+ endian->wr16(&bhead->n_sections, 2 + szs->subfile.used - n_directories );
+
+
+ //--- setup BRRES root
+
+ brres_root_t * broot = (brres_root_t*)(szs->data+sizeof(brres_header_t));
+ memcpy(broot->magic,BRRES_ROOT_MAGIC,sizeof(broot->magic));
+ endian->wr32(&broot->size, grp_end - grp_offset + sizeof(brres_root_t) );
+
+
+ //--- setup stack for directory offsets
+
+ typedef struct entry_pt_t
+ {
+ u8 * dest; // destination
+ u32 delta; // delta, that must be substracted
+
+ } entry_pt_t;
+
+ entry_pt_t * ep_list = CALLOC(n_directories+1,sizeof(entry_pt_t));
+ entry_pt_t * ep_read = ep_list;
+ entry_pt_t * ep_write = ep_list + 1; // skip first
+
+
+ //--- setup subfile int dir_id order
+
+ const uint dir_order_size = ( n_directories + 1 ) * sizeof(u16);
+ u16 * dir_order = MALLOC(dir_order_size);
+ memset(dir_order,~0,dir_order_size); // special value for empty directories
+
+ bool * dir_done = CALLOC(n_directories+1,sizeof(*dir_done));
+
+ u16 * dir_ptr = dir_order;
+ szs_subfile_t *f_ptr, *f_end = szs->subfile.list + szs->subfile.used;
+ for ( f_ptr = szs->subfile.list; f_ptr < f_end; f_ptr++ )
+ {
+ const u16 dir_id = f_ptr->dir_id;
+ DASSERT( dir_id < n_directories );
+ if (!dir_done[dir_id])
+ {
+ dir_done[dir_id] = true;
+ *dir_ptr++ = dir_id;
+ }
+ }
+ PRINT("DIR-PTR = %zu/%u\n", dir_ptr - dir_order, n_directories );
+ DASSERT( dir_ptr - dir_order <= n_directories );
+ FREE(dir_done);
+
+
+ //--- setup info
+
+ // that's enough!
+ const size_t info_size = ( szs->subfile.used + 1 ) * sizeof(brres_info_t);
+ brres_info_t * info_list = MALLOC(info_size);
+
+
+ //--- setup groups
+
+ u8 * data_ptr = szs->data + data_offset;
+ brres_group_t * grp = BRRES_ROOT_GROUP(broot);
+
+ int dir_index;
+ for ( dir_index = 0; dir_index < n_directories; dir_index++ )
+ {
+ u16 dir_id = dir_order[dir_index];
+ szs_subfile_t *f_ptr = szs->subfile.list;
+ while ( f_ptr < f_end && f_ptr->dir_id != dir_id )
+ f_ptr++;
+
+ int n_entries = 0;
+ szs_subfile_t *f_temp;
+ for ( f_temp = f_ptr; f_temp < f_end; f_temp++ )
+ if ( f_temp->dir_id == dir_id )
+ n_entries++;
+
+ noPRINT("----- NEW GRP #%u: N=%u, %zx -----\n",
+ dir_id, n_entries, (u8*)grp - szs->data );
+
+ //--- set entry point
+
+ noPRINT("EP-READ #%zu: %zx-%x := %zx\n",
+ ep_read - ep_list,
+ ep_read->dest ? ep_read->dest - szs->data : 0,
+ ep_read->delta,
+ (u8*)grp - szs->data - ep_read->delta );
+ if (ep_read->dest)
+ endian->wr32( ep_read->dest, (u8*)grp - szs->data - ep_read->delta );
+ ep_read++;
+
+
+ //--- setup info list
+
+ memset(info_list,0,info_size);
+ info_list->id = 0xffff;
+ brres_info_t *info_ptr = info_list + 1;
+
+
+ //--- setup group
+
+ endian->wr32(&grp->size, sizeof(brres_group_t)
+ + (n_entries+1) * sizeof(brres_entry_t) );
+ endian->wr32(&grp->n_entries,n_entries);
+
+ brres_entry_t * entry = grp->entry;
+ endian->wr16(&entry->id,0xffff);
+ entry++;
+
+ //--- iterate files
+
+ for ( ; f_ptr < f_end; f_ptr++ )
+ {
+ if ( f_ptr->dir_id != dir_id )
+ continue;
+
+ noPRINT("*** %d %3u %s\n",f_ptr->is_dir,f_ptr->dir_id,f_ptr->path);
+
+ //--- write name data
+
+ IsolateFilename(path_buf,sizeof(path_buf),f_ptr->path,f_ptr->fform);
+ const u32 str_off = FindStringPool(&szs->string_pool,path_buf,0);
+ endian->wr32( &entry->name_off, (ccp)szs->data + str_off - (ccp)grp );
+
+ info_ptr->name = (ccp)szs->string_pool.data + str_off - str_offset;
+ info_ptr->nlen = strlen(info_ptr->name);
+ info_ptr++;
+ DASSERT( info_ptr - info_list <= szs->subfile.used );
+
+ //---
+
+ CalcEntryBRRES( info_list, entry - grp->entry );
+
+ //---
+
+ if (f_ptr->is_dir)
+ {
+ ep_write->dest = (u8*)&entry->data_off;
+ ep_write->delta = (u8*)grp - szs->data;
+ noPRINT("EP-WRITE #%zu: %zx-%x\n",
+ ep_write - ep_list,
+ ep_write->dest - szs->data, ep_write->delta );
+ ep_write++;
+ DASSERT( ep_write - ep_list <= n_directories );
+ }
+ else
+ {
+ endian->wr32(&entry->data_off,(ccp)data_ptr-(ccp)grp);
+ u32 data_size = f_ptr->size;
+ memcpy(data_ptr,file_data+f_ptr->offset,data_size);
+
+ szs_file_t sub;
+// [[fname+]]
+ InitializeSubSZS( &sub,szs, data_ptr - szs->data, data_size,
+ FF_UNKNOWN, f_ptr->path, false );
+ AdjustStringsBRSUB(&sub,data_ptr,data_size);
+ ResetSZS(&sub);
+
+ brsub_list_t * list;
+ for ( list = szs->brsub_list; list; list = list->next )
+ if ( list->data == file_data + f_ptr->offset )
+ {
+ noPRINT("ADJUST\n");
+ DASSERT( list->size <= f_ptr->size );
+ data_size = list->size;
+ memset( data_ptr + data_size, 0, f_ptr->size - data_size );
+ break;
+ }
+
+ data_ptr += ALIGN32(data_size,opt_align_brres);
+ }
+
+ entry++;
+ }
+
+ //--- transfer data
+
+ info_ptr = info_list;
+ brres_entry_t * entry_ptr = grp->entry;
+ for ( ; entry_ptr < entry; entry_ptr++, info_ptr++ )
+ {
+ endian->wr16( &entry_ptr->id, info_ptr->id );
+ endian->wr16( &entry_ptr->left_idx, info_ptr->left_idx );
+ endian->wr16( &entry_ptr->right_idx, info_ptr->right_idx );
+ }
+
+ grp = (brres_group_t*)entry;
+ }
+
+
+ //--- copy new string pool
+
+ memcpy( szs->data + str_offset, szs->string_pool.data, szs->string_pool.size );
+
+
+ //--- clean
+
+ if (logging)
+ dump_brres_group(broot,endian);
+
+ FREE(ep_list);
+ FREE(info_list);
+ FREE(dir_order);
+ FREE(file_data);
+
+ return max_err;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// brres file iterator ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int iterate_brres_group
+(
+ szs_iterator_t * it, // valid iterator
+ const brres_group_t * grp, // valid grp
+ uint path_len // current path length
+)
+{
+ DASSERT(it);
+ DASSERT(it->szs);
+ DASSERT(it->func_it);
+ DASSERT(it->endian);
+ DASSERT(it->root_end);
+ DASSERT(grp);
+
+ szs_file_t * szs = it->szs;
+
+ const uint base_dir_index = it->index;
+ const uint n_entries = it->endian->rd32(&grp->n_entries);
+ const brres_entry_t * entry = grp->entry + 1;
+ const brres_entry_t * entry_end = entry + n_entries;
+ int stat = 0;
+
+ if (it->cut_files)
+ {
+ it->index = 0;
+ it->fst_item = 0;
+ it->is_dir = 0;
+
+ it->off = (u8*)grp - szs->data;
+ it->size = (u8*)entry_end - (u8*)grp;
+ StringCopyE(it->path+path_len,it->path+sizeof(it->path),".BRRES.group");
+ it->func_it(it,false);
+ }
+
+ for ( ; entry < entry_end && !stat; entry++ )
+ {
+ it->name = 0;
+ ccp name = "";
+ uint name_len = 0;
+ uint new_path_len = path_len;
+ const uint name_off = it->endian->rd32(&entry->name_off);
+ if (name_off)
+ {
+ it->name = name = (ccp)grp + name_off;
+ name_len = it->endian->rd32(name-4);
+ if ( path_len + name_len < sizeof(it->path)-2 )
+ {
+ it->trail_path = it->path+path_len;
+ memcpy(it->trail_path,name,name_len);
+ new_path_len += name_len;
+ it->path[new_path_len] = 0;
+ }
+ }
+
+ const u8 * data = (u8*)grp + it->endian->rd32(&entry->data_off);
+ it->index++;
+ if ( data < it->root_end )
+ {
+ it->is_dir = true;
+ it->off = base_dir_index;
+ it->size = 0;
+ it->path[new_path_len++] = '/';
+ it->path[new_path_len] = 0;
+ stat = it->func_it(it,false);
+ if (!stat)
+ stat = iterate_brres_group(it,(brres_group_t*)data,new_path_len);
+ }
+ else
+ {
+ it->is_dir = false;
+ it->off = (u8*)grp - szs->data + it->endian->rd32(&entry->data_off);
+ it->size = it->endian->rd32(szs->data+it->off+4);
+
+ if ( szs->min_data_off > it->off )
+ szs->min_data_off = it->off;
+
+ const u32 end_off = it->off + it->size;
+ if ( szs->max_data_off < end_off )
+ szs->max_data_off = end_off;
+
+ if ( opt_ext && it->szs->fform_arch == FF_BRRES )
+ {
+// [[analyse-magic]]
+ it->fform = GetByMagicFF(szs->data+it->off,it->size,it->size);
+ ccp ext = opt_ext > 1
+ ? GetMagicExtFF(0,it->fform)
+ : GetExtFF(0,it->fform);
+ StringCopyE( it->path+new_path_len, it->path+sizeof(it->path), ext );
+ }
+
+ noPRINT("DATA-OFF: %x .. %x\n",szs->min_data_off,szs->max_data_off);
+ stat = it->func_it(it,false);
+ it->fform = 0;
+ }
+ }
+ it->name = 0;
+
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int IterateFilesBRRES
+(
+ struct szs_iterator_t *it, // iterator struct with all infos
+ bool term // true: termination hint
+)
+{
+ if (term)
+ return 0;
+
+ DASSERT(it);
+ DASSERT(it->func_it);
+ szs_file_t * szs = it->szs;
+ DASSERT(szs);
+
+ //--- brres header
+
+ if ( !szs->data || szs->size < sizeof(brres_header_t) )
+ return -1;
+
+ const brres_header_t * bh = (brres_header_t*)szs->data;
+ if ( !IsRepairMagic() && memcmp(bh->magic,BRRES_MAGIC,sizeof(bh->magic)) )
+ return -1;
+
+ const endian_func_t * endian = GetEndianFunc(&bh->bom);
+ if (!endian)
+ return -1;
+ szs->endian = endian;
+
+ const uint data_size = endian->rd32(&bh->size);
+ const uint root_off = endian->rd16(&bh->root_off);
+ PRINT("data_size=%u, root_off=%u, n_sections=%u\n",
+ data_size,root_off, endian->rd16(&bh->n_sections));
+ if ( data_size > szs->size || root_off >= szs->size )
+ return -1;
+
+
+ //--- root
+
+ const brres_root_t * root = (brres_root_t*)(szs->data+root_off);
+ if ( !IsRepairMagic() && memcmp(root->magic,BRRES_ROOT_MAGIC,sizeof(root->magic)) )
+ return -1;
+
+ const uint root_size = endian->rd32(&root->size);
+ const u8 * root_end = (u8*)root + root_size;
+
+
+ //----- cut files?
+
+ if (it->cut_files)
+ {
+ it->index = 0;
+ it->fst_item = 0;
+ it->is_dir = 0;
+
+ it->off = 0;
+ it->size = sizeof(brres_header_t);
+ StringCopyS(it->path,sizeof(it->path),".BRRES.header");
+ it->func_it(it,false);
+
+ it->off = (u8*)root - szs->data;
+ it->size = sizeof(brres_root_t);
+ StringCopyS(it->path,sizeof(it->path),".BRRES.root");
+ it->func_it(it,false);
+ }
+
+
+ //--- iterate groups
+
+ #if defined(TEST) || 1
+ if (logging)
+ dump_brres_group(root,endian);
+ #endif
+
+ szs->max_data_off = 0;
+ szs->min_data_off = ~(u32)0;
+ it->endian = endian;
+ it->root_end = root_end;
+ int stat = iterate_brres_group(it,BRRES_ROOT_GROUP(root),0);
+
+ if ( !stat && szs->max_data_off )
+ {
+ it->index++;
+ it->is_dir = false;
+ it->off = szs->max_data_off;
+ it->size = data_size - szs->max_data_off;
+ StringCopyS(it->path,sizeof(it->path),BRRES_STRING_POOL_FNAME);
+ stat = it->func_it(it,false);
+ }
+ return stat;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// brres sub file ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetStringBRSUB
+(
+ const void * data, // relevant data
+ const void * end_of_src, // end of 'data', 0 if unknown
+ u32 min_sp_off, // minimum string pool offset
+ // maybe end of sub file, 0 if unknown
+ const void * base, // base for offset
+ u32 offset, // offset of string
+ ccp if_invalid, // result if invalid
+ const endian_func_t * endian // NULL (=use be) or endian functions
+)
+{
+ DASSERT(data);
+ DASSERT(base);
+
+ u32 data_size = end_of_src ? end_of_src - data : ~(u32)0;
+ if ( offset && !(offset&3) && offset < data_size && data )
+ {
+ offset += (u8*)base - (u8*)data;
+ if ( offset >= min_sp_off && offset < data_size )
+ {
+ ccp ptr = (ccp)data + offset;
+ if (!endian)
+ endian = &be_func;
+ if ( endian->rd32(ptr-4) == strlen(ptr) )
+ return ptr;
+ }
+ }
+ return if_invalid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetStringIteratorBRSUB
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ const void * base, // pointer to base for calculation
+ const void * ptr, // pointer to u32 string offset
+ ccp if_null, // result if offset == NULL
+ ccp if_invalid // result if invalid
+)
+{
+ u32 offset = it->endian->rd32(ptr);
+ return !offset
+ ? if_null
+ : GetStringBRSUB(
+ it->brsub,
+ (u8*)it->brsub + it->file_size,
+ it->static_strings ? 0 : it->data_size,
+ base,
+ offset,
+ if_invalid,
+ it->endian );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetStringBRRES
+(
+ const szs_file_t * szs, // base BRRES file
+ const void * base, // base for offset
+ u32 offset, // offset of string
+ ccp if_invalid // result if invalid
+)
+{
+ // this functions find all strings, not only relocation candidates
+
+ return szs
+ ? GetStringBRSUB(
+ szs->data,
+ szs->data + szs->size,
+ 0, //szs->max_data_off,
+ base,
+ offset,
+ if_invalid,
+ szs->endian )
+ : if_invalid;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// BRSUB iterator ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int brsub_string_func
+(
+ brsub_cut_t *bcut, // pointer to data structure
+ int grp, // index of group, <0: grp_entry_t
+ int entry // index of entry, -1: no entry
+)
+{
+ noPRINT("##A### XXX( %2d, %2d, %s )\n",grp,entry);
+
+ DASSERT(bcut);
+ szs_iterator_t *it = bcut->it;
+ DASSERT(it);
+ brsub_iterator_t *brit = bcut->param;
+ DASSERT(brit);
+ const endian_func_t * endian = it->endian;
+ DASSERT(endian);
+
+ if ( entry == ENTRY_IDX_GROUP )
+ {
+ PRINT_IF( bcut->fform == FF_PAT,
+ "#PAT# g+e=%d,%d | %s #%u\n",grp,entry,__FUNCTION__,__LINE__);
+ if (brit->grp_func)
+ it->index += brit->grp_func(brit,bcut->gptr,grp);
+ }
+ else if ( grp >= 0 && entry == ENTRY_IDX_DATA && !brit->raw_func )
+ {
+ PRINT_IF( bcut->fform == FF_PAT,
+ "#PAT# g+e=%d,%d v=%d | %s #%u\n",
+ grp, entry, bcut->version, __FUNCTION__, __LINE__ );
+
+ u8 *data = (u8*)bcut->data + it->off;
+ switch(BRSUB_MODE3(bcut->fform,bcut->version,grp))
+ {
+ case BRSUB_MODE3(FF_PAT,4,0):
+ {
+ pat_analyse_t ana;
+ IsValidPAT(bcut->data,bcut->size,bcut->size,0,0,0,&ana);
+ PRINT("\e[32m#PAT# grp=%d val=%d,%d | %s #%u\e[0m\n",
+ grp, ana.valid, ana.data_complete,
+ __FUNCTION__, __LINE__ );
+ if (!ana.data_complete)
+ break;
+
+ HEXDUMP16(10,0,data,it->size);
+ uint i;
+ for ( i = 0; i < ana.n_sect0; i++ )
+ {
+ pat_s0_belem_t *elem = ana.s0_base->elem + i;
+ it->index += brit->string_func(brit,data,&elem->offset_name);
+ }
+
+ for ( i = 0; i < PAT_MAX_ELEM; i++ )
+ {
+ pat_s0_sref_t *sref = ana.s0_sref[i];
+ if (sref)
+ it->index += brit->string_func(brit,(u8*)sref,&sref->offset_name);
+ }
+ HEXDUMP16(10,0,data,it->size);
+ }
+ break;
+
+ case BRSUB_MODE3(FF_PAT,4,1):
+ {
+ pat_analyse_t ana;
+ IsValidPAT(bcut->data,bcut->size,bcut->size,0,0,0,&ana);
+ PRINT("\e[32m#PAT# grp=%d val=%d,%d | %s #%u\e[0m\n",
+ grp, ana.valid, ana.data_complete,
+ __FUNCTION__, __LINE__ );
+ if (!ana.data_complete)
+ break;
+
+ HEXDUMP16(10,0,data,it->size);
+ uint i;
+ for ( i = 0; i < ana.n_sect1; i++ )
+ it->index += brit->string_func(brit,data,(u32*)data+i);
+ HEXDUMP16(10,0,data,it->size);
+ }
+ break;
+ }
+ }
+ else if ( grp < 0 || entry < 0 )
+ {
+ PRINT_IF( bcut->fform == FF_PAT,
+ "#PAT# g+e=%d,%d raw=%p | %s #%u\n",
+ grp,entry, brit->raw_func, __FUNCTION__, __LINE__ );
+ if ( entry == ENTRY_IDX_DATA && brit->raw_func )
+ it->index += brit->raw_func( brit, (u8*)bcut->data + it->off, it->size );
+ }
+ else
+ {
+ PRINT_IF( bcut->fform == FF_PAT,
+ "#PAT# g+e=%d,%d | %s #%u\n",grp,entry,__FUNCTION__,__LINE__);
+ entry++;
+ u8 *edata = (u8*)bcut->gptr + endian->rd32(&bcut->eptr->data_off);
+ if (brit->entry_func)
+ it->index += brit->entry_func(brit,bcut->gptr,grp,bcut->eptr,edata);
+
+ if (brit->string_func)
+ {
+ it->index += brit->string_func(brit,(u8*)bcut->gptr,&bcut->eptr->name_off);
+
+ u32 data_off = endian->rd32(&bcut->eptr->data_off);
+ u8 *data = (u8*)bcut->gptr + data_off;
+ const u8 *data_end = bcut->data + bcut->file_size;
+
+ if ( data_off && data < data_end )
+ {
+ if ( bcut->fform == FF_MDL && bcut->version == 11 )
+ {
+ switch(grp)
+ {
+ case 1:
+ it->index += brit->string_func(brit,data,(u32*)(data+0x08));
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ it->index += brit->string_func(brit,data,(u32*)(data+0x0c));
+ break;
+
+ case 8:
+ {
+ it->index += brit->string_func(brit,data,(u32*)(data+0x08));
+
+ const uint n_layer = endian->rd32(data+0x2c);
+ u32 layer_off = endian->rd32(data+0x30);
+ uint i;
+ for ( i = 0; i < n_layer; i++, layer_off += 0x34 )
+ {
+ u8 * d = data + layer_off;
+ it->index += brit->string_func(brit,d,(u32*)d);
+ }
+ }
+ break;
+
+ case 10:
+ it->index += brit->string_func(brit,data,(u32*)(data+0x38));
+ break;
+ }
+ }
+ else if ( grp == 0 )
+ {
+ switch(BRSUB_MODE2(bcut->fform,bcut->version))
+ {
+ case BRSUB_MODE2(FF_CHR,3):
+ case BRSUB_MODE2(FF_CHR,5):
+ case BRSUB_MODE2(FF_CLR,4):
+ case BRSUB_MODE2(FF_SRT,4):
+ case BRSUB_MODE2(FF_SRT,5):
+ it->index += brit->string_func(brit,data,(u32*)(data+0x00));
+ break;
+
+ case BRSUB_MODE2(FF_SHP,4):
+ it->index += brit->string_func(brit,data,(u32*)(data+0x04));
+ break;
+
+ case BRSUB_MODE2(FF_SCN,4):
+ case BRSUB_MODE2(FF_SCN,5):
+ it->index += brit->string_func(brit,data,(u32*)(data+0x20));
+ break;
+
+ case BRSUB_MODE2(FF_PAT,4):
+ DASSERT(0);
+ break;
+
+ default:
+ break;
+ } // switch
+ } // else
+ } // if
+ } // if string_func
+ } // else entry >= 0
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// [[IterateStringsBRSUB]]
+
+int IterateStringsBRSUB
+(
+ brsub_iterator_t * it // valid iterator data
+)
+{
+ DASSERT(it);
+ DASSERT(it->szs);
+ DASSERT( it->szs->file_size > 0 );
+ DASSERT( it->szs->file_size < 0x1000000 );
+ DASSERT( it->file_size > 0 );
+ DASSERT( it->file_size < 0x1000000 );
+
+ const endian_func_t * endian = it->endian;
+ if (!endian)
+ it->endian = endian = it->szs && it->szs->endian ? it->szs->endian : &be_func;
+
+ file_format_t fform;
+ if (it->force_fform)
+ fform = it->force_fform;
+ else
+ {
+// [[analyse-magic]]
+ fform = GetByMagicFF(it->brsub,it->file_size,it->file_size);
+ if ( IsValidBRSUB( it->brsub, it->file_size, it->file_size,
+ it->adjust ? 0 : it->szs,
+ fform, false, 0, endian) >= VALID_ERROR )
+ {
+ PRINT_IF(it->adjust,"INVALID, adjust=%d\n",it->adjust);
+ return -1;
+ }
+ }
+
+ brsub_header_t *bh = it->brsub;
+ u8 * brsub_data = (u8*)bh;
+ it->data_size = endian->rd32(&bh->size);
+ const int n_grp = GetSectionNumBRSUB(brsub_data,it->data_size,endian);
+ u32 name_off = 0x10 + n_grp * 4;
+ uint count = 0;
+
+ PRINT_IF( fform == FF_PAT, "#PAT# %s @ %s #%u | n_grp=%d\n",
+ __FUNCTION__,__FILE__,__LINE__,n_grp);
+
+
+ //--- static data
+
+ it->fform = fform;
+
+ if (it->offset_func)
+ count += it->offset_func(it,brsub_data,&bh->brres_offset);
+
+ if ( it->string_func && n_grp >= 0 )
+ count += it->string_func(it,brsub_data,(u32*)(brsub_data+name_off));
+
+ it->fform = FF_UNKNOWN;
+
+ szs_iterator_t it2;
+ memset(&it2,0,sizeof(it2));
+ it2.szs = it->szs;
+ it2.size = it->file_size;
+ it2.endian = it->endian;
+ it2.cut_files = true;
+ CutFilesBRSUB( &it2, brsub_string_func, it, 0, 0 );
+ return count + it2.index;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// BRSUB iterator callback functions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int clear_brsub_offset
+(
+ struct brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 string offset
+)
+{
+ DASSERT(it);
+ DASSERT(it->endian);
+ it->endian->wr32(ptr,0);
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+static int adjust_brsub_offset
+(
+ struct brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 offset
+)
+{
+ DASSERT(it);
+ DASSERT(it->endian);
+ DASSERT(it->szs);
+ DASSERT(it->szs->parent);
+ DASSERT(it->szs->parent->data);
+
+ noPRINT("##A## ADJUST OFFSET: %x -> %x\n",
+ it->endian->rd32(ptr), (u32)(it->szs->parent->data - base) );
+ it->endian->wr32( ptr, it->szs->parent->data - base );
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+static int insert_brsub_string
+(
+ struct brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 string offset
+)
+{
+ DASSERT(it);
+ DASSERT(it->endian);
+ DASSERT(it->param);
+
+ ccp string = GetStringIteratorBRSUB(it,base,ptr,0,0);
+ if (!string)
+ return 0;
+ PRINT_IF( it->fform == FF_PAT, "#PAT# INSERT %s @%s\n",string,__FUNCTION__);
+ PRINT_IF( it->fform == FF_MDL, "#MDL# INSERT %s @%s\n",string,__FUNCTION__);
+
+ InsertStringPool(it->param,string,false,0);
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+
+static int adjust_brsub_string_szs
+(
+ struct brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 string offset
+)
+{
+ noPRINT("##A## size=%x,%x, base=%zx, ptr=%zx\n",
+ it->data_size, it->file_size,
+ base - (u8*)it->brsub, (u8*)ptr - (u8*)it->brsub );
+
+ DASSERT(it);
+ DASSERT(it->endian);
+ DASSERT(it->szs);
+ DASSERT(it->szs->parent);
+ DASSERT(it->szs->parent->data);
+ DASSERT(it->param);
+
+ ccp string = GetStringIteratorBRSUB(it,base,ptr,0,0);
+ if (!string)
+ return 0;
+
+ const u32 off = FindStringPool(it->param,string,0) - (base - it->szs->parent->data);
+ noPRINT("##A## ADJUST STRING: %x -> %x : %s\n", it->endian->rd32(ptr), off, string );
+ PRINT_IF( it->fform == FF_PAT,
+ "#PAT# ADJUST %x -> %x : %s @%s\n",
+ it->endian->rd32(ptr), off, string, __FUNCTION__ );
+ it->endian->wr32(ptr,off);
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+
+static int adjust_brsub_string
+(
+ struct brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 string offset
+)
+{
+ DASSERT(it);
+ DASSERT(it->endian);
+ DASSERT(it->szs);
+ DASSERT(it->szs->data);
+ DASSERT(it->param);
+
+ ccp string = GetStringIteratorBRSUB(it,base,ptr,0,0);
+ if (!string)
+ return 0;
+
+ const u32 off = FindStringPool(it->param,string,0) - (base - (u8*)it->brsub);
+ noPRINT("ADJUST STRING: %x -> %x : %s\n", it->endian->rd32(ptr), off, string );
+ it->endian->wr32(ptr,off);
+ return 1;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// BRSUB iterator interface ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int CollectStringsBRSUB
+(
+ string_pool_t * sp, // string pool
+ bool init_sp, // true: initialize 'sp'
+ szs_file_t * szs, // szs of data
+ bool adjust // true: create string pool and
+ // adjust offsets and strings
+)
+{
+ DASSERT(sp);
+ DASSERT(szs);
+
+ if (init_sp)
+ InitializeStringPool(sp);
+
+ brsub_iterator_t it;
+ memset(&it,0,sizeof(it));
+ it.szs = szs;
+ it.brsub = (void*)szs->data;
+ it.file_size = szs->file_size;
+ it.string_func = insert_brsub_string;
+ it.offset_func = adjust ? clear_brsub_offset : 0;
+ it.param = sp;
+ it.endian = szs->endian;
+
+ const int stat = IterateStringsBRSUB(&it);
+
+ if ( adjust && stat > 0 )
+ {
+ CalcStringPool( sp, it.data_size, szs->endian );
+ it.adjust = true;
+ it.string_func = adjust_brsub_string;
+ it.offset_func = 0;
+ IterateStringsBRSUB(&it);
+ }
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int AdjustStringsBRSUB
+(
+ szs_file_t * szs, // base BRRES file
+ void * brsub_data, // brres sub data
+ size_t brsub_size // size of 'brsub_data' including string pool
+)
+{
+ DASSERT(szs);
+ DASSERT(szs->parent);
+ DASSERT(brsub_data);
+
+ brsub_iterator_t it;
+ memset(&it,0,sizeof(it));
+ it.szs = szs;
+ it.brsub = brsub_data;
+ it.file_size = brsub_size;
+ it.string_func = adjust_brsub_string_szs;
+ it.offset_func = adjust_brsub_offset;
+ it.param = &szs->parent->string_pool;
+ it.adjust = true;
+ it.endian = szs->endian;
+
+ return IterateStringsBRSUB(&it);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// BRSUB Dump Structure ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int dump_brsub_raw
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ u8 * data, // data pointer
+ u32 data_size // size of 'data'
+)
+{
+ DASSERT(it);
+ FILE * f = it->param;
+ DASSERT(f);
+
+ u32 data_offset = data - (u8*)it->brsub;
+ fprintf(f," - RAW DATA %x .. %x, size %x\n",
+ data_offset, data_offset + data_size, data_size );
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+static int dump_brsub_grp
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ brres_group_t * grp, // pointer to group
+ uint grp_index // index of group
+)
+{
+ DASSERT(it);
+ FILE * f = it->param;
+ DASSERT(f);
+
+ fprintf(f," - GRP #%-2u %4x\n", grp_index, (int)( (u8*)grp - (u8*)it->brsub ));
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+static int dump_brsub_entry
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ brres_group_t * grp, // pointer to group
+ uint grp_index, // index of group
+ brres_entry_t * entry, // pointer to entry
+ u8 * data // pointer to entry related data
+)
+{
+ DASSERT(it);
+ FILE * f = it->param;
+ DASSERT(f);
+
+ ccp string = GetStringIteratorBRSUB(it,grp,&entry->name_off,
+ "<ignored>", "**invalid**");
+
+ fprintf(f," - ENTRY #%-2u %4x [+%3x] : %8x %s-> %s\n",
+ (int)( entry - grp->entry ),
+ (int)( (u8*)entry - (u8*)it->brsub ),
+ (int)( (u8*)entry - (u8*)grp ),
+ it->endian->rd32(&entry->name_off),
+ string <= (ccp)it->brsub + it->data_size ? "[intern] " : "",
+ string );
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+static int dump_brsub_string
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 string offset
+)
+{
+ DASSERT(it);
+ FILE * f = it->param;
+ DASSERT(f);
+
+ ccp string = GetStringIteratorBRSUB(it,base,ptr,"<ignored>","**invalid**");
+
+ fprintf(f," > STRING %5x [+%3x] : %8x %s-> %s\n",
+ (int)( (u8*)ptr - (u8*)it->brsub ),
+ (int)( (u8*)ptr - base ),
+ it->endian->rd32(ptr),
+ string <= (ccp)it->brsub + it->data_size ? "[intern] " : "",
+ string );
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+static int dump_brsub_offset
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 string offset
+)
+{
+ DASSERT(it);
+ FILE * f = it->param;
+ DASSERT(f);
+
+ fprintf(f," > OFFSET %5x [+%3x] : %8x\n",
+ (int)( (u8*)ptr - (u8*)it->brsub ),
+ (int)( (u8*)ptr - base ),
+ it->endian->rd32(ptr) );
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+int DumpStructureBRSUB
+(
+ FILE * f, // output file
+ szs_file_t * base_szs, // valid base SZS
+ uint off, // offset of selected data within 'base'
+ uint size, // not NULL: size of selected data
+ file_format_t fform // not FF_UNKNOWN: use this type
+)
+{
+ DASSERT(f);
+ DASSERT(base_szs);
+ PRINT("DumpStructureBRSUB(off=%x,size=%x,ff=%d)\n",off,size,fform);
+
+ szs_file_t szs;
+// [[fname-]]
+ InitializeSubSZS(&szs,base_szs,off,size,fform,0,false);
+
+ brsub_iterator_t it;
+ memset(&it,0,sizeof(it));
+ it.szs = &szs;
+ it.brsub = (void*)szs.data;
+ it.file_size = szs.file_size;
+ it.raw_func = dump_brsub_raw;
+ it.grp_func = dump_brsub_grp;
+ it.entry_func = dump_brsub_entry;
+ it.string_func = dump_brsub_string;
+ it.offset_func = dump_brsub_offset;
+ it.param = f;
+ it.static_strings = true;
+
+ return IterateStringsBRSUB(&it);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int dump_func
+(
+ struct szs_iterator_t *it, // iterator struct with all infos
+ bool term // true: termination hint
+)
+{
+ DASSERT(it);
+ if ( term || it->is_dir )
+ return 0;
+
+ szs_file_t * szs = it->szs;
+ DASSERT(szs);
+
+ u8 * data = szs->data+it->off;
+// [[analyse-magic]]
+ file_format_t fform = GetByMagicFF(data,it->size,it->size);
+ if (IsBRSUB(fform))
+ fprintf(it->param,"\n * Sub file: %s.%u:%s\n",
+ GetNameFF(fform,fform),
+ it->endian->rd32(data+8),
+ it->path );
+ else
+ fprintf(it->param,"\n * Data: %s\n",it->path);
+
+ return DumpStructureBRSUB(it->param,szs,it->off,it->size,fform);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int DumpStructureBRRES
+(
+ FILE * f, // output file
+ szs_file_t * szs // BRRES file
+)
+{
+ DASSERT(f);
+ DASSERT(szs);
+
+ if ( szs->fform_arch != FF_BRRES )
+ return -1;
+
+ return IterateFilesSZS(szs,dump_func,f,false,0,-1,SORT_NONE);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// NAME-REF support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeNameRef ( name_ref_t *nr )
+{
+ DASSERT(nr);
+ memset(nr,0,sizeof(*nr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetNameRef ( name_ref_t *nr )
+{
+ DASSERT(nr);
+
+ if (nr->vlist)
+ {
+ name_ref_var_t *vptr, *vend = nr->vlist + nr->vused;
+ for ( vptr = nr->vlist; vptr < vend; vptr++ )
+ {
+ FreeString(vptr->var_name);
+
+ if (vptr->elist)
+ {
+ name_ref_elem_t *eptr, *eend = vptr->elist + vptr->eused;
+ for ( eptr = vptr->elist; eptr < eend; eptr++ )
+ FreeString(eptr->info);
+
+ FREE(vptr->elist);
+ vptr->elist = 0;
+ }
+ vptr->eused = vptr->esize = 0;
+
+ }
+ FREE(nr->vlist);
+ nr->vlist = 0;
+ }
+ nr->vused = nr->vsize = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void add_ref_string
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr, // pointer to u32 string offset
+
+ ccp format, // format string for info
+ ... // arguments for 'format'
+)
+__attribute__ ((__format__(__printf__,4,5)));
+
+//-----------------------------------------------------------------------------
+
+static void add_ref_string
+(
+ struct
+ brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr, // pointer to u32 string offset
+
+ ccp format, // format string for info
+ ... // arguments for 'format'
+)
+{
+ ccp info = 0;
+ if (format)
+ {
+ char buf[200];
+ va_list arg;
+ va_start(arg,format);
+ int len = vsnprintf(buf,sizeof(buf),format,arg);
+ va_end(arg);
+ if ( len > 0 )
+ info = MEMDUP(buf,len);
+ }
+
+ name_ref_t *nr = it->param;
+ DASSERT(nr);
+
+ ccp var_name = GetStringIteratorBRSUB(it,base,ptr,0,0);
+ const u32 var_off = be32(ptr) + base - nr->data;
+ const u32 file_off = (u8*)ptr - nr->data;
+ const u32 base_off = (u8*)ptr - it->szs->data;
+
+ PRINT0(">>> %06x %05x %06x %s [%s]\n",
+ base_off, file_off, var_off, var_name, info );
+
+
+ //--- insert into vlist
+
+ uint vi;
+ name_ref_var_t *vptr = nr->vlist;
+ for ( vi = 0; vi < nr->vused; vi++, vptr++ )
+ if ( var_off <= vptr->var_off )
+ break;
+
+ if ( vi == nr->vused || var_off != vptr->var_off )
+ {
+ // insert element
+ if ( nr->vused == nr->vsize )
+ {
+ nr->vsize += 100;
+ nr->vlist = REALLOC( nr->vlist, nr->vsize * sizeof(*nr->vlist) );
+ vptr = nr->vlist + vi;
+ }
+
+ if ( vi < nr->vused )
+ memmove(vptr+1,vptr,(nr->vused-vi)*sizeof(*vptr));
+
+ memset(vptr,0,sizeof(*vptr));
+ vptr->var_name = var_name ? STRDUP(var_name) : EmptyString;
+ vptr->var_off = var_off;
+ vptr->min_file_off = file_off;
+ nr->vused++;
+ }
+ else if ( file_off < vptr->min_file_off )
+ vptr->min_file_off = file_off;
+
+
+ //--- insert into elist
+
+ uint ei;
+ name_ref_elem_t *eptr = vptr->elist;
+ for ( ei = 0; ei < vptr->eused; ei++, eptr++ )
+ if ( file_off < eptr->file_off )
+ break;
+
+ if ( vptr->eused == vptr->esize )
+ {
+ vptr->esize += 20;
+ vptr->elist = REALLOC( vptr->elist, vptr->esize * sizeof(*vptr->elist) );
+ eptr = vptr->elist + ei;
+ }
+
+ if ( ei < vptr->eused )
+ memmove(eptr+1,eptr,(vptr->eused-ei)*sizeof(*eptr));
+
+ memset(eptr,0,sizeof(*eptr));
+ eptr->file_off = file_off;
+ eptr->base_off = base_off;
+ eptr->info = info;
+ vptr->eused++;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int insert_name_ref
+(
+ struct brsub_iterator_t * it, // iterator struct with all infos
+ u8 * base, // pointer to base for calculation
+ u32 * ptr // pointer to u32 string offset
+)
+{
+ DASSERT(it);
+ DASSERT(it->szs);
+ name_ref_t *nr = it->param;
+ DASSERT(nr);
+
+ if ( nr->brief > 2 )
+ add_ref_string(it,base,ptr,"%s",nr->file_info);
+ else
+ add_ref_string(it,base,ptr,"%s" "offset %x",
+ nr->file_info, (uint)( (u8*)ptr - it->szs->data ));
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+
+static void iterate_nr_brsub
+(
+ name_ref_t *nr, // valid struct
+ szs_file_t *szs, // valid struct
+ cvp data, // pointer to data
+ uint data_size, // size of data
+ file_format_t fform // FF_UNKNOWN or file format of 'data'
+)
+{
+ DASSERT(nr);
+ DASSERT(szs);
+ DASSERT(data||!data_size);
+
+ if ( !data || !data_size )
+ return;
+
+ if ( fform == FF_UNKNOWN )
+ fform = AnalyseMagicByOpt(0,data,data_size,data_size,FF_UNKNOWN,0);
+
+ if (IsBRSUB(fform))
+ {
+ if ( nr->brief > 2 )
+ snprintf(nr->file_info,sizeof(nr->file_info),"%s",
+ GetNameFF(0,fform) );
+ else
+ snprintf(nr->file_info,sizeof(nr->file_info),"%s @%x, ",
+ GetNameFF(0,fform), (uint)( (u8*)data - szs->data ));
+
+ szs_file_t sub;
+ InitializeSubSZS( &sub, szs, (u8*)data - szs->data, 0, fform, 0, false );
+
+ brsub_iterator_t it;
+ memset(&it,0,sizeof(it));
+ it.szs = &sub;
+ it.brsub = (void*)data;
+ it.file_size = szs->data + szs->size - (u8*)data;
+ it.fform = fform;
+ it.param = nr;
+ it.endian = szs->endian;
+ it.string_func = insert_name_ref;
+
+ IterateStringsBRSUB(&it);
+ ResetSZS(&sub);
+ *nr->file_info = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static int iterate_nr_brres
+(
+ struct szs_iterator_t *it, // iterator struct with all infos
+ bool term // true: termination hint
+)
+{
+ DASSERT(it);
+ if (term)
+ return 0;
+
+ const u8 * data = it->szs->data+it->off;
+ iterate_nr_brsub(it->param,it->szs,data,it->size,FF_UNKNOWN);
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+enumError CreateNameRef
+ ( name_ref_t *nr, bool init_nr, szs_file_t *szs, int brief )
+{
+ DASSERT(nr);
+ DASSERT(szs);
+
+ if (init_nr)
+ InitializeNameRef(nr);
+ else
+ ResetNameRef(nr);
+
+ nr->data = szs->data;
+ nr->data_size = szs->file_size;
+ nr->fform = szs->fform_arch;
+ nr->fname = STRDUP(szs->fname);
+ nr->brief = brief;
+
+ if ( szs->fform_arch == FF_BRRES )
+ IterateFilesSZS( szs, iterate_nr_brres,nr, true,0,-1, SORT_NONE );
+ else
+ iterate_nr_brsub(nr,szs,szs->data,szs->file_size,FF_UNKNOWN);
+#if 0
+ {
+ brsub_iterator_t it;
+ memset(&it,0,sizeof(it));
+ it.szs = szs;
+ it.brsub = (void*)szs->data;
+ it.file_size = szs->file_size;
+ it.string_func = insert_name_ref;
+ it.param = nr;
+ it.endian = szs->endian;
+
+ IterateStringsBRSUB(&it);
+ }
+#endif
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int sort_nr_by_name ( const void *va, const void *vb )
+{
+ name_ref_var_t **a = (name_ref_var_t**)va;
+ name_ref_var_t **b = (name_ref_var_t**)vb;
+ return strcasecmp( (*a)->var_name, (*b)->var_name );
+}
+
+//-----------------------------------------------------------------------------
+
+int sort_nr_by_brres ( const void *va, const void *vb )
+{
+ name_ref_var_t **a = (name_ref_var_t**)va;
+ name_ref_var_t **b = (name_ref_var_t**)vb;
+ return strcmp( (*a)->var_name, (*b)->var_name );
+}
+
+//-----------------------------------------------------------------------------
+
+int sort_nr_by_offset ( const void *va, const void *vb )
+{
+ name_ref_var_t **a = (name_ref_var_t**)va;
+ name_ref_var_t **b = (name_ref_var_t**)vb;
+ return (int)(*a)->min_file_off - (int)(*b)->min_file_off;
+}
+
+//-----------------------------------------------------------------------------
+
+static void list_var ( FILE *f, int indent, name_ref_var_t *vptr, int brief )
+{
+ DASSERT(f);
+ DASSERT(vptr);
+
+ if ( brief > 0 )
+ fprintf(f,"\n%*s»%s«\n",indent,"",
+ vptr->var_name ? vptr->var_name : "" );
+ else
+ fprintf(f,"\n%*s%6x »%s«\n",indent,"",
+ vptr->var_off, vptr->var_name ? vptr->var_name : "" );
+
+ name_ref_elem_t *eptr, *eend = vptr->elist + vptr->eused;
+ if ( brief > 1 )
+ for ( eptr = vptr->elist; eptr < eend; eptr++ )
+ fprintf(f,"%*s %s\n",indent,"",
+ eptr->info ? eptr->info : "" );
+ else
+ for ( eptr = vptr->elist; eptr < eend; eptr++ )
+ fprintf(f,"%*s %6x %s\n",indent,"",
+ eptr->file_off, eptr->info ? eptr->info : "" );
+}
+
+//-----------------------------------------------------------------------------
+
+void ListNameRef ( FILE *f, int indent, name_ref_t *nr, SortMode_t sort )
+{
+ qsort_func sort_func;
+ ccp sort_info;
+ switch (sort)
+ {
+ case SORT_INAME:
+ sort_func = sort_nr_by_name;
+ sort_info = "sorted by name";
+ break;
+
+ case SORT_BRRES:
+ sort_func = sort_nr_by_brres;
+ sort_info = "sorted like BRRES";
+ break;
+
+ case SORT_OFFSET:
+ sort_func = sort_nr_by_offset;
+ sort_info = "sorted by offset";
+ break;
+
+ default:
+ sort_func = 0;
+ sort_info = "unsorted";
+ break;
+ }
+
+ indent = NormalizeIndent(indent);
+ printf("%*s" "Name reference, %s, file %s:%s\n",
+ indent,"", sort_info, GetNameFF(nr->fform,0), nr->fname );
+ indent += 3;
+
+ if ( !sort_func || nr->vused < 2 )
+ {
+ //-- unsorted
+
+ name_ref_var_t *vptr, *vend = nr->vlist + nr->vused;
+ for ( vptr = nr->vlist; vptr < vend; vptr++ )
+ list_var(f,indent,vptr,nr->brief);
+ }
+ else
+ {
+ //--- sorted
+
+ name_ref_var_t **ref_list = MALLOC(nr->vused*sizeof(*ref_list));
+ DASSERT(ref_list);
+ uint i;
+ for ( i = 0; i < nr->vused; i++ )
+ ref_list[i] = nr->vlist+i;
+
+ qsort( ref_list, nr->vused, sizeof(*ref_list), sort_func );
+
+ for ( i = 0; i < nr->vused; i++ )
+ list_var(f,indent,ref_list[i],nr->brief);
+
+ FREE(ref_list);
+ }
+
+ fputc('\n',f);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+