diff options
Diffstat (limited to 'project/src/lib-objflow.c')
| -rw-r--r-- | project/src/lib-objflow.c | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/project/src/lib-objflow.c b/project/src/lib-objflow.c new file mode 100644 index 0000000..ddecb37 --- /dev/null +++ b/project/src/lib-objflow.c @@ -0,0 +1,641 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * 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 <stddef.h> + +#include "lib-szs.h" +#include "lib-common.h" +#include "lib-numeric.h" +#include "lib-bzip2.h" + +#include "objflow.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OBJFLOW setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeOBJFLOW ( objflow_t * objflow ) +{ + DASSERT(objflow); + memset(objflow,0,sizeof(*objflow)); + objflow->fname = EmptyString; + + InitializeObjMgr(&objflow->obj_mgr,FF_OBJFLOW); + BZ2Manager_t *bz_orig = GetCommonBZ2Manager(FF_OBJFLOW); + DASSERT(bz_orig); + const uint copy_size = bz_orig->size < sizeof(objflow->bin) + ? bz_orig->size : sizeof(objflow->bin); + memcpy( objflow->bin, bz_orig->data, copy_size ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetOBJFLOW ( objflow_t * objflow ) +{ + DASSERT(objflow); + ResetObjMgr(&objflow->obj_mgr); + FreeString(objflow->fname); + InitializeOBJFLOW(objflow); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(objflow); + if (init_objflow) + InitializeOBJFLOW(objflow); + + #if 0 // [[todo]] [[?]] ??? + if ( IsValidOBJFLOW(data,data_size,data_size,objflow->fname) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid OBJFLOW file: %s\n" + "Add option --objflow=force or --force to ignore some validity checks.", + objflow->fname ? objflow->fname : "?" ); + } + #endif + + const uint copy_size = data_size < sizeof(objflow->bin) + ? data_size : sizeof(objflow->bin); + memcpy(objflow->bin,data,copy_size); + objflow->fform = FF_OBJFLOW; + +// [[obj-type]] + #if USE_OBJECT_MANAGER + VerifyBinObjMgr(objflow->bin,sizeof(objflow->bin),"ObjFlow/raw"); + #endif + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextOBJFLOW() helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + OBJFLOW_IGNORE, + OBJFLOW_SETUP, + OBJFLOW_OBJECTS, +}; + +//----------------------------------------------------------------------------- + +static const KeywordTab_t objflow_section_name[] = +{ + { OBJFLOW_IGNORE, "END", 0, 0 }, + { OBJFLOW_SETUP, "SETUP", 0, 0 }, + { OBJFLOW_OBJECTS, "OBJECTS", 0, 0 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextSETUP +( + objflow_t * objflow, // OBJFLOW data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(objflow); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + ScanU32SI(si,(u32*)&objflow->revision,1,0); + si->init_revision = si->cur_file->revision = objflow->revision; + DefineIntVar(&si->gvar,"REVISION$SETUP",objflow->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",objflow->revision); + } +// [[obj-type]] + #if USE_OBJECT_MANAGER > 1 + else if (!SetupObjMgrBySI(&objflow->obj_mgr,si,name)) + GotoEolSI(si); + #else + else + GotoEolSI(si); + #endif + CheckEolSI(si); + } + + CheckLevelSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static objflow_bin_t * FindObjectOBJFLOW ( cvp data, uint oid ) +{ + oid = htons(oid); + const uint N = be16(data); + objflow_bin_t *bin = (objflow_bin_t*)((u8*)data+2); + uint i; + for ( i = 0; i < N; i++, bin++ ) + if ( bin->id == oid ) + return bin; + return 0; +} + +//----------------------------------------------------------------------------- + +static enumError ScanTextOBJECTS +( + objflow_t * objflow, // OBJFLOW data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(objflow); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + objflow_bin_t *obj = 0; + si->point_is_null++; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,0); + continue; + } + + char name[50]; + const uint namelen = ScanNameSI(si,name,sizeof(name),true,true,0); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + noPRINT("### %s = %.4s...\n",name,sf->ptr); + + if (!namelen) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing name for option: [%s @%u]: %.*s\n", + sf->name, sf->line, (int)(eol-sf->ptr), sf->ptr ); + } + continue; + } + + if (!strcmp(name,"OBJECT")) + { + long oid; + ScanUValueSI(si,&oid,0); + obj = FindObjectOBJFLOW(objflow->bin,oid); + PRINT0("OID: %lx -> %p\n",oid,obj); + if (!obj) + GotoEolSI(si); + else + { + DEFINE_VAR(var); + if (SkipCharSI(si,',')) + { + ScanStringSI(si,&var); + memset(obj->name,0,sizeof(obj->name)); + StringCopyS(obj->name,sizeof(obj->name),var.str); + } + + if (SkipCharSI(si,',')) + { + ScanStringSI(si,&var); + memset(obj->resources,0,sizeof(obj->resources)); + StringCopyS(obj->resources,sizeof(obj->resources),var.str); + } + FreeV(&var); + } + } + else if (!strcmp(name,"PARAM")) + { + if (!obj) + { + sf->line_err++; + si->total_err++; + if ( si->no_warn <= 0 ) + ERROR0(ERR_WARNING, + "No object selected [%s @%u]: %s\n", + sf->name, sf->line, name ); + GotoEolSI(si); + } + else + { + uint i; + const uint N = sizeof(obj->param) / sizeof(*obj->param); + for ( i = 0; i < N; i++ ) + { + const char ch = NextCharSI(si,false); + if (!ch) + break; + if ( ch == '=' ) + { + si->cur_file->ptr++; + SkipCharSI(si,','); + } + else + { + long num; + ScanUValueSI(si,&num,0); + obj->param[i] = htons(num); + } + } + } + } + else + { + sf->line_err++; + si->total_err++; + if ( si->no_warn <= 0 ) + ERROR0(ERR_WARNING, + "Unknown keyword [%s @%u]: %s\n", + sf->name, sf->line, name ); + GotoEolSI(si); + } + CheckEolSI(si); + } + + si->point_is_null--; + CheckLevelSI(si); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + PRINT("ScanTextOBJFLOW(init=%d)\n",init_objflow); + + DASSERT(objflow); + if (init_objflow) + InitializeOBJFLOW(objflow); + + ScanInfo_t si; + InitializeSI(&si,data,data_size,objflow->fname,objflow->revision); + si.predef = SetupVarsCOMMON(); + + enumError max_err = ERR_OK; + objflow->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN OBJFLOW SECTIONS, PASS%u ...\n",objflow->is_pass2+1); + + max_err = ERR_OK; + si.no_warn = !objflow->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", objflow->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,objflow->revision); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + noPRINT("--> pass=%u: #%04u: %s\n",objflow->is_pass2+1,si.cur_file->line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,objflow_section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case OBJFLOW_SETUP: + err = ScanTextSETUP(objflow,&si); + break; + + case OBJFLOW_OBJECTS: + err = ScanTextOBJECTS(objflow,&si); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (objflow->is_pass2) + break; + + objflow->is_pass2 = true; + + RestartSI(&si); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextOBJFLOW) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OBJFLOW: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(objflow); + + enumError err; +// [[analyse-magic]] +// [[obj-type]] + switch (GetByMagicFF(data,data_size,data_size)) + { + case FF_OBJFLOW: + objflow->fform = FF_OBJFLOW; + err = ScanRawOBJFLOW(objflow,init_objflow,data,data_size); + break; + + case FF_OBJFLOW_TXT: + objflow->fform = FF_OBJFLOW_TXT; + err = ScanTextOBJFLOW(objflow,init_objflow,data,data_size); + break; + + default: + if (init_objflow) + InitializeOBJFLOW(objflow); + return ERROR0(ERR_INVALID_DATA, + "No OBJFLOW file: %s\n", objflow->fname ? objflow->fname : "?"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(objflow); + DASSERT(fname); + if (init_objflow) + InitializeOBJFLOW(objflow); + else + ResetOBJFLOW(objflow); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + objflow->fname = raw.fname; + raw.fname = 0; + err = ScanOBJFLOW(objflow,false,raw.data,raw.data_size); + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawOBJFLOW +( + objflow_t * objflow, // pointer to valid OBJFLOW + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(objflow); + DASSERT(fname); + + PRINT("SaveRawOBJFLOW(%s,%d)\n",fname,set_time); + + + //--- write to file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&objflow->fatt,0); + + if ( fwrite(objflow->bin,1,sizeof(objflow->bin),F.f) != sizeof(objflow->bin) ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextOBJFLOW +( + const objflow_t * objflow, // pointer to valid OBJFLOW + ccp fname, // filename of destination + bool set_time, // true: set time stamps +// [[minimize]] + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >9: minimize output +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(objflow); + DASSERT(fname); + PRINT("SaveTextOBJFLOW(%s,%d)\n",fname,set_time); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,objflow->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&objflow->fatt,0); + + + //--- print header + syntax info + + const bool explain = print_header && !brief_count && !export_count; + if (explain) + fputs(text_objflow_info_cr,F.f); + else + fputs(text_objflow_head_cr,F.f); + + const bool tiny = brief_count > 1 || export_count; + if (!tiny) + fprintf(F.f,text_objflow_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + + #if USE_OBJECT_MANAGER>1 + WriteObjMgrToSetupSection( F.f, &objflow->obj_mgr, + long_count>0, tiny, explain ); + #endif + + + //--- iterate records + + BZ2Manager_t *bz_orig = GetCommonBZ2Manager(FF_OBJFLOW); + DASSERT(bz_orig); + uint n_rec = be16(objflow->bin); + if ( n_rec > be16(bz_orig->data) ) + n_rec = be16(bz_orig->data); + + uint i; + const objflow_bin_t *bin = (objflow_bin_t*)(objflow->bin + 2); + const objflow_bin_t *ref = (objflow_bin_t*)(bz_orig->data + 2); + + bool sect_head_done = false; + for ( i = 0; i < n_rec; i++, bin++, ref++ ) + { + const int differ = memcmp( bin, ref, sizeof(*bin) ); + if ( differ || !minimize ) + { + if (!sect_head_done) + { + sect_head_done = true; + fputs( brief_count < 2 ? section_sep : "\r\n", F.f ); + fprintf(F.f,text_objflow_objects_cr,REVISION_NUM); + } + + fprintf(F.f,"\r\nOBJECT 0x%03x, \"%s\", \"%s\"\r\n", + ntohs(bin->id), bin->name, bin->resources ); + + if (memcmp(bin,ref,offsetof(objflow_bin_t,param))) + fprintf(F.f,"#ORIG: 0x%03x \"%s\" \"%s\"\r\n", + ntohs(ref->id), ref->name, ref->resources ); + + fputs("PARAM ",F.f); + PrintParamGEOHIT(F.f,6,bin->param,0,9); + + if (memcmp(bin->param,ref->param,sizeof(bin->param))) + { + fputs("\r\n#ORIG:",F.f); + PrintParamGEOHIT(F.f,6,ref->param,bin->param,9); + } + fputs("\r\n",F.f); + } + } + + //--- terminate + + fputs( brief_count < 2 ? section_end :"\r\n", F.f ); + ResetFile(&F,set_time); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + |
