Mercurial > otakunoraifu
view scn2k/scn2k_impl.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 |
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 <stdexcept> #include "scn2k_impl.h" #include "system/file.h" #include "system/system_config.h" #include "window/picture.h" #include "window/system.h" // #define DEBUG 1 using namespace std; /********************************************** ** Scn2k */ void kconv(const unsigned char* src, unsigned char* dest); void kconv_rev(const unsigned char* src, unsigned char* dest); string kconv(const string& s); string kconv_rev(const string& s); Scn2k::Scn2k(Event::Container& _event, PicContainer& _parent) : Event::Time(_event), event(_event), parent(_parent), text_exec(_event, _parent), grp_exec(_event, _parent, flag, flag.cgm_data) { config = AyuSysConfig::GetInstance(); system_version = 0; skip_mode = SKIP_NO; script_start = NULL; script = NULL; script_end = NULL; backlog_script_scn = -1; backlog_script_start = NULL; backlog_script_end = NULL; save_scn = 0; save_point = 0; scn_number = 0; scn_point = 0; cmd_stack_str = cmd_stack_str_orig; dialog = NULL; dialog_type = CMD_NOP; menu = NULL; menu_mouseshown = false; /* マウスカーソルを作成 */ mouse_type = 0; mouse_surface = NULL; mouse_pressed = 0; ShowCursor(); LoadSys(); text_exec.InitWindow(); grp_exec.InitSel(); } Scn2k::~Scn2k() { if (script_start) delete[] script_start; HideCursor(); SaveSys(); } char* Scn2k::OpenScript(int new_scn_number, char*& end, int* call_vec, int& system_version) { char fname[1024]; const char* data; char* ret_data; int offset = 0; int scenario_magic; FileSearcher* file_searcher = FileSearcher::GetInstance(); sprintf(fname, "SEEN%04d.TXT", new_scn_number); ARCINFO* info = file_searcher->Find(FileSearcher::SCN, fname, ""); if (info == NULL) goto err; data = info->Read(); /* version 確認 */ scenario_magic = read_little_endian_int(data + 4); if (scenario_magic != 0x2712 && scenario_magic != 0x1adb2) { fprintf(stderr,"Invalid scenario header : scenario number %d\n",new_scn_number); goto err; } if (read_little_endian_int(data) == 0x1cc) { system_version = 0; offset = 0x1cc + read_little_endian_int(data+0x20) + 4; } else if (read_little_endian_int(data) == 0x1d0) { system_version = 1; offset = read_little_endian_int(data + 0x20); } else { fprintf(stderr,"Invalid scenario header : scenario number %d\n",new_scn_number); goto err; } /* header から subroutine number とりだし */ if (call_vec) { int i; for (i=0; i<100; i++) { call_vec[i] = read_little_endian_int(data + 0x34 + i * 4); } } ret_data = new char[info->Size() - offset + 1024]; memcpy(ret_data, data+offset, info->Size()-offset); memset(ret_data+info->Size()-offset, 0, 1024); end = ret_data + info->Size() - offset; delete info; return ret_data; err: delete info; fprintf(stderr,"Cannot open scenario number %d\n",new_scn_number); throw std::invalid_argument("Scn2k::OpenScript"); return false; } bool Scn2k::ChangeScript(int new_scn_number, int call_no) { int old_scn_number = scn_number; int old_scn_pt = script - script_start; int scn_pt = 0; if (script_start) delete[] script_start; script_start = NULL; script = NULL; script_end = NULL; int call_vec[100]; try { script_start = OpenScript(new_scn_number, script_end, call_vec, system_version); } catch(...) { fprintf(stderr,"\tFrom script %d pt %d\n",old_scn_number, old_scn_pt); throw; } if (call_no > 0 && call_no < 100) { scn_pt = call_vec[call_no]; if (scn_pt == 0) { fprintf(stderr,"Invalid subroutine number: scn %d sub %d\n",new_scn_number, call_no); scn_pt = 0; } } else if (call_no < 0) { scn_pt = -call_no; // デバッグ用 } scn_number = new_scn_number; scn_point = scn_pt; script = script_start + scn_pt; if (script < script_start || script >= script_end) fprintf(stderr,"scn %d pt %d: Cannot jump to %d:%d; fall back to the top\n",old_scn_number, old_scn_pt, scn_number, scn_pt); return true; } bool Scn2k::ReadCmdAt(Cmd& cmd, int scn, int pt) { const char* d; if (scn == scn_number) { d = script_start + pt; if (d < script_start || d >= script_end) { fprintf(stderr,"Cannot read script at current scn %d pt %d\n", scn, pt); return false; } } else { if (backlog_script_scn != scn) { if (backlog_script_start) delete[] backlog_script_start; backlog_script_start = OpenScript(scn, backlog_script_end, 0, system_version); } d = backlog_script_start + pt; if (d < backlog_script_start || d >= backlog_script_end) { fprintf(stderr,"Cannot read script at scn %d pt %d\n", scn, pt); return false; } } cmd.GetCmd(flag, d); return true; } extern bool save_req, load_req; // キーボードからセーブ・ロードできるように extern bool pressAreq; void Scn2k::Elapsed(unsigned int current_time) { SetWakeup(current_time + 10); // 10msに一回シナリオスクリプト解釈 if (script == NULL) return; //VarInfo info; info.type = 6; info.number = 0; // PB の「一回ゲームを開始したことがある」フラグ //flag.Set(info,1); //info.type = 0; info.number = 604; // Princess Bride: クリア対象設定フラグ (聖) //flag.Set(info, 1); Cmd cmd(flag, system_version); int cnt1; int cnt2 = 1000; // flag / jump / flag 系コマンドの最大実行回数 /* XXX */ if (save_req) { save_req = false; load_req = false; cmd.cmd_type = CMD_SAVEREQ; } else if (load_req) { load_req = false; save_req = false; cmd.cmd_type = CMD_LOADREQ; } if (pressAreq) { pressAreq = false; LoadRollback(cmd); return; } /* キー入力などに対応 */ // メニュー内以外で shift キーが押されたらスキップ開始 if ( (skip_mode&SKIP_IN_MENU) == 0) { if (event.pressed(KEY_SHIFT)) { if (skip_mode & SKIP_TEXT) { ; // スキップ中ならなにもしない } else { SetSkipMode(SkipMode(SKIP_TEXT | SKIP_GRP_NOEFFEC | SKIPEND_KEY)); } } else { if ( skip_mode & SKIPEND_KEY) { if ( (skip_mode & SKIPEND_TEXT) && (skip_mode & SKIP_TEXT)) { SkipMode new_skip_mode = SkipMode(skip_mode & (~SKIPEND_KEY)); if ( (new_skip_mode & SKIP_GRP_FAST) || (new_skip_mode & SKIP_GRP_NODRAW)) { new_skip_mode = SkipMode(skip_mode & (~SKIP_GRP_NOEFFEC)); } SetSkipMode(new_skip_mode); } else { SetSkipMode(SKIP_NO); } } } } for (cnt1=0; cnt1<20; cnt1++) { // 一回につき 20 個のコマンド実行 // 他のコマンド実行中なら終了 if ( (cmd.cmd_type == CMD_NOP && SysWait(cmd)) || // (cmd.cmd_type == CMD_NOP && text_exec.Wait(current_time, cmd)) || // (cmd.cmd_type == CMD_NOP && grp_exec.Wait(current_time, cmd))) { (cmd.cmd_type == CMD_NOP && grp_exec.Wait(current_time, cmd)) || (cmd.cmd_type == CMD_NOP && text_exec.Wait(current_time, cmd))) { break; } // コマンド読み込み for (; cnt2 > 0; cnt2--) { scn_point = script - script_start; eprintf("%d / %d :", script - script_start, script_end-script_start); // fprintf(stderr,"%d: %d / %d :",scn_number, script - script_start, script_end-script_start); cmd.GetCmd(flag, script); // if (cmd.cmd_type != CMD_NOP) { if (0) { fprintf(stderr,"%d / %d : 0x23 - cmd %02x-%02x:%04x:%02x[%2d] \n", scn_point, script_end-script_start, cmd.cmd1,cmd.cmd2,cmd.cmd3,cmd.cmd4,cmd.argc); int i; for (i = 0; i<cmd.args.size(); i++) { if (i == 0) fprintf(stderr,"\t"); VarInfo info = cmd.args[i]; if (info.type == TYPE_STR || info.type == TYPE_VARSTR) fprintf(stderr,"\"%s\",", cmd.Str(info)); else fprintf(stderr,"%d,",info.value); } fprintf(stderr,"\n"); } cmd.scn = scn_number; cmd.pos = scn_point; if (cmd.IsError()) break; if (cmd.cmd_type == CMD_NOP) continue; if (cmd.cmd_type == CMD_JMP) { // local jump if (cmd.cmd1 == 0 && cmd.cmd2 == 1 && cmd.cmd3 == 16) { int i; for (i=0; i<cmd.args.size()-1; i++) { VarInfo var; var.type = 11; var.number = i; flag.Set(var, cmd.args[i].value); } cmd.args[0].value = cmd.args[i].value; } if ( cmd.cmd1 == 0 && cmd.cmd2 == 1 && (cmd.cmd3 == 5 || cmd.cmd3 == 8 || cmd.cmd3 == 16) ) { // local call / simple switch int scn_pt = script - script_start; // fprintf(stderr,"\nlocal call %d:%d from %d\n",scn_number,cmd.args[0].value,scn_pt); stack.push_back(StackItem(-1, scn_pt)); } if (cmd.cmd1 == 0 && cmd.cmd2 == 1 && cmd.cmd3 == 1) {//TODO fprintf(stderr,"*** unsupported: cond 1\n"); } script = script_start + cmd.args[0].value; if (script < script_start || script >= script_end) { fprintf(stderr,"scn %d pt %d: Cannot jump to %d; fall back to the top\n", scn_number, scn_point, cmd.args[0].value); script = script_start; } cmd.clear(); continue; } if (flag.Exec(cmd)) continue; break; } if (cmd.IsError()) { fprintf(stderr,"cmd error occured: scn %d pt %d / cur %d",scn_number,scn_point,script-script_start); while(script < script_end) { if (*script == 0x29 && script[1] == 0x0a) {script++;break;} if (*script == 0 && script[1] == 0x0a) {script++;break;} if (*script == 0 && script[1] == 0x23) {script++;break;} script++; fprintf(stderr," -> fall back to %d\n",script-script_start); } const char* dprev = script - 0x60; if (dprev < script_start) dprev = script_start; int ilen = (script-dprev+65)/16; int i; for (i=0; i<ilen; i++) { fprintf(stderr, "%6d: ",dprev-script_start); int j; for (j=0; j<16; j++) { if (dprev >= script_end) break; fprintf(stderr, "%02x ",*(unsigned char*)(dprev)); dprev++; } fprintf(stderr, "\n"); } break; } if (cmd.cmd_type == CMD_NOP) continue; if (cmd.cmd_type == CMD_TEXT && cmd.pos != -1) { set<int>& readflag = text_readflag[scn_number]; if (readflag.find(cmd.pos) == readflag.end()) { // 未読テキスト発見 readflag.insert(cmd.pos); if (skip_mode & SKIPEND_TEXT) { if (!(skip_mode & SKIPEND_KEY)) SetSkipMode(SKIP_NO); } } } text_exec.Exec(cmd); grp_exec.Exec(cmd); SysExec(cmd); if (cmd.cmd_type == CMD_WAITFRAMEUPDATE) { SetWakeup(Event::Time::FRAME_UPDATE); break; } else if (cmd.cmd_type != CMD_NOP) { #if DEBUG fprintf(stderr,"%d-%d / %d : unsupported command; 0x23 - cmd %02x-%02x:%04x:%02x[%2d] \n", cmd.scn, script - script_start, script_end-script_start, cmd.cmd1,cmd.cmd2,cmd.cmd3,cmd.cmd4,cmd.argc); int i; for (i = 0; i<cmd.args.size(); i++) { if (i == 0) fprintf(stderr,"\t"); VarInfo info = cmd.args[i]; if (info.type == TYPE_STR || info.type == TYPE_VARSTR) fprintf(stderr,"\"%s\",", cmd.Str(info)); else fprintf(stderr,"%d,",info.value); } fprintf(stderr,"\n"); #endif cmd.clear(); } } } void Scn2k::ShowCursor(void) { HideCursor(); char key[1024]; sprintf(key, "#MOUSE_CURSOR.%03d.NAME", mouse_type); const char* name = config->GetParaStr(key); if (name == NULL || name[0] == 0) mouse_surface = DEFAULT_MOUSECURSOR; else mouse_surface = parent.Root().NewSurface(name, COLOR_MASK); if (mouse_surface == NULL) mouse_surface = DEFAULT_MOUSECURSOR; System::Main::SetCursor(mouse_surface, Rect(8, 8, 8+32, 8+32)); } void Scn2k::HideCursor(void) { if (mouse_surface) { System::Main::SetCursor(0, Rect(0,0)); if (mouse_surface != DEFAULT_MOUSECURSOR) parent.Root().DeleteSurface(mouse_surface); mouse_surface = NULL; } } bool Scn2k::SysWait(Cmd& cmd) { if (menu) { menu->Exec(cmd); if (menu->status & Scn2kMenu::MENU_DELETE || menu->pimpl == NULL) { delete menu; menu = NULL; if (! menu_mouseshown) HideCursor(); else ShowCursor(); SetSkipMode(SkipMode(skip_mode & (~SKIP_IN_MENU) )); } if (cmd.cmd_type == CMD_NOP) return true; else return false; /* exec command */ } return false; } void DllCall_LB(Cmd& cmd, Flags& flags); void Scn2k::SysExec(Cmd& cmd) { if (cmd.cmd_type == CMD_SYSVAR) { int i; for (i=0; i<cmd.args.size(); i++) { if (cmd.args[i].type == TYPE_SYS) { if (cmd.args[i].number == TYPE_SYS_SYS) { flag.SetSys(cmd.args[i].value); } else if (cmd.args[i].number == TYPE_SYS_SKIPMODE) { SetSkipMode(SkipMode(cmd.args[i].value)); } } else if (cmd.args[i].type == TYPE_VARSTR) { flag.SetStr(cmd.args[i].number, cmd.Str(cmd.args[i])); } else { flag.Set(cmd.args[i], cmd.args[i].value); } } cmd.clear(); } if (cmd.cmd_type == CMD_SAVEPOINT || cmd.cmd_type == CMD_ROLLBACKPOINT) { if (text_exec.backlog_item.scn != -1) { text_exec.backlog.push_back(text_exec.backlog_item); text_exec.backlog_item.Clear(); } save_scn = scn_number; save_point = scn_point; if (!new_rollback_save.empty()) { rollback_save.push_back(new_rollback_save); new_rollback_save = ""; } if (cmd.cmd_type == CMD_ROLLBACKPOINT) SaveRollback(); cmd.clear(); } if (cmd.cmd_type == CMD_SAVEREQ || cmd.cmd_type == CMD_SAVE) { Save(cmd); return; } if (cmd.cmd_type == CMD_LOADREQ || cmd.cmd_type == CMD_LOAD) { Load(cmd); return; } if (cmd.cmd_type == CMD_BACKLOGREQ || cmd.cmd_type == CMD_BACKLOGREQ_FWD) { if (menu) { fprintf(stderr,"BACKLOG_REQ requested!!!\n"); return; } if (cmd.cmd_type == CMD_BACKLOGREQ_FWD) { cmd.clear(); // backlog mode 以外で fwd を押されてもなにもしない return; } SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで中断 menu = new Scn2kMenu(Scn2kMenu::MENU_BACKLOG, *this, flag, text_exec, system_version); menu->InitPanel(event, parent); menu->InitTitle(Scn2kSaveTitle(*this)); if (mouse_surface) menu_mouseshown = true; else menu_mouseshown = false; ShowCursor(); return; } if (cmd.cmd_type == CMD_MENUREQ) { int scn=0, pt=0; config->GetParam("#CANCELCALL", 2, &scn, &pt); if (scn) { // 右クリックされたら global call を行う cmd.cmd_type = CMD_OTHER; cmd.cmd1 = 0; cmd.cmd2 = 1; cmd.cmd3 = 0x0c; cmd.cmd4 = 1; cmd.args.clear(); cmd.args.push_back(VarInfo(SCN_INFO_MENU)); cmd.args.push_back(0); SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで中断 } } if (cmd.cmd_type == CMD_SAVECMDGRP || cmd.cmd_type == CMD_SAVECMDGRP_START || cmd.cmd_type == CMD_SAVECMDGRP_ONCE || cmd.cmd_type == CMD_SAVECMD_ONCE) { // 画像コマンド等はスタックに保存し、セーブ時に保存できるようにする if (cmd.cmd_type == CMD_SAVECMDGRP_START) { vector<CmdSimplified>::iterator it, cur; cur = cmd_stack.begin(); cmd_stack_str = cmd_stack_str_orig; /* 画像関連コマンド以外を別にする */ for (it=cmd_stack.begin(); it != cmd_stack.end(); it++) { if (it->type != CMD_SAVECMDGRP && it->type != CMD_SAVECMDGRP_START && it->type != CMD_SAVECMDGRP_ONCE) { cur->copy(*it, cmd_stack_str); cur++; } } cmd_stack.erase(cur, cmd_stack.end()); } if (cmd.cmd_type == CMD_SAVECMD_ONCE || cmd.cmd_type == CMD_SAVECMDGRP_ONCE) { // 同じコマンドがあれば削除する vector<CmdSimplified>::iterator it; for (it = cmd_stack.end(); it != cmd_stack.begin(); ) { --it; if (it->cmd1 == cmd.cmd1 && it->cmd2 == cmd.cmd2 && it->cmd3 == cmd.cmd3 && it->cmd4 == cmd.cmd4) { cmd_stack.erase(it); break; } } } CmdSimplified cmd_item; cmd.write(cmd_item, cmd_stack_str); cmd_stack.push_back(cmd_item); cmd.clear(); if (cmd_stack_str > cmd_stack_str_orig + 30000) { // char cmd_stack_str_orig[32768] fprintf(stderr,"Error in Scn2k::SysExec: too long cmdstack (%d): stack string overflow\n",cmd_stack.size()); cmd_stack_str = cmd_stack_str_orig; cmd_stack.clear(); } } if (cmd.cmd_type != CMD_OTHER) return; if (cmd.cmd1 == 0 && cmd.cmd2 == 1) { if (cmd.cmd3 == 0x0b) { // global jump int call_no = 0; if (cmd.args.size() >= 2) call_no = cmd.args[1].value; eprintf("global jump to %d\n",cmd.args[0].value); if (! ChangeScript(cmd.args[0].value, call_no)) return; // 読み込めない; abort. cmd.clear(); } else if (cmd.cmd3 == 0x0c || cmd.cmd3 == 0x12) { // call (0x12 の方は微妙) int new_scn = cmd.args[0].value; int new_pt = 0; if (cmd.args.size() >= 2) { // subroutine number が付く // 引数が付くのもあるらしい new_pt = cmd.args[1].value; } if (new_scn == SCN_INFO_MENU) { // menu call config->GetParam("#CANCELCALL", 2, &new_scn, &new_pt); stack.push_back(StackItem(SCN_INFO, SCN_INFO_MENU)); // menu call を示す特殊な記号 } else { int i; VarInfo var; // ローカル変数を伴う subroutine call var.type = 11; var.number = 0; int saved_vars = 0; for (i=0; i<40; i++) { int val = flag.Get(var.type, i); if (val != 0) { stack.push_back(StackItem(SCN_INFO_LOCALS + i, val)); saved_vars++; } } var.type = TYPE_VARLOCSTR; for (i=0; i<3; i++) { string s = flag.Str(var.type, i); if (s.size()) { int sp = stack_strbuffer.size(); stack.push_back(StackItem(SCN_INFO_LOCALSTR+i, sp)); stack_strbuffer.push_back(s); saved_vars++; } } stack.push_back(StackItem(SCN_INFO, SCN_INFO_LOCALS + saved_vars)); var.type = 11; var.number = 0; // 特殊な subroutine call なので、余計な情報を引数に渡す for (i=2; i<cmd.args.size(); i++) { flag.Set(var, cmd.args[i].value); // fprintf(stderr,"<%d:%d>=%d;",var.type,var.number,cmd.args[i].value); var.number++; } // fprintf(stderr,"%d; ",stack.size()); } int scn_pt = script - script_start; stack.push_back(StackItem(scn_number, scn_pt)); // fprintf(stderr,"\nglobal call %d:%d from %d:%d\n",new_scn,new_pt,scn_number,scn_pt); eprintf("global call to %d, %d\n",new_scn, new_pt); if (! ChangeScript(new_scn, new_pt)) return; // 読み込めない; abort. cmd.clear(); } else if (cmd.cmd3 == 0x65) { // 文字列の返り値をセットする int arg1 = cmd.args[0].value; string s = cmd.Str(cmd.args[1]); int sp = stack_strbuffer.size(); stack.push_back(StackItem(SCN_INFO_RETSTR+arg1, sp)); stack_strbuffer.push_back(s); cmd.clear(); } else if (cmd.cmd3 == 0x0d || cmd.cmd3 == 0x0a || cmd.cmd3 == 0x11 || cmd.cmd3 == 0x13) { // return (0a: local return) (0x13はよくわからない) // fprintf(stderr,"global return : stack size %d\n",stack.size()); if (stack.empty()) { cmd.clear(); return; // スタックがおかしい:abort } map<int, string> retstr; while( (!stack.empty()) && stack.back().scn_number >= SCN_INFO_RETSTR) { int ret_num = stack.back().scn_number - SCN_INFO_RETSTR; // fprintf(stderr,"\nRetStr;"); string str = stack_strbuffer.back(); stack_strbuffer.pop_back(); retstr[ret_num] = str; stack.pop_back(); } if (stack.empty()) { cmd.clear(); return; // スタックがおかしい:abort } StackItem s = stack.back(); stack.pop_back(); bool localvar_init = false; while( (!stack.empty()) && stack.back().scn_number == SCN_INFO) { int mode = stack.back().scn_pt; stack.pop_back(); if (mode == SCN_INFO_MENU) { // fprintf(stderr,"\nInfo Menu;"); // menu モード終了 SetSkipMode(SkipMode(skip_mode & (~SKIP_IN_MENU) )); } else if (mode >= SCN_INFO_LOCALS && mode <= SCN_INFO_LOCALS+50) { // fprintf(stderr,"\nInfo Local;"); int i; // ローカル変数を元に戻す VarInfo var; var.type = 11; var.number = 0; for (i=0; i<40; i++) { var.number = i; flag.Set(var, 0); } var.type = TYPE_VARLOCSTR; for (i=0; i<3; i++) { var.number = i; flag.SetStr(var, ""); } int args = mode - SCN_INFO_LOCALS; // fprintf(stderr," args = %d; ",args); for (i=0; i<args; i++) { if (stack.empty() || stack.back().scn_number < SCN_INFO) { fprintf(stderr,"Fatal : Invalid stack found in preserved local variables!\n"); break; } var.number = stack.back().scn_number; // fprintf(stderr,"%d:%d; ",stack.back().scn_number,stack.back().scn_pt); if (var.number >= SCN_INFO_LOCALS && var.number < SCN_INFO_LOCALSTR) { var.type = 11; var.number -= SCN_INFO_LOCALS; flag.Set(var, stack.back().scn_pt); } else if (var.number >= SCN_INFO_LOCALSTR && var.number < SCN_INFO_RETSTR) { var.type = TYPE_VARLOCSTR; var.number -= SCN_INFO_LOCALSTR; flag.SetStr(var, stack_strbuffer.back()); stack_strbuffer.pop_back(); } stack.pop_back(); } } // fprintf(stderr,"stack size %d string size %d\n",stack.size(),stack_strbuffer.size()); } if (cmd.cmd3 == 0x11 || cmd.cmd3 == 0x13) { // fprintf(stderr,"\nSet RetLocal;"); // 返り値をセットする map<int,string>::iterator it; VarInfo var; var.type = TYPE_VARLOCSTR; for (it=retstr.begin(); it!=retstr.end(); it++) { var.number = it->first; flag.SetStr(var, it->second); } var.type = 11; // fprintf(stderr,"return : cmd.cmd3 == 0x11; size %d\n",cmd.args.size()); if (cmd.args.size() == 1) { // fprintf(stderr,"return value %d\n",cmd.args[0].value); flag.SetSys(cmd.args[0].value); } else { int i;for (i=0; i<cmd.args.size(); i++) { var.number = i; flag.Set(var, cmd.args[i].value); } } } // fprintf(stderr,"global return : return to %d:%d\n",s.scn_number,s.scn_pt); // fprintf(stderr,"\nglobal return %d:%d from %d:%d\n",s.scn_number,s.scn_pt,scn_number, script - script_start); if (s.scn_number != -1) { if (! ChangeScript(s.scn_number, 0)) return; // 読み込めない; abort. } script = script_start + s.scn_pt; cmd.clear(); } } else if (cmd.cmd1 == 2 && cmd.cmd2 == 1 && cmd.cmd3 == 12) { // DLL Call const char* regname = config->GetParaStr("#REGNAME"); const char key_lb[] = "KEY\\LittleBusters";//FIXME: too specific to be here? if (strcmp(regname, key_lb) == 0) { DllCall_LB(cmd, flag); cmd.clear(); } } else if (cmd.cmd1 == 0 && cmd.cmd2 == 0x04) { // メニューモード if (cmd.cmd3 == 300 || cmd.cmd3 == 301 || cmd.cmd3 == 302) { // メニューからのreturn cmd.cmd2 = 1; cmd.cmd3 = 0x0d; SysExec(cmd); } } else if (cmd.cmd1 == 1 && cmd.cmd2 == 0x04) { if (cmd.cmd3 == 0 && cmd.cmd4 == 0) { // タイトル名設定 const char* name = cmd.Str(cmd.args[0]); if (name == NULL) name = ""; window_title = name; const char* config_name = config->GetParaStr("#CAPTION"); if (config_name == NULL) config_name = ""; string setname = kconv(string(config_name) + " " + window_title); parent.Root().SetWindowCaption(setname.c_str()); cmd.clear(); } else if (cmd.cmd3 == 0x82 && cmd.cmd4 == 0) { /* cmd.cmd3 == 0x82 : マウスの press 状態クリアかも */ event.presscount(MOUSE_LEFT); event.presscount(MOUSE_RIGHT); cmd.clear(); } else if (cmd.cmd3 == 0x85 && cmd.cmd4 == 0) { int x,y,left,right; event.MousePos(x,y); if (event.presscount(MOUSE_LEFT)) left = 2; else if (event.pressed(MOUSE_LEFT)) left = 1; else left = 0; if (event.presscount(MOUSE_RIGHT)) right = 2; else if (event.pressed(MOUSE_RIGHT)) right = 1; else right = 0; // eprintf("mouse pos\n"); flag.Set(cmd.args[0], x); flag.Set(cmd.args[1], y); flag.Set(cmd.args[2], left); flag.Set(cmd.args[3], right); cmd.clear(); } else if (cmd.cmd3 == 0x15e || cmd.cmd3 == 0x161 || cmd.cmd3 == 0x162 || cmd.cmd3 == 0x14c || cmd.cmd3 == 0x7d1) { /* 15e, 161, 162, 14c, 7d1 : なんらかのシステム情報を返す(skip modeなど?) */ /* 7d1: == 1 || 14c: == 1 || (15e==1&&161==1&&162==0) || (press_val == 2) : スキップ中? タイトル画面のアニメーション終了 */ flag.SetSys(0); cmd.clear(); } else if (cmd.cmd3 == 0x4b0) { // 終了 System::Main::Quit(); //script = NULL; script_start = NULL; script_end = NULL; cmd.clear(); cmd.cmd_type = CMD_WAITFRAMEUPDATE; } else if (cmd.cmd3 == 0x4b4 || cmd.cmd3 == 0x4b5) { // 選択肢巻き戻し LoadRollback(cmd); } else if (cmd.cmd3 == 0x58d) { // 前にロード|セーブされた番号を返す。 int lastsave; config->GetParam("#LASTSAVE", 1, &lastsave); flag.SetSys(lastsave-1); } else if (cmd.cmd3 == 0x585) { // 第一引数の記録された日付、タイトルなどが返される // データがないなら sys に 0が、あるなら 1 が返る int y,m,d,wd,h,min,s,ms; string title; fprintf(stderr,"StatSave %d:",cmd.args[0].value+1); if (StatSaveFile(cmd.args[0].value+1,y,m,d,wd,h,min,s,ms,title) == true) { flag.Set(cmd.args[1], y); flag.Set(cmd.args[2], m); flag.Set(cmd.args[3], d); flag.Set(cmd.args[4], wd); flag.Set(cmd.args[5], h); flag.Set(cmd.args[6], min); flag.Set(cmd.args[7], s); flag.Set(cmd.args[8], ms); if (cmd.args[9].type == TYPE_VARSTR) { flag.SetStr(cmd.args[9], kconv_rev(title)); } flag.SetSys(1); } else { flag.SetSys(0); } cmd.clear(); } else if (cmd.cmd3 == 0xc23) { // save Save(cmd); } else if (cmd.cmd3 == 0xc25) { // load Load(cmd); } else if (cmd.cmd3 == 0x4b1 || cmd.cmd3 == 0x4b3) { // menu へ戻る (4b3: バッドエンド) int scn_start; if (config->GetParam("#SEEN_MENU", 1, &scn_start) == 0) { ChangeScript(scn_start, 0); save_scn = 0; save_point = 0; window_title = ""; const char* window_title_config = config->GetParaStr("#CAPTION"); if (window_title_config) window_title = window_title_config; parent.Root().SetWindowCaption(kconv(window_title).c_str()); stack.clear(); cmd_stack.clear(); cmd_stack_str = cmd_stack_str_orig; flag.Load(""); text_exec.Load(""); grp_exec.Load(""); SetSkipMode(SKIP_NO); } } else if (cmd.cmd3 == 0xcc) { eprintf("show mouse cursor\n"); ShowCursor(); cmd.clear(); } else if (cmd.cmd3 == 0xcd) { eprintf("hide mouse cursor\n"); HideCursor(); cmd.clear(); } else if (cmd.cmd3 == 0xcf) { mouse_type = cmd.args[0].value; eprintf("change mouse cursor : %d\n", mouse_type); if (mouse_surface) ShowCursor(); cmd.clear(); } } } #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <unistd.h> // セーブファイルの名前をつくる string Scn2k::MakeSaveFile(void) const { struct stat sstatus; string dir = "~/.xkanon"; if (dir.c_str()[0] == '~' && dir.c_str()[1] == '/') { char* home = getenv("HOME"); if (home != NULL) { string new_dir = string(home) + (dir.c_str()+1); dir = new_dir; } } // savepathにファイル名が入っていれば、それをセーブファイルとして使う if (stat(dir.c_str(), &sstatus) == -1) { if (errno != ENOENT) { fprintf(stderr,"Cannot open save file; dir %s is not directory\n",dir.c_str()); return ""; } if (mkdir(dir.c_str(), S_IRWXU) != 0 && errno != EEXIST) { fprintf(stderr, "Cannot create directory %s ; Please create manually!!\n",dir.c_str()); } } else { if ( (sstatus.st_mode & S_IFMT) == S_IFREG) { return dir; } } // ファイル名を作る const char* regname = config->GetParaStr("#REGNAME"); char* fname = new char[strlen(regname)+1]; /* レジストリ名をファイル名として有効なものにする */ int i; for (i=0; regname[i]!=0; i++) { char c = regname[i]; if (c == '\\' || c == '/' || c == ':' || c <= 0x20) c = '_'; fname[i] = tolower(c); } fname[i] = 0; dir += "/save."; dir += fname; delete[] fname; return dir; } // セーブファイルの名前をつくる string Scn2kSaveTitle::operator() (int number) const { int y,m,d,wd,h,min,sec,msec; string title; if (! impl.StatSaveFile(number, y,m,d,wd,h,min,sec,msec,title)) { return ""; } else { char buf[1024]; sprintf(buf, "%2d/%2d %2d:%2d ",m,d,h,min); return string(buf) + title; } } void Scn2k::SaveSys(void) { char buf[1024]; string save; string path = MakeSaveFile(); sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME")); save += buf; string save_config; config->DiffOriginal(save_config); save += "CONFIG="; save += save_config; save += "\n"; string save_flag; flag.SaveSys(save_flag); save += save_flag; string save_grp; grp_exec.SaveSys(save_grp); save += save_grp; map<int,set<int> >::iterator it; save += "[TextRead]\n"; for (it=text_readflag.begin(); it != text_readflag.end(); it++) { set<int>& read_flag = it->second; set<int>::iterator jt; char buf[1024]; sprintf(buf,"T<%05d>=",it->first); string save_readflag = buf; for (jt=read_flag.begin(); jt != read_flag.end(); jt++) { sprintf(buf, "%d,", *jt); save_readflag += buf; } save_readflag += "\n"; save += save_readflag; } path += ".0"; FILE* f = fopen(path.c_str(), "w"); if (f == NULL) { fprintf(stderr,"Cannot open save file %s\n",path.c_str()); return; } fwrite(save.c_str(), save.length(), 1, f); fclose(f); return; } void Scn2k::LoadSys(void) { char buf[1024]; string path = MakeSaveFile(); path += ".0"; FILE* f = fopen(path.c_str(), "r"); if (f == NULL) { fprintf(stderr, "Cannot open save file %s\n",path.c_str()); } else { fseek(f, 0, SEEK_END); int sz = ftell(f); fseek(f, 0, SEEK_SET); char* savedata = new char[sz+1]; fread(savedata, sz, 1, f); savedata[sz] = 0; fclose(f); sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME")); if (strncmp(savedata, buf, strlen(buf)) != 0) { fprintf(stderr,"Invalid header in save file %s: it must be started with \"%s\"\n", path.c_str(), buf); } else { char* config_str = strstr(savedata, "\nCONFIG="); if (config_str) { config_str += strlen("\nCONFIG="); char* strend = strchr(config_str, '\n'); if (strend) { int l = strend - config_str; char* config_copy = new char[l+1]; strncpy(config_copy, config_str, l); config_copy[l] = 0; config->PatchOriginal(config_copy); delete[] config_copy; } } flag.LoadSys(savedata); grp_exec.LoadSys(savedata); char* save = strstr(savedata, "\n[TextRead]\n"); if (save) { save += strlen("\n[TextRead]\n"); do { if (save[0] == '[') break; // next section char* next_save = strchr(save, '\n'); if (next_save) { *next_save++ = 0; } // T<XXXXX>=YYY,YYY,YYY,... if (strncmp(save,"T<",2) == 0) { int scn_num = atoi(save+2); set<int>& read_flag = text_readflag[scn_num]; save += strlen("T<XXXXX>="); while(save && *save) { if (save[0] >= '0' && save[0] <= '9') { int num = atoi(save); read_flag.insert(num); } save = strchr(save, ','); if (save) save++; } } save = next_save; } while(save); } } delete[] savedata; } /* 初期化 */ int scn_start; config->GetParam("#SEEN_START", 1, &scn_start); ChangeScript(scn_start, 0); save_scn = 0; save_point = 0; window_title = ""; const char* window_title_config = config->GetParaStr("#CAPTION"); if (window_title_config) window_title = window_title_config; parent.Root().SetWindowCaption(kconv(window_title).c_str()); stack.clear(); cmd_stack.clear(); cmd_stack_str = cmd_stack_str_orig; return; } bool Scn2k::StatSaveFile(int num, int& year, int& month, int& day, int& wday, int& hour,int& min, int& sec, int& msec, string& title) const { char buf[1024]; string path = MakeSaveFile(); if (num <= 0 || num > 100) return false; sprintf(buf,".%d",num); path += buf; struct stat sb; if (stat(path.c_str(), &sb) == -1) return false; struct tm* t = localtime(&sb.st_mtime); year = t->tm_year; month = t->tm_mon + 1; day = t->tm_mday; hour = t->tm_hour; min = t->tm_min; sec = t->tm_sec; msec = 0; /* タイトルの取得 */ FILE* savefile = fopen(path.c_str(), "rb"); if (savefile == NULL) return false; char regname[1024]; sprintf(regname, "KEY=%s\n", config->GetParaStr("#REGNAME")); fgets(buf,1000,savefile); if (strncmp(regname, buf, strlen(regname)) != 0) { fprintf(stderr,"invalid save file %s (registory name is not %s)\n",path.c_str(),regname); fclose(savefile); return false; } title="none"; while(!feof(savefile)) { fgets(buf,1000,savefile); if (strncmp(buf,"Title=",6) == 0) { if (buf[strlen(buf)-2] == 0x0a) buf[strlen(buf)-2] = 0; if (strlen(buf) > 20) buf[20] = 0, buf[21] = 0; title = kconv(buf+6); break; } } fclose(savefile); return true; } void Scn2k::SaveRollback(void) { fprintf(stderr,"Save rollback\n"); new_rollback_save = ""; string save_sys; SaveImpl(save_sys); string save_flag; flag.Save(save_flag); string save_text; text_exec.Save(save_text, true); string save_grp; grp_exec.Save(save_grp); new_rollback_save += save_sys; new_rollback_save += save_flag; new_rollback_save += save_text; new_rollback_save += save_grp; } void Scn2k::LoadRollback(Cmd& cmd) { if (rollback_save.empty()) return; new_rollback_save = ""; string savedata = rollback_save.back(); rollback_save.pop_back(); LoadImpl(savedata.c_str()); flag.Load(savedata.c_str()); text_exec.Load(savedata.c_str()); grp_exec.Load(savedata.c_str()); /* 画面の回復など */ SetSkipMode(SKIP_NO); vector<CmdSimplified>::iterator it; cmd.clear(); for (it = cmd_stack.begin(); it != cmd_stack.end(); it++) { cmd.read(*it); cmd.cmd_type = CMD_OTHER; flag.Exec(cmd); text_exec.Exec(cmd); grp_exec.Exec(cmd); } cmd.clear(); return; } void Scn2k::Save(Cmd& cmd) { if (cmd.cmd_type == CMD_SAVEREQ) { if (menu == NULL) { SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで中断 menu = new Scn2kMenu(Scn2kMenu::MENU_SAVE, *this, flag, text_exec, system_version); menu->InitPanel(event, parent); menu->InitTitle(Scn2kSaveTitle(*this)); if (mouse_surface) menu_mouseshown = true; else menu_mouseshown = false; ShowCursor(); return; } } char buf[1024]; string save; FILE* f = NULL; if (save_scn == 0) { fprintf(stderr,"Cannot decide save point\n"); return; // セーブ位置が保存されてない } string path = MakeSaveFile(); int file_number = 1; if (cmd.args.size() == 1) file_number = cmd.args[0].value + 1; if (file_number <= 0) { fprintf(stderr, "Cannot open save file %s\n",path.c_str()); return; } sprintf(buf, ".%d",file_number); path += buf; /* セーブファイル確認 */ sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME")); save += buf; string save_sys; SaveImpl(save_sys); string save_flag; flag.Save(save_flag); string save_text; text_exec.Save(save_text, false); string save_grp; grp_exec.Save(save_grp); save += save_sys; save += save_flag; save += save_text; save += save_grp; vector<string>::iterator it; for (it=rollback_save.begin(); it != rollback_save.end(); it++) { save += "[Rollback Data]\n"; save += *it; save += "[Rollback End]\n"; } f = fopen(path.c_str(), "w"); if (f == NULL) { fprintf(stderr,"Cannot open save file %s\n",path.c_str()); return; } fwrite(save.c_str(), save.length(), 1, f); fclose(f); config->SetParam("#LASTSAVE", 1, file_number); cmd.clear(); } void Scn2k::Load(Cmd& cmd) { if (cmd.cmd_type == CMD_LOADREQ) { if (menu == NULL) { menu = new Scn2kMenu(Scn2kMenu::MENU_LOAD, *this, flag, text_exec, system_version); menu->InitPanel(event, parent); menu->InitTitle(Scn2kSaveTitle(*this)); SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで中断 if (mouse_surface) menu_mouseshown = true; else menu_mouseshown = false; ShowCursor(); return; } } char buf[1024]; string path = MakeSaveFile(); int file_number = 1; if (cmd.args.size() == 1) file_number = cmd.args[0].value + 1; sprintf(buf, ".%d",file_number); path += buf; FILE* f = NULL; if (file_number > 0) f = fopen(path.c_str(), "r"); if (f == NULL) { fprintf(stderr, "Cannot open save file %s\n",path.c_str()); return; } fseek(f, 0, SEEK_END); int sz = ftell(f); fseek(f, 0, SEEK_SET); char* savedata = new char[sz+1]; fread(savedata, sz, 1, f); savedata[sz] = 0; fclose(f); sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME")); if (strncmp(savedata, buf, strlen(buf)) != 0) { fprintf(stderr,"Invalid header in save file %s: it must be started with \"%s\"\n", path.c_str(), buf); delete[] savedata; return; } LoadImpl(savedata); flag.Load(savedata); text_exec.Load(savedata); grp_exec.Load(savedata); rollback_save.clear(); new_rollback_save = ""; char* rollback_data = savedata; while( (rollback_data = strstr(rollback_data,"[Rollback Data]\n")) != NULL) { rollback_data += strlen("[Rollback Data]\n"); char* rollback_end = strstr(rollback_data, "[Rollback End]\n"); if (rollback_end == NULL) rollback_end = rollback_data + strlen(rollback_data); string s(rollback_data, rollback_end); rollback_save.push_back(s); rollback_data = rollback_end; } /* 画面の回復など */ SetSkipMode(SKIP_NO); vector<CmdSimplified>::iterator it; for (it = cmd_stack.begin(); it != cmd_stack.end(); it++) { cmd.read(*it); cmd.cmd_type = CMD_OTHER; flag.Exec(cmd); text_exec.Exec(cmd); grp_exec.Exec(cmd); } cmd.clear(); delete[] savedata; } void Scn2k::SaveImpl(string& save) { char buf[1024]; /* save point */ sprintf(buf, "\n[SCENARIO]\nScn=%d\nPoint=%d\n",save_scn, save_point); save += buf; sprintf(buf, "Title=%s\nMouseType=%d\nMouseShown=1\n",window_title.c_str(), mouse_type); save += buf; vector<StackItem>::iterator sit; for (sit=stack.begin(); sit!=stack.end(); sit++) { if (sit->scn_number == SCN_INFO && sit->scn_pt == SCN_INFO_MENU) break; // メニューに入る直前までのスタックを保存 sprintf(buf, "Stack=%d,%d\n",sit->scn_number,sit->scn_pt); save += buf; } vector<string>::reverse_iterator ssit; for (ssit=stack_strbuffer.rbegin(); ssit != stack_strbuffer.rend(); ssit++) { sprintf(buf, "StackStr=%s\n",ssit->c_str()); save += buf; } vector<CmdSimplified>::iterator cit; for (cit=cmd_stack.begin(); cit != cmd_stack.end(); cit++) { if (cit->type == CMD_SAVECMDGRP || cit->type == CMD_SAVECMDGRP_ONCE || cit->type == CMD_SAVECMDGRP_START) { save += "CmdG="; } else { save += "Cmd="; } string s; cit->Save(s); save += s; save += "\n"; } } void Scn2k::LoadImpl(const char* save) { save_scn = 0; save_point = 0; window_title = ""; stack.clear(); cmd_stack.clear(); cmd_stack_str = cmd_stack_str_orig; save = strstr(save, "\n[SCENARIO]\n"); if (save == NULL) return; save += strlen("\n[SCENARIO]\n"); while(save[0] != 0 && save[0] != '[') { // while next section start if (strncmp(save, "Scn=", 4) == 0) { sscanf(save, "Scn=%d", &save_scn); } else if (strncmp(save, "Point=", 6) == 0) { sscanf(save, "Point=%d", &save_point); } else if (strncmp(save, "Title=", 6) == 0) { save += 6; const char* s = strchr(save, '\n'); if (s == NULL) window_title = save; else window_title.assign(save, s-save); const char* config_name = config->GetParaStr("#CAPTION"); if (config_name == NULL) config_name = ""; string setname = kconv(string(config_name)+" "+window_title); parent.Root().SetWindowCaption(setname.c_str()); } else if (strncmp(save, "MouseType=", 10) == 0) { sscanf(save, "MouseType=%d", &mouse_type); } else if (strncmp(save, "MouseShown=", 11) == 0) { int v; sscanf(save, "MouseShown=%d", &v); if (v) ShowCursor(); else HideCursor(); } else if (strncmp(save, "Stack=", 6) == 0) { int scn, pt; sscanf(save, "Stack=%d,%d", &scn, &pt); stack.push_back( StackItem(scn, pt)); } else if (strncmp(save, "StackStr=", 9) == 0) { save += 9; const char* s = strchr(save, '\n'); if (s == NULL) stack_strbuffer.push_back(""); else stack_strbuffer.push_back(string(save, s-save)); } else if (strncmp(save, "Cmd=", 4) == 0) { CmdSimplified cmd; cmd.Load(save+4, cmd_stack_str); cmd_stack.push_back(cmd); } else if (strncmp(save, "CmdG=", 5) == 0) { CmdSimplified cmd; cmd.Load(save+5, cmd_stack_str); cmd.type = CMD_SAVECMDGRP; cmd_stack.push_back(cmd); } save = strchr(save, '\n'); if (save != NULL) save++; } ChangeScript(save_scn, 0); script = script_start + save_point; } void Scn2k::SetSkipMode(SkipMode mode) { if (skip_mode != mode) { skip_mode = mode; text_exec.SetSkipMode(mode); grp_exec.SetSkipMode(mode); } } /*********************************************************** ** ** DLL Call Implementation ** **/ static double* lb_ef_param = 0; void DLLCall_LB_EF00_0(Cmd& cmd, Flags& flags) { // エフェクトの設定 if (lb_ef_param == 0) { lb_ef_param = new double[sizeof(double) * 0x60 * 8]; } int i,j; int param_top, param_size; if (cmd.args[2].value == 1) { param_top = 0; param_size = 0x20; } else { param_top = cmd.args[3].value; param_size = cmd.args[4].value; if (param_top < 0) param_top = 0; if (param_top > 0x20) param_top = 0x20; if (param_size+param_top > 0x20) param_size = 0x20 - param_top; } for (i=0; i<8; i++) { double* param = lb_ef_param + i*0x60 + param_top*3; for (j=0; j<param_size; j++) { *param++ = random() % 800 - 400; *param++ = random() % 600 - 300; *param++ = random() % 700 - 350; } } if (cmd.args[5].value != 1) return; static int random_dirtable[] = { 0, 2, 1, 3, 0, 2, 1, 3, 1, 3, 2, 0, 1, 3, 2, 0, 0, 0, 0, 0, 3, 1, 2, 0, 3, 1, 3, 1, 0, 2, 3, 1 }; int* dir = &random_dirtable[(random()&3) * 8]; for (i=0; i<8; i++) { double* param = lb_ef_param + i*0x60; double x = random()%600 - 300; double y = random()%480-240; if (x < 0) x -= 80; else x += 80; if (y < 0) y -= 80; else y += 80; switch(*dir++) { case 0: if (x < 0) x = -x; if (y < 0) y = -y; break; case 1: if (x > 0) x = -x; if (y < 0) y = -y; break; case 2: if (x < 0) x = -x; if (y > 0) y = -y; break; case 4: if (x > 0) x = -x; if (y > 0) y = -y; break; } param[9] = x*1.2; param[10] = y*1.2; param[11] *= 1.2; param[12] *= -0.08; param[13] *= -0.08; param[14] *= -0.08; param[15] = -param[9]; param[16] = -param[10]; param[17] = -param[11]; } return; } void DLLCall_LB_EF00_1(Cmd& cmd, Flags& flags) { // 計算を行う if (lb_ef_param == 0) { fprintf(stderr,"Warning : DLLCall_LB_EF00_1 : Script error : effect calculation was called before setting\n"); return; } int index = cmd.args[2].value; int v5_1154 = flags.Get(5, 1154+index); int j = ((v5_1154) & 0x1f) + index * 0x20; int k = ((v5_1154+1) & 0x1f) + index * 0x20; int l = ((v5_1154+2) & 0x1f) + index * 0x20; int m = ((v5_1154+3) & 0x1f) + index * 0x20; j *= 3; k *= 3; l *= 3; m *= 3; // 0 < x < 1 // va - vd は 0-1 の範囲で対称性を持つ3次関数 double x = double(flags.Get(5, 1162 + index)) * 0.001; double va = (x * x * x)/6; double vb = (-x*x*x + 3*x*x - 3*x + 1) / 6; double vc = (3*x*x*x - 6*x*x + 4) / 6; double vd = (-3*x*x*x+3*x*x+3*x+1) / 6; double r1 = va * lb_ef_param[m+3] + vd * lb_ef_param[l+3] + vc * lb_ef_param[k+3] + vb * lb_ef_param[j+3]; double r2 = va * lb_ef_param[m+2] + vd * lb_ef_param[l+2] + vc * lb_ef_param[k+2] + vb * lb_ef_param[j+2]; double r3 = va * lb_ef_param[m+1] + vd * lb_ef_param[l+1] + vc * lb_ef_param[k+1] + vb * lb_ef_param[j+1]; if (r1 != 400) { r2 = r2 * 800 / (400-r1); r3 = r3 * 700 / (400-r1); } VarInfo var; var.type = 5; var.number = 1151; flags.Set(var, int(r2)); var.number = 1152; flags.Set(var, int(r3)); var.number = 1153; flags.Set(var, int(r1)); return; } void DllCall_LB(Cmd& cmd, Flags& flags) { // リトルバスターズ!の EF00.dll をエミュレート if (cmd.args[0].value == 1) { // "EF00.dll" if (cmd.args[1].value == 0) { // エフェクトの設定 DLLCall_LB_EF00_0(cmd, flags); } else if (cmd.args[1].value == 1) { // 計算を行う DLLCall_LB_EF00_1(cmd, flags); } } else { fprintf(stderr,"Unsupported DLL call for DLL<%d>\n",cmd.args[0].value); } return; } /********************************************************** ** ** MenuImpl ** */ #include "window/widget.h" #include "window/menuitem.h" void DSurfaceFill(Surface* src, const Rect& rect, int r, int g, int b, int a = 0xff); struct Scn2kMenuImpl { Scn2kMenu& interface; MenuItem* menu; Event::Container* pevent; PicContainer* pparent; virtual void InitPanel(Event::Container& event, PicContainer& parent) = 0; virtual void InitTitle(const SaveTitle&) = 0; virtual void Cancel(void) = 0; virtual void Exec(Cmd& cmd) = 0; Scn2kMenuImpl(Scn2kMenu& _interface) : interface(_interface) { menu = NULL; pevent = NULL; pparent = NULL; } virtual ~Scn2kMenuImpl() { if (menu) delete menu; menu = NULL; } }; struct LoadMenu : Scn2kMenuImpl { vector<string> title; vector<int> title_valid; RadioButton* btn_local; RadioButton* btn_page; RadioButton* btn_set; Scale* btn_scale; Dialog* awk_dialog; int btn_page_val, btn_set_val, btn_local_val, select_page, select_value; LoadMenu(Scn2kMenu& _interface); ~LoadMenu(); void InitPanel(Event::Container& event, PicContainer& parent); void InitTitle(const SaveTitle&); void Cancel(void); void Exec(Cmd& cmd); static void ChangeBtnPage(void* pointer, MenuItem* widget); static void ChangeBtnLocal(void* pointer, MenuItem* widget); static void ChangeBtnScale(void* pointer, Scale* widget); static void ChangeBtnSet(void* pointer, MenuItem* widget); static void ChangeDialog(void* pointer, Dialog* widget); bool in_setpage; void SetPage(int new_page); void SetValue(int new_value); void PressOk(void); }; LoadMenu::LoadMenu(Scn2kMenu& _interface) : Scn2kMenuImpl(_interface) { btn_local = NULL; btn_scale = NULL; btn_set = NULL; btn_page_val = 0; btn_set_val = -1; btn_local_val = -1; awk_dialog = NULL; in_setpage = false; select_page = 0; select_value = -1; } LoadMenu::~LoadMenu() { if (awk_dialog) delete awk_dialog; } void LoadMenu::InitPanel(Event::Container& event, PicContainer& parent) { pevent = &event; pparent = &parent; if (menu) delete menu; menu = NULL; menu = new MenuItem(&parent, Rect(80,30,560, 450), 1, 3, 0); Surface* surface = parent.Root().NewSurface(menu->Pic()->Width(), menu->Pic()->Height(), ALPHA_MASK); if (interface.type == Scn2kMenu::MENU_LOAD) { menu->SetLabelTop(new Label(menu->PicNode(), Rect(0,0), true, "Load", 26), Rect(0,0,10,0), Rect(0,0,0,20)); DSurfaceFill(surface, Rect(*surface), 0, 0, 0x80, 0x80); } else { menu->SetLabelTop(new Label(menu->PicNode(), Rect(0,0), true, "Save", 26), Rect(0,0,10,0), Rect(0,0,0,20)); DSurfaceFill(surface, Rect(*surface), 0, 0x80, 0, 0x80); } menu->Pic()->SetSurface(surface, 0, 0); menu->Pic()->SetSurfaceFreeFlag(); btn_page = new RadioButton(event, menu->PicNode(), Rect(0, 0, 480, 40), 10, 1, &btn_page_val, Rect(0,0,0,0), 18, Color(0,0,0),Color(0xff,0,0),Color(0xff,0x80,0)); btn_page->set_func = &ChangeBtnPage; btn_page->set_pointer = this; btn_page->SetLabelLeft(new Label(btn_page->PicNode(), Rect(0,0), true, "Page", 18), Rect(0, 0, 180, 0), Rect(0,0)); btn_page->Add(" 1 "); btn_page->Add(" 2 "); btn_page->Add(" 3 "); btn_page->Add(" 4 "); btn_page->Add(" 5 "); btn_page->Add(" 6 "); btn_page->Add(" 7 "); btn_page->Add(" 8 "); btn_page->Add(" 9 "); btn_page->Add(" 10 "); btn_page->pack(); /* surface = parent.Root().NewSurface(btn_page->Pic()->Width(), btn_page->Pic()->Height(), ALPHA_MASK); DSurfaceFill(surface, Rect(*surface), 0xff, 0, 0, 0x80); btn_page->Pic()->SetSurface(surface, 0, 0); btn_page->Pic()->SetSurfaceFreeFlag(); */ menu->item[0] = btn_page; btn_set = new RadioButton(event, menu->PicNode(), Rect(0, 0, 480, 40), 2, 1, &btn_set_val, Rect(0,0,0,0), 18, Color(0,0,0),Color(0xff,0,0),Color(0xff,0x80,0)); btn_set->set_func = &ChangeBtnSet; btn_set->set_pointer = this; btn_set->SetLabelLeft(new Label(btn_set->PicNode(), Rect(0,0)), Rect(0,0,200,0), Rect(0,0)); if (interface.type == Scn2kMenu::MENU_LOAD) { btn_set->Add(" Load "); } else { btn_set->Add(" Save "); } btn_set->Add(" Cancel "); btn_set->pack(); /* surface = parent.Root().NewSurface(btn_set->Pic()->Width(), btn_set->Pic()->Height(), ALPHA_MASK); DSurfaceFill(surface, Rect(*surface), 0, 0, 0xff, 0x80); btn_set->Pic()->SetSurface(surface, 0, 0); btn_set->Pic()->SetSurfaceFreeFlag(); */ menu->item[2] = btn_set; // void btn_set_press(void* pointer, MenuItem* widget); // btn_set->set_func = btn_set_press; // btn_set->set_pointer = this; btn_local = new RadioButton(*pevent, menu->PicNode(), Rect(0, 0, 480, 300), 1, 100, &btn_local_val, Rect(0,0,300,30), 18, Color(0,0,0),Color(0xff,0,0),Color(0xff,0x80,0)); btn_local->set_func = &ChangeBtnLocal; btn_local->set_pointer = this; /* surface = pparent->Root().NewSurface(btn_local->Pic()->Width(), btn_local->Pic()->Height(), ALPHA_MASK); DSurfaceFill(surface, Rect(*surface), 0, 0xff, 0, 0x80); btn_local->Pic()->SetSurface(surface, 0, 0); btn_local->Pic()->SetSurfaceFreeFlag(); */ menu->item[1] = btn_local; int i; for (i=0; i<12; i++) btn_local->Add("",false); btn_local->pack(); btn_local->show_all(); menu->pack(); PicBase* local_pic = btn_local->Pic(); int local_x2 = local_pic->PosX() + local_pic->Width(); int local_y2 = local_pic->PosY() + local_pic->Height(); btn_scale = new Scale(*pevent, menu->PicNode(), Rect(local_x2-16, local_pic->PosY(), local_x2, local_y2), Color(0xff, 0x80, 0), true); btn_scale->SetRange(0, 900); btn_scale->InitCursor(1024/10); btn_scale->SetValue(0); btn_scale->change_func = &ChangeBtnScale; btn_scale->change_pointer = this; menu->PicNode()->show_all(); } void LoadMenu::InitTitle(const SaveTitle& title_op) { title.clear(); int i; for (i=1; i<=100; i++) { char buf[100]; sprintf(buf,"%2d:",i); string t = title_op(i); string s = string(buf) + t; if (t.length() == 0) { string s = string(buf) + "--------"; title_valid.push_back(0); } else { title_valid.push_back(1); } title.push_back(s); } if (btn_local==0) return; for (i=0; i<10; i++) { TextButton* button = dynamic_cast<TextButton*>(btn_local->item[i]); if (button) button->SetText(title[i].c_str()); } } void LoadMenu::SetPage(int new_page) { if (new_page < 0) new_page = 0; if (new_page > 900) new_page = 900; if (select_page == new_page) return; if (in_setpage) return; in_setpage = true; int prev_page = select_page / 10; int cur_page = new_page / 10; int prev_point = select_page%10; int new_point = new_page%10; select_page = new_page; if (prev_page != cur_page) { int i; for (i=0; i<12; i++) { TextButton* button = dynamic_cast<TextButton*>(btn_local->item[i]); if (button) { if (cur_page+i < title.size()) button->SetText(title[cur_page+i].c_str()); else button->SetText("----"); } } // ボタンの内容を変更する if (select_value < cur_page || select_value > cur_page+12) btn_local->SetValue(-1); else btn_local->SetValue(select_value - cur_page); } if (prev_point != new_point) { int i; for (i=0; i<12; i++) { int old_x = btn_local->item[i]->Pic()->PosX(); btn_local->item[i]->Pic()->Move(old_x, i*30-new_point*3); } } if (btn_page != NULL) { if (select_page%100 == 0) btn_page->SetValue(select_page/100); else btn_page->SetValue(-1); } if (btn_scale != NULL) { btn_scale->SetValue(select_page); } in_setpage = false; } void LoadMenu::SetValue(int new_value) { if (in_setpage) return; in_setpage = true; if (new_value < 0 || new_value > title.size() || (interface.type == Scn2kMenu::MENU_LOAD && title_valid[new_value] == 0) ) { // 無効な選択肢 if (select_value < select_page/10 || select_value > select_page/10+12) btn_local->SetValue(-1); else btn_local->SetValue(select_value-select_page/10); } else { // 選択肢を変更する if (select_value == new_value) { PressOk(); // ダブルクリック } else { select_value = new_value; if (interface.type == Scn2kMenu::MENU_SAVE && title_valid[select_value] == 0) { PressOk(); // 新しいセーブデータなら無条件に選択 } } } in_setpage = false; } void LoadMenu::PressOk(void) { if (select_value == -1) { btn_set->SetValue(-1); // なにもしない return; } menu->deactivate(); if (interface.type == Scn2kMenu::MENU_LOAD) { interface.cmd.cmd_type = CMD_LOAD; interface.cmd.args.push_back(VarInfo(select_value)); awk_dialog = new Dialog(*pevent, pparent, "ファイルをロードしますか?", true); awk_dialog->set_pointer = this; awk_dialog->set_func = ChangeDialog; } else {// MENU_SAVE interface.cmd.cmd_type = CMD_SAVE; interface.cmd.args.push_back(VarInfo(select_value)); if (title_valid[select_value] == 0) { // 新しいセーブデータ interface.status = Scn2kMenu::MenuStatus(Scn2kMenu::MENU_CMD | Scn2kMenu::MENU_DELETE); } else { // セーブデータを上書き:確認 awk_dialog = new Dialog(*pevent, pparent, "データを上書きしますか?", true); awk_dialog->set_pointer = this; awk_dialog->set_func = ChangeDialog; } } } void LoadMenu::Cancel(void) { if (awk_dialog != NULL) { // ダイアログのキャンセル awk_dialog->status = Dialog::CANCEL; ChangeDialog(this, awk_dialog); } else { // 一般キャンセル btn_set->SetValue(1); } } void LoadMenu::Exec(Cmd& cmd) { } void LoadMenu::ChangeBtnPage(void* pointer, MenuItem* widget) { LoadMenu* instance = (LoadMenu*)pointer; if (instance->btn_page_val == -1) return; instance->SetPage(instance->btn_page_val*100); } void LoadMenu::ChangeBtnScale(void* pointer, Scale* from) { LoadMenu* instance = (LoadMenu*)pointer; int value = from->GetValue(); instance->SetPage(value); } void LoadMenu::ChangeBtnSet(void* pointer, MenuItem* widget) { LoadMenu* instance = (LoadMenu*)pointer; if (instance->btn_set_val == 1) { // cancel instance->interface.status = Scn2kMenu::MENU_DELETE; return; } else if (instance->btn_set_val == 0) { // OK instance->PressOk(); } } void LoadMenu::ChangeDialog(void* pointer, Dialog* widget) { LoadMenu* instance = (LoadMenu*)pointer; if (widget->status == Dialog::CANCEL) { // ダイアログ消去、OK ボタン復帰 delete instance->awk_dialog; instance->awk_dialog = NULL; instance->menu->activate(); instance->btn_set->SetValue(-1); return; } else if (widget->status == Dialog::OK) { instance->interface.status = Scn2kMenu::MenuStatus(Scn2kMenu::MENU_CMD | Scn2kMenu::MENU_DELETE); return; } } void LoadMenu::ChangeBtnLocal(void* pointer, MenuItem* widget) { LoadMenu* instance = (LoadMenu*)pointer; if (instance->btn_local_val == -1) return; instance->SetValue( (instance->select_page/10) + instance->btn_local_val); } struct BacklogMenu : Scn2kMenuImpl { Scn2k& scn_impl; Text& text_exec; bool backlog_update; int backlog_cnt; BacklogMenu(Scn2kMenu& _interface, Scn2k& scn_impl, Text& text_exec); ~BacklogMenu(); void InitPanel(Event::Container& event, PicContainer& parent); void InitTitle(const SaveTitle&); void Cancel(void); void Exec(Cmd& cmd); }; BacklogMenu::BacklogMenu(Scn2kMenu& _interface, Scn2k& _scn, Text& parent_text_exec) : Scn2kMenuImpl(_interface), scn_impl(_scn), text_exec(parent_text_exec) { backlog_cnt = -1; backlog_update = false; } BacklogMenu::~BacklogMenu() { } void BacklogMenu::InitPanel(Event::Container& event, PicContainer& parent) { pevent = &event; } void BacklogMenu::InitTitle(const SaveTitle& title_op) { } void BacklogMenu::Cancel(void) { interface.status = Scn2kMenu::MenuStatus(Scn2kMenu::MENU_DELETE); } void BacklogMenu::Exec(Cmd& cmd) { int command_direction = 0; // forward if (cmd.cmd_type == CMD_NOP) text_exec.Wait(0xffffffffUL, cmd); if (cmd.cmd_type == CMD_BACKLOGREQ || pevent->presscount(MOUSE_UP)) { if (cmd.cmd_type == CMD_BACKLOGREQ) cmd.clear(); backlog_cnt++; backlog_update = false; command_direction = 1; } if (cmd.cmd_type == CMD_BACKLOGREQ_FWD || pevent->presscount(MOUSE_DOWN)) { if (cmd.cmd_type == CMD_BACKLOGREQ_FWD) cmd.clear(); backlog_cnt--; backlog_update = false; if (backlog_cnt == -2 || ( (backlog_cnt == -1 && text_exec.backlog_item.scn == -1 && text_exec.backlog_item.pos == -1)) ){ Cancel(); return; } command_direction = -1; } if (cmd.cmd_type != CMD_NOP) return; if (backlog_update) return; // backlog を最新の状態に更新 cmd.clear(); BacklogItem item; retry: if (backlog_cnt < -1) backlog_cnt = -1; if (backlog_cnt >= int(text_exec.backlog.size())) backlog_cnt = text_exec.backlog.size() - 1; if (backlog_cnt == -1) { if (text_exec.backlog_item.scn == -1 && text_exec.backlog_item.pos == -1) { if (text_exec.backlog.size() == 0 || command_direction < 0) { Cancel(); return; } item = text_exec.backlog.back(); backlog_cnt = 0; } else { // item = text_exec.backlog.back(); item = text_exec.backlog_item; } } else { item = text_exec.backlog[text_exec.backlog.size()-1-backlog_cnt]; } if (item.scn == BacklogItem::SaveSelect) { // select marker ; skip this item if (command_direction == 0) command_direction = 1; backlog_cnt += command_direction; goto retry; } if (item.scn == 0 && item.pos == -1) ; // not read cmd else { scn_impl.ReadCmdAt(cmd, item.scn, item.pos); } text_exec.DrawBacklog(item, cmd); cmd.clear(); backlog_update = true; } /******************************************************************************* ** ** */ Scn2kMenu::Scn2kMenu(MenuType _type, Scn2k& scn_impl, const Flags& flags, Text& text_exec, int system_version) : cmd(flags, system_version), type(_type) { pimpl = NULL; status = MENU_CONTINUE; switch(type) { case MENU_LOAD: pimpl = new LoadMenu(*this); break; case MENU_SAVE: pimpl = new LoadMenu(*this); break; case MENU_BACKLOG: pimpl = new BacklogMenu(*this, scn_impl, text_exec); break; } } Scn2kMenu::~Scn2kMenu() { if (pimpl) delete pimpl; pimpl = NULL; } void Scn2kMenu::InitPanel(Event::Container& event, PicContainer& parent) { if (pimpl != NULL) pimpl->InitPanel(event, parent); } void Scn2kMenu::InitTitle(const SaveTitle& t) { if (pimpl != NULL) pimpl->InitTitle(t); } void Scn2kMenu::Cancel(void) { if (pimpl) pimpl->Cancel(); } void Scn2kMenu::Exec(Cmd& ret_cmd) { if (pimpl == NULL) return; pimpl->Exec(ret_cmd); if (pimpl->pevent->presscount(MOUSE_RIGHT)) { Cancel(); } if (status & MENU_CMD && cmd.cmd_type != CMD_NOP) { status = Scn2kMenu::MenuStatus(status & (~Scn2kMenu::MENU_CMD) ); CmdSimplified tmp_cmd; char cmd_str[32768]; char* tmp_cmd_str = cmd_str; cmd.write(tmp_cmd, tmp_cmd_str); ret_cmd.read(tmp_cmd); } } void Scn2kMenu::activate(void) { if (pimpl != NULL && pimpl->menu) pimpl->menu->activate(); } void Scn2kMenu::deactivate(void) { if (pimpl != NULL && pimpl->menu) pimpl->menu->deactivate(); }