Mercurial > otakunoraifu
diff scn2k/scn2k_cmd.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | a05bf0823154 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/scn2k/scn2k_cmd.cc @@ -0,0 +1,1749 @@ +/* + * Copyright (c) 2004-2006 Kazunori "jagarl" Ueno + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include"scn2k.h" + +#include<stdlib.h> +#include<stdarg.h> +#include<stdio.h> +#include<string.h> +#include<string> +#include"system/file.h" + +using namespace std; + + +// #define SCN_DUMP +/* 注意点: @@@ で表記 */ + + + +//bool debug_flag = true; +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); + } +} + + +void eprintf(const char* fmt, ...) { + va_list ap; va_start(ap, fmt); +// vprintf(fmt, ap); + va_end(ap); +} + +/************************************************************** +** Flag +*/ + +Flags::Flags(void) { + int i,j; + for (i=0; i<=TYPE_VARMAX; i++) { + for (j=0; j<2000; j++) { + var[i][j] = 0; + } + } + sys = 0; +} + +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 sys; // 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) { + if (var[7][number] != 0) return var[7][number]; + if (cgm_data.find(number) == cgm_data.end()) return 0; + else return 1; + } + if (index == 10) index = 8; + if (index == 11) index = 9; + if (index > TYPE_VARMAX || 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) { + if (uint(info.number) >= 2000) return; + if (value == 0) + cgm_data.erase(info.number); + else + cgm_data.insert(info.number); + index = 7; + } + if (index == 10) index = 8; + if (index == 11) index = 9; + if (index < 0 || index > TYPE_VARMAX) { + fprintf(stderr,"Error: invalid access to Var<%d>[%d]\n",info.type,info.number); + } + 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(VarInfo info, string val) { + switch(info.type) { + case TYPE_VARLOCSTR: + if (info.number >= 3) return; + loc_str[info.number] = val; + break; + case TYPE_VARSYSSTR: + if (info.number >= 2000) return; + sys_str[info.number] = val; + break; + case TYPE_VARSTR: + if (info.number >= 2000) return; + str[info.number] = val; + break; + } + return; +} +void Flags::Str(int type, unsigned int number, char* buf, int sz) const { + if (sz <= 0) return; + buf[0] = 0; + const string* sptr; + switch(type) { + case TYPE_VARLOCSTR: + if (number >= 3) return; + sptr = &loc_str[number]; + break; + case TYPE_VARSYSSTR: + if (number >= 2000) return; + sptr = &sys_str[number]; + break; + case TYPE_VARSTR: + if (number >= 2000) return; + sptr = &str[number]; + break; + } + + int len = sptr->length(); + if (sz-1 > len) sz = len; + sptr->copy(buf, sz, 0); + buf[sz] = 0; + return; +} +string Flags::Str(int type, unsigned int number) const { + switch(type) { + case TYPE_VARLOCSTR: + if (number >= 3) return ""; + return loc_str[number]; + case TYPE_VARSYSSTR: + if (number >= 2000) return ""; + return sys_str[number]; + case TYPE_VARSTR: + if (number >= 2000) return ""; + return str[number]; + } + return ""; +} + +void Flags::Save(string& save) { + char buf[1024]; + save = "\n[Flags]\n"; + int i,j; + for (i=0; i<=TYPE_NONSYSVARMAX; i++) { + for (j=0; j<2000; j++) { + if (var[i][j] != 0) { + sprintf(buf, "V<%d>[%04d]=%d\n",i,j,var[i][j]); + save += buf; + } + } + } + for (j=0; j<2000; j++) { + if (str[j].length() != 0) { + sprintf(buf, "V<C>[%04d]=%s\n", j, str[j].c_str()); + save += buf; + } + } +} +void Flags::Load(const char* save) { + int i,j; + for (i=0; i<=TYPE_NONSYSVARMAX; i++) { + for (j=0; j<2000; j++) { + var[i][j] = 0; + } + } + sys = 0; + for (j=0; j<2000; j++) { + str[j] = ""; + } + + save = strstr(save, "\n[Flags]\n"); + + if (save) { + save += strlen("\n[Flags]\n"); + do { + if (save[0] == '[') break; // next section + if (strncmp(save, "V<",2) == 0) { + if (strncmp(save, "V<C>[",5) == 0) { // string + char buf[1024]; + int n; + if (sscanf(save, "V<C>[%04d]=",&n) == 1) { + char* s = strchr(save, '='); + s++; + char* send = strchr(s, '\n'); + int slen = send - s; + strncpy(buf, s, slen); + buf[slen] = 0; + if (n >= 0 && n < 2000) str[n] = buf; + } + } else if (save[2] >= '0' && save[2] <= '9') { + int c,n,v; + if (sscanf(save, "V<%d>[%04d]=%d\n",&c,&n,&v) == 3) { + if (c >= 0 && c <= TYPE_NONSYSVARMAX && n >= 0 && n < 2000) + var[c][n] = v; + } + } + } + save = strchr(save, '\n'); + if (save) save++; + } while (save); + } + return; +} + +void Flags::SaveSys(string& save) { + char buf[1024]; + int j; + save = "\n[Flags]\n"; + for (j=0; j<2000; j++) { + if (var[6][j] != 0) { + sprintf(buf, "V<6>[%04d]=%d\n",j,var[6][j]); + save += buf; + } + } + for (j=0; j<2000; j++) { + if (var[7][j] != 0) { + sprintf(buf, "V<25>[%04d]=%d\n",j,var[7][j]); + save += buf; + } + } + for (j=0; j<2000; j++) { + if (sys_str[j].length() != 0) { + sprintf(buf, "V<M>[%04d]=%s\n", j, sys_str[j].c_str()); + save += buf; + } + } +} +void Flags::LoadSys(const char* save) { + int i,j; + for (i=6; i<=7; i++) { + for (j=0; j<2000; j++) { + var[i][j] = 0; + } + } + for (j=0; j<2000; j++) { + sys_str[j] = ""; + } + sys = 0; + + save = strstr(save, "\n[Flags]\n"); + + if (save) { + save += strlen("\n[Flags]\n"); + do { + if (save[0] == '[') break; // next section + if (strncmp(save, "V<",2) == 0) { + if (strncmp(save, "V<M>[",5) == 0) { // string + char buf[1024]; + int n; + if (sscanf(save, "V<M>[%04d]=",&n) == 1) { + char* s = strchr(save, '='); + s++; + char* send = strchr(s, '\n'); + int slen = send - s; + strncpy(buf, s, slen); + buf[slen] = 0; + if (n >= 0 && n < 2000) sys_str[n] = buf; + } + } else if (save[2] >= '0' && save[2] <= '9') { + int c,n,v; + if (sscanf(save, "V<%d>[%04d]=%d\n",&c,&n,&v) == 3) { + if (c == 6 && n >= 0 && n < 2000) + var[6][n] = v; + else if (c == 25 && n >= 0 && n < 2000) + var[7][n] = v; + } + } + } + save = strchr(save, '\n'); + if (save) save++; + } while (save); + } + return; +} + +bool Flags::Exec(Cmd& cmd) { + if (cmd.cmd_type == CMD_FLAGS) { // 代入演算 + if (cmd.args.size() != 2) return false; + Set(cmd.args[0], cmd.args[1].value); + cmd.clear(); + return true; + } + if (cmd.cmd1 == 1 && cmd.cmd2 == 0x0a) { // 文字列演算 + VarInfo arg1 = cmd.args[0]; + switch(cmd.cmd3) { + case 0: + if (cmd.cmd4 == 0) { + SetStr(arg1, cmd.Str(cmd.args[1])); + } else if (cmd.cmd4 == 1) { + string s = cmd.Str(cmd.args[1]); + const char* sc = s.c_str(); + int len = cmd.args[2].value; + int i;for (i=0; i<sc[i]!=0 && len != 0; i++, len--) { + if (sc[i]<0 && sc[i+1]!=0) i++; + } + s.erase(i); // 全角で len 文字まで切り詰める + SetStr(arg1, s); +// fprintf(stderr,"Set[%d,%d]<-%s\n",arg1.type,arg1.number,s.c_str()); + } else break; + cmd.clear(); + break; + case 1: + if (cmd.cmd4 == 0) { + SetStr(arg1, ""); + cmd.clear(); + } else if (cmd.cmd4 == 1) { + // 領域指定で文字列クリア + VarInfo v1 = cmd.args[0]; + VarInfo v2 = cmd.args[1]; + eprintf("memclear(str). Var[%d]<%d> - Var[%d]<%d>\n",v1.type, v1.number, v2.type, v2.number); + if (v1.type != v2.type || (v1.type != TYPE_VARSTR && v1.type != TYPE_VARSYSSTR && v1.type != TYPE_VARLOCSTR)) { + eprintf(" error: bad args\n"); + } else { + if (v1.number < 0) v1.number = 0; + if (v2.number > 2000) v2.number = 2000; + for (; v1.number <= v2.number; v1.number++) { + SetStr(v1, ""); + } + } + cmd.clear(); + } + case 2: + SetStr(arg1, Str(arg1.type,arg1.number) + cmd.Str(cmd.args[1])); +// fprintf(stderr,"Append[%d,%d]<-%s(%d:%d)\n",arg1.type,arg1.number,Str(arg1.type,arg1.number).c_str(),cmd.args[1].type,cmd.args[1].number); + cmd.clear(); + break; + case 3: + SetSys(strlen(cmd.Str(cmd.args[0]))); + cmd.clear(); + break; + case 4: + { int v = strcmp(cmd.Str(cmd.args[0]), cmd.Str(cmd.args[1])); +// string s1=cmd.Str(cmd.args[0]); +// string s2=cmd.Str(cmd.args[1]); +// fprintf(stderr,"Cmp %s(%d:%d):%s(%d:%d):%d\n",s1.c_str(),cmd.args[0].type,cmd.args[0].number,s2.c_str(),cmd.args[1].type,cmd.args[1].number,v); + if (v < 0) SetSys(-1); + else if (v > 0) SetSys(1); + else SetSys(0); + cmd.clear(); + break; } + case 5: // substring, index from left + case 6: // substring, index from right + // 全角対応らしい + { int offset = cmd.args[2].value; + int len = strlen(cmd.Str(cmd.args[1])); + string str = cmd.Str(cmd.args[1]); + const char* s = str.c_str(); + if (cmd.cmd3 == 6) offset = len - offset; + if (offset < 0) offset = 0; + // 先頭 N 文字を読み飛ばす + int i; + int offset_top = 0; + for (i=0; i<offset && s[offset_top] != 0; i++) { + if (s[offset_top] < 0 && s[offset_top+1] != 0) offset_top += 2; + else offset_top += 1; + } + if (s[offset_top] == 0) { + SetStr(arg1, ""); + } else if (cmd.cmd4 == 0) { // 長さ制限なし + SetStr(arg1, string(s, offset_top)); + } else { // cmd.cmd4 == 1 + int slen = cmd.args[3].value; + int offset_end = offset_top; + for (i=0; i<slen && s[offset_end] != 0; i++) { + if (s[offset_end] < 0 && s[offset_end]+1 != 0) offset_end += 2; + else offset_end += 1; + } + string result(s, offset_top, offset_end-offset_top); + SetStr(arg1, result); + } + cmd.clear(); + break; } + case 7: {// strlen w/ kanji + const char* s = cmd.Str(cmd.args[0]); int i; + for (i=0; *s != 0; i++) { + if (*s < 0 && s[1] != 0) s += 2; + else s++; + } + SetSys(i); + cmd.clear(); + break; } + case 8: // 文字列を切って短くする + if (cmd.args[1].value <= 0) { + SetStr(arg1, ""); + } else if (cmd.args[1].value < strlen(cmd.Str(cmd.args[1]))) { + Str(arg1.type,arg1.number).erase(cmd.args[1].value); + } + cmd.clear(); + break; + case 0x0e: // 漢字モードでitoa + { + int arg1 = cmd.args[0].value; + string result; + char wc[3]; wc[2]=0; + char buf[20]; + if (cmd.cmd4 == 0) { + sprintf(buf, "%d", arg1); + } else { // cmd.cmd4 == 1 + char fmt[20]; + sprintf(fmt, "%%%dd", cmd.args[2].value); + sprintf(buf, fmt, arg1); + } + int i; + for (i=0; buf[i] != 0; i++) { + if (buf[i] == ' ') { + wc[0] = 0x81; + wc[0] = 0x40; + } else if (buf[i] == '-') { + wc[0] = 0x81; + wc[0] = 0x7c; + } else if (isdigit(buf[i])) { + wc[0] = 0x82; + wc[1] = buf[i] - '0' + 0x4f; + } else { + continue; + } + result += wc; + } + SetStr(cmd.args[1], result); + cmd.clear(); + } + break; + case 0x0f: case 0x11: // itoa (0x11 の方は zero padding するっぽい) + if (cmd.cmd4 == 0) { + int arg1 = cmd.args[0].value; + char buf[1024]; sprintf(buf, "%d", arg1); + SetStr(cmd.args[1], buf); + cmd.clear(); + } else if (cmd.cmd4 == 1) { + // 漢字(SJIS) : 82 [4f+N] + // やはり漢字じゃない? + int arg1 = cmd.args[0].value; + char buf[1024]; char fmt[1024]; + if (cmd.cmd3 == 0x0f) { + sprintf(fmt, "%%%dd",cmd.args[2].value); /* 空白でパディング */ + } else { + sprintf(fmt, "%%0%dd",cmd.args[2].value); + } + sprintf(buf, fmt, arg1); + SetStr(cmd.args[1], buf); + cmd.clear(); + } + break; + case 0x64: // 文字列の表示 : 引数をテキストウィンドウに表示 + if (cmd.cmd4 == 1) { + char buf[256]; + snprintf(buf, 255, "%d", Get(cmd.args[0].type, cmd.args[0].number)); + cmd.args[0].type = TYPE_STR; + cmd.args[0].value = cmd.AddStr(buf); + cmd.cmd4 = 0; + } + +#if 0 +@@@ +save 27 +ともよメガネのところ +- オブジェクト関連:seen9061:0 呼び出しで黒い背景画をかさねるところ、変になる +@@@ +%Xで置換する名前の設定。0x51e で読みだし。セーブファイルごとに保存されるはずなので実装を考えること +%は0-3 (4 以降は使ってない)で、渚、秋生、渚、伊吹先生、など +StrVar を拡張して代入すること +初期値はこの辺 +Text側に納め、セーブファイルでも同じようにすべきだろうなあ + args:0,"渚" + args:1,"秋生" + args:2,"渚" + args:3,"伊吹先生" + args:4,"朋也くん" + args:5,"岡崎さん" + + +106737 : 0x23 - cmd 01-04:051f:00[ 2] + args:0,"古河" +106758 : line 1712 +106761 : 0x23 - cmd 01-04:051f:00[ 2] + args:2,"古河" +106782 : line 1713 +106785 : 0x23 - cmd 01-04:051f:00[ 2] + args:4,"岡崎さん" + +47382 : 0x23 - cmd 01-04:051e:00[ 2] + args:4,V<18>[0](=0) + +47408 : 0x23 - cmd 01-0a:0004:00[ 2] + args:V<18>[0](=0),"岡崎さん" +47437 : expr: V<0>[1000](=0)=V<sys> +47451 : 0x23 - cmd 01-0a:0004:00[ 2] + args:V<18>[0](=0),"朋也くん" +47480 : expr: V<0>[1001](=0)=V<sys> +47494 : V<0>[1000](=0)==0(=true)-> 47589 +47526 : 0x23 - cmd 01-04:0514:00[ 2] + args:0,V<18>[0](=0) /* NAME.A を帰す */ +47552 : 0x23 - cmd 01-0a:0002:00[ 2] + args:V<18>[0](=0),"さん" +47577 : jmp -> 47672 +47589 : V<0>[1001](=0)==0(=true)-> 47672 +47621 : 0x23 - cmd 01-04:0514:00[ 2] + args:1,V<18>[0](=0) /* NAME.B を帰す */ +47647 : 0x23 - cmd 01-0a:0002:00[ 2] + args:V<18>[0](=0),"くん" +47672 : pos. 279 +47675 : 0x23 - cmd 01-0a:0064:00[ 1] + args:V<18>[0](=0) + +#endif + cmd.cmd_type = CMD_TEXT; + break; + } + } + if (cmd.cmd1 == 1 && cmd.cmd2 == 0x0b) { // 数値変数演算 + if (cmd.cmd3 == 0 && cmd.cmd4 == 0) { + /* 複数の変数をセット */ + VarInfo v1 = cmd.args[0]; + eprintf("set multiple-var Var[%d]<%d> <- ",v1.type, v1.number); + int i; + if (cmd.args.size() < cmd.argc) { + eprintf(" error: argsize changed %d -> %d\n",cmd.argc, cmd.args.size()); + cmd.argc = cmd.args.size(); + } + for (i=0; i<cmd.argc; i++) { + eprintf("%d, ",cmd.args[i+1].value); + Set(v1, cmd.args[i+1].value); + v1.number++; + } + eprintf("\n"); + cmd.clear(); + } else if (cmd.cmd3 == 1 && cmd.cmd4 == 0) { + /* 領域指定で変数をクリア */ + VarInfo v1 = cmd.args[0]; + VarInfo v2 = cmd.args[1]; + eprintf("memclear. Var[%d]<%d> - Var[%d]<%d>\n",v1.type, v1.number, v2.type, v2.number); + if (v1.type != v2.type || !IsInt(v1.type)) eprintf(" error: bad args\n"); + else { + if (v1.number < 0) v1.number = 0; + if (v2.number > MaxIndex(v2.type)) v2.number = MaxIndex(v2.type); + for (; v1.number <= v2.number; v1.number++) + Set(v1, 0); + } + cmd.clear(); + } else if (cmd.cmd3 == 1 && cmd.cmd4 == 1) { + /* 領域指定で変数をセット */ + VarInfo v1 = cmd.args[0]; + VarInfo v2 = cmd.args[1]; + int value = cmd.args[2].value; + eprintf("memset. Var[%d]<%d> - Var[%d]<%d> <- %d\n",v1.type, v1.number, v2.type, v2.number, value); + if (v1.type != v2.type || !IsInt(v1.type)) eprintf(" error: bad args\n"); + else { + if (v1.number < 0) v1.number = 0; + if (v2.number > MaxIndex(v2.type)) v2.number = MaxIndex(v2.type); + for (; v1.number <= v2.number; v1.number++) + Set(v1, value); + } + cmd.clear(); + } else if (cmd.cmd3 == 4 && cmd.cmd4 == 1) { // 領域クリア(sysfunc.txt) + VarInfo v1 = cmd.args[0]; + int step = cmd.args[1].value; + int deal = cmd.args[2].value; + int val = cmd.args[3].value; + eprintf("memclear. Var[%d]<%d> step %d deal %d <- val %d\n",v1.type, v1.number, step, deal, val); + int i; for (i=0; i<deal; i++) { + Set(v1, val); + v1.number += step; + } + cmd.clear(); + } else if (cmd.cmd3 == 0x64 && cmd.cmd4 == 0) { //領域で数値を合計する + VarInfo v1 = cmd.args[0]; + VarInfo v2 = cmd.args[1]; + eprintf("sum var. Var[%d]<%d> - Var[%d]<%d>\n",v1.type, v1.number, v2.type, v2.number); + int sum = 0; + if (v1.type != v2.type || !IsInt(v1.type)) eprintf(" error: bad args\n"); + else { + if (v1.number < 0) v1.number = 0; + if (v2.number > MaxIndex(v2.type)) v2.number = MaxIndex(v2.type); + for (; v1.number <= v2.number; v1.number++) + sum += (*this)(v1); + } + eprintf(" ret %d\n",sum); + cmd.SetSysvar(sum); + } + } + return false; +} + +/********************************************************************* +** Cmd +*/ + +/* 数値 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 || info.type == TYPE_VARSYSSTR || info.type == TYPE_VARLOCSTR) { + value = 0; + info.value = StrVar(info.type, 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] = { +// + - * / % & | ^ << >> + 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 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 8; + 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); + while(*d == 0x29 && stack_count > 0 && op_stack[stack_count-1] == OP_LB) { + d++; + dprintf(")"); + stack_count--; + } + 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) 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; + string text = ""; + int cond_result = false; + int sel_no = 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); + if (text.length() != 0) { + if (cond_result) ; // 条件節が true なら表示しない + else { + const char* str = text.c_str(); + VarInfo var; + var.type = TYPE_STR; + var.value = CopyString(str); + args.push_back(var); + var.type = TYPE_VAL; + var.value = sel_no; + args.push_back(var); + } + sel_no++; + } + text = ""; + cond_result = false; + } else if (d[0] == 0x2c) { + dprintf(":comma:"); + } else if (d[0] == 0x28) { + dprintf(":cond:"); + d++; + while(d[0] != 0x29) { + int result = GetExpressionCond(d); // PRINT- 節でないばあい、条件表示。次は文字節、またはPRINT節のはず + if (*d == 0x32) { // 0x32 なら、現在の条件節を表示しない + d++; dprintf("##"); + cond_result = result; + } else if (*d == 0x31) { // 0x31 なら、現在の条件節を表示する + // Little Busters! : この条件で正しいかは未検証 + d++; dprintf("***"); + cond_result = !result; + } + dprintf(":"); + } + d++; + } else if (StrType(d)) { + int strpt = GetString(d); + text += strheap + strpt; + 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(";"); + // 数値を全角文字に変換して登録 + char str[10], str2[20]; // itoa + sprintf(str, "%d", info.value); + int i; for (i=0; str[i] != 0; i++) { + str2[i*2] = 0xa3; + str2[i*2+1] = 0xb0 + str[i]-'0'; + } + str2[i*2] = 0; + text += str2; + } + } else { SetError(); break;} + } + d++; + /* @@@ */ + /* 一致しない場合があるのでコメントアウト */ + // if (arg_count != argc) SetError(); + dprintf("\n}\n"); + return; +} + +static char* op_str3[11] = { "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", "="}; +void Cmd::GetCmd(Flags& flags_orig, const char*& d ) { + if (d == 0) { SetError(); return;} + if (cmd_type != CMD_NOP) return; + + cmdstr[0] = 0; + rawdata = d; + 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",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; + if (cmd3 == 0) + dprintf("\tjmp -> %d\n", jump_arg); + else /* cmd3 == 5 */ + dprintf("\tcall -> %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 (cmd3 == 1) cond = !cond; // 逆になる + if (*d++ != 0x29) { SetError(); return; } + int jumpto = read_little_endian_int(d); + d += 4; + dprintf("-> %d\n", jumpto); + if (! cond) jump_arg = jumpto; /* condition が満たされない場合、ジャンプ */ + is_special = 1; + } else if (cmd3 == 4) { + /* switch to */ + jump_arg = GetSwitch(d); + is_special = 1; + } else if (cmd3 == 8 || cmd3 == 3) { + /* switch to */ + jump_arg = GetSimpleSwitch(d); + is_special = 1; + } else if (cmd3 == 16) { // call with parameters + GetArgs(d); + jump_arg = read_little_endian_int(d); + d += 4; + is_special = 1; + } else goto retry; + if (jump_arg == -1) { + cmd_type = CMD_NOP; + } + else { + cmd_type = CMD_JMP; + 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; + } + } +retry: + /* 一般引数のもの */ + if (!is_special) { + dprintf(" 0x23 - cmd %02x-%02x:%04x:%02x[%2d] \n",cmd1,cmd2,cmd3,cmd4,argc); + 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 == 4 && cmd3 == 0x586) GetArgsSpecial(1, d); + else if (cmd1 == 1 && (cmd2 == 0x21 && cmd3 == 0x4b) || (cmd2 == 0x28 && cmd3 == 0x64)) GetArgsSpecial(2,d); + else GetArgs(d); + dprintf("\n"); + + } + } 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; + dprintf("\n"); + } else if (*d == 0x0a || *d == 0x40 || *d == 0x21) { /* デバッグ用データと既読フラグ */ + cmd_type = CMD_NOP; + 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; +} +void Cmd::clear(void) { + cmd_type = CMD_NOP; + ResetString(); + args.clear(); + errorflag = false; + pos = -1; +} + +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 */ // 0123456789ABCDEF + 1,0,3,0, 0,0,0,1, 0,0,0,0, 0,1,1,0, /* +20 */ // !"#$%&'()*+,-./ + 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,1, /* +30 */ // 0123456789:;<=>? + 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, /* +40 */ // @ABCDEFGHIJKLMNO + 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,1, /* +50 */ // PQRSTUVWXYZ[\]^_ + 0,0,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, /* +60 */ // `abcdefghijklmno + 1,1,1,1, 1,1,1,1, 1,1,1,1, 0,0,0,0, /* +70 */ // pqrstuvwxyz{|}~ + 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; + bool quote_flag = false; + int stype; + retnum = strend; + while(1) { + if (*d == '\\') { + d++; + strheap[strend++] = *d++; + } else if (*d == '"') { + if (quote_flag) quote_flag = false; + else quote_flag = true; + d++; + } else if (quote_flag) { + strheap[strend++] = *d++; + } else if (stype = StrType(d)) { + strheap[strend++] = *d++; + if (stype == 2) strheap[strend++] = *d++; + } else break; + } + strheap[strend++] = 0; + dprintf("\"%s\"", strheap + retnum); + if (strend >= STRHEAP_SIZE) { + dprintf("Error: string heap overflow\n"); + } + return retnum; +} + +int Cmd::CopyString(const char* d) { + int retnum = strend; + int len = strlen(d); + memcpy(strheap+strend, d, len+1); + strend += len+1; + d += len+1; + return retnum; +} + +int Cmd::StrVar(int type, int var_num) { + int retnum = strend; + flags.Str(type, var_num, strheap+strend, STRHEAP_SIZE-strend); + strend += strlen(strheap+strend)+1; + return retnum; +} + +void Cmd::SetSysvar(int n, int val) { + VarInfo info; + if (cmd_type != CMD_SYSVAR) { + args.clear(); + } + cmd_type = CMD_SYSVAR; + + info.type = TYPE_SYS; + info.number = n; + info.value = val; + args.push_back(info); +} +void Cmd::SetFlagvar(VarInfo info, int val) { + if (cmd_type != CMD_SYSVAR) { + args.clear(); + } + cmd_type = CMD_SYSVAR; + + info.value = val; + args.push_back(info); +} + +void Cmd::SetStrvar(VarInfo info, const string& s) { + if (cmd_type != CMD_SYSVAR) { + args.clear(); + } + + cmd_type = CMD_SYSVAR; + const char* ss = s.c_str(); + info.value = CopyString(ss); + args.push_back(info); +} + +void Cmd::read(const CmdSimplified& from) { + errorflag = false; + ResetString(); + + cmd_type = Cmdtype(from.type); + cmd1 = from.cmd1; + cmd2 = from.cmd2; + cmd3 = from.cmd3; + cmd4 = from.cmd4; + argc = from.argc; + /* args の読み込み */ + args.clear(); + char* d = from.args; + if (d == 0) return; + while(*d != TYPE_END) { + VarInfo info; + switch(*d) { + case TYPE_VAL: + info.type = TYPE_VAL; + info.number = 0; + info.value = read_little_endian_int(d+1); + d += 5; + args.push_back(info); + break; + case TYPE_STR: + info.type = TYPE_STR; + info.number = 0; + d++; + info.value = CopyString( d); + d += strlen(d)+1; + args.push_back(info); + break; + default: + fprintf(stderr,"Cmd::read: Invalid Load Data\n"); + *d = TYPE_END; + } + } + return; +} +void Cmd::write(CmdSimplified& to, char*& buffer) const { +/* + if (cmd_type != CMD_OTHER) { + fprintf(stderr,"Cmd::write: Invalid Cmd during Saving Data\n"); + to.cmd1 = 0; to.cmd2 = 0; to.cmd3 = 0; to.cmd4 = 0; to.argc = 0; to.args = 0; + return; + } +*/ + to.type = cmd_type; + to.cmd1 = cmd1; + to.cmd2 = cmd2; + to.cmd3 = cmd3; + to.cmd4 = cmd4; + to.argc = argc; + /* args の書き込み */ + if (args.empty()) { + to.args = 0; + } else { + to.args = buffer; + char* d = to.args; + vector<VarInfo>::const_iterator it; + for (it = args.begin(); it != args.end(); it++) { + int type = it->type; + if ( (type >= 0 && type < 7) || type == TYPE_VAL || type == char(TYPE_SYS)) { // digits + *d++ = TYPE_VAL; + write_little_endian_int(d, it->value); + d += 4; + } else if (type == TYPE_VARSTR || type == TYPE_VARSYSSTR || type == TYPE_VARLOCSTR || type == TYPE_STR) { // string + *d++ = TYPE_STR; + const char* s = Str(*it); + int len = strlen(s); + memcpy(d, s, len+1); + d += len+1; + } else { + fprintf(stderr,"Cmd::write: Invalid Cmd args during Saving Data\n"); + } + } + *d++ = TYPE_END; + buffer = d; + } +} +void CmdSimplified::copy(const CmdSimplified& from, char*& args_buffer) { + *this = from; + if (args == 0) return; + char* args_old = from.args; + /* args のコピー */ + while(*args_old != TYPE_END) { + if (*args_old == TYPE_VAL) { + args_old += 5; + } else { /* TYPE_STR */ + args_old += strlen(args_old)+1; + } + } + args_old++; + int args_len = args_old - from.args; + memmove(args_buffer, from.args, args_len); + args = args_buffer; + args_buffer += args_len; +} +void CmdSimplified::Save(string& saveret) { + char buf[1024]; + sprintf(buf, "%02x-%02x:%04x:%02x(%02d),", cmd1, cmd2, cmd3, cmd4, argc); + saveret += buf; + + /* args のコピー */ + char* d = args; + while(d && *d != TYPE_END) { + if (*d == TYPE_VAL) { + d++; + sprintf(buf, "%d,", read_little_endian_int(d)); + d += 4; + } else { /* TYPE_STR と仮定 */ + d++; + if (strlen(d) > 1000) d[1000] = 0; // ありえない・・・ + int i; int cnt = 0; + buf[cnt++] = '"'; + for (i=0; d[i] != 0; i++) { + if (d[i] == '"') buf[cnt++] = '"'; + buf[cnt++] = d[i]; + } + buf[cnt++]='"'; + buf[cnt++] = ','; + buf[cnt++] = 0; + d += strlen(d)+1; + } + saveret += buf; + } + saveret += 'E'; +} + +void CmdSimplified::Load(const char* save, char*& args_buffer) { + args = args_buffer; + + type = CMD_OTHER; + sscanf(save, "%02x-%02x:%04x:%02x(%02d),", &cmd1, &cmd2, &cmd3, &cmd4, &argc); + save = strchr(save, ','); + if (save == 0) { + *args_buffer++ = TYPE_END; + return; + } + save++; + while(*save != 'E' && *save != '\n' && *save != '\0') { + if (isdigit(*save)) { + int v; + sscanf(save,"%d,",&v); + *args_buffer++ = TYPE_VAL; + write_little_endian_int(args_buffer, v); + args_buffer+= 4; + save = strchr(save, ','); + if (save) save++; + } else { // *save == '"' + save++; + *args_buffer++ = TYPE_STR; + while(1) { + if (*save == 0) break; + if (*save == '"') { + if (save[1] != '"') break; + save++; + } + *args_buffer++ = *save++; + } + save += 2; + *args_buffer++ = 0; + } + } + *args_buffer++ = TYPE_END; + return; +} + +#ifdef SCN_DUMP +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"); + int system_version = 0; + 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); + Flags flags; + /* 最初から最後までコマンド取得 -> 出力を繰り返す */ + while(dcur<dend) { + const char* dprev = dcur; + Cmd 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;} + 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; +} +#endif +