view scn2k/scn2kdump.cc @ 70:281dcd7217df

Add all Air, Clannad and Kanon function names in scn2kdump, based on rldev’s names.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Fri, 01 Apr 2011 22:54:40 +0200
parents 4416cfac86ae
children e4a12dd9a51b
line wrap: on
line source

/*
 *
 *  Copyright (C) 2002-   Kazunori Ueno(JAGARL) <jagarl@creator.club.ne.jp>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
*/

#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#include <map>

using namespace std;

#include "system/file.h"
#include "system/file_impl.h"

#include "scn2k.h"


#define SCN_DUMP

class CmdD : public Cmd
{
  public:
    CmdD(const Flags& flags, int system_version) : Cmd(flags, system_version) {}
    //CmdD(const Flags& f) : flags(f) { argc = 0; errorflag = false; cmdstr[0] = 0;}
    bool ClearError() { /*errorflag = false;*/}
    static map<int, struct CmdDescrItem*> cmd_descr;
	const char* CmdDescr(int cmd1, int cmd2, int cmd3, int cmd4);
};

int system_version = 0;
bool ruby_flag = false;
bool ret_flag = false;
bool text_flag = false;
bool selection_flag = false;


inline int eval(int v1, int op, int v2) {
	switch(op) {
		case 0: return v1+v2;
		case 1: return v1-v2;
		case 2: return v1*v2;
		case 3: return v2!=0 ? v1/v2 : v1;
		case 4: return v2!=0 ? v1%v2 : v1;
		case 5: return v1&v2;
		case 6: return v1|v2;
		case 7: return v1^v2;
		case 8: return v1<<v2;
		case 9: return v1>>v2;
		case 40: return v1 == v2;
		case 41: return v1 != v2;
		case 42: return v1 <= v2;
		case 43: return v1 <  v2;
		case 44: return v1 >= v2;
		case 45: return v1 >  v2;
		case 60: return v1 && v2;
		case 61: return v1 || v2;
	}
	return v2;
}


void usage(void) {
	fprintf(stderr,"usage : scn2kdump [inputfile] [outputfile]\n");
	fprintf(stderr,"  inputfile:  seen.txt(default)\n");
	fprintf(stderr,"  outputfile: seen.txt_out(default)\n");
	exit(-1);
}

extern int debug_flag;

int main(int argc, char** argv) {
	/* determine file names */
	debug_flag = true;
	bool verbose = false;
	char* inname = "seen.txt";
	char* outname = 0;
	if (argc > 2 && strcmp(argv[1],"-v") == 0) {
		int i; for (i=1; i<argc; i++) argv[i] = argv[i+1];
		argc--;
		verbose = true;
	}
	switch(argc) {
	case 1: break;
	case 2: inname = argv[1]; break;
	case 3: inname = argv[1]; outname = argv[2]; break;
	default: usage();
	}
	/* open output file */
	FILE* outstream = stdout;
	/* create archive instance */
	SCN2kFILE archive(inname);
	archive.Init();
	if (archive.Deal() == 0) {
		fprintf(stderr,"Cannot open / Invalid archive file %s\n",inname);
		usage();
	}
	/* dump files */
	archive.InitList();
	char* fname;
	fprintf(stderr,"Dump start\n");
	while( (fname = archive.ListItem()) != 0) {
		ARCINFO* info = archive.Find(fname, "");
		if (info == NULL) continue;
		char* data = info->CopyRead();
		char* d = data;
		char* dend = d + info->Size();
		/* version 確認 */
		if (read_little_endian_int(d) == 0x1cc) {
			system_version = 0;
		} else if (read_little_endian_int(d) == 0x1d0) {
			system_version = 1;
		} else {
			continue;
		}
		if (read_little_endian_int(d+4) == 0x1adb2) ; // little busters!
		else if (read_little_endian_int(d+4) != 0x2712) continue;
		int header_size;
		if (system_version == 0) {
			header_size = 0x1cc + read_little_endian_int(d+0x20) * 4;
		} else {
			header_size = read_little_endian_int(d+0x20);
		}
		d += header_size;

		const char* dcur = d;
		const char* dstart = d;
		fprintf(stderr,"Dumping %s\n",fname);
		fprintf(stdout,"Dumping %s\n",fname);
{ int i; for (i=0; i<100; i++) {
		int n = read_little_endian_int(data + 0x34 + i*4);
		if (n != 6) fprintf(stdout,"subroutine table %2d: %6d\n",i,n);
}}
		Flags flags;
		/* 最初から最後までコマンド取得 -> 出力を繰り返す */
		while(dcur<dend) {
			const char* dprev = dcur;
			CmdD cmd(flags, system_version); cmd.ClearError();

			/* end? */
			if (*dcur == -1) {
				/* 0xff x 32byte + 0x00 : end sign */
				int i; for (i=0; i<0x20; i++)
					if (dcur[i] != -1) break;
				if (i == 0x20 && dcur[i] == 0) break;
			}
			dprintf("%d : ",dcur-dstart);
			cmd.GetCmd(flags, dcur);
			if (cmd.IsError()) {
				fprintf(outstream, "Error at %6d\n",dprev-dstart);
				while(dcur < dend) {
					if (*dcur == 0x29 && dcur[1] == 0x0a) {dcur++;break;}
					if (*dcur == 0 && dcur[1] == 0x0a) {dcur++;break;}
					if (*dcur == 0 && dcur[1] == 0x23) {dcur++;break;}
					dcur++;
				}
				dprev -= 2*16;
				int ilen = (dcur-dprev+15)/16;
				int i; for (i=0; i<ilen; i++) {
					fprintf(outstream, "%6d: ",dprev-dstart);
					int j; for (j=0; j<16; j++) {
						if (dprev >= dend) break;
						if (dprev < data) continue;
						fprintf(outstream, "%02x ",*(unsigned char*)(dprev));
						dprev++;
					}
					fprintf(outstream, "\n");
				}
			}
		}
		delete info;
	}
	return 0;
}

/*
SetStr
	0x23 - cmd 01-0a:0000:00[ 2] 
	args:V<18>[17],"PB47"
CatStr
	0x23 - cmd 01-0a:0002:00[ 2] 
	args:V<18>[17],V<18>[20]

WaitClick
	0x23 - cmd 00-03:0011:00[ 0] 
	
ChangeFaceGraphics
	0x23 - cmd 00-03:03e8:00[ 1] 
	args:V<18>[17]
DeleteFaceGraphics
	0x23 - cmd 00-03:03e9:01[ 0] 
KoePlay
	0x23 - cmd 01-17:0000:01[ 2] 
	args:100000026,5
DrawGraphics(前景画あり)
	0x23 - cmd 01-21:004b:00[ 1] 
	args:V<18>[1],10,kasane. #1 <args:V<18>[17],11>

DrawGraphics(背景のみ)
	0x23 - cmd 01-21:0049:00[ 2] 
	args:V<18>[1],10
	
Ruby
	0x23 - cmd 00-03:0078:01[ 0] 
	"理由"
	0x23 - cmd 00-03:0078:00[ 1] 
	"わけ"
SetTitle
	0x23 - cmd 01-04:0000:00[ 1] 
	args:"Long Long Time Ago..."
WaitTime
	0x23 - cmd 01-14:0069:00[ 1] 
	args:3000
ChangeBGM	数値引数はフェードアウト、インの時間と推測
0x23 - cmd 01-14:0000:02[ 3] 
	args:"BGM18",700,700
*/

struct CmdDescrItem {
	CmdDescrItem* next;
	int cmd4;
	int cmd1;
	int cmd2;
	int cmd3;
	const char* cmd_descr;
};
CmdDescrItem cmd_descr_orig[] = {
	// scn2k_impl.cc; Scn2k::SysExec()
	{0,0,0x01,0x05,0,    "shell"},
	{0,0,0x01,0x05,1,    "launch"},
	{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,0x01,0x04,0x00, "SetWindowCaption"},
	{0,0,0x00,0x04,0x0d, "Menu_return"},
	{0,0,0x01,0x04,0x74, "SetTimer"},
	{0,0,0x01,0x04,0x82, "ClearMousePress"},
	{0,0,0x01,0x04,0x83, "GetMouse(2)"},
	{0,0,0x01,0x04,0x85, "GetMouse"},
	{0,0,0x01,0x04,0x12c,"rtlButton"},
	{0,0,0x01,0x04,0x14c,"LocalSkipMode"},
	{0,0,0x01,0x04,0x15e,"CtrlKeySkip"},
	{0,0,0x01,0x04,0x15f,"CtrlKeySkipOn"},
	{0,0,0x01,0x04,0x160,"CtrlKeySkipOff"},
	{0,0,0x01,0x04,0x161,"CtrlPressed"},
	{0,0,0x01,0x04,0x162,"ShiftPressed"},
	{0,0,0x01,0x04,0x4b0,"QuitGame"},
	{0,0,0x01,0x04,0x4b1,"GoMenu"},
	{0,0,0x01,0x04,0x4b3,"GoMenu_Badend"},
	{0,0,0x01,0x04,0x4bb,"EnableSyscom"},
	{0,0,0x01,0x04,0x4bc,"HideSyscom"},
	{0,0,0x01,0x04,0x4cf,"??? (Air, Clannad, Kanon)"},
	{0,0,0x01,0x04,0x515,"SetName"},
	{0,0,0x01,0x04,0x58d,"PrevSaveNumber"},
	{0,0,0x01,0x04,0x585,"SavedDate"},
	{0,0,0x01,0x04,0x5de,"cgGetViewedPcnt"},
	{0,0,0x01,0x04,0x7d1,"??? (Air, Clannad, Kanon)"},
	{0,0,0x01,0x04,0x803,"SetSkipAnimations"},
	{0,0,0x01,0x04,0x8e3,"SetScreenMode"},
	{0,0,0x01,0x04,0x947,"ScreenMode"},
	{0,0,0x01,0x04,0xbb9,"menu_load"},
	{0,0,0x01,0x04,0xc23,"Save"},
	{0,0,0x01,0x04,0xc25,"Load"},
	{0,0,0x01,0x04,0xdaf,"? CloseAll"},
	{0,0,0x01,0x04,0xcc, "ShowMouseCursor"},
	{0,0,0x01,0x04,0xcd, "HideMouseCursor"},
	{0,0,0x01,0x04,0xcf, "SetCursorType"},

	// debug commands
	{0,0,0x01,0xff,0x32, "??? (Clannad)"},
	{0,0,0x01,0xff,0x65, "__SaveBufferIdx"},

	// scn2k_cmd.cc; CmdD::GetCmd()
	{0,0,0x00,0x01,0,    "local jump"},
	{0,0,0x00,0x01,1,    "local jump-if"},
	{0,0,0x00,0x01,2,    "local jump-unless"},
	{0,0,0x00,0x01,3,    "local jump-switch??"},
	{0,0,0x00,0x01,4,    "local switch"},
	{0,0,0x00,0x01,5,    "local call"},
	{0,0,0x00,0x01,8,    "local switch(simple form)"},
	{0,0,0x01,0x0b,0,    "set multiple variables"},
	{0,0,0x01,0x0b,1,    "set variables in a range"},
	{0,0,0x01,0x0b,2,    "cpyrng"},
	{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,0x0b, "zentohan"},
	{0,0,0x01,0x0a,0x0f, "IntToStr"},
	{0,0,0x01,0x0a,0x11, "IntToStr_Fill"},
	{0,0,0x01,0x0a,0x1e, "strpos"},
	{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,0x03, "TextReturn"},
	{0,0,0x00,0x03,0x11, "WaitText"},
	{0,0,0x00,0x03,0x65, "FontSize"},
	{0,0,0x00,0x03,0x66, "SetTextWindowType"},
	{0,0,0x00,0x03,0x67, "OpenTextWindow"},
	{0,0,0x00,0x03,0x68, "ShowText"},
	{0,0,0x00,0x03,0x78, "TextRuby"},
	{0,0,0x00,0x03,0x97, "CloseTextWindow"},
	{0,0,0x00,0x03,0x98, "ClearTextWindow"},
	{0,0,0x00,0x03,0xa1, "msgHideAll"},
	{0,0,0x00,0x03,0xa2, "msgClearAll"},
	{0,0,0x00,0x03,0xc9, "TextReturn"},
	{0,0,0x00,0x03,0xcd, "spause"},
	{0,0,0x00,0x03,0x3e8,"SetFaceGraphic"},
	{0,0,0x00,0x03,0x3e9,"SetFaceGraphic"},
	{0,0,0x00,0x02,0x01, "Select"},
	{0,0,0x00,0x02,0x03, "Select"},

	{0,0,0x00,0x04,0x12d,"rtlCancel"},
	{0,0,0x00,0x04,0x3e8,"CloseTextWindow (ShowBackground)"},
	{0,0,0x00,0x04,0x44c,"TextSkipStart"},
	{0,0,0x00,0x04,0x44d,"ClearSkipMode"},
	{0,0,0x00,0x04,0x44e,"SkipMode"},
	{0,0,0x00,0x04,0x4b0,"end"},
	{0,0,0x00,0x04,0x4b1,"MenuReturn"},
	{0,0,0x00,0x04,0x4b2,"MenuReturn2"},
	{0,0,0x00,0x04,0x4b3,"ReturnMenu"},

	{0,0,0x01,0x04,0x12d,"??? (Clannad)"},
	{0,0,0x01,0x04,0x12e,"??? (Clannad)"},
	{0,0,0x01,0x04,0x16e,"??? cursor? (Clannad)"},
	{0,0,0x01,0x04,0x3ea,"abs"},
	{0,0,0x01,0x04,0x4b4,"ReturnPrevSelect"},
	{0,0,0x01,0x04,0x4be,"SyscomEnabled"},
	{0,0,0x01,0x04,0x4bf,"InvokeSyscom"},
	{0,0,0x01,0x04,0x7d0,"CursorMono"},
	{0,0,0x01,0x04,0x7d2,"LowPriority"},
	{0,0,0x01,0x04,0x7d3,"ConfirmSaveLoad"},
	{0,0,0x01,0x04,0x7d4,"ReduceDistortion"},
	{0,0,0x01,0x04,0x7d9,"SoundQuality"},
	{0,0,0x01,0x04,0x802,"SetCursorMono"},
	{0,0,0x01,0x04,0x804,"SetLowPriority"},
	{0,0,0x01,0x04,0x805,"SetConfirmSaveLoad"},
	{0,0,0x01,0x04,0x806,"SetReduceDistortion"},
	{0,0,0x01,0x04,0x80b,"SetSoundQuality"},
	{0,0,0x01,0x04,0x8b6,"(Set)BgmVolMod"},
	{0,0,0x01,0x04,0x8b8,"(Set)PcmVolMod"},
	{0,0,0x01,0x04,0x8b9,"(Set)SeVolMod"},
	{0,0,0x01,0x04,0x8c0,"(Set)BgmEnabled"},
	{0,0,0x01,0x04,0x8c2,"(Set)PcmEnabled"},
	{0,0,0x01,0x04,0x8c3,"(Set)SeEnabled"},
	{0,0,0x01,0x04,0x8de,"(Set)ShowObject1"},
	{0,0,0x01,0x04,0x91a,"BgmVolMod"},
	{0,0,0x01,0x04,0x91c,"PcmVolMod"},
	{0,0,0x01,0x04,0x91d,"SeVolMod"},
	{0,0,0x01,0x04,0x924,"BgmEnabled"},
	{0,0,0x01,0x04,0x926,"PcmEnabled"},
	{0,0,0x01,0x04,0x927,"SeEnabled"},
	{0,0,0x01,0x04,0x942,"ShowObject1"},
	{0,0,0x01,0x04,0xdac,"Savepoint"},
	{0,0,0x01,0x04,0xdad,"EnableAutoSavepoints"},
	{0,0,0x01,0x04,0xdae,"DisableAutoSavepoints"},

	{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,0x258,"InitFrames"},
	{0,0,0x01,0x04,0x259,"InitFramesLoop"},
	{0,0,0x01,0x04,0x262,"ReadFrames"},
	{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"},
	{0,0,0x01,0x04,0x6a4,"CreateInput"},
	{0,0,0x01,0x04,0x6a5,"CloseInput"},
	{0,0,0x01,0x04,0x6a6,"CloseAllInput"},
	{0,0,0x01,0x04,0x6a7,"FocusInput"},
	{0,0,0x01,0x04,0x6ae,"SetInput"},
	{0,0,0x01,0x04,0x6af,"GetInput"},
	// 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,0x0c, "bgmSetVolume"},
	{0,0,0x01,0x14,0x0d, "bgmUnMute"},
	{0,0,0x01,0x14,0x0e, "bgmMute"},
	{0,0,0x01,0x14,0x69, "bgmFadeOut"},
	{0,0,0x01,0x14,0x6a, "bgmFadeOutEx"},
	{0,0,0x01,0x14,0x3ea,"??? bgm (Air)"},

	{0,0,0x01,0x15,0,    "wavPlay"},
	{0,0,0x01,0x15,1,    "wavPlayEx"},
	{0,0,0x01,0x15,2,    "wavLoop"},
	{0,0,0x01,0x15,3,    "wavWait"},
	{0,0,0x01,0x15,4,    "wavPlaying"},
	{0,0,0x01,0x15,5,    "wavStop"},
	{0,0,0x01,0x15,7,    "wavPlaying2"},
	{0,0,0x01,0x15,8,    "wavRewind"},
	{0,0,0x01,0x15,9,    "wavStop3"},
	{0,0,0x01,0x15,10,   "wavStop4"},
	{0,0,0x01,0x15,11,   "wavVolume"},
	{0,0,0x01,0x15,12,   "wavSetVolume"},
	{0,0,0x01,0x15,13,   "wavUnMute"},
	{0,0,0x01,0x15,14,   "wavMute"},
	{0,0,0x01,0x15,20,   "wavStopAll"},
	{0,0,0x01,0x15,105,  "wavFadeOut"},
	{0,0,0x01,0x15,106,  "wavFadeOut2"},

	{0,0,0x01,0x16,0,    "sePlay"},

	{0,0,0x01,0x17,0,    "PlayKoe"},
	{0,0,0x01,0x1a,1,    "PlayMovie"},
	{0,0,0x01,0x1a,0x14, "PlayMovie"},
	// graphic commands
	{0,0,0x01,0x0c,0,    "ShakeLayersStop"},
	{0,0,0x01,0x0c,0x44c,"__shlud ShakeLayers"},
	{0,0,0x01,0x0c,0x44d,"__shlrl ShakeLayers"},
	{0,0,0x01,0x0c,0xce4,"ShakeLayersSpecEx"},
	{0,0,0x01,0xc,0x1005,"??? shake (Clannad)"},
	{0,0,0x01,0xc,0x106a,"??? shake (Clannad)"},

	{0,0,0x01,0x0d,0,    "ShakeStop"},
	{0,0,0x01,0x0d,0x44e,"ShakeScreen2D"},
	{0,0,0x01,0x0d,0x514,"ShakeSpec"},
	{0,0,0x01,0x0d,0xc1e,"ShakeScreen2DEx"},

	{0,0,0x01,0x1e,0,    "GraphicStackClear"},
	{0,0,0x01,0x1e,0x01, "GraphicStackNop"},
	{0,0,0x01,0x1e,0x02, "GraphicStackPop"},
	{0,0,0x01,0x1e,0x03, "GraphicStackSize"},
	{0,0,0x01,0x1e,0x04, "GraphicStackTrunc"},
	{0,0,0x01,0x1e,0x14, "DrawAuto"},
	{0,0,0x01,0x1e,0x15, "DrawSemiAuto"},
	{0,0,0x01,0x1e,0x16, "DrawManual"},
	{0,0,0x01,0x1e,0x1e, "ModeToScreenSize"},
	{0,0,0x01,0x1e,0x1f, "GetDCPixel"},

	{0,0,0x01,0x1f,0,    "GraphicStackClear"},

	{0,0,0x01,0x21,0x10, "freeDC"},
	{0,0,0x01,0x21,0x1f, "wipe"},
	{0,0,0x01,0x21,0x20, "shake"},
	{0,0,0x01,0x21,0x46, "LoadSurface"},
	{0,0,0x01,0x21,0x49, "LoadBackSurface"},
	{0,0,0x01,0x21,0x4a, "grpMaskOpen"},
	{0,0,0x01,0x21,0x4b, "LoadForeSurface"},
	{0,0,0x01,0x21,0x4c, "LoadSurface"},
	{0,0,0x01,0x21,0x4d, "grpMulti"},
	{0,0,0x01,0x21,0x64, "CopySurface"},
	{0,0,0x01,0x21,0x65, "grpMaskCopy"},
	{0,0,0x01,0x21,0xc9, "grpFill"},
	{0,0,0x01,0x21,0x12c,"grpInvert"},
	{0,0,0x01,0x21,0x12d,"grpMono"},
	{0,0,0x01,0x21,0x12f,"grpLight"},
	{0,0,0x01,0x21,0x192,"grpZoom"},
	{0,0,0x01,0x21,0x193,"grpFade"},
	{0,0,0x01,0x21,0x194,"grpFlash"},
	{0,0,0x01,0x21,0x195,"DefaultWindowResetPos"},
	{0,0,0x01,0x21,0x41a,"recLoad"},
	{0,0,0x01,0x21,0x41b,"recMaskLoad"},
	{0,0,0x01,0x21,0x41d,"recOpenBg"},
	{0,0,0x01,0x21,0x420,"recOpen"},
	{0,0,0x01,0x21,0x44c,"recCopy"},
	{0,0,0x01,0x21,0x44d,"recMaskCopy"},
	{0,0,0x01,0x21,0x4b1,"recFill"},
	{0,0,0x01,0x21,0x514,"recInvert"},
	{0,0,0x01,0x21,0x515,"recMono"},
	{0,0,0x01,0x21,0x579,"recStretchBlt"},
	{0,0,0x01,0x21,0x640,"recAdd"},
	{0,0,0x01,0x21,0x6a4,"recSub"},
	{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)"},

	// Backgrounds
	{0,0,0x01,0x28,0xa,  "bgrLoadHaikei"},
	{0,0,0x01,0x28,0x64, "bgrMulti"},

	{0,0,0x01,0x04,0x1cc,"EnableWindowAnm"},
	{0,0,0x01,0x04,0x1cd,"DisableWindowAnm"},
	{0,0,0x01,0x04,0x1ce,"GetOpenAnmMod"},
	{0,0,0x01,0x04,0x1cf,"SetOpenAnmMod"},
	{0,0,0x01,0x04,0x1d0,"GetOpenAnmTime"},
	{0,0,0x01,0x04,0x1d1,"SetOpenAnmTime"},
	{0,0,0x01,0x04,0x1d2,"GetCloseAnmMod"},
	{0,0,0x01,0x04,0x1d3,"SetCloseAnmMod"},
	{0,0,0x01,0x04,0x1d4,"GetCloseAnmTime"},
	{0,0,0x01,0x04,0x1d5,"SetCloseAnmTime"},

	// 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"},

	// Obj
	{0,0,0x01,0x3d,0x02, "objCopy"},
	{0,0,0x01,0x3d,0x04, "objWipeCopyOn"},
	{0,0,0x01,0x3d,0x0a, "ClearGrpObj (objClear)"},
	{0,0,0x01,0x3d,0x0b, "ClearGrpObj (objDelete)"},
	{0,0,0x01,0x3d,0x6f, "??? (Kanon)"},

	// ObjBg
	{0,0,0x01,0x3e,0x0a, "ClearGrpObj"},
	{0,0,0x01,0x3e,0x0a, "ClearGrpObj"},
	{0,0,0x01,0x3c,0x00, "??? grp (Clannad)"},
	{0,0,0x01,0x3c,0x01, "??? grp (Air, Clannad, Kanon)"},
	{0,0,0x01,0x3c,0x02, "objCopyFgToBg"},
	{0,0,0x01,0x3c,0x64, "??? grp (Clannad)"},
	{0,0,0x01,0x3c,0x6e, "??? grp (Clannad)"},
	{0,0,0x01,0x3c,0x6f, "??? grp (Air)"},
	{0,0,0x01,0x3e,0x02, "objBgCopyToFg"},
	{0,0,0x01,0x47,0x3e8,"SetGrpObj_Fname (objOfFile)"},
	{0,0,0x01,0x47,0x3e9,"SetGrpObj_Fname (objOfFile2)"},
	{0,0,0x01,0x47,0x3eb,"SetGrpObj_GANname"},
	{0,0,0x01,0x47,0x4b0,"SetGrpObj_Text"},
	{0,0,0x01,0x47,0x514,"objDriftOfFile"},
	{0,0,0x01,0x48,0x3e8,"objBgOfFile"},
	{0,0,0x01,0x48,0x3e9,"objBgOfFile2"},
	{0,0,0x01,0x48,0x3eb,"objBgOfFileGan/Anm"},
	{0,0,0x01,0x48,0x44c,"objBgOfArea"},
	{0,0,0x01,0x48,0x44d,"objBgOfRect"},
	{0,0,0x01,0x48,0x4b0,"objBgOfText"},
	{0,0,0x01,0x48,0x514,"objBgDriftOfFile"},
	{0,0,0x01,0x48,0x578,"objBgOfDigit"},

	// Gan
	{0,0,0x01,0x49,0,    "StopAnimation"},
	{0,0,0x01,0x49,3,    "QueryExecAnimation"},
	{0,0,0x01,0x49,4,    "??? (Clannad)"},
	{0,0,0x01,0x49,0x68, "??? (Clannad)"},
	{0,0,0x01,0x49,0x3ee,"ganPlayEx"},
	{0,0,0x01,0x49,0x7d1,"objLoop"},
	{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,0x49,0xc1f,"??? (Clannad)"},

	// obj
	{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,0x3f8,"objColour"},
	{0,0,0x01,0x51,0x3fd,"SetGrpObj_centering?"},
	{0,0,0x01,0x51,0x401,"SetGrpObj_textsize"},
	{0,0,0x01,0x51,0x407,"SetGrpObj_objDriftOpts"},
	{0,0,0x01,0x51,0x40a,"SetGrpObj_clipregion"},
	{0,0,0x01,0x51,0x40f,"SetGrpObj_surfacenum"},
	{0,0,0x01,0x51,0x410,"SetGrpObj_??? (Clannad)"},
	{0,0,0x01,0x51,0x416,"SetGrpObj_expand"},
	{0,0,0x01,0x51,0x418,"objHeight"},
	{0,0,0x01,0x51,0x419,"SetGrpObj_rotate"},
	{0,0,0x01,0x51,0x41a,"objRepOrigin"},
	{0,0,0x01,0x51,0x41c,"objRepOriginY"},
	{0,0,0x01,0x51,0x420,"objFadeOpts"},

	// objBg
	{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,0x3f1,"objBgMono"},
	{0,0,0x01,0x52,0x3f8,"objBgColour"},
	{0,0,0x01,0x52,0x3fd,"SetGrpObj_centering?(2)"},
	{0,0,0x01,0x52,0x401,"SetGrpObj_textsize(2)"},
	{0,0,0x01,0x52,0x407,"objBgDriftOpts"},
	{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,0x01,0x55,0x3e8,"S/GetGrpObj_??? (Kanon)"},

	{0,0,0x02,0x3d,0x0a, "ClearGrpObj(2)"},
	{0,0,0x02,0x3d,0x0b, "ClearGrpObj(2)"},
	{0,0,0x02,0x3e,0x0a, "ClearGrpObj(2)"},
	{0,0,0x02,0x3e,0x0a, "ClearGrpObj(2)"},
	{0,0,0x02,0x3c,0x01, "??? grp (CLANNAD)(2)"},
	{0,0,0x02,0x47,0x3e8,"SetGrpObj_Fname(2)"},
	{0,0,0x02,0x47,0x3eb,"SetGrpObj_GANname(2)"},
	{0,0,0x02,0x47,0x4b0,"SetGrpObj_Text(2)"},
	{0,0,0x02,0x48,0x3e8,"SetGrpObj_ForeGrp?(2)"},
	{0,0,0x02,0x49,0,    "StopAnimation(2)"},
	{0,0,0x02,0x49,3,    "QueryExecAnimation(2)"},
	{0,0,0x02,0x49,0x7d3,"SetGrpObj_GAN?(2)"},
	{0,0,0x02,0x49,0xbb9,"StartAnimation(2)"},
	{0,0,0x02,0x49,0xbbb,"StartAnimation(2)"},
	{0,0,0x02,0x49,0xbbd,"StartAnimation(2)"},
	{0,0,0x02,0x51,0x3e8,"SetGrpObj_xy(2)"},
	{0,0,0x02,0x51,0x3ea,"SetGrpObj_y(2)"},
	{0,0,0x02,0x51,0x3eb,"SetGrpObj_alpha(2)"},
	{0,0,0x02,0x51,0x3ec,"SetGrpObj_visible(2)"},
	{0,0,0x02,0x51,0x3ee,"SetGrpObj_xy?(2)"},
	{0,0,0x02,0x51,0x3fd,"SetGrpObj_centering?(2)"},
	{0,0,0x02,0x51,0x401,"SetGrpObj_textsize(2)"},
	{0,0,0x02,0x51,0x40a,"SetGrpObj_clipregion(2)"},
	{0,0,0x02,0x51,0x40f,"SetGrpObj_surfacenum(2)"},
	{0,0,0x02,0x51,0x416,"SetGrpObj_expand(2)"},
	{0,0,0x02,0x51,0x419,"SetGrpObj_rotate(2)"},
	{0,0,0x02,0x52,0x3e8,"SetGrpObj_xy(2)(2)"},
	{0,0,0x02,0x52,0x3ea,"SetGrpObj_y(2)(2)"},
	{0,0,0x02,0x52,0x3eb,"SetGrpObj_alpha(2)(2)"},
	{0,0,0x02,0x52,0x3ec,"SetGrpObj_visible(2)(2)"},
	{0,0,0x02,0x52,0x3ee,"SetGrpObj_xy?(2)(2)"},
	{0,0,0x02,0x52,0x3fd,"SetGrpObj_centering?(2)(2)"},
	{0,0,0x02,0x52,0x401,"SetGrpObj_textsize(2)(2)"},
	{0,0,0x02,0x52,0x40a,"SetGrpObj_clipregion(2)(2)"},
	{0,0,0x02,0x52,0x40f,"SetGrpObj_surfacenum(2)(2)"},
	{0,0,0x02,0x52,0x416,"SetGrpObj_expand(2)(2)"},
	{0,0,0x02,0x52,0x419,"SetGrpObj_rotate(2)(2)"},
	{0,0,0x02,0x54,0x3e8,"GetGrpObj_xy(2)"},
	{0,0,0x02,0x54,0x44c,"GetGrpObj_wh(2)"},

	{0,0,0,0,0,0}
};
map<int, CmdDescrItem*> CmdD::cmd_descr;
const char* CmdD::CmdDescr(int cmd1, int cmd2, int cmd3, int cmd4) {
	if (cmd_descr.empty()) {
		int i;
		for (i=0; cmd_descr_orig[i].cmd_descr != 0; i++) {
			CmdDescrItem& cur = cmd_descr_orig[i];
			int item_num = cur.cmd1*1000000+cur.cmd2*10000+cur.cmd3;
			map<int,CmdDescrItem*>::iterator it = cmd_descr.find(item_num);
			if (it == cmd_descr.end()) cmd_descr[item_num] = &cur;
			else {
				cur.next = it->second;
				it->second = &cur;
			}
		}
	}
	int item_num = cmd1*1000000+cmd2*10000+cmd3;
	map<int,CmdDescrItem*>::iterator it = cmd_descr.find(item_num);
	if (it == cmd_descr.end()) return "No descr (unsupported)";
	CmdDescrItem* cur = it->second;
	do {
		if (cur->cmd1 == cmd1 && cur->cmd2 == cmd2 && cur->cmd3 == cmd3) {
			return cur->cmd_descr;
		}
		cur = cur->next;
	} while(cur != 0);
	return "No descr (unsupported)";
}