view scn2k/scn2k_cmd.cc @ 66:d112357a0ec1

Fix a bug with savegames introduced with changeset c7bcc0ec2267. Warning: savegames created since c7bcc0ec2267 are probably corrupted, you may have to start the game over. If you chose not to do so, you should replace all occurrences of 'TextWindow' by 'TextImplWindow', and 'Text Window' by 'TextImpl Window' in your save files.
author Thibaut Girka <thib@sitedethib.com>
date Sat, 11 Dec 2010 18:36:20 +0100
parents 4416cfac86ae
children e4a12dd9a51b
line wrap: on
line source

/*
 * 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::Load(const char* save) {
	Load(save, false);
}

void Flags::LoadSys(const char* save) {
	Load(save, true);
}

void Flags::Load(const char* save, bool sys) {
	int i, j;
	int start, end;
	std::string *var_str;
	char string_field[] = "V<?>[%04d]=";

	start = (sys) ? (TYPE_NONSYSVARMAX + 1) : 0;
	end = (sys) ? (TYPE_NONSYSVARMAX + 2) : TYPE_NONSYSVARMAX;

	var_str = (sys) ? sys_str : str;
	string_field[2] = (sys) ? 'M' : 'C';

	for (i=start; i <= end; i++) {
		for (j=0; j < 2000; j++) {
			var[i][j] = 0;
		}
	}
	for (j=0; j<2000; j++) {
		var_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, string_field, 2) == 0) {
				if (strncmp(save, string_field, 5) == 0) { // string
					char buf[1024];
					int n;
					if (sscanf(save, string_field, &n) == 1) {
						const char* s = strchr(save, '=');
						s++;
						const char* send = strchr(s, '\n');
						int slen = send - s;
						strncpy(buf, s, slen);
						buf[slen] = 0;
						if (n >= 0 && n < 2000)
							var_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 (n >= 0 && n < 2000)
						{
							if (!sys && c >= 0 && c <= TYPE_NONSYSVARMAX)
								var[c][n] = v;
							else if (sys && c == TYPE_NONSYSVARMAX + 1)
								var[TYPE_NONSYSVARMAX + 1][n] = v;
							else if (sys && c == 25)
								var[7][n] = v;
						}
					}
				}
			}
			save = strchr(save, '\n');
			if (save)
				save++;
		} while (save);
	}
}

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::SaveSys(string& save) { //FIXME: see how to factorize with 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;
		}
	}
}

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] && 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
				// 全角対応らしい
				//FIXME: Make sure it works properly
				{ 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, len-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; // ' ' in SHIFT_JIS
						wc[1] = 0x40;
					} else if (buf[i] == '-') {
						wc[0] = 0x81; // '-' in SHIFT_JIS
						wc[1] = 0x7c;
					} else if (isdigit(buf[i])) {
						wc[0] = 0x82; // number in SHIFT_JIS
						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;
				}
				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;
}

/*********************************************************************
**   SimpleCmd
*/

SimpleCmd::SimpleCmd(int a, int b, int c)
{
	cmd1 = a;
	cmd2 = b;
	cmd3 = c;
}

SimpleCmd::SimpleCmd(void)
{
	cmd1 = cmd2 = cmd3 = 0;
}

bool SimpleCmd::operator<(const SimpleCmd& cmd) const
{
	if (cmd1 < cmd.cmd1) return true;
	else if (cmd1 > cmd.cmd1) return false;

	if (cmd2 < cmd.cmd2) return true;
	else if (cmd2 > cmd.cmd2) return false;

	if (cmd3 < cmd.cmd3) return true;
	else return false;
}

bool SimpleCmd::operator==(const SimpleCmd& cmd) const
{
	return (cmd1 == cmd.cmd1 && cmd2 == cmd.cmd2 && cmd3 == cmd.cmd3);
}


/*********************************************************************
**   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 const 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;
}

Cmd::Cmd(const Flags& f, int _sys_ver) : flags(f), system_version(_sys_ver) {
	cmd_type = CMD_NOP;
	argc = 0;
	errorflag = false;
	cmdstr[0] = 0;
	strend = 0;
	pos = -1;
}

/* 演算子 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;}
		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 += strheap + info.value;
			}
		} else { SetError(); break;}
	}
	d++;
	/* @@@ */
	/* 一致しない場合があるのでコメントアウト */
	// if (arg_count != argc) SetError();
	dprintf("\n}\n");
}

void Cmd::GetCmd(Flags& flags_orig, const char*& d ) {
	if (d == NULL) { 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  : %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;
					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]  : %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 == 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();
	}
}

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);
}


const char* Cmd::Str(const VarInfo& info) const {
	if (info.type != TYPE_STR && info.type != TYPE_VARSTR && info.type != TYPE_VARLOCSTR && info.type != TYPE_VARSYSSTR) 
		return "";
	int pt = info.value;
	if (pt < 0 || pt >= STRHEAP_SIZE) return "";
	return strheap + pt;
}

int Cmd::AddStr(char* s) {
	// 1-0a-0064 はこういうものが必要らしい
	int start = strend;
	while (*s) strheap[strend++] = *s++;
	strheap[strend++] = 0;
	return start;
}


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 == NULL) 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;
		}
	}
}

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 = NULL;
	} 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 == NULL) 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 == NULL) {
		*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;
}

#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 = NULL;
	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 == 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);
		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