Mercurial > otakunoraifu
view scn2k/scn2kdump.cc @ 52:15a18fbe6f21
* Known bugs added to the README
* Code cleaning (0 -> NULL when needed, indentation, spaces, ...)
author | thib |
---|---|
date | Sat, 18 Apr 2009 18:35:39 +0000 |
parents | d229cce98f50 |
children | 4416cfac86ae |
line wrap: on
line source
/* * * Copyright (C) 2002- Kazunori Ueno(JAGARL) <jagarl@creator.club.ne.jp> * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include <stdlib.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #include <string> #include <vector> #include <map> using namespace std; #include "system/file.h" #include "system/file_impl.h" #include "scn2k.h" #define SCN_DUMP class CmdD : public Cmd { public: CmdD(const Flags& flags, int system_version) : Cmd(flags, system_version) {} //CmdD(const Flags& f) : flags(f) { argc = 0; errorflag = false; cmdstr[0] = 0;} bool ClearError() { /*errorflag = false;*/} static map<int, struct CmdDescrItem*> cmd_descr; const char* CmdDescr(int cmd1, int cmd2, int cmd3, int cmd4); }; int system_version = 0; bool ruby_flag = false; bool ret_flag = false; bool text_flag = false; bool selection_flag = false; inline int eval(int v1, int op, int v2) { switch(op) { case 0: return v1+v2; case 1: return v1-v2; case 2: return v1*v2; case 3: return v2!=0 ? v1/v2 : v1; case 4: return v2!=0 ? v1%v2 : v1; case 5: return v1&v2; case 6: return v1|v2; case 7: return v1^v2; case 8: return v1<<v2; case 9: return v1>>v2; case 40: return v1 == v2; case 41: return v1 != v2; case 42: return v1 <= v2; case 43: return v1 < v2; case 44: return v1 >= v2; case 45: return v1 > v2; case 60: return v1 && v2; case 61: return v1 || v2; } return v2; } void usage(void) { fprintf(stderr,"usage : scn2kdump [inputfile] [outputfile]\n"); fprintf(stderr," inputfile: seen.txt(default)\n"); fprintf(stderr," outputfile: seen.txt_out(default)\n"); exit(-1); } extern int debug_flag; int main(int argc, char** argv) { /* determine file names */ debug_flag = true; bool verbose = false; char* inname = "seen.txt"; char* outname = 0; if (argc > 2 && strcmp(argv[1],"-v") == 0) { int i; for (i=1; i<argc; i++) argv[i] = argv[i+1]; argc--; verbose = true; } switch(argc) { case 1: break; case 2: inname = argv[1]; break; case 3: inname = argv[1]; outname = argv[2]; break; default: usage(); } /* open output file */ FILE* outstream = stdout; /* create archive instance */ SCN2kFILE archive(inname); archive.Init(); if (archive.Deal() == 0) { fprintf(stderr,"Cannot open / Invalid archive file %s\n",inname); usage(); } /* dump files */ archive.InitList(); char* fname; fprintf(stderr,"Dump start\n"); while( (fname = archive.ListItem()) != 0) { ARCINFO* info = archive.Find(fname, ""); if (info == NULL) continue; char* data = info->CopyRead(); char* d = data; char* dend = d + info->Size(); /* version 確認 */ if (read_little_endian_int(d) == 0x1cc) { system_version = 0; } else if (read_little_endian_int(d) == 0x1d0) { system_version = 1; } else { continue; } if (read_little_endian_int(d+4) == 0x1adb2) ; // little busters! else if (read_little_endian_int(d+4) != 0x2712) continue; int header_size; if (system_version == 0) { header_size = 0x1cc + read_little_endian_int(d+0x20) * 4; } else { header_size = read_little_endian_int(d+0x20); } d += header_size; const char* dcur = d; const char* dstart = d; fprintf(stderr,"Dumping %s\n",fname); fprintf(stdout,"Dumping %s\n",fname); { int i; for (i=0; i<100; i++) { int n = read_little_endian_int(data + 0x34 + i*4); if (n != 6) fprintf(stdout,"subroutine table %2d: %6d\n",i,n); }} Flags flags; /* 最初から最後までコマンド取得 -> 出力を繰り返す */ while(dcur<dend) { const char* dprev = dcur; CmdD cmd(flags, system_version); cmd.ClearError(); /* end? */ if (*dcur == -1) { /* 0xff x 32byte + 0x00 : end sign */ int i; for (i=0; i<0x20; i++) if (dcur[i] != -1) break; if (i == 0x20 && dcur[i] == 0) break; } dprintf("%d : ",dcur-dstart); cmd.GetCmd(flags, dcur); if (cmd.IsError()) { fprintf(outstream, "Error at %6d\n",dprev-dstart); while(dcur < dend) { if (*dcur == 0x29 && dcur[1] == 0x0a) {dcur++;break;} if (*dcur == 0 && dcur[1] == 0x0a) {dcur++;break;} if (*dcur == 0 && dcur[1] == 0x23) {dcur++;break;} dcur++; } dprev -= 2*16; int ilen = (dcur-dprev+15)/16; int i; for (i=0; i<ilen; i++) { fprintf(outstream, "%6d: ",dprev-dstart); int j; for (j=0; j<16; j++) { if (dprev >= dend) break; if (dprev < data) continue; fprintf(outstream, "%02x ",*(unsigned char*)(dprev)); dprev++; } fprintf(outstream, "\n"); } } } delete info; } return 0; } /* SetStr 0x23 - cmd 01-0a:0000:00[ 2] args:V<18>[17],"PB47" CatStr 0x23 - cmd 01-0a:0002:00[ 2] args:V<18>[17],V<18>[20] WaitClick 0x23 - cmd 00-03:0011:00[ 0] ChangeFaceGraphics 0x23 - cmd 00-03:03e8:00[ 1] args:V<18>[17] DeleteFaceGraphics 0x23 - cmd 00-03:03e9:01[ 0] KoePlay 0x23 - cmd 01-17:0000:01[ 2] args:100000026,5 DrawGraphics(前景画あり) 0x23 - cmd 01-21:004b:00[ 1] args:V<18>[1],10,kasane. #1 <args:V<18>[17],11> DrawGraphics(背景のみ) 0x23 - cmd 01-21:0049:00[ 2] args:V<18>[1],10 Ruby 0x23 - cmd 00-03:0078:01[ 0] "理由" 0x23 - cmd 00-03:0078:00[ 1] "わけ" SetTitle 0x23 - cmd 01-04:0000:00[ 1] args:"Long Long Time Ago..." WaitTime 0x23 - cmd 01-14:0069:00[ 1] args:3000 ChangeBGM 数値引数はフェードアウト、インの時間と推測 0x23 - cmd 01-14:0000:02[ 3] args:"BGM18",700,700 */ struct CmdDescrItem { CmdDescrItem* next; int cmd4; int cmd1; int cmd2; int cmd3; const char* cmd_descr; }; CmdDescrItem cmd_descr_orig[] = { // scn2k_impl.cc; Scn2k::SysExec() {0,0,0x00,0x01,0x0a, "local return"}, {0,0,0x00,0x01,0x0b, "global jump"}, {0,0,0x00,0x01,0x0c, "global call"}, {0,0,0x00,0x01,0x0d, "global return"}, {0,0,0x00,0x01,0x12, "global call"}, {0,0,0x00,0x01,0x13, "global return(?)"}, {0,0,0x00,0x04,0x0d, "Menu_return"}, {0,0,0x01,0x04,0x00, "SetWindowCaption"}, {0,0,0x01,0x04,0x82, "ClearMousePress"}, {0,0,0x01,0x04,0x83, "GetMouse(2)"}, {0,0,0x01,0x04,0x85, "GetMouse"}, {0,0,0x01,0x04,0x4b0,"QuitGame"}, {0,0,0x01,0x04,0x58d,"PrevSaveNumber"}, {0,0,0x01,0x04,0x585,"SavedDate"}, {0,0,0x01,0x04,0xc23,"Save"}, {0,0,0x01,0x04,0xc25,"Load"}, {0,0,0x01,0x04,0x4b1,"GoMenu"}, {0,0,0x01,0x04,0x4b3,"GoMenu_Badend"}, {0,0,0x01,0x04,0xcc, "ShowMouseCursor"}, {0,0,0x01,0x04,0xcd, "HideMouseCursor"}, {0,0,0x01,0x04,0xcf, "SetCursorType"}, // scn2k_cmd.cc; CmdD::GetCmd() {0,0,0x00,0x01,0, "local jump"}, {0,0,0x00,0x01,1, "local jump-if"}, {0,0,0x00,0x01,2, "local jump-unless"}, {0,0,0x00,0x01,3, "local jump-switch??"}, {0,0,0x00,0x01,4, "local switch"}, {0,0,0x00,0x01,5, "local call"}, {0,0,0x00,0x01,8, "local switch(simple form)"}, {0,0,0x01,0x0b,0, "set multiple variables"}, {0,0,0x01,0x0b,1, "set variables in a range"}, {0,0,0x01,0x0b,4, "clear variables in a range"}, {0,0,0x01,0x0b,0x64, "get summation of variables in a range"}, // scn2k_cmd.cc; Flags::Exec() {0,0,0x01,0x0a,0, "SetStr"}, {0,0,0x01,0x0a,1, "ClearStr"}, {0,0,0x01,0x0a,2, "AppendStr"}, {0,0,0x01,0x0a,3, "StrLen"}, {0,0,0x01,0x0a,4, "StrCmp"}, {0,0,0x01,0x0a,5, "SubStrL"}, {0,0,0x01,0x0a,6, "SubStrR"}, {0,0,0x01,0x0a,7, "StrLenWideChar"}, {0,0,0x01,0x0a,8, "TrimStr"}, {0,0,0x01,0x0a,0x0f, "IntToStr"}, {0,0,0x01,0x0a,0x11, "IntToStr_Fill"}, {0,0,0x01,0x0a,0x64, "ShowStr"}, // scn2k_text.cc; TextImpl::Exec() {0,0,0x01,0x21,0x49, "SetFaceGraphic"}, {0,0,0x01,0x21,0x4b, "SetFaceGraphic"}, {0,0,0x01,0x21,0x4c, "SetFaceGraphic"}, {0,0,0x00,0x03,0x97, "CloseTextWindow"}, {0,0,0x00,0x03,0x11, "WaitText"}, {0,0,0x00,0x03,0x03, "TextReturn"}, {0,0,0x00,0x03,0xc9, "TextReturn"}, {0,0,0x00,0x03,0x3e8,"SetFaceGraphic"}, {0,0,0x00,0x03,0x3e9,"SetFaceGraphic"}, {0,0,0x00,0x03,0x78, "TextRuby"}, {0,0,0x00,0x03,0x66, "SetTextWindowType"}, {0,0,0x00,0x03,0x67, "OpenTextWindow"}, {0,0,0x00,0x03,0x98, "ClearTextWindow"}, {0,0,0x00,0x03,0x68, "ShowText"}, {0,0,0x00,0x02,0x01, "Select"}, {0,0,0x00,0x02,0x03, "Select"}, {0,0,0x00,0x04,0x44c,"TextSkipStart"}, {0,0,0x00,0x04,0x3e8,"CloseTextWindow"}, {0,0,0x01,0x04,0x64, "WaitTime"}, {0,0,0x01,0x04,0x6f, "WaitTime"}, {0,0,0x01,0x04,0x79, "WaitTime"}, {0,0,0x01,0x04,0x65, "WaitTime w/ Cancel"}, {0,0,0x01,0x04,0x70, "WaitTime w/ Cancel"}, {0,0,0x01,0x04,0x1fe,"GetTimer"}, {0,0,0x01,0x04,0x201,"ResetTimer (unsupported; see rldev)"}, {0,0,0x01,0x04,0x202,"ResetTimerAll (unsupported; see rldev)"}, {0,0,0x01,0x04,0x72, "GetTimer"}, {0,0,0x01,0x04,0x7c, "GetTimer(2)"}, {0,0,0x01,0x04,0x6e, "ClearTimer"}, {0,0,0x01,0x04,0x78, "ClearTimer(2)"}, {0,0,0x01,0x04,0x26c,"ClearTimer(multi)"}, {0,0,0x01,0x04,0x270,"ClearTimer(multi)"}, {0,0,0x01,0x04,0x276,"GetTimer(multi)"}, {0,0,0x01,0x04,0x1f4,"SetTimer"}, {0,0,0x01,0x04,0x3e8,"rand(x,y)"}, {0,0,0x01,0x04,0x3ec,"min(x,y)"}, {0,0,0x01,0x04,0x3ef,"min(x,y)"}, {0,0,0x01,0x04,0x320,"range conversion(V,?,ResultMin,ValMin,ValMax,ResultMax,?)"}, {0,0,0x01,0x04,0x3f1,"in_range(x,y,a)"}, {0,0,0x01,0x04,0x16c,"SetCursorType?"}, {0,0,0x01,0x04,0xbc1,"LoadFromMenu"}, {0,0,0x01,0x04,0x8d4,"SetTextWindowColor"}, {0,0,0x01,0x04,0x8d5,"SetTextWindowColor"}, {0,0,0x01,0x04,0x8d6,"SetTextWindowColor"}, {0,0,0x01,0x04,0x8d7,"SetTextWindowColor"}, {0,0,0x01,0x04,0x8d8,"SetTextWindowColor"}, {0,0,0x01,0x04,0x8db,"SetTextWindowColor"}, {0,0,0x01,0x04,0x93f,"SetTextWindowColor"}, {0,0,0x01,0x04,0xa39,"SetTextWindowColor"}, {0,0,0x01,0x04,0xa28,"Get #INIT_MESSAGE_SPEED (original) from gameexe.ini"}, {0,0,0x01,0x04,0xa29,"Get #INIT_MESSAGE_SPEED (original) from gameexe.ini"}, {0,0,0x01,0x04,0xa2c,"Get #MESSAGE_KEY_WAIT_USE (original) from gameexe.ini"}, {0,0,0x01,0x04,0xa2d,"Get #INIT_MESSAGE_SPEED (original) from gameexe.ini"}, {0,0,0x01,0x04,0xa2e,"Get #MESSAGE_KEY_WAIT_TIME (original) from gameexe.ini"}, {0,0,0x01,0x04,0x913,"Get #INIT_MESSAGE_SPEED"}, {0,0,0x01,0x04,0x914,"Get #INIT_MESSAGE_SPEED_MOD"}, {0,0,0x01,0x04,0x92e,"Get #MESSAGE_KEY_WAIT_USE"}, {0,0,0x01,0x04,0x92f,"Get #INIT_MESSAGE_SPEED_MOD"}, {0,0,0x01,0x04,0x930,"Get #MESSAGE_KEY_WAIT_TIME"}, {0,0,0x01,0x04,0x8af,"Set #INIT_MESSAGE_SPEED"}, {0,0,0x01,0x04,0x8b0,"Set #INIT_MESSAGE_SPEED_MOD"}, {0,0,0x01,0x04,0x8ca,"Set #MESSAGE_KEY_WAIT_USE"}, {0,0,0x01,0x04,0x8cb,"Set #INIT_MESSAGE_SPEED_MOD"}, {0,0,0x01,0x04,0x8cc,"Set #MESSAGE_KEY_WAIT_USE"}, {0,0,0x01,0x04,0x51f,"Set Name Text"}, {0,0,0x01,0x04,0x51e,"Get Name Text"}, {0,0,0x01,0x04,0x514,"Get Name Text"}, // scn2k_grp.cc; GrpImpl::Exec() // music commands {0,0,0x01,0x14,0, "PlayBGM"}, {0,0,0x01,0x14,2, "PlayBGM"}, {0,0,0x01,0x14,0x05, "StopBGM"}, {0,0,0x01,0x14,0x69, "FadeBGM"}, {0,0,0x01,0x15,0, "PlaySE"}, {0,0,0x01,0x15,2, "PlaySE"}, {0,0,0x01,0x17,0, "PlayKoe"}, {0,0,0x01,0x1a,1, "PlayMovie"}, {0,0,0x01,0x1a,0x14, "PlayMovie"}, // graphic commands {0,0,0x01,0x1e,0, "GraphicStackClear"}, {0,0,0x01,0x1f,0, "GraphicStackClear"}, {0,0,0x01,0x21,0x46, "LoadSurface"}, {0,0,0x01,0x21,0x49, "LoadBackSurface"}, {0,0,0x01,0x21,0x4b, "LoadForeSurface"}, {0,0,0x01,0x21,0x4c, "LoadSurface"}, {0,0,0x01,0x21,0x64, "CopySurface"}, {0,0,0x01,0x21,0x4b1,"ClearSurface"}, {0,0,0x01,0x21,0x44c,"AlphaCopy"}, {0,0,0x01,0x21,0x640,"SaturateCopy"}, {0,0,0x01,0x21,0x196,"??? grp"}, {0,0,0x01,0x22,0xc30,"ScrollEffect (Princess Bride)"}, {0,0,0x01,0x22,0xc1c,"FallEffect (Princess Bride)"}, {0,0,0x01,0x22,0x835,"FallEffect (Princess Bride)"}, // grphic object commands {0,0,0x01,0x04,0xd2, "??? grp"}, {0,0,0x01,0x04,0xd3, "??? grp"}, {0,0,0x01,0x04,0xd7, "??? grp"}, {0,0,0x01,0x04,0xd8, "??? grp"}, {0,0,0x01,0x04,0x5e0,"GetShownGrpFlag"}, {0,0,0x01,0x3d,0x0a, "ClearGrpObj"}, {0,0,0x01,0x3d,0x0b, "ClearGrpObj"}, {0,0,0x01,0x3e,0x0a, "ClearGrpObj"}, {0,0,0x01,0x3e,0x0a, "ClearGrpObj"}, {0,0,0x01,0x3c,0x01, "??? grp (CLANNAD)"}, {0,0,0x01,0x47,0x3e8,"SetGrpObj_Fname"}, {0,0,0x01,0x47,0x3eb,"SetGrpObj_GANname"}, {0,0,0x01,0x47,0x4b0,"SetGrpObj_Text"}, {0,0,0x01,0x48,0x3e8,"SetGrpObj_ForeGrp?"}, {0,0,0x01,0x49,0, "StopAnimation"}, {0,0,0x01,0x49,3, "QueryExecAnimation"}, {0,0,0x01,0x49,0x7d3,"SetGrpObj_GAN?"}, {0,0,0x01,0x49,0xbb9,"StartAnimation"}, {0,0,0x01,0x49,0xbbb,"StartAnimation"}, {0,0,0x01,0x49,0xbbd,"StartAnimation"}, {0,0,0x01,0x51,0x3e8,"SetGrpObj_xy"}, {0,0,0x01,0x51,0x3e9,"SetGrpObj_x"}, {0,0,0x01,0x51,0x3ea,"SetGrpObj_y"}, {0,0,0x01,0x51,0x3eb,"SetGrpObj_alpha"}, {0,0,0x01,0x51,0x3ec,"SetGrpObj_visible"}, {0,0,0x01,0x51,0x3ee,"SetGrpObj_xy?"}, {0,0,0x01,0x51,0x3fd,"SetGrpObj_centering?"}, {0,0,0x01,0x51,0x401,"SetGrpObj_textsize"}, {0,0,0x01,0x51,0x40a,"SetGrpObj_clipregion"}, {0,0,0x01,0x51,0x40f,"SetGrpObj_surfacenum"}, {0,0,0x01,0x51,0x416,"SetGrpObj_expand"}, {0,0,0x01,0x51,0x419,"SetGrpObj_rotate"}, {0,0,0x01,0x52,0x3e8,"SetGrpObj_xy(2)"}, {0,0,0x01,0x52,0x3ea,"SetGrpObj_y(2)"}, {0,0,0x01,0x52,0x3eb,"SetGrpObj_alpha(2)"}, {0,0,0x01,0x52,0x3ec,"SetGrpObj_visible(2)"}, {0,0,0x01,0x52,0x3ee,"SetGrpObj_xy?(2)"}, {0,0,0x01,0x52,0x3fd,"SetGrpObj_centering?(2)"}, {0,0,0x01,0x52,0x401,"SetGrpObj_textsize(2)"}, {0,0,0x01,0x52,0x408,"SetGrpObj_order (not supported)"}, {0,0,0x01,0x52,0x40a,"SetGrpObj_clipregion(2)"}, {0,0,0x01,0x52,0x40f,"SetGrpObj_surfacenum(2)"}, {0,0,0x01,0x52,0x416,"SetGrpObj_expand(2)"}, {0,0,0x01,0x52,0x419,"SetGrpObj_rotate(2)"}, {0,0,0x01,0x54,0x3e8,"GetGrpObj_xy"}, {0,0,0x01,0x54,0x44c,"GetGrpObj_wh"}, {0,0,0x02,0x3d,0x0a, "ClearGrpObj(2)"}, {0,0,0x02,0x3d,0x0b, "ClearGrpObj(2)"}, {0,0,0x02,0x3e,0x0a, "ClearGrpObj(2)"}, {0,0,0x02,0x3e,0x0a, "ClearGrpObj(2)"}, {0,0,0x02,0x3c,0x01, "??? grp (CLANNAD)(2)"}, {0,0,0x02,0x47,0x3e8,"SetGrpObj_Fname(2)"}, {0,0,0x02,0x47,0x3eb,"SetGrpObj_GANname(2)"}, {0,0,0x02,0x47,0x4b0,"SetGrpObj_Text(2)"}, {0,0,0x02,0x48,0x3e8,"SetGrpObj_ForeGrp?(2)"}, {0,0,0x02,0x49,0, "StopAnimation(2)"}, {0,0,0x02,0x49,3, "QueryExecAnimation(2)"}, {0,0,0x02,0x49,0x7d3,"SetGrpObj_GAN?(2)"}, {0,0,0x02,0x49,0xbb9,"StartAnimation(2)"}, {0,0,0x02,0x49,0xbbb,"StartAnimation(2)"}, {0,0,0x02,0x49,0xbbd,"StartAnimation(2)"}, {0,0,0x02,0x51,0x3e8,"SetGrpObj_xy(2)"}, {0,0,0x02,0x51,0x3ea,"SetGrpObj_y(2)"}, {0,0,0x02,0x51,0x3eb,"SetGrpObj_alpha(2)"}, {0,0,0x02,0x51,0x3ec,"SetGrpObj_visible(2)"}, {0,0,0x02,0x51,0x3ee,"SetGrpObj_xy?(2)"}, {0,0,0x02,0x51,0x3fd,"SetGrpObj_centering?(2)"}, {0,0,0x02,0x51,0x401,"SetGrpObj_textsize(2)"}, {0,0,0x02,0x51,0x40a,"SetGrpObj_clipregion(2)"}, {0,0,0x02,0x51,0x40f,"SetGrpObj_surfacenum(2)"}, {0,0,0x02,0x51,0x416,"SetGrpObj_expand(2)"}, {0,0,0x02,0x51,0x419,"SetGrpObj_rotate(2)"}, {0,0,0x02,0x52,0x3e8,"SetGrpObj_xy(2)(2)"}, {0,0,0x02,0x52,0x3ea,"SetGrpObj_y(2)(2)"}, {0,0,0x02,0x52,0x3eb,"SetGrpObj_alpha(2)(2)"}, {0,0,0x02,0x52,0x3ec,"SetGrpObj_visible(2)(2)"}, {0,0,0x02,0x52,0x3ee,"SetGrpObj_xy?(2)(2)"}, {0,0,0x02,0x52,0x3fd,"SetGrpObj_centering?(2)(2)"}, {0,0,0x02,0x52,0x401,"SetGrpObj_textsize(2)(2)"}, {0,0,0x02,0x52,0x40a,"SetGrpObj_clipregion(2)(2)"}, {0,0,0x02,0x52,0x40f,"SetGrpObj_surfacenum(2)(2)"}, {0,0,0x02,0x52,0x416,"SetGrpObj_expand(2)(2)"}, {0,0,0x02,0x52,0x419,"SetGrpObj_rotate(2)(2)"}, {0,0,0x02,0x54,0x3e8,"GetGrpObj_xy(2)"}, {0,0,0x02,0x54,0x44c,"GetGrpObj_wh(2)"}, {0,0,0,0,0,0} }; map<int, CmdDescrItem*> CmdD::cmd_descr; const char* CmdD::CmdDescr(int cmd1, int cmd2, int cmd3, int cmd4) { if (cmd_descr.empty()) { int i; for (i=0; cmd_descr_orig[i].cmd_descr != 0; i++) { CmdDescrItem& cur = cmd_descr_orig[i]; int item_num = cur.cmd1*1000000+cur.cmd2*10000+cur.cmd3; map<int,CmdDescrItem*>::iterator it = cmd_descr.find(item_num); if (it == cmd_descr.end()) cmd_descr[item_num] = &cur; else { cur.next = it->second; it->second = &cur; } } } int item_num = cmd1*1000000+cmd2*10000+cmd3; map<int,CmdDescrItem*>::iterator it = cmd_descr.find(item_num); if (it == cmd_descr.end()) return "No descr (unsupported)"; CmdDescrItem* cur = it->second; do { if (cur->cmd1 == cmd1 && cur->cmd2 == cmd2 && cur->cmd3 == cmd3) { return cur->cmd_descr; } cur = cur->next; } while(cur != 0); return "No descr (unsupported)"; }