Mercurial > otakunoraifu
diff scn2k/scn2kdump.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | 3a6aaeab7b4e |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/scn2k/scn2kdump.cc @@ -0,0 +1,1360 @@ +/* + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" + +/* 注意点: @@@ で表記 */ + +struct VarInfo { +#define TYPE_VARSTR 18 +#define TYPE_VARMAX 7 +#define TYPE_STR 58 +#define TYPE_VAL 68 +#define TYPE_SYS 0xc8 + int type; + int number; + int value; + VarInfo() { type = TYPE_VAL; value = 0;} + VarInfo(int n) { type = TYPE_VAL; value = n;} + VarInfo(const VarInfo& i) { type = i.type; number = i.number; value = i.value;} +}; +class Flags { +/* flag: +** type 0-5 : ローカル整数、各2000個 +** type 6, 25 : グローバル整数、2000個 +** type 12 : グローバル文字列、2000個 (今は無視しても良いが) +** type 18 : ローカル文字列、2000個 +** type 25: システム変数(マウス座標など?) 1000 個? +** type 26-32, 51 : 1-bit access to 0-6, 25 +** type 52-58, 77 : 2-bit access to 0-6, 25 +** type 78-84, 103 : 4-bit access to 0-6, 25 +** type 104-110, 129 : 8-bit access to 0-6, 25 +*/ + typedef unsigned int uint; + int sys; + int var[8][2000]; + string str[2000]; +public: + int operator () () const; + int operator () (VarInfo info) const; + void Str(unsigned int number, char* buf, int sz) const; + + bool IsInt(int type) const; + int MaxIndex(int type) const; + + void Set(VarInfo info, int value); + int Get(int type, int number) const; + void SetSys(int value); + void SetStr(unsigned int number, string val); +}; + +bool Flags::IsInt(int type) const { + int v = type % 26; + return v >= 0 && v < 7 || v == 25; +} + +int Flags::MaxIndex(int type) const { + switch (type / 26) { + case 1: + return 63999; + case 2: + return 31999; + case 3: + return 15999; + case 4: + return 7999; + default: + return 1999; + } +} + +int Flags::operator()() const { + return rand() % 10000; +} +int Flags::operator() (VarInfo info) const { + return Get(info.type, info.number); +} + int Flags::Get(int type, int number) const { + int index = type % 26; + type /= 26; + if (index == 25) index = 7; + if (index > 7 || uint(type) > 4) return 0; + if (type == 0) { + // A[]..G[], Z[] を直に読む + if (uint(number) >= 2000) return 0; + return var[index][number]; + } else { + // Ab[]..G4b[], Z8b[] などを読む + int factor = 1 << (type - 1); + int eltsize = 32 / factor; + if (uint(number) >= (64000 / factor)) return 0; + return (var[index][number / eltsize] >> ((number % eltsize) * factor)) & ((1 << factor) - 1); + } + } + +void Flags::Set(VarInfo info, int value) { + int type = info.type / 26; + int index = info.type % 26; + if (index == 25) index = 7; + if (type == 0) { + // A[]..G[], Z[] を直に書く + if (uint(info.number) >= 2000) return; + var[index][info.number] = value; + } else { + // Ab[]..G4b[], Z8b[] などを書く + int factor = 1 << (type - 1); + int eltsize = 32 / factor; + int eltmask = (1 << factor) - 1; + int shift = (info.number % eltsize) * factor; + if (uint(info.number) >= (64000 / factor)) return; + var[index][info.number / eltsize] = + (var[index][info.number / eltsize] & ~(eltmask << shift)) + | (value & eltmask) << shift; + } +} + +void Flags::SetSys(int value) { + sys = value; +} +void Flags::SetStr(unsigned int number, string val) { + if (number >= 2000) return; + str[number] = val; +} +void Flags::Str(unsigned int number, char* buf, int sz) const { + if (number >= 2000) {if(sz>0) buf[0] = 0; return;} + const string& s = str[number]; + int len = s.length(); + if (sz-1 > len) sz = len; + s.copy(buf, sz, 0); + buf[sz] = 0; + return; +} + +/* commands */ +enum Cmdtype { CMD_FLAGS, CMD_JMP, CMD_TEXT, CMD_OTHER}; +class Cmd { + Cmdtype cmd_type; + int cmd1, cmd2, cmd3, cmd4; + int argc; + bool errorflag; + char cmdstr[1024]; + const Flags& flags; + vector<VarInfo> args; + + int GetArgs(const char*& d); + int GetArgsSpecial(int normal_args,const char*& d); + void GetSelection(const char*& d); + int GetSwitch(const char*& d); + int GetSimpleSwitch(const char*& d); + int GetExpression(const char*& d, struct VarInfo* info = 0); + int GetExpressionCond(const char*& d); + int GetLeftToken(const char*& d, struct VarInfo& info); + static int GetString(const char*& d); + int StrVar(int number); + static char strtype[256]; + static int StrType(const char* d) { return strtype[*(unsigned const char*)d];} + int AddStr(char* s) { + // 1-0a-0064 はこういうものが必要らしい + int start = strend; + while (*s) strheap[strend++] = *s++; + strheap[strend++] = 0; + return start; + } +#define STRHEAP_SIZE 10000 + static char strheap[STRHEAP_SIZE]; + static int strend; + void SetError(void) { errorflag = true;} + static void ResetString(void) { + strend = 0; + } + static map<int, struct CmdDescrItem*> cmd_descr; + const char* CmdDescr(int cmd1, int cmd2, int cmd3, int cmd4); +public: + void GetCmd(Flags& f, const char*& d); + bool IsError() { return errorflag;} + bool ClearError() { errorflag = false;} + Cmd(const Flags& f) : flags(f) { argc = 0; errorflag = false; cmdstr[0] = 0;} + +}; + +bool debug_flag = false; +void dprintf(const char* fmt, ...) { + if (debug_flag) { + va_list ap; va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + } +} + + +#define SCN_DUMP + +int system_version = 0; +bool ruby_flag = false; +bool ret_flag = false; +bool text_flag = false; +bool selection_flag = false; +char Cmd::strheap[STRHEAP_SIZE]; +int Cmd::strend = 0; + +/* 数値 num := 0x24 0xff <int num> */ +/* 変数 var := 0x24 <uchar type> 0x5b <exp> 0x5d */ +/* 項 token := num | var | 0x28 <exp> 0x29 | <plus|minus> token */ + +int Cmd::GetLeftToken(const char*& d, VarInfo& info) { + bool var_flag = true; + int minus_flag = 0; + int value = 0; + if (d[0] == 0x5c && (d[1] == 1 || d[1] == 0) ) { + if (d[1] == 1) {dprintf("minus-"); minus_flag ^= 1;} + else dprintf("plus-"); + d += 2; + var_flag = false; + } + if (d[0] == 0x24 && ((unsigned const char*)d)[1] == 0xff) { + // if ( (d[0] == 0x30 || d[0] == 0x31) && d[1] == 0x24 && ((unsigned const char*)d)[2] == 0xff) /* @@@ not supported; selection 内で、0x30|0x31 が付随することがある */ + // numerical atom + d += 6; + value = read_little_endian_int(d-4); + dprintf("%d",value); + var_flag = false; + } else if (d[0] == 0x24 && *(unsigned char*)(d+1) == 0xc8) { + dprintf("V<sys>"); + d += 2; + info.type = TYPE_SYS; info.number = 0; + value = info.value = flags(); + } else if (d[0] == 0x24 && d[2] == 0x5b) { + // 0x24,<type>,0x5b,<expr>,0x5d-terminated term + info.type = *(unsigned char*)(d+1); + d += 3; + dprintf("V<%d>[",info.type); + info.number = GetExpression(d); + dprintf("]"); + if (*d == 0x5d) d++; + else SetError(); + if (info.type == TYPE_VARSTR) { + value = 0; + info.value = StrVar(info.number); + } else { + value = info.value = flags(info); + } + dprintf("(=%d)",value); + } else SetError(); + + if (minus_flag) value = -value; + if (!var_flag) { + info.type = TYPE_VAL; + info.value = value; + } + return value; +} + +static char* op_str[70] = { +// 0 1 2 3 4 5 6 7 8 9 + "+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", // +00 + "err.","err.","err.","err.","err.","err.","err.","err.","err.","err.", // +10 + "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", // +20 + "=", "err.","err.","err.","err.","err.","err.","err.","err.","err.", // +30 + "==", "!=", "<=", "<", ">=", ">", "err.","err.","err.","err.", // +40 + "err.","err.","err.","err.","err.","err.","err.","err.","err.","err.", // +50 + "&&", "||", "err.","err.","err.","err.","err.","err.","err.","err.", // +60 +}; + +static int op_pri_tbl[12] = { +// + - * / % & | ^ << >> + 1, 1, 0, 0, 0, 3, 5, 4, 2, 2, 10, 10}; + +inline int op_pri(int op) { + if (op > 11) return 10; + return op_pri_tbl[op]; +} +inline int op_pri_cond(int op) { + if (op <= 11) return op_pri_tbl[op]; + else if (op < 50) return 7; + else if (op == 60) return 8; + else if (op == 61) return 9; + else return 10; +} + + +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; +} + +/* 演算子 op := 0x5c <uchar op> */ +/* 数式 exp: [op] <token> [op <token> [...]] */ +int Cmd::GetExpression(const char*& d, VarInfo* info_ptr) { +#define STACK_DEPTH 1024 +#define OP_LB 11 + char op_stack[STACK_DEPTH]; + int val_stack[STACK_DEPTH]; + int stack_count = 0; + + // 第一項の読み込み + while(*d == 0x28) { + d++; + dprintf("("); + op_stack[stack_count++] = OP_LB; + } + VarInfo info; + int value = GetLeftToken(d, info); + + while(*d == 0x29 && stack_count > 0 && op_stack[stack_count-1] == OP_LB) { + d++; + dprintf(")"); + stack_count--; + } + + if (*d != 0x5c && stack_count == 0) { + if (info_ptr) *info_ptr = info; + return value; // 単純なleft-termはここで終了。有効なinfo_ptrを帰す(可能性がある) + } + + while(*d == 0x5c) { + int op_type = *(unsigned char*)(d+1); + d += 2; + if (op_type < 70) dprintf("%s",op_str[op_type]); + else dprintf("err."); + if (op_type >= 10) SetError(); + int cur_pri = op_pri(op_type); + while(stack_count != 0 && op_pri(op_stack[stack_count-1]) <= cur_pri) { + // 優先順位の高い、先行する演算を行う + value = eval(val_stack[stack_count-1], op_stack[stack_count-1], value); + stack_count--; + } + val_stack[stack_count] = value; + op_stack[stack_count++] = op_type; + while(*d == 0x28) { + d++; + dprintf("("); + op_stack[stack_count++] = OP_LB; + } + if (stack_count >= STACK_DEPTH) SetError(); + value = GetLeftToken(d, info); + + while (*d != 0x5c && stack_count > 0) { + // 未実行の演算を終わらせる + if (op_stack[stack_count-1] != OP_LB) { + value = eval(val_stack[stack_count-1], op_stack[stack_count-1], value); + stack_count--; + } else if (*d == 0x29) { /* op_stack == OP_LB */ + // bracket 終端があれば、閉じておく + d++; + dprintf(")"); + stack_count--; + } else break; // error + } + } + if (stack_count) SetError(); // unbalanced bracket + dprintf("(=%d)",value); + if (info_ptr) { + info_ptr->type = TYPE_VAL; + info_ptr->value = value; + } + return value; +} + +// 条件分岐専用に、条件演算と算術演算の混合を検知できる専用ルーチン(本来はGetExpressionで差し支えない) +int Cmd::GetExpressionCond(const char*& d) { + char op_stack[STACK_DEPTH]; + int val_stack[STACK_DEPTH]; + int valattr_stack[STACK_DEPTH]; +#define ATTR_VAL 0 +#define ATTR_FLAG 1 + int stack_count = 0; + + // 第一項の読み込み + while(*d == 0x28) { + d++; + dprintf("("); + op_stack[stack_count++] = OP_LB; + } + VarInfo info; + int value = GetLeftToken(d, info); + bool valattr = ATTR_VAL; + + while(*d == 0x5c) { + int op_type = *(unsigned char*)(d+1); + d += 2; + if (op_type < 70) dprintf("%s",op_str[op_type]); + else dprintf("err."); + int cur_pri = op_pri_cond(op_type); + while(stack_count != 0 && op_pri_cond(op_stack[stack_count-1]) <= cur_pri) { + // 優先順位の高い、先行する演算を行う + if (op_stack[stack_count-1] >= 60) { + if (valattr_stack[stack_count-1] != ATTR_FLAG || valattr != ATTR_FLAG) SetError(); + } else { + if (valattr_stack[stack_count-1] != ATTR_VAL || valattr != ATTR_VAL) SetError(); + } + value = eval(val_stack[stack_count-1], op_stack[stack_count-1], value); + if (op_stack[stack_count-1] >= 40) valattr = ATTR_FLAG; + stack_count--; + } + val_stack[stack_count] = value; + valattr_stack[stack_count] = valattr; + op_stack[stack_count++] = op_type; + while(*d == 0x28) { + d++; + dprintf("("); + op_stack[stack_count++] = OP_LB; + } + if (stack_count >= STACK_DEPTH) SetError(); + value = GetLeftToken(d, info); + valattr = ATTR_VAL; + + while (*d != 0x5c && stack_count > 0) { + // 未実行の演算を終わらせる + if (op_stack[stack_count-1] != OP_LB) { + if (op_stack[stack_count-1] >= 60) { + if (valattr_stack[stack_count-1] != ATTR_FLAG || valattr != ATTR_FLAG) SetError(); + } else { + if (valattr_stack[stack_count-1] != ATTR_VAL || valattr != ATTR_VAL) SetError(); + } + value = eval(val_stack[stack_count-1], op_stack[stack_count-1], value); + if (op_stack[stack_count-1] >= 40) valattr = ATTR_FLAG; + stack_count--; + // bracket 終端があれば、閉じておく + } else if (*d == 0x29) { /* op_stack == OP_LB */ + d++; + dprintf(")"); + stack_count--; + } else break; // error + } + } + if (stack_count) SetError(); // unbalanced bracket + if (value) dprintf("(=true)"); + else dprintf("(=false)"); + return value; +} + + +/* +str = +arg = +args = 0x28 <exp> [[0x2c] <exp> [[0x2c] <exp> [...] ]] +*/ + +int Cmd::GetArgs(const char*& d) { + if (*d != 0x28) return 0; /* 引数なし */ + d++; + dprintf("args:"); + VarInfo var; + int i; for (i=0; i<100 ; i++) { + /* number, variable, string の種別なく値を得る */ + if (*d == 0x61) { // よくわからない(智代アフター) + dprintf("@%d",d[1]); + d += 2; + if (*d == 0x28) { + dprintf("{"); + GetArgs(d); // (A,B,C)節が含まれることがある + dprintf("}"); + } else { + dprintf("{}"); + } + } else if (d[0] == 0x0a || d[0] == 0x40) { // よくわからない (Little Busters!) + int var; + if (system_version == 0) { var = read_little_endian_int(d+1); d += 5;} + else { var = read_little_endian_short(d+1); d += 3;} + dprintf("line %d; ",var); + } else if (*d == 0x24 || (*d == 0x5c && (d[1] == 1 || d[1] == 0)) || *d == 0x28) { + GetExpression(d, &var); + args.push_back(var); + } else if (StrType(d)) { + var.type = TYPE_STR; + var.value = GetString(d); + args.push_back(var); + } else SetError(); + if (*d == 0x29) break; + if (*d == 0x2c) {d++;} // 次の arg が演算子で始まる、などがなければ存在しない + dprintf(","); + } + if (*d == 0x29) d++; + else SetError(); + return i; +} + +int Cmd::GetArgsSpecial(int normal_args,const char*& d) { + if (*d != 0x28) return 0; /* 引数なし */ + d++; + dprintf("args:"); + int i; for (i=0; i<normal_args; i++) { + /* number, variable, string の種別なく値を得る */ + if (*d == 0x24 || (*d == 0x5c && (d[1] == 1 || d[1] == 0)) || *d == 0x28) { + GetExpression(d); + } else if (StrType(d)) { + GetString(d); + } else SetError(); + if (*d == 0x29) break; + if (*d == 0x2c) {d++;} // 次の arg が演算子で始まる、などがなければ存在しない + dprintf(","); + } + for (i=0; i<argc ; i++) { + if (*d == 0x28) { +/* +** cmd 01-22:0c1c, 01-22:0835 +** Princess Bride のカードが落ちるアニメの場面 +** なお、_PBCARDANM* の画像はこのコマンドでのみ使われているので、特殊処理として無視することも可能 +** +** cmd 01-04:0276, 026c, 0270 +** 複数の enum が args の数だけ続く処理。特殊処理として分離する +*/ +dprintf("enum.<"); + /* (...) は列挙型 or 構造体の可能性がある */ + const char* d_orig = d; + int pt = args.size(); args.push_back(VarInfo(0)); + int count = GetArgs(d); + args[pt] = VarInfo(count); +dprintf(">"); + } else if (*d == 0x61 && (d[1] >= 0x00 && d[1] <= 0x04) && d[2] == 0x28 ) { + /* 使われるコマンドは 01-21:004b, 01-28:0064 のいずれか(R,C,PB,LO) + ** それらのコマンドは + ** arg1: 画像ファイル名 + ** arg2 : Sel 番号 + ** らしく、arg3 以降が 0x61 <00-04> (a,b,c,...) となる(ダンプ上は enum と表記される) + ** () 内の引数はさまざまで、a のみ(画像ファイル名)、 + ** a,b b=SEL? + ** a,b,c (b,c)=座標? + ** a,(b,c,d,e,f,g) b-g = src / dest? + ** らしい + */ + dprintf("kasane. #%d <",d[1]); + d += 2; + int pt = args.size(); args.push_back(VarInfo(0)); + int count = GetArgs(d); + args[pt] = VarInfo(count); + dprintf(">"); + } else if (*d == 0x24 || (*d == 0x5c && (d[1] == 1 || d[1] == 0))) { + /* cmd 01-15:0028 ; 始めに 0x24 節があり、続いて 0x28 節になる */ + VarInfo var; + GetExpression(d, &var); + args.push_back(var); + i--; // この引数はargc の数には入らない + } else SetError(); + if (d[0] == 0x0a || d[0] == 0x40) { + /* cmd 01-15:0028 ; 0x28 節の後に毎回 0x0a 節が来る */ + int var; + if (system_version == 0) { var = read_little_endian_int(d+1); d += 5;} + else { var = read_little_endian_short(d+1); d += 3;} + dprintf("line %d; ",var); + } + if (*d == 0x29) break; + if (*d == 0x2c) {d++;} // 次の arg が演算子で始まる、などがなければ存在しない + dprintf(","); + } + if (*d == 0x29) d++; + else SetError(); + return 0; +} + +/* switch + <exp> + 0x7b + <exp> <int> + ... + 0x7d +*/ + +int Cmd::GetSwitch(const char*& d) { + if (*d != 0x28) {SetError(); return -1;} + d++; + dprintf("switch. "); + int var = GetExpression(d); + if (*d != 0x29) {SetError(); return -1;} + d++; + dprintf("->\n"); + if (*d == 0x7b) { + d++; + } else SetError(); + + int default_jmp = -1; int jmpto = -1; + int i; for (i=0; i<argc; i++) { + dprintf("\t"); + if (*d++ != 0x28) {SetError(); return -1;} + int item = -1; // default + if (*d != 0x29) { + int item = GetExpression(d); + if (*d++ != 0x29) {SetError(); return -1;} + int jmp = read_little_endian_int(d); + if (var == item) { + dprintf("(selected)"); + jmpto = jmp; + } + dprintf(" -> %d\n", jmp); + } else { + d++; + default_jmp = read_little_endian_int(d); + } + d += 4; + } + if (default_jmp != -1) { + dprintf("default -> %d\n",default_jmp); + if (jmpto == -1) jmpto = default_jmp; + } + if (*d == 0x7d) { + d++; + } else SetError(); + return jmpto; +} +/* simple switch + <exp> + 0x7b + <int> + ... + 0x7d +*/ +int Cmd::GetSimpleSwitch(const char*& d) { + if (*d != 0x28) {SetError(); return -1;} + d++; + dprintf("simple switch. "); + int var = GetExpression(d); + if (*d != 0x29) {SetError(); return -1;} + d++; + dprintf(" ->\n"); + int jumpto = -1; + if (*d == 0x7b) { + d++; + } else SetError(); + int i; for (i=0; i<argc; i++) { + int j = read_little_endian_int(d); + d += 4; + dprintf("\t%d -> %d\n", i+1, j); + if (var == i+1) jumpto = j; + } + if (*d == 0x7d) { + d++; + } else SetError(); + return jumpto; +} + +/* +selection + ? <exp> + 0x7b + <0x0a|0x40> <ushort | uint> +*/ +void Cmd::GetSelection(const char*& d) { + dprintf("selection. "); + if (*d == 0x28) { + d++; + GetExpression(d); + if (*d != 0x29) { SetError(); return;} + d++; + } + if (*d == 0x7b) { + d++; + dprintf("{\n\t"); + } else SetError(); + int arg_count = 0; + while(*d != 0x7d) { + if (d[0] == 0x0a || d[0] == 0x40) { + int var; + if (system_version == 0) { var = read_little_endian_int(d+1); d += 5;} + else { var = read_little_endian_short(d+1); d += 3;} + dprintf("line %d; ",var); + } else if (d[0] == 0x2c) { + dprintf(":comma:"); + } else if (d[0] == 0x28) { + dprintf(":cond:"); + d++; + while(d[0] != 0x29) { + GetExpressionCond(d); // PRINT- 節でないばあい、条件表示。次は文字節、またはPRINT節のはず + if (IsError()) break; + if (*d == 0x32) { d++; dprintf("##");} // 0x32 なら、現在の条件節を表示しない + if (*d == 0x31) { d++; dprintf("**");} // 0x31 なら、現在の条件節を表示する(Little Busters! : 処理が正しいかは分からない) + dprintf(":"); + } + d++; + } else if (StrType(d)) { + GetString(d); + arg_count++; + dprintf("\n\t"); + } else if (*d == 0x23 && strncmp(d,"###PRINT",8) == 0) { + d += 8; + if (d[0] != 0x28) SetError(); + else { // 文字変数の内容の表示 + d++; + dprintf("Print."); + VarInfo info; + GetLeftToken(d, info); + if (d[0] != 0x29 || info.type == -1) SetError(); + d++; + dprintf(";"); + } + } else { SetError(); break;} + } + d++; + /* @@@ */ + /* 一致しない場合があるのでコメントアウト */ + // if (arg_count != argc) SetError(); + dprintf("\n}\n"); + return; +} + +char* op_str3[11] = { "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", "="}; +void Cmd::GetCmd(Flags& flags_orig, const char*& d ) { + ResetString(); + + cmdstr[0] = 0; + debug_flag = true; + if (*d == 0x23) { /* コマンド */ + cmd_type = CMD_OTHER; + cmd1 = *(unsigned const char*)(d+1); + cmd2 = *(unsigned const char*)(d+2); + cmd3 = read_little_endian_short(d+3); + argc = read_little_endian_short(d+5); + cmd4 = *(unsigned const char*)(d+7); + d += 8; + /* verbose */ + // dprintf(" 0x23 - cmd %02x-%02x:%04x:%02x[%2d] \n",cmd1,cmd2,cmd3,cmd4,argc); + sprintf(cmdstr, "%02x-%02x:%04x:%02x : %s",cmd1,cmd2,cmd3,cmd4,CmdDescr(cmd1,cmd2,cmd3,cmd4)); + /* 引数を得る */ + /* 特殊引数のもの */ + int is_special = 0; + if (cmd1 == 0) { + if (cmd2 == 1) { + int jump_arg = -1; + if (cmd3 == 0 || cmd3 == 5) { + /* gosub / goto */ + jump_arg =read_little_endian_int(d); + d += 4; + dprintf("\tjmp -> %d\n", jump_arg); + is_special = 1; + } else if (cmd3 == 1 || cmd3 == 2) { + /* conditional jump (if / unless) */ + if (*d++ != 0x28) { SetError(); return;} + dprintf("\t"); + int cond = GetExpressionCond(d); + if (IsError()) return; + if (*d++ != 0x29) { SetError(); return; } + int jumpto = read_little_endian_int(d); + d += 4; + dprintf("-> %d\n", jumpto); + if (cond) jump_arg = jumpto; + is_special = 1; + } else if (cmd3 == 4) { + /* switch to */ + jump_arg = GetSwitch(d); + is_special = 1; + } else if (cmd3 == 16) { + dprintf("local call with paramters;\n"); + GetArgs(d); + int jumpto = read_little_endian_int(d); + d += 4; + dprintf("\tjmp -> %d\n",jumpto); + is_special = 1; + } else if (cmd3 == 8 || cmd3 == 3) { + /* switch to */ + jump_arg = GetSimpleSwitch(d); + dprintf("\tjmp -> %d\n",jump_arg); + is_special = 1; + } + cmd_type = CMD_OTHER; + args.push_back(VarInfo(jump_arg)); + } else if (cmd2 == 2 && (cmd3 == 0 || cmd3 == 1 || cmd3 == 2 || cmd3 == 3 || cmd3 == 0x0d) ) { + /* selection */ + GetSelection(d); + is_special = 1; + } + } + /* 一般引数のもの */ + if (!is_special) { + dprintf(" 0x23 - cmd %02x-%02x:%04x:%02x[%2d] : %s\n",cmd1,cmd2,cmd3,cmd4,argc,CmdDescr(cmd1,cmd2,cmd3,cmd4)); + dprintf("\t"); + if (cmd1 == 1 && cmd2 == 0x22 && (cmd3 == 0xc1c || cmd3 == 0x835)) GetArgsSpecial(3, d); + else if (cmd1 == 1 && cmd2 == 0x0b && cmd3 == 0x65) GetArgsSpecial(0, d); + else if (cmd1 == 1 && cmd2 == 0x15 && cmd3 == 0x28) GetArgsSpecial(0, d); + else if (cmd1 == 1 && cmd2 == 4 && (cmd3 == 0x26c || cmd3 == 0x26d || cmd3 == 0x270 || cmd3 == 0x276)) GetArgsSpecial(0, d); + else if (cmd1 == 1 && (cmd2 == 0x21 && cmd3 == 0x4b) || (cmd2 == 0x28 && cmd3 == 0x64)) GetArgsSpecial(2,d); + else GetArgs(d); + dprintf("\n"); + + } + if (cmd2 == 3 && cmd3 == 0x78 && cmd4 == 0) ruby_flag = true; + if (cmd2 == 3 && cmd3 == 0x11) ret_flag = true; + } else if (*d == 0x24) { /* 代入演算 */ + if (d[1] == 0x12 || d[2] != 0x5b) SetError(); + dprintf("expr: "); + sprintf(cmdstr, "expr"); + + VarInfo info; + int value = GetLeftToken(d, info); + if (d[0] != 0x5c) SetError(); + int type = d[1]; + if (type < 20 || type > 30) SetError(); + else dprintf("%s",op_str[type]); + d += 2; + int value2 = GetExpression(d); + // 代入情報を埋め込む + if (type != 30) value2 = eval(value, type-20, value2); + cmd_type = CMD_FLAGS; + args.push_back(info); + args.push_back(value2); + dprintf("\n"); + } else if (StrType(d)) { /* 文字出力 */ + VarInfo info; + info.type = TYPE_STR; + info.value = GetString(d); + args.push_back(info); + cmd_type = CMD_TEXT; + text_flag = true; + dprintf("\n"); + } else if (*d == 0x0a || *d == 0x40 || *d == 0x21) { /* デバッグ用データと既読フラグ */ + cmd_type = CMD_OTHER; + if (*d == 0x0a) { + dprintf("line "); + d++; + int l; + if (system_version == 0) { + l = read_little_endian_int(d); + d += 4; + } else { + l = read_little_endian_short(d); + d += 2; + } + dprintf("%d\n", l); + } else { /* 0x40, 0x21 */ + // 既読マーカーらしい。エントリーポイントとセーブポイントも使われる。 + // RealLive 1.2.5から、0x40はセーブポイント、0x21はエントリーポイント。 + // 1.2.5以前、どちらも0x40が使われる。 + int kidoku_index; + d++; + if (system_version == 0) { + kidoku_index = read_little_endian_int(d); + d += 4; + } else { + kidoku_index = read_little_endian_short(d); + d += 2; + } + dprintf("kidoku marker %d\n", kidoku_index); + // text_readflagは、このkidoku_indexを使ったら良いかな。 + } + } else if (*d == 0x2c) { /* ??? */ + dprintf("commd;0x2c\n"); // conditional jump の行き先によくあるらしい(常に、かはわからない) + d++; + } else { + SetError(); + } + return; +} + +char Cmd::strtype[256] = { + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +00 */ + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +10 */ + 0,0,3,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +20 */ + 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,1, /* +30 */ + 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, /* +40 */ + 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,1, /* +50 */ + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +60 */ + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +70 */ + 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, /* +80 */ + 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, /* +90 */ + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +A0 */ + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +B0 */ + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +C0 */ + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* +D0 */ + 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, /* +E0 */ + 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,0,0 /* +F0 */ +}; + +int Cmd::GetString(const char*& d) { + int retnum = -1; + while(1) { + if (*d == '\\') { + d++; + strheap[strend++] = *d++; + } else if (*d == '"') { + d++; + if (retnum == -1) retnum = strend; + while(*d != '"') strheap[strend++] = *d++; + d++; + } else if (StrType(d)) { + if (retnum == -1) retnum = strend; + int stype; + while( (stype = StrType(d)) ) { + if (stype == 3) break; // 文中に '"' が現れた場合 + strheap[strend++] = *d++; + if (stype == 2) strheap[strend++] = *d++; + } + } else break; + } + if (retnum != -1) strheap[strend++] = 0; + dprintf("\"%s\"", strheap + retnum); + if (strend >= STRHEAP_SIZE) { + dprintf("Error: string heap overflow\n"); + } + return retnum; +} + +int Cmd::StrVar(int var_num) { + int retnum = strend; + flags.Str(var_num, strheap+strend, STRHEAP_SIZE-strend); + strend += strlen(strheap+strend)+1; + return retnum; +} + +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); +} +int main(int argc, char** argv) { + /* determine file names */ + 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 == 0) 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; + Cmd cmd(flags); 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; Cmd::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*> Cmd::cmd_descr; +const char* Cmd::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)"; +}