root/scn2k/scn2k_impl.cc

Revision 65:4416cfac86ae, 58.7 KB (checked in by Thibaut Girka <thib@…>, 18 months ago)
Convert EUC-JP files to UTF8
Line 
1/*
2 * Copyright (c) 2004-2006  Kazunori "jagarl" Ueno
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <stdexcept>
29#include "scn2k_impl.h"
30#include "system/file.h"
31#include "system/system_config.h"
32#include "window/picture.h"
33#include "window/system.h"
34
35// #define DEBUG 1
36
37using namespace std;
38
39/**********************************************
40** Scn2k
41*/
42
43void kconv(const unsigned char* src, unsigned char* dest);
44void kconv_rev(const unsigned char* src, unsigned char* dest);
45string kconv(const string& s);
46string kconv_rev(const string& s);
47
48Scn2k::Scn2k(Event::Container& _event, PicContainer& _parent) :
49        Event::Time(_event),
50        event(_event),
51        parent(_parent),
52        text_exec(_event, _parent),
53        grp_exec(_event, _parent, flag, flag.cgm_data)
54{
55        config = AyuSysConfig::GetInstance();
56
57        system_version = 0;
58        skip_mode = SKIP_NO;
59
60        script_start = NULL;
61        script = NULL;
62        script_end = NULL;
63
64        backlog_script_scn = -1;
65        backlog_script_start = NULL;
66        backlog_script_end = NULL;
67
68        save_scn = 0;
69        save_point = 0;
70        scn_number = 0;
71        scn_point = 0;
72        cmd_stack_str = cmd_stack_str_orig;
73
74        dialog = NULL;
75        dialog_type = CMD_NOP;
76        menu = NULL;
77        menu_mouseshown = false;
78
79        /* マりスカヌ゜ルを䜜成 */
80        mouse_type = 0;
81        mouse_surface = NULL;
82        mouse_pressed = 0;
83        ShowCursor();
84
85        LoadSys();
86        text_exec.InitWindow();
87        grp_exec.InitSel();
88}
89
90Scn2k::~Scn2k() {
91        if (script_start) delete[] script_start;
92        HideCursor();
93        SaveSys();
94}
95
96char* Scn2k::OpenScript(int new_scn_number, char*& end, int* call_vec, int& system_version) {
97        char fname[1024];
98        const char* data;
99        char* ret_data;
100        int offset = 0;
101        int scenario_magic;
102
103        FileSearcher* file_searcher = FileSearcher::GetInstance();
104
105        sprintf(fname, "SEEN%04d.TXT", new_scn_number);
106        ARCINFO* info = file_searcher->Find(FileSearcher::SCN, fname, "");
107        if (info == NULL) goto err;
108        data = info->Read();
109
110        /* version 確認 */
111        scenario_magic = read_little_endian_int(data + 4);
112        if (scenario_magic != 0x2712 && scenario_magic != 0x1adb2) {
113                fprintf(stderr,"Invalid scenario header : scenario number %d\n",new_scn_number);
114                goto err;
115        }
116        if (read_little_endian_int(data) == 0x1cc) {
117                system_version = 0;
118                offset = 0x1cc + read_little_endian_int(data+0x20) + 4;
119        } else if (read_little_endian_int(data) == 0x1d0) {
120                system_version = 1;
121                offset = read_little_endian_int(data + 0x20);
122        } else {
123                fprintf(stderr,"Invalid scenario header : scenario number %d\n",new_scn_number);
124                goto err;
125        }
126        /* header から subroutine number ずりだし */
127        if (call_vec) {
128                int i;
129                for (i=0; i<100; i++) {
130                        call_vec[i] = read_little_endian_int(data + 0x34 + i * 4);
131                }
132        }
133        ret_data = new char[info->Size() - offset + 1024];
134        memcpy(ret_data, data+offset, info->Size()-offset);
135        memset(ret_data+info->Size()-offset, 0, 1024);
136        end = ret_data + info->Size() - offset;
137        delete info;
138        return ret_data;
139
140err:
141        delete info;
142        fprintf(stderr,"Cannot open scenario number %d\n",new_scn_number);
143        throw std::invalid_argument("Scn2k::OpenScript");
144
145        return false;
146}
147
148bool Scn2k::ChangeScript(int new_scn_number, int call_no) {
149        int old_scn_number = scn_number;
150        int old_scn_pt = script - script_start;
151        int scn_pt = 0;
152
153        if (script_start) delete[] script_start;
154        script_start = NULL;
155        script = NULL;
156        script_end = NULL;
157
158        int call_vec[100];
159
160        try {
161                script_start = OpenScript(new_scn_number, script_end, call_vec, system_version);
162        } catch(...) {
163                fprintf(stderr,"\tFrom script %d pt %d\n",old_scn_number, old_scn_pt);
164                throw;
165        }
166        if (call_no > 0 && call_no < 100) {
167                scn_pt = call_vec[call_no];
168                if (scn_pt == 0) {
169                        fprintf(stderr,"Invalid subroutine number: scn %d sub %d\n",new_scn_number, call_no);
170                        scn_pt = 0;
171                }
172        } else if (call_no < 0) {
173                scn_pt = -call_no; // デバッグ甚
174        }
175
176        scn_number = new_scn_number;
177        scn_point = scn_pt;
178        script = script_start + scn_pt;
179        if (script < script_start || script >= script_end) 
180                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);
181        return true;
182}
183
184bool Scn2k::ReadCmdAt(Cmd& cmd, int scn, int pt) {
185        const char* d;
186        if (scn == scn_number) {
187                d = script_start + pt;
188                if (d < script_start || d >= script_end) {
189                        fprintf(stderr,"Cannot read script at current scn %d pt %d\n", scn, pt);
190                        return false;
191                }
192        } else {
193                if (backlog_script_scn != scn) {
194                        if (backlog_script_start) delete[] backlog_script_start;
195                        backlog_script_start = OpenScript(scn, backlog_script_end, 0, system_version);
196                }
197                d = backlog_script_start + pt;
198                if (d < backlog_script_start || d >= backlog_script_end) {
199                        fprintf(stderr,"Cannot read script at scn %d pt %d\n", scn, pt);
200                        return false;
201                }
202        }
203       
204        cmd.GetCmd(flag, d);
205        return true;
206}
207
208extern bool save_req, load_req; // キヌボヌドからセヌブ・ロヌドできるように
209extern bool pressAreq;
210
211void Scn2k::Elapsed(unsigned int current_time) {
212        SetWakeup(current_time + 10); // 10msに䞀回シナリオスクリプト解釈
213        if (script == NULL) return;
214//VarInfo info; info.type = 6; info.number = 0; // PB の「䞀回ゲヌムを開始したこずがある」フラグ
215//flag.Set(info,1);
216//info.type = 0; info.number = 604; // Princess Bride: クリア察象蚭定フラグ (聖)
217//flag.Set(info, 1);
218
219
220        Cmd cmd(flag, system_version);
221        int cnt1;
222        int cnt2 = 1000; // flag / jump / flag 系コマンドの最倧実行回数
223
224        /* XXX */
225        if (save_req) {
226                save_req = false;
227                load_req = false;
228                cmd.cmd_type = CMD_SAVEREQ;
229        } else if (load_req) {
230                load_req = false;
231                save_req = false;
232                cmd.cmd_type = CMD_LOADREQ;
233        }
234        if (pressAreq) {
235                pressAreq = false;
236                LoadRollback(cmd);
237                return;
238        }
239
240        /* キヌ入力などに察応 */
241        // メニュヌ内以倖で shift キヌが抌されたらスキップ開始
242        if ( (skip_mode&SKIP_IN_MENU) == 0) {
243                if (event.pressed(KEY_SHIFT)) {
244                        if (skip_mode & SKIP_TEXT) {
245                                ; // スキップ䞭ならなにもしない
246                        } else {
247                                SetSkipMode(SkipMode(SKIP_TEXT | SKIP_GRP_NOEFFEC | SKIPEND_KEY));
248                        }
249                } else {
250                        if ( skip_mode & SKIPEND_KEY) {
251                                if ( (skip_mode & SKIPEND_TEXT) && (skip_mode & SKIP_TEXT)) {
252                                        SkipMode new_skip_mode = SkipMode(skip_mode & (~SKIPEND_KEY));
253                                        if ( (new_skip_mode & SKIP_GRP_FAST) || (new_skip_mode & SKIP_GRP_NODRAW)) {
254                                                new_skip_mode = SkipMode(skip_mode & (~SKIP_GRP_NOEFFEC));
255                                        }
256                                        SetSkipMode(new_skip_mode);
257                                } else {
258                                        SetSkipMode(SKIP_NO);
259                                }
260                        }
261                }
262        }
263
264        for (cnt1=0; cnt1<20; cnt1++) { // 䞀回に぀き 20 個のコマンド実行
265                // 他のコマンド実行䞭なら終了
266                if ( (cmd.cmd_type == CMD_NOP && SysWait(cmd)) ||
267                     // (cmd.cmd_type == CMD_NOP && text_exec.Wait(current_time, cmd)) ||
268                     // (cmd.cmd_type == CMD_NOP && grp_exec.Wait(current_time, cmd))) {
269                     (cmd.cmd_type == CMD_NOP && grp_exec.Wait(current_time, cmd)) ||
270                     (cmd.cmd_type == CMD_NOP && text_exec.Wait(current_time, cmd))) {
271                        break;
272                }
273                // コマンド読み蟌み
274                for (; cnt2 > 0; cnt2--) {
275                        scn_point = script - script_start;
276                        eprintf("%d / %d :", script - script_start, script_end-script_start);
277// fprintf(stderr,"%d: %d / %d :",scn_number,  script - script_start, script_end-script_start);
278                        cmd.GetCmd(flag, script);
279//              if (cmd.cmd_type != CMD_NOP) {
280if (0) {
281                        fprintf(stderr,"%d / %d : 0x23 - cmd %02x-%02x:%04x:%02x[%2d] \n",
282                                scn_point, script_end-script_start,
283                                cmd.cmd1,cmd.cmd2,cmd.cmd3,cmd.cmd4,cmd.argc);
284                        int i;
285                        for (i = 0; i<cmd.args.size(); i++) {
286                                if (i == 0) fprintf(stderr,"\t");
287                                VarInfo info = cmd.args[i];
288                                if (info.type == TYPE_STR || info.type == TYPE_VARSTR)
289                                        fprintf(stderr,"\"%s\",", cmd.Str(info));
290                                else
291                                        fprintf(stderr,"%d,",info.value);
292                        }
293                        fprintf(stderr,"\n");
294                }
295                        cmd.scn = scn_number;
296                        cmd.pos = scn_point;
297                        if (cmd.IsError()) break;
298                        if (cmd.cmd_type == CMD_NOP) continue;
299                        if (cmd.cmd_type == CMD_JMP) {
300                                // local jump
301                                if (cmd.cmd1 == 0 && cmd.cmd2 == 1 && cmd.cmd3 == 16) {
302                                        int i;
303                                        for (i=0; i<cmd.args.size()-1; i++) {
304                                                VarInfo var;
305                                                var.type = 11;
306                                                var.number = i;
307                                                flag.Set(var, cmd.args[i].value);
308                                        }
309                                        cmd.args[0].value = cmd.args[i].value;
310                                }
311                                if ( cmd.cmd1 == 0 && cmd.cmd2 == 1 && (cmd.cmd3 == 5 || cmd.cmd3 == 8 || cmd.cmd3 == 16) ) { // local call / simple switch
312                                        int scn_pt = script - script_start;
313// fprintf(stderr,"\nlocal call %d:%d from %d\n",scn_number,cmd.args[0].value,scn_pt);
314                                        stack.push_back(StackItem(-1, scn_pt));
315                                }
316if (cmd.cmd1 == 0 && cmd.cmd2 == 1 && cmd.cmd3 == 1) {//TODO
317        fprintf(stderr,"***  unsupported: cond 1\n");
318}
319                                script = script_start + cmd.args[0].value;
320                                if (script < script_start || script >= script_end) {
321                                        fprintf(stderr,"scn %d pt %d: Cannot jump to %d; fall back to the top\n", scn_number, scn_point, cmd.args[0].value);
322                                        script = script_start;
323                                }
324                                cmd.clear();
325                                continue;
326                        }
327                        if (flag.Exec(cmd)) continue;
328                        break;
329                }
330                if (cmd.IsError()) {
331fprintf(stderr,"cmd error occured: scn %d pt %d / cur %d",scn_number,scn_point,script-script_start);
332                        while(script < script_end) {
333                                if (*script == 0x29 && script[1] == 0x0a) {script++;break;}
334                                if (*script == 0 && script[1] == 0x0a) {script++;break;}
335                                if (*script == 0 && script[1] == 0x23) {script++;break;}
336                                script++;
337fprintf(stderr," -> fall back to %d\n",script-script_start);
338                        }
339                        const char* dprev = script - 0x60;
340                        if (dprev < script_start) dprev = script_start;
341                        int ilen = (script-dprev+65)/16;
342                        int i; for (i=0; i<ilen; i++) {
343                                fprintf(stderr, "%6d: ",dprev-script_start);
344                                int j; for (j=0; j<16; j++) {
345                                        if (dprev >= script_end) break;
346                                        fprintf(stderr, "%02x ",*(unsigned char*)(dprev));
347                                        dprev++;
348                                }
349                                fprintf(stderr, "\n");
350                        }
351                        break;
352                }
353
354                if (cmd.cmd_type == CMD_NOP) continue;
355
356                if (cmd.cmd_type == CMD_TEXT && cmd.pos != -1) {
357                        set<int>& readflag = text_readflag[scn_number];
358                        if (readflag.find(cmd.pos) == readflag.end()) { // 未読テキスト発芋
359                                readflag.insert(cmd.pos);
360                                if (skip_mode & SKIPEND_TEXT) {
361                                        if (!(skip_mode & SKIPEND_KEY)) SetSkipMode(SKIP_NO);
362                                }
363                        }
364                }
365                text_exec.Exec(cmd);
366                grp_exec.Exec(cmd);
367                SysExec(cmd);
368                if (cmd.cmd_type == CMD_WAITFRAMEUPDATE) {
369                        SetWakeup(Event::Time::FRAME_UPDATE);
370                        break;
371                } else if (cmd.cmd_type != CMD_NOP) {
372#if DEBUG
373                        fprintf(stderr,"%d-%d / %d : unsupported command; 0x23 - cmd %02x-%02x:%04x:%02x[%2d] \n",
374                                cmd.scn, script - script_start, script_end-script_start,
375                                cmd.cmd1,cmd.cmd2,cmd.cmd3,cmd.cmd4,cmd.argc);
376                        int i;
377                        for (i = 0; i<cmd.args.size(); i++) {
378                                if (i == 0) fprintf(stderr,"\t");
379                                VarInfo info = cmd.args[i];
380                                if (info.type == TYPE_STR || info.type == TYPE_VARSTR)
381                                        fprintf(stderr,"\"%s\",", cmd.Str(info));
382                                else
383                                        fprintf(stderr,"%d,",info.value);
384                        }
385                        fprintf(stderr,"\n");
386#endif
387                        cmd.clear();
388                }
389        }
390}
391
392void Scn2k::ShowCursor(void) {
393        HideCursor();
394        char key[1024];
395        sprintf(key, "#MOUSE_CURSOR.%03d.NAME", mouse_type);
396        const char* name = config->GetParaStr(key);
397        if (name == NULL || name[0] == 0) mouse_surface = DEFAULT_MOUSECURSOR;
398        else
399                mouse_surface = parent.Root().NewSurface(name, COLOR_MASK);
400        if (mouse_surface == NULL)
401                mouse_surface = DEFAULT_MOUSECURSOR;
402        System::Main::SetCursor(mouse_surface, Rect(8, 8, 8+32, 8+32));
403}
404
405void Scn2k::HideCursor(void) {
406        if (mouse_surface) {
407                System::Main::SetCursor(0, Rect(0,0));
408                if (mouse_surface != DEFAULT_MOUSECURSOR)
409                        parent.Root().DeleteSurface(mouse_surface);
410                mouse_surface = NULL;
411        }
412}
413
414bool Scn2k::SysWait(Cmd& cmd) {
415        if (menu) {
416                menu->Exec(cmd);
417                if (menu->status & Scn2kMenu::MENU_DELETE || menu->pimpl == NULL) {
418                        delete menu;
419                        menu = NULL;
420                        if (! menu_mouseshown) HideCursor();
421                        else ShowCursor();
422                        SetSkipMode(SkipMode(skip_mode & (~SKIP_IN_MENU) ));
423                }
424                if (cmd.cmd_type == CMD_NOP) return true;
425                else return false; /* exec command */
426        }
427        return false;
428}
429
430void DllCall_LB(Cmd& cmd, Flags& flags);
431void Scn2k::SysExec(Cmd& cmd) {
432        if (cmd.cmd_type == CMD_SYSVAR) {
433                int i;
434                for (i=0; i<cmd.args.size(); i++) {
435                        if (cmd.args[i].type == TYPE_SYS) {
436                                if (cmd.args[i].number == TYPE_SYS_SYS) {
437                                        flag.SetSys(cmd.args[i].value);
438                                } else if (cmd.args[i].number == TYPE_SYS_SKIPMODE) {
439                                        SetSkipMode(SkipMode(cmd.args[i].value));
440                                }
441                        } else if (cmd.args[i].type == TYPE_VARSTR) {
442                                flag.SetStr(cmd.args[i].number, cmd.Str(cmd.args[i]));
443                        } else {
444                                flag.Set(cmd.args[i], cmd.args[i].value);
445                        }
446                }
447                cmd.clear();
448        }
449        if (cmd.cmd_type == CMD_SAVEPOINT || cmd.cmd_type == CMD_ROLLBACKPOINT) {
450                if (text_exec.backlog_item.scn != -1) {
451                        text_exec.backlog.push_back(text_exec.backlog_item);
452                        text_exec.backlog_item.Clear();
453                }
454                save_scn = scn_number;
455                save_point = scn_point;
456                if (!new_rollback_save.empty()) {
457                        rollback_save.push_back(new_rollback_save);
458                        new_rollback_save = "";
459                }
460                if (cmd.cmd_type == CMD_ROLLBACKPOINT) SaveRollback();
461                cmd.clear();
462        }
463        if (cmd.cmd_type == CMD_SAVEREQ || cmd.cmd_type == CMD_SAVE) {
464                Save(cmd);
465                return;
466        }
467        if (cmd.cmd_type == CMD_LOADREQ || cmd.cmd_type == CMD_LOAD) {
468                Load(cmd);
469                return;
470        }
471        if (cmd.cmd_type == CMD_BACKLOGREQ || cmd.cmd_type == CMD_BACKLOGREQ_FWD) {
472                if (menu) {
473                        fprintf(stderr,"BACKLOG_REQ requested!!!\n");
474                        return;
475                }
476                if (cmd.cmd_type == CMD_BACKLOGREQ_FWD) {
477                        cmd.clear(); // backlog mode 以倖で fwd を抌されおもなにもしない
478                        return;
479                }
480                SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで䞭断
481                menu = new Scn2kMenu(Scn2kMenu::MENU_BACKLOG, *this, flag, text_exec, system_version);
482                menu->InitPanel(event, parent);
483                menu->InitTitle(Scn2kSaveTitle(*this));
484                if (mouse_surface) menu_mouseshown = true;
485                else menu_mouseshown = false;
486                ShowCursor();
487                return;
488        }
489        if (cmd.cmd_type == CMD_MENUREQ) {
490                int scn=0, pt=0;
491                config->GetParam("#CANCELCALL", 2, &scn, &pt);
492                if (scn) {
493                        // 右クリックされたら global call を行う
494                        cmd.cmd_type = CMD_OTHER;
495                        cmd.cmd1 = 0;
496                        cmd.cmd2 = 1;
497                        cmd.cmd3 = 0x0c;
498                        cmd.cmd4 = 1;
499                        cmd.args.clear();
500                        cmd.args.push_back(VarInfo(SCN_INFO_MENU));
501                        cmd.args.push_back(0);
502                        SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで䞭断
503                }
504        }
505        if (cmd.cmd_type == CMD_SAVECMDGRP || cmd.cmd_type == CMD_SAVECMDGRP_START || cmd.cmd_type == CMD_SAVECMDGRP_ONCE || cmd.cmd_type == CMD_SAVECMD_ONCE) {
506                // 画像コマンド等はスタックに保存し、セヌブ時に保存できるようにする
507                if (cmd.cmd_type == CMD_SAVECMDGRP_START) {
508                        vector<CmdSimplified>::iterator it, cur;
509                        cur = cmd_stack.begin();
510                        cmd_stack_str = cmd_stack_str_orig;
511                        /* 画像関連コマンド以倖を別にする */
512                        for (it=cmd_stack.begin(); it != cmd_stack.end(); it++) {
513                                if (it->type != CMD_SAVECMDGRP && it->type != CMD_SAVECMDGRP_START && it->type != CMD_SAVECMDGRP_ONCE) {
514                                        cur->copy(*it, cmd_stack_str);
515                                        cur++;
516                                }
517                        }
518                        cmd_stack.erase(cur, cmd_stack.end());
519                }
520                if (cmd.cmd_type == CMD_SAVECMD_ONCE || cmd.cmd_type == CMD_SAVECMDGRP_ONCE) { // 同じコマンドがあれば削陀する
521                        vector<CmdSimplified>::iterator it;
522                        for (it = cmd_stack.end(); it != cmd_stack.begin(); ) {
523                                --it;
524                                if (it->cmd1 == cmd.cmd1 && it->cmd2 == cmd.cmd2 && it->cmd3 == cmd.cmd3 && it->cmd4 == cmd.cmd4) {
525                                        cmd_stack.erase(it);
526                                        break;
527                                }
528                        }
529                }
530                CmdSimplified cmd_item;
531                cmd.write(cmd_item, cmd_stack_str);
532                cmd_stack.push_back(cmd_item);
533                cmd.clear();
534                if (cmd_stack_str > cmd_stack_str_orig + 30000) { // char cmd_stack_str_orig[32768]
535                        fprintf(stderr,"Error in Scn2k::SysExec: too long cmdstack (%d): stack string overflow\n",cmd_stack.size());
536                        cmd_stack_str = cmd_stack_str_orig;
537                        cmd_stack.clear();
538                }
539        }
540        if (cmd.cmd_type != CMD_OTHER) return;
541        if (cmd.cmd1 == 0 && cmd.cmd2 == 1) {
542                if (cmd.cmd3 == 0x0b) { // global jump
543                    int call_no = 0;
544                    if (cmd.args.size() >= 2) call_no = cmd.args[1].value;
545                        eprintf("global jump to %d\n",cmd.args[0].value);
546                        if (! ChangeScript(cmd.args[0].value, call_no)) return; // 読み蟌めない; abort.
547                        cmd.clear();
548                } else if (cmd.cmd3 == 0x0c || cmd.cmd3 == 0x12) { // call (0x12 の方は埮劙)
549                        int new_scn = cmd.args[0].value;
550                        int new_pt = 0;
551                        if (cmd.args.size() >= 2) { // subroutine number が付く
552                                        // 匕数が付くのもあるらしい
553                                new_pt = cmd.args[1].value;
554                        }
555                        if (new_scn == SCN_INFO_MENU) { // menu call
556                                config->GetParam("#CANCELCALL", 2, &new_scn, &new_pt);
557                                stack.push_back(StackItem(SCN_INFO, SCN_INFO_MENU)); // menu call を瀺す特殊な蚘号
558                        } else {
559                                int i;
560                                VarInfo var;
561                                // ロヌカル倉数を䌎う subroutine call
562                                var.type = 11;
563                                var.number = 0;
564                                int saved_vars = 0;
565                                for (i=0; i<40; i++) {
566                                        int val = flag.Get(var.type, i);
567                                        if (val != 0) {
568                                                stack.push_back(StackItem(SCN_INFO_LOCALS + i, val));
569                                                saved_vars++;
570                                        }
571                                }
572                                var.type = TYPE_VARLOCSTR;
573                                for (i=0; i<3; i++) {
574                                        string s = flag.Str(var.type, i);
575                                        if (s.size()) {
576                                                int sp = stack_strbuffer.size();
577                                                stack.push_back(StackItem(SCN_INFO_LOCALSTR+i, sp));
578                                                stack_strbuffer.push_back(s);
579                                                saved_vars++;
580                                        }
581                                }
582                                stack.push_back(StackItem(SCN_INFO, SCN_INFO_LOCALS + saved_vars));
583                                       
584                                var.type = 11;
585                                var.number = 0;
586                                // 特殊な subroutine call なので、䜙蚈な情報を匕数に枡す
587                                for (i=2; i<cmd.args.size(); i++) {
588                                        flag.Set(var, cmd.args[i].value);
589// fprintf(stderr,"<%d:%d>=%d;",var.type,var.number,cmd.args[i].value);
590                                        var.number++;
591                                }
592// fprintf(stderr,"%d; ",stack.size());
593                        }
594                        int scn_pt = script - script_start;
595                        stack.push_back(StackItem(scn_number, scn_pt));
596// fprintf(stderr,"\nglobal call %d:%d from %d:%d\n",new_scn,new_pt,scn_number,scn_pt);
597                        eprintf("global call to %d, %d\n",new_scn, new_pt);
598                        if (! ChangeScript(new_scn, new_pt)) return; // 読み蟌めない; abort.
599                        cmd.clear();
600                } else if (cmd.cmd3 == 0x65) { // 文字列の返り倀をセットする
601                        int arg1 = cmd.args[0].value;
602                        string s = cmd.Str(cmd.args[1]);
603                        int sp = stack_strbuffer.size();
604                        stack.push_back(StackItem(SCN_INFO_RETSTR+arg1, sp));
605                        stack_strbuffer.push_back(s);
606                        cmd.clear();
607                } else if (cmd.cmd3 == 0x0d || cmd.cmd3 == 0x0a || cmd.cmd3 == 0x11 || cmd.cmd3 == 0x13) { // return (0a: local return) (0x13はよくわからない)
608// fprintf(stderr,"global return : stack size %d\n",stack.size());
609                        if (stack.empty()) {
610                                cmd.clear();
611                                return; // スタックがおかしいabort
612                        }
613                        map<int, string> retstr;
614                        while( (!stack.empty()) && stack.back().scn_number >= SCN_INFO_RETSTR) {
615                                int ret_num = stack.back().scn_number - SCN_INFO_RETSTR;
616// fprintf(stderr,"\nRetStr;");
617                                string str = stack_strbuffer.back();
618                                stack_strbuffer.pop_back();
619                                retstr[ret_num] = str;
620                                stack.pop_back();
621                        }
622                        if (stack.empty()) {
623                                cmd.clear();
624                                return; // スタックがおかしいabort
625                        }
626                        StackItem s = stack.back();
627                        stack.pop_back();
628                        bool localvar_init = false;
629                        while( (!stack.empty()) && stack.back().scn_number == SCN_INFO) {
630                                int mode = stack.back().scn_pt;
631                                stack.pop_back();
632                                if (mode == SCN_INFO_MENU) {
633// fprintf(stderr,"\nInfo Menu;");
634                                        // menu モヌド終了
635                                        SetSkipMode(SkipMode(skip_mode & (~SKIP_IN_MENU) ));
636                                } else if (mode >= SCN_INFO_LOCALS && mode <= SCN_INFO_LOCALS+50) {
637// fprintf(stderr,"\nInfo Local;");
638                                        int i;
639                                        // ロヌカル倉数を元に戻す
640                                        VarInfo var;
641                                        var.type = 11;
642                                        var.number = 0;
643                                        for (i=0; i<40; i++) {
644                                                var.number = i;
645                                                flag.Set(var, 0);
646                                        }
647                                        var.type = TYPE_VARLOCSTR;
648                                        for (i=0; i<3; i++) {
649                                                var.number = i;
650                                                flag.SetStr(var, "");
651                                        }
652                                        int args = mode - SCN_INFO_LOCALS;
653// fprintf(stderr," args = %d; ",args);
654                                        for (i=0; i<args; i++) {
655                                                if (stack.empty() || stack.back().scn_number < SCN_INFO) {
656                                                        fprintf(stderr,"Fatal : Invalid stack found in preserved local variables!\n");
657                                                        break;
658                                                }
659                                                var.number = stack.back().scn_number;
660// fprintf(stderr,"%d:%d; ",stack.back().scn_number,stack.back().scn_pt);
661                                                if (var.number >= SCN_INFO_LOCALS && var.number < SCN_INFO_LOCALSTR) {
662                                                        var.type = 11;
663                                                        var.number -=  SCN_INFO_LOCALS;
664                                                        flag.Set(var, stack.back().scn_pt);
665                                                } else if (var.number >= SCN_INFO_LOCALSTR && var.number < SCN_INFO_RETSTR) {
666                                                        var.type = TYPE_VARLOCSTR;
667                                                        var.number -= SCN_INFO_LOCALSTR;
668                                                        flag.SetStr(var, stack_strbuffer.back());
669                                                        stack_strbuffer.pop_back();
670                                                }
671                                                stack.pop_back();
672                                        }
673                                }
674// fprintf(stderr,"stack size %d string size %d\n",stack.size(),stack_strbuffer.size());
675                        }
676                        if (cmd.cmd3 == 0x11 || cmd.cmd3 == 0x13) {
677// fprintf(stderr,"\nSet RetLocal;");
678                                // 返り倀をセットする
679                                map<int,string>::iterator it;
680                                VarInfo var;
681                                var.type = TYPE_VARLOCSTR;
682                                for (it=retstr.begin(); it!=retstr.end(); it++) {
683                                        var.number = it->first;
684                                        flag.SetStr(var, it->second);
685                                }
686                                var.type = 11;
687// fprintf(stderr,"return : cmd.cmd3 == 0x11; size %d\n",cmd.args.size());
688                                if (cmd.args.size() == 1) {
689// fprintf(stderr,"return value %d\n",cmd.args[0].value);
690                                        flag.SetSys(cmd.args[0].value);
691                                } else {
692                                        int i;for (i=0; i<cmd.args.size(); i++) {
693                                                var.number = i;
694                                                flag.Set(var, cmd.args[i].value);
695                                        }
696                                }
697                        }
698// fprintf(stderr,"global return : return to %d:%d\n",s.scn_number,s.scn_pt);
699// fprintf(stderr,"\nglobal return %d:%d from %d:%d\n",s.scn_number,s.scn_pt,scn_number, script - script_start);
700                        if (s.scn_number != -1) {
701                                if (! ChangeScript(s.scn_number, 0)) return; // 読み蟌めない; abort.
702                        }
703                        script = script_start + s.scn_pt;
704                        cmd.clear();
705                }
706        } else if (cmd.cmd1 == 2 && cmd.cmd2 == 1 && cmd.cmd3 == 12) { // DLL Call
707                const char* regname = config->GetParaStr("#REGNAME");
708                const char key_lb[] = "KEY\\LittleBusters";//FIXME: too specific to be here?
709                if (strcmp(regname, key_lb) == 0) {
710                        DllCall_LB(cmd, flag);
711                        cmd.clear();
712                }
713        } else if (cmd.cmd1 == 0 && cmd.cmd2 == 0x04) { // メニュヌモヌド
714                if (cmd.cmd3 == 300 || cmd.cmd3 == 301 || cmd.cmd3 == 302) {
715                        // メニュヌからのreturn
716                        cmd.cmd2 = 1;
717                        cmd.cmd3 = 0x0d;
718                        SysExec(cmd);
719                }
720        } else if (cmd.cmd1 == 1 && cmd.cmd2 == 0x04) {
721                if (cmd.cmd3 == 0 && cmd.cmd4 == 0) { // タむトル名蚭定
722                        const char* name = cmd.Str(cmd.args[0]);
723                        if (name == NULL) name = "";
724                        window_title = name;
725                        const char* config_name = config->GetParaStr("#CAPTION");
726                        if (config_name == NULL) config_name = "";
727                        string setname = kconv(string(config_name) + "  " + window_title);
728                        parent.Root().SetWindowCaption(setname.c_str());
729                        cmd.clear();
730                } else if (cmd.cmd3 == 0x82 && cmd.cmd4 == 0) {
731                        /* cmd.cmd3 == 0x82 : マりスの press 状態クリアかも */
732                        event.presscount(MOUSE_LEFT);
733                        event.presscount(MOUSE_RIGHT);
734                        cmd.clear();
735                } else if (cmd.cmd3 == 0x85 && cmd.cmd4 == 0) {
736                        int x,y,left,right;
737                        event.MousePos(x,y);
738                        if (event.presscount(MOUSE_LEFT)) left = 2;
739                        else if (event.pressed(MOUSE_LEFT)) left = 1;
740                        else left = 0;
741                       
742                        if (event.presscount(MOUSE_RIGHT)) right = 2;
743                        else if (event.pressed(MOUSE_RIGHT)) right = 1;
744                        else right = 0;
745                       
746                        // eprintf("mouse pos\n");
747                        flag.Set(cmd.args[0], x);
748                        flag.Set(cmd.args[1], y);
749                        flag.Set(cmd.args[2], left);
750                        flag.Set(cmd.args[3], right);
751                        cmd.clear();
752                } else if (cmd.cmd3 == 0x15e || cmd.cmd3 == 0x161 || cmd.cmd3 == 0x162 || cmd.cmd3 == 0x14c || cmd.cmd3 == 0x7d1) {
753/* 15e, 161, 162, 14c, 7d1 : なんらかのシステム情報を返すskip modeなど */
754/* 7d1: == 1 || 14c: == 1 || (15e==1&&161==1&&162==0) || (press_val == 2) : スキップ䞭 タむトル画面のアニメヌション終了 */
755                        flag.SetSys(0);
756                        cmd.clear();
757                } else if (cmd.cmd3 == 0x4b0) { // 終了
758                        System::Main::Quit();
759                        //script = NULL; script_start = NULL; script_end = NULL;
760                        cmd.clear();
761                        cmd.cmd_type = CMD_WAITFRAMEUPDATE;
762                } else if (cmd.cmd3 == 0x4b4 || cmd.cmd3 == 0x4b5) { // 遞択肢巻き戻し
763                        LoadRollback(cmd);
764                } else if (cmd.cmd3 == 0x58d) {
765                        // 前にロヌド|セヌブされた番号を返す。
766                int lastsave;
767                config->GetParam("#LASTSAVE", 1, &lastsave);
768                flag.SetSys(lastsave-1);
769                } else if (cmd.cmd3 == 0x585) {
770                        // 第䞀匕数の蚘録された日付、タむトルなどが返される
771                        // デヌタがないなら sys に 0が、あるなら 1 が返る
772                        int y,m,d,wd,h,min,s,ms;
773                        string title;
774fprintf(stderr,"StatSave %d:",cmd.args[0].value+1);
775                        if (StatSaveFile(cmd.args[0].value+1,y,m,d,wd,h,min,s,ms,title) == true) {
776                                flag.Set(cmd.args[1], y);
777                                flag.Set(cmd.args[2], m);
778                                flag.Set(cmd.args[3], d);
779                                flag.Set(cmd.args[4], wd);
780                                flag.Set(cmd.args[5], h);
781                                flag.Set(cmd.args[6], min);
782                                flag.Set(cmd.args[7], s);
783                                flag.Set(cmd.args[8], ms);
784                                if (cmd.args[9].type == TYPE_VARSTR) {
785                                        flag.SetStr(cmd.args[9], kconv_rev(title));
786                                }
787                                flag.SetSys(1);
788                        } else {
789                                flag.SetSys(0);
790                        }
791                        cmd.clear();
792                } else if (cmd.cmd3 == 0xc23) { // save
793                        Save(cmd);
794                } else if (cmd.cmd3 == 0xc25) { // load
795                        Load(cmd);
796                } else if (cmd.cmd3 == 0x4b1 || cmd.cmd3 == 0x4b3) { // menu ぞ戻る (4b3: バッド゚ンド)
797                        int scn_start;
798                        if (config->GetParam("#SEEN_MENU", 1, &scn_start) == 0) {
799                                ChangeScript(scn_start, 0);
800                                save_scn = 0;
801                                save_point = 0;
802                                window_title = "";
803                                const char* window_title_config = config->GetParaStr("#CAPTION");
804                                if (window_title_config) window_title = window_title_config;
805                                parent.Root().SetWindowCaption(kconv(window_title).c_str());
806                                stack.clear();
807                                cmd_stack.clear();
808                                cmd_stack_str = cmd_stack_str_orig;
809                                flag.Load("");
810                                text_exec.Load("");
811                                grp_exec.Load("");
812                                SetSkipMode(SKIP_NO);
813                        }
814                } else if (cmd.cmd3 == 0xcc) {
815                        eprintf("show mouse cursor\n");
816                        ShowCursor();
817                        cmd.clear();
818                } else if (cmd.cmd3 == 0xcd) {
819                        eprintf("hide mouse cursor\n");
820                        HideCursor();
821                        cmd.clear();
822                } else if (cmd.cmd3 == 0xcf) {
823                        mouse_type = cmd.args[0].value;
824                        eprintf("change mouse cursor : %d\n", mouse_type);
825                        if (mouse_surface) ShowCursor();
826                        cmd.clear();
827                }
828        }
829
830}
831
832#include <sys/types.h>
833#include <sys/stat.h>
834#include <errno.h>
835#include <unistd.h>
836
837// セヌブファむルの名前を぀くる
838string Scn2k::MakeSaveFile(void) const {
839        struct stat sstatus;
840        string dir = "~/.xkanon";
841
842        if (dir.c_str()[0] == '~' && dir.c_str()[1] == '/') {
843                char* home = getenv("HOME");
844                if (home != NULL) {
845                        string new_dir = string(home) + (dir.c_str()+1);
846                        dir = new_dir;
847                }
848        }
849        // savepathにファむル名が入っおいれば、それをセヌブファむルずしお䜿う
850        if (stat(dir.c_str(), &sstatus) == -1) {
851                if (errno != ENOENT) {
852                        fprintf(stderr,"Cannot open save file; dir %s is not directory\n",dir.c_str());
853                        return "";
854                }
855                if (mkdir(dir.c_str(), S_IRWXU) != 0 && errno != EEXIST) {
856                        fprintf(stderr, "Cannot create directory %s ; Please create manually!!\n",dir.c_str());
857                }
858        } else {
859                if ( (sstatus.st_mode & S_IFMT) == S_IFREG) {
860                        return dir;
861                }
862        }
863        // ファむル名を䜜る
864        const char* regname = config->GetParaStr("#REGNAME");
865
866        char* fname = new char[strlen(regname)+1];
867        /* レゞストリ名をファむル名ずしお有効なものにする */
868        int i; for (i=0; regname[i]!=0; i++) {
869                char c = regname[i];
870                if (c == '\\' || c == '/' || c == ':' || c <= 0x20) c = '_';
871                fname[i] = tolower(c);
872        }
873        fname[i] = 0;
874        dir += "/save.";
875        dir += fname;
876        delete[] fname;
877        return dir;
878}
879
880// セヌブファむルの名前を぀くる
881string Scn2kSaveTitle::operator() (int number) const {
882        int y,m,d,wd,h,min,sec,msec;
883        string title;
884        if (! impl.StatSaveFile(number, y,m,d,wd,h,min,sec,msec,title)) {
885                return "";
886        } else {
887                char buf[1024];
888                sprintf(buf, "%2d/%2d %2d:%2d ",m,d,h,min);
889                return string(buf) + title;
890        }
891}
892
893void Scn2k::SaveSys(void) {
894        char buf[1024];
895        string save;
896        string path = MakeSaveFile();
897       
898        sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME"));
899        save += buf;
900        string save_config;
901        config->DiffOriginal(save_config);
902        save += "CONFIG=";
903        save += save_config;
904        save += "\n";
905        string save_flag; flag.SaveSys(save_flag);
906        save += save_flag;
907        string save_grp; grp_exec.SaveSys(save_grp);
908        save += save_grp;
909        map<int,set<int> >::iterator it;
910        save += "[TextRead]\n";
911        for (it=text_readflag.begin(); it != text_readflag.end(); it++) {
912                set<int>& read_flag = it->second;
913                set<int>::iterator jt;
914                char buf[1024];
915                sprintf(buf,"T<%05d>=",it->first);
916                string save_readflag = buf;
917                for (jt=read_flag.begin(); jt != read_flag.end(); jt++) {
918                        sprintf(buf, "%d,", *jt);
919                        save_readflag += buf;
920                }
921                save_readflag += "\n";
922                save += save_readflag;
923        }
924
925        path += ".0";
926        FILE* f = fopen(path.c_str(), "w");
927        if (f == NULL) {
928                fprintf(stderr,"Cannot open save file %s\n",path.c_str());
929                return;
930        }
931        fwrite(save.c_str(), save.length(), 1, f);
932        fclose(f);
933        return;
934}
935
936void Scn2k::LoadSys(void) {
937        char buf[1024];
938        string path = MakeSaveFile();
939        path += ".0";
940        FILE* f = fopen(path.c_str(), "r");
941        if (f == NULL) {
942                fprintf(stderr, "Cannot open save file %s\n",path.c_str());
943        } else {
944                fseek(f, 0, SEEK_END);
945                int sz = ftell(f);
946                fseek(f, 0, SEEK_SET);
947                char* savedata = new char[sz+1];
948                fread(savedata, sz, 1, f);
949                savedata[sz] = 0;
950                fclose(f);
951
952                sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME"));
953                if (strncmp(savedata, buf, strlen(buf)) != 0) {
954                        fprintf(stderr,"Invalid header in save file %s: it must be started with \"%s\"\n", path.c_str(), buf);
955                } else  {
956                        char* config_str = strstr(savedata, "\nCONFIG=");
957                        if (config_str) {
958                                config_str += strlen("\nCONFIG=");
959                                char* strend = strchr(config_str, '\n');
960                                if (strend) {
961                                        int l = strend - config_str;
962                                        char* config_copy = new char[l+1];
963                                        strncpy(config_copy, config_str, l);
964                                        config_copy[l] = 0;
965                                        config->PatchOriginal(config_copy);
966                                        delete[] config_copy;
967                                }
968                        }
969                        flag.LoadSys(savedata);
970                        grp_exec.LoadSys(savedata);
971                        char* save = strstr(savedata, "\n[TextRead]\n");
972                        if (save) {
973                                save += strlen("\n[TextRead]\n");
974                                do {
975                                        if (save[0] == '[') break; // next section
976                                        char* next_save = strchr(save, '\n');
977                                        if (next_save) {
978                                                *next_save++ = 0;
979                                        }
980                                        // T<XXXXX>=YYY,YYY,YYY,...
981                                        if (strncmp(save,"T<",2) == 0) {
982                                                int scn_num = atoi(save+2);
983                                                set<int>& read_flag = text_readflag[scn_num];
984                                                save += strlen("T<XXXXX>=");
985                                                while(save && *save) {
986                                                        if (save[0] >= '0' && save[0] <= '9') {
987                                                                int num = atoi(save);
988                                                                read_flag.insert(num);
989                                                        }
990                                                        save = strchr(save, ',');
991                                                        if (save) save++;
992                                                }
993                                        }
994                                        save = next_save;               
995                                } while(save);
996                        }
997
998                }
999                delete[] savedata;
1000        }
1001
1002        /* 初期化 */
1003        int scn_start; config->GetParam("#SEEN_START", 1, &scn_start);
1004        ChangeScript(scn_start, 0);
1005        save_scn = 0;
1006        save_point = 0;
1007        window_title = "";
1008        const char* window_title_config = config->GetParaStr("#CAPTION");
1009        if (window_title_config) window_title = window_title_config;
1010        parent.Root().SetWindowCaption(kconv(window_title).c_str());
1011        stack.clear();
1012        cmd_stack.clear();
1013        cmd_stack_str = cmd_stack_str_orig;
1014
1015        return;
1016}
1017
1018bool Scn2k::StatSaveFile(int num, int& year, int& month, int& day, int& wday, int& hour,int& min, int& sec, int& msec, string& title) const {
1019        char buf[1024];
1020        string path = MakeSaveFile();
1021        if (num <= 0 || num > 100) return false;
1022        sprintf(buf,".%d",num);
1023        path += buf;
1024
1025        struct stat sb;
1026        if (stat(path.c_str(), &sb) == -1) return false;
1027        struct tm* t = localtime(&sb.st_mtime);
1028        year = t->tm_year;
1029        month = t->tm_mon + 1;
1030        day = t->tm_mday;
1031        hour = t->tm_hour;
1032        min = t->tm_min;
1033        sec = t->tm_sec;
1034        msec = 0;
1035        /* タむトルの取埗 */
1036        FILE* savefile = fopen(path.c_str(), "rb");
1037        if (savefile == NULL) return false;
1038        char regname[1024];
1039        sprintf(regname, "KEY=%s\n", config->GetParaStr("#REGNAME"));
1040        fgets(buf,1000,savefile);
1041        if (strncmp(regname, buf, strlen(regname)) != 0) {
1042                fprintf(stderr,"invalid save file %s (registory name is not %s)\n",path.c_str(),regname);
1043                fclose(savefile);
1044                return false;
1045        }
1046        title="none";
1047        while(!feof(savefile)) {
1048                fgets(buf,1000,savefile);
1049                if (strncmp(buf,"Title=",6) == 0) {
1050                        if (buf[strlen(buf)-2] == 0x0a) buf[strlen(buf)-2] = 0;
1051                        if (strlen(buf) > 20) buf[20] = 0, buf[21] = 0;
1052                        title = kconv(buf+6);
1053                        break;
1054                }
1055        }
1056        fclose(savefile);
1057        return true;
1058}
1059
1060void Scn2k::SaveRollback(void) {
1061fprintf(stderr,"Save rollback\n");
1062        new_rollback_save = "";
1063        string save_sys; SaveImpl(save_sys);
1064        string save_flag; flag.Save(save_flag);
1065        string save_text; text_exec.Save(save_text, true);
1066        string save_grp; grp_exec.Save(save_grp);
1067        new_rollback_save += save_sys;
1068        new_rollback_save += save_flag;
1069        new_rollback_save += save_text;
1070        new_rollback_save += save_grp;
1071}
1072
1073void Scn2k::LoadRollback(Cmd& cmd) {
1074        if (rollback_save.empty()) return;
1075        new_rollback_save = "";
1076        string savedata = rollback_save.back();
1077        rollback_save.pop_back();
1078        LoadImpl(savedata.c_str());
1079        flag.Load(savedata.c_str());
1080        text_exec.Load(savedata.c_str());
1081        grp_exec.Load(savedata.c_str());
1082
1083        /* 画面の回埩など */
1084        SetSkipMode(SKIP_NO);
1085        vector<CmdSimplified>::iterator it;
1086        cmd.clear();
1087        for (it = cmd_stack.begin(); it != cmd_stack.end(); it++) {
1088                cmd.read(*it);
1089                cmd.cmd_type = CMD_OTHER;
1090                flag.Exec(cmd);
1091                text_exec.Exec(cmd);
1092                grp_exec.Exec(cmd);
1093        }
1094        cmd.clear();
1095        return;
1096}
1097
1098void Scn2k::Save(Cmd& cmd) {
1099        if (cmd.cmd_type == CMD_SAVEREQ) {
1100                if (menu == NULL) {
1101                        SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで䞭断
1102                        menu = new Scn2kMenu(Scn2kMenu::MENU_SAVE, *this, flag, text_exec, system_version);
1103                        menu->InitPanel(event, parent);
1104                        menu->InitTitle(Scn2kSaveTitle(*this));
1105                        if (mouse_surface) menu_mouseshown = true;
1106                        else menu_mouseshown = false;
1107                        ShowCursor();
1108                        return;
1109                }
1110        }
1111        char buf[1024];
1112        string save;
1113        FILE* f = NULL;
1114        if (save_scn == 0) {
1115                fprintf(stderr,"Cannot decide save point\n");
1116                return; // セヌブ䜍眮が保存されおない
1117        }
1118        string path = MakeSaveFile();
1119        int file_number = 1;
1120        if (cmd.args.size() == 1)
1121                file_number = cmd.args[0].value + 1;
1122        if (file_number <= 0) {
1123                fprintf(stderr, "Cannot open save file %s\n",path.c_str());
1124                return;
1125        }
1126        sprintf(buf, ".%d",file_number);
1127        path += buf;
1128
1129        /* セヌブファむル確認 */
1130       
1131        sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME")); save += buf;
1132        string save_sys; SaveImpl(save_sys);
1133        string save_flag; flag.Save(save_flag);
1134        string save_text; text_exec.Save(save_text, false);
1135        string save_grp; grp_exec.Save(save_grp);
1136        save += save_sys;
1137        save += save_flag;
1138        save += save_text;
1139        save += save_grp;
1140        vector<string>::iterator it;
1141        for (it=rollback_save.begin(); it != rollback_save.end(); it++) {
1142                save += "[Rollback Data]\n";
1143                save += *it;
1144                save += "[Rollback End]\n";
1145        }
1146
1147        f = fopen(path.c_str(), "w");
1148        if (f == NULL) {
1149                fprintf(stderr,"Cannot open save file %s\n",path.c_str());
1150                return;
1151        }
1152        fwrite(save.c_str(), save.length(), 1, f);
1153        fclose(f);
1154        config->SetParam("#LASTSAVE", 1, file_number);
1155        cmd.clear();
1156}
1157
1158void Scn2k::Load(Cmd& cmd) {
1159        if (cmd.cmd_type == CMD_LOADREQ) {
1160                if (menu == NULL) {
1161                        menu = new Scn2kMenu(Scn2kMenu::MENU_LOAD, *this, flag, text_exec, system_version);
1162                        menu->InitPanel(event, parent);
1163                        menu->InitTitle(Scn2kSaveTitle(*this));
1164                        SetSkipMode(SKIP_IN_MENU); // テキストスキップ等はここで䞭断
1165                        if (mouse_surface) menu_mouseshown = true;
1166                        else menu_mouseshown = false;
1167                        ShowCursor();
1168                        return;
1169                }
1170        }
1171        char buf[1024];
1172        string path = MakeSaveFile();
1173        int file_number = 1;
1174        if (cmd.args.size() == 1)
1175                file_number = cmd.args[0].value + 1;
1176        sprintf(buf, ".%d",file_number);
1177        path += buf;
1178        FILE* f = NULL;
1179        if (file_number > 0) f = fopen(path.c_str(), "r");
1180        if (f == NULL) {
1181                fprintf(stderr, "Cannot open save file %s\n",path.c_str());
1182                return;
1183        }
1184       
1185        fseek(f, 0, SEEK_END);
1186        int sz = ftell(f);
1187        fseek(f, 0, SEEK_SET);
1188        char* savedata = new char[sz+1];
1189        fread(savedata, sz, 1, f);
1190        savedata[sz] = 0;
1191        fclose(f);
1192
1193        sprintf(buf, "KEY=%s\n", config->GetParaStr("#REGNAME"));
1194        if (strncmp(savedata, buf, strlen(buf)) != 0) {
1195                fprintf(stderr,"Invalid header in save file %s: it must be started with \"%s\"\n", path.c_str(), buf);
1196                delete[] savedata;
1197                return;
1198        }
1199        LoadImpl(savedata);
1200        flag.Load(savedata);
1201        text_exec.Load(savedata);
1202        grp_exec.Load(savedata);
1203        rollback_save.clear();
1204        new_rollback_save = "";
1205        char* rollback_data = savedata;
1206        while( (rollback_data = strstr(rollback_data,"[Rollback Data]\n")) != NULL) {
1207                rollback_data += strlen("[Rollback Data]\n");
1208                char* rollback_end = strstr(rollback_data, "[Rollback End]\n");
1209                if (rollback_end == NULL) rollback_end = rollback_data + strlen(rollback_data);
1210                string s(rollback_data, rollback_end);
1211                rollback_save.push_back(s);
1212                rollback_data = rollback_end;
1213        }
1214
1215        /* 画面の回埩など */
1216        SetSkipMode(SKIP_NO);
1217        vector<CmdSimplified>::iterator it;
1218        for (it = cmd_stack.begin(); it != cmd_stack.end(); it++) {
1219                cmd.read(*it);
1220                cmd.cmd_type = CMD_OTHER;
1221                flag.Exec(cmd);
1222                text_exec.Exec(cmd);
1223                grp_exec.Exec(cmd);
1224        }
1225        cmd.clear();
1226
1227        delete[] savedata;
1228}
1229
1230void Scn2k::SaveImpl(string& save) {
1231        char buf[1024];
1232
1233        /* save point */
1234        sprintf(buf, "\n[SCENARIO]\nScn=%d\nPoint=%d\n",save_scn, save_point); save += buf;
1235        sprintf(buf, "Title=%s\nMouseType=%d\nMouseShown=1\n",window_title.c_str(), mouse_type); save += buf;
1236        vector<StackItem>::iterator sit;
1237        for (sit=stack.begin(); sit!=stack.end(); sit++) {
1238                if (sit->scn_number == SCN_INFO && sit->scn_pt == SCN_INFO_MENU) break; // メニュヌに入る盎前たでのスタックを保存
1239                sprintf(buf, "Stack=%d,%d\n",sit->scn_number,sit->scn_pt);
1240                save += buf;
1241        }
1242        vector<string>::reverse_iterator ssit;
1243        for (ssit=stack_strbuffer.rbegin(); ssit != stack_strbuffer.rend(); ssit++) {
1244                sprintf(buf, "StackStr=%s\n",ssit->c_str());
1245                save += buf;
1246        }
1247        vector<CmdSimplified>::iterator cit;
1248        for (cit=cmd_stack.begin(); cit != cmd_stack.end(); cit++) {
1249                if (cit->type == CMD_SAVECMDGRP || cit->type == CMD_SAVECMDGRP_ONCE || cit->type == CMD_SAVECMDGRP_START) {
1250                        save += "CmdG=";
1251                } else {
1252                        save += "Cmd=";
1253                }
1254                string s; cit->Save(s);
1255                save += s;
1256                save += "\n";
1257        }
1258}
1259
1260void Scn2k::LoadImpl(const char* save) {
1261        save_scn = 0;
1262        save_point = 0;
1263        window_title = "";
1264        stack.clear();
1265        cmd_stack.clear();
1266        cmd_stack_str = cmd_stack_str_orig;
1267
1268        save = strstr(save, "\n[SCENARIO]\n");
1269        if (save == NULL) return;
1270        save += strlen("\n[SCENARIO]\n");
1271        while(save[0] != 0 && save[0] != '[') { // while next section start
1272                if (strncmp(save, "Scn=", 4) == 0) {
1273                        sscanf(save, "Scn=%d", &save_scn);
1274                } else if (strncmp(save, "Point=", 6) == 0) {
1275                        sscanf(save, "Point=%d", &save_point);
1276                } else if (strncmp(save, "Title=", 6) == 0) {
1277                        save += 6;
1278                        const char* s = strchr(save, '\n');
1279                        if (s == NULL) window_title = save;
1280                        else window_title.assign(save, s-save);
1281                        const char* config_name = config->GetParaStr("#CAPTION");
1282                        if (config_name == NULL) config_name = "";
1283                        string setname = kconv(string(config_name)+"  "+window_title);
1284                        parent.Root().SetWindowCaption(setname.c_str());
1285                } else if (strncmp(save, "MouseType=", 10) == 0) {
1286                        sscanf(save, "MouseType=%d", &mouse_type);
1287                } else if (strncmp(save, "MouseShown=", 11) == 0) {
1288                        int v;
1289                        sscanf(save, "MouseShown=%d", &v);
1290                        if (v) ShowCursor();
1291                        else HideCursor();
1292                } else if (strncmp(save, "Stack=", 6) == 0) {
1293                        int scn, pt;
1294                        sscanf(save, "Stack=%d,%d", &scn, &pt);
1295                        stack.push_back( StackItem(scn, pt));
1296                } else if (strncmp(save, "StackStr=", 9) == 0) {
1297                        save += 9;
1298                        const char* s = strchr(save, '\n');
1299                        if (s == NULL) stack_strbuffer.push_back("");
1300                        else stack_strbuffer.push_back(string(save, s-save));
1301                } else if (strncmp(save, "Cmd=", 4) == 0) {
1302                        CmdSimplified cmd;
1303                        cmd.Load(save+4, cmd_stack_str);
1304                        cmd_stack.push_back(cmd);
1305                } else if (strncmp(save, "CmdG=", 5) == 0) {
1306                        CmdSimplified cmd;
1307                        cmd.Load(save+5, cmd_stack_str);
1308                        cmd.type = CMD_SAVECMDGRP;
1309                        cmd_stack.push_back(cmd);
1310                }
1311                save = strchr(save, '\n');
1312                if (save != NULL) save++;
1313        }
1314        ChangeScript(save_scn, 0);
1315        script = script_start + save_point;
1316}
1317
1318void Scn2k::SetSkipMode(SkipMode mode) {
1319        if (skip_mode != mode) {
1320                skip_mode = mode;
1321                text_exec.SetSkipMode(mode);
1322                grp_exec.SetSkipMode(mode);
1323        }
1324}
1325
1326/***********************************************************
1327**
1328**      DLL Call Implementation
1329**
1330**/
1331static double* lb_ef_param = 0;
1332void DLLCall_LB_EF00_0(Cmd& cmd, Flags& flags) { // ゚フェクトの蚭定
1333        if (lb_ef_param == 0) {
1334                lb_ef_param = new double[sizeof(double) * 0x60 * 8];
1335        }
1336        int i,j;
1337        int param_top, param_size;
1338        if (cmd.args[2].value == 1) {
1339                param_top = 0;
1340                param_size = 0x20;
1341        } else {
1342                param_top = cmd.args[3].value;
1343                param_size = cmd.args[4].value;
1344                if (param_top < 0) param_top = 0;
1345                if (param_top > 0x20) param_top = 0x20;
1346                if (param_size+param_top > 0x20) param_size = 0x20 - param_top;
1347        }
1348        for (i=0; i<8; i++) {
1349                double* param = lb_ef_param + i*0x60 + param_top*3;
1350                for (j=0; j<param_size; j++) { 
1351                        *param++ = random() % 800 - 400;
1352                        *param++ = random() % 600 - 300;
1353                        *param++ = random() % 700 - 350;
1354                }
1355        }
1356        if (cmd.args[5].value != 1) return;
1357
1358        static int random_dirtable[] = {
1359                0, 2, 1, 3, 0, 2, 1, 3,
1360                1, 3, 2, 0, 1, 3, 2, 0,
1361                0, 0, 0, 0, 3, 1, 2, 0,
1362                3, 1, 3, 1, 0, 2, 3, 1
1363        };
1364
1365        int* dir = &random_dirtable[(random()&3) * 8];
1366        for (i=0; i<8; i++) {
1367                double* param = lb_ef_param + i*0x60;
1368                double x = random()%600 - 300;
1369                double y = random()%480-240;
1370                if (x < 0) x -= 80;
1371                else x += 80;
1372                if (y < 0) y -= 80;
1373                else y += 80;
1374                switch(*dir++) {
1375                case 0:
1376                        if (x < 0) x = -x;
1377                        if (y < 0) y = -y;
1378                        break;
1379                case 1:
1380                        if (x > 0) x = -x;
1381                        if (y < 0) y = -y;
1382                        break;
1383                case 2:
1384                        if (x < 0) x = -x;
1385                        if (y > 0) y = -y;
1386                        break;
1387                case 4:
1388                        if (x > 0) x = -x;
1389                        if (y > 0) y = -y;
1390                        break;
1391                }
1392                param[9] = x*1.2;
1393                param[10] = y*1.2;
1394                param[11] *= 1.2;
1395                param[12] *= -0.08;
1396                param[13] *= -0.08;
1397                param[14] *= -0.08;
1398                param[15] = -param[9];
1399                param[16] = -param[10];
1400                param[17] = -param[11];
1401        }
1402        return;
1403}
1404
1405void DLLCall_LB_EF00_1(Cmd& cmd, Flags& flags) { // 蚈算を行う
1406        if (lb_ef_param == 0) {
1407                fprintf(stderr,"Warning : DLLCall_LB_EF00_1 : Script error : effect calculation was called before setting\n");
1408                return;
1409        }
1410        int index = cmd.args[2].value;
1411        int v5_1154 = flags.Get(5, 1154+index);
1412        int j = ((v5_1154) & 0x1f) + index * 0x20;
1413        int k = ((v5_1154+1) & 0x1f) + index * 0x20;
1414        int l = ((v5_1154+2) & 0x1f) + index * 0x20;
1415        int m = ((v5_1154+3) & 0x1f) + index * 0x20;
1416        j *= 3;
1417        k *= 3;
1418        l *= 3;
1419        m *= 3;
1420
1421        // 0 < x < 1
1422        // va - vd は 0-1 の範囲で察称性を持぀次関数
1423        double x = double(flags.Get(5, 1162 + index)) * 0.001;
1424        double va = (x * x * x)/6;
1425        double vb = (-x*x*x + 3*x*x - 3*x + 1) / 6;
1426        double vc = (3*x*x*x - 6*x*x + 4) / 6;
1427        double vd = (-3*x*x*x+3*x*x+3*x+1) / 6;
1428
1429        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];
1430        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];
1431        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];
1432        if (r1 != 400) {
1433                r2 = r2 * 800 / (400-r1);
1434                r3 = r3 * 700 / (400-r1);
1435        }
1436        VarInfo var;
1437        var.type = 5;
1438        var.number = 1151;
1439        flags.Set(var, int(r2));
1440        var.number = 1152;
1441        flags.Set(var, int(r3));
1442        var.number = 1153;
1443        flags.Set(var, int(r1));
1444        return;
1445}
1446
1447
1448void DllCall_LB(Cmd& cmd, Flags& flags) {       // リトルバスタヌズの EF00.dll を゚ミュレヌト
1449        if (cmd.args[0].value == 1) {
1450                // "EF00.dll"
1451                if (cmd.args[1].value == 0) { // ゚フェクトの蚭定
1452                        DLLCall_LB_EF00_0(cmd, flags);
1453                } else if (cmd.args[1].value == 1) { // 蚈算を行う
1454                        DLLCall_LB_EF00_1(cmd, flags);
1455                }
1456        } else {
1457                fprintf(stderr,"Unsupported DLL call for DLL<%d>\n",cmd.args[0].value);
1458        }
1459        return;
1460}
1461
1462/**********************************************************
1463**
1464**      MenuImpl
1465**
1466*/
1467
1468#include "window/widget.h"
1469#include "window/menuitem.h"
1470
1471void DSurfaceFill(Surface* src, const Rect& rect, int r, int g, int b, int a = 0xff);
1472
1473struct Scn2kMenuImpl {
1474        Scn2kMenu& interface;
1475        MenuItem* menu;
1476        Event::Container* pevent;
1477        PicContainer* pparent;
1478
1479        virtual void InitPanel(Event::Container& event, PicContainer& parent) = 0;
1480        virtual void InitTitle(const SaveTitle&) = 0;
1481        virtual void Cancel(void) = 0;
1482        virtual void Exec(Cmd& cmd) = 0;
1483        Scn2kMenuImpl(Scn2kMenu& _interface) : interface(_interface) {
1484                menu = NULL;
1485                pevent = NULL;
1486                pparent = NULL;
1487        }
1488        virtual ~Scn2kMenuImpl() {
1489                if (menu) delete menu;
1490                menu = NULL;
1491        }
1492};
1493
1494struct LoadMenu : Scn2kMenuImpl {
1495        vector<string> title;
1496        vector<int> title_valid;
1497        RadioButton* btn_local;
1498        RadioButton* btn_page;
1499        RadioButton* btn_set;
1500        Scale* btn_scale;
1501        Dialog* awk_dialog;
1502        int btn_page_val, btn_set_val, btn_local_val, select_page, select_value;
1503        LoadMenu(Scn2kMenu& _interface);
1504        ~LoadMenu();
1505        void InitPanel(Event::Container& event, PicContainer& parent);
1506        void InitTitle(const SaveTitle&);
1507        void Cancel(void);
1508        void Exec(Cmd& cmd);
1509        static void ChangeBtnPage(void* pointer, MenuItem* widget);
1510        static void ChangeBtnLocal(void* pointer, MenuItem* widget);
1511        static void ChangeBtnScale(void* pointer, Scale* widget);
1512        static void ChangeBtnSet(void* pointer, MenuItem* widget);
1513        static void ChangeDialog(void* pointer, Dialog* widget);
1514        bool in_setpage;
1515        void SetPage(int new_page);
1516        void SetValue(int new_value);
1517        void PressOk(void);
1518};
1519LoadMenu::LoadMenu(Scn2kMenu& _interface) : Scn2kMenuImpl(_interface) {
1520        btn_local = NULL;
1521        btn_scale = NULL;
1522        btn_set = NULL;
1523        btn_page_val = 0;
1524        btn_set_val = -1;
1525        btn_local_val = -1;
1526        awk_dialog = NULL;
1527        in_setpage = false;
1528        select_page = 0;
1529        select_value = -1;
1530}
1531LoadMenu::~LoadMenu() {
1532        if (awk_dialog) delete awk_dialog;
1533}
1534void LoadMenu::InitPanel(Event::Container& event, PicContainer& parent) {
1535        pevent = &event;
1536        pparent = &parent;
1537
1538        if (menu) delete menu;
1539        menu = NULL;
1540        menu = new MenuItem(&parent, Rect(80,30,560, 450), 1, 3, 0);
1541        Surface* surface = parent.Root().NewSurface(menu->Pic()->Width(), menu->Pic()->Height(), ALPHA_MASK);
1542        if (interface.type == Scn2kMenu::MENU_LOAD) {
1543                menu->SetLabelTop(new Label(menu->PicNode(), Rect(0,0), true, "Load", 26), Rect(0,0,10,0), Rect(0,0,0,20));
1544                DSurfaceFill(surface, Rect(*surface), 0, 0, 0x80, 0x80);
1545        } else {
1546                menu->SetLabelTop(new Label(menu->PicNode(), Rect(0,0), true, "Save", 26), Rect(0,0,10,0), Rect(0,0,0,20));
1547                DSurfaceFill(surface, Rect(*surface), 0, 0x80, 0, 0x80);
1548        }
1549        menu->Pic()->SetSurface(surface, 0, 0);
1550        menu->Pic()->SetSurfaceFreeFlag();
1551
1552        btn_page = new RadioButton(event, menu->PicNode(), Rect(0, 0, 480, 40), 10, 1, &btn_page_val,
1553                Rect(0,0,0,0), 18, Color(0,0,0),Color(0xff,0,0),Color(0xff,0x80,0));
1554        btn_page->set_func = &ChangeBtnPage;
1555        btn_page->set_pointer = this;
1556        btn_page->SetLabelLeft(new Label(btn_page->PicNode(), Rect(0,0), true, "Page", 18), Rect(0, 0, 180, 0), Rect(0,0));
1557        btn_page->Add(" 1 ");
1558        btn_page->Add(" 2 ");
1559        btn_page->Add(" 3 ");
1560        btn_page->Add(" 4 ");
1561        btn_page->Add(" 5 ");
1562        btn_page->Add(" 6 ");
1563        btn_page->Add(" 7 ");
1564        btn_page->Add(" 8 ");
1565        btn_page->Add(" 9 ");
1566        btn_page->Add(" 10 ");
1567        btn_page->pack();
1568/*
1569        surface = parent.Root().NewSurface(btn_page->Pic()->Width(), btn_page->Pic()->Height(), ALPHA_MASK);
1570        DSurfaceFill(surface, Rect(*surface), 0xff, 0, 0, 0x80);
1571        btn_page->Pic()->SetSurface(surface, 0, 0);
1572        btn_page->Pic()->SetSurfaceFreeFlag();
1573*/
1574        menu->item[0] = btn_page;
1575        btn_set = new RadioButton(event, menu->PicNode(), Rect(0, 0, 480, 40), 2, 1, &btn_set_val,
1576                Rect(0,0,0,0), 18, Color(0,0,0),Color(0xff,0,0),Color(0xff,0x80,0));
1577        btn_set->set_func = &ChangeBtnSet;
1578        btn_set->set_pointer = this;
1579        btn_set->SetLabelLeft(new Label(btn_set->PicNode(), Rect(0,0)), Rect(0,0,200,0), Rect(0,0));
1580        if (interface.type == Scn2kMenu::MENU_LOAD) {
1581                btn_set->Add(" Load ");
1582        } else {
1583                btn_set->Add(" Save ");
1584        }
1585        btn_set->Add(" Cancel ");
1586        btn_set->pack();
1587/*
1588        surface = parent.Root().NewSurface(btn_set->Pic()->Width(), btn_set->Pic()->Height(), ALPHA_MASK);
1589        DSurfaceFill(surface, Rect(*surface), 0, 0, 0xff, 0x80);
1590        btn_set->Pic()->SetSurface(surface, 0, 0);
1591        btn_set->Pic()->SetSurfaceFreeFlag();
1592*/
1593        menu->item[2] = btn_set;
1594        // void btn_set_press(void* pointer, MenuItem* widget);
1595        // btn_set->set_func = btn_set_press;
1596        // btn_set->set_pointer = this;
1597        btn_local = new RadioButton(*pevent, menu->PicNode(), Rect(0, 0, 480, 300), 1, 100, &btn_local_val,
1598                Rect(0,0,300,30), 18, Color(0,0,0),Color(0xff,0,0),Color(0xff,0x80,0));
1599        btn_local->set_func = &ChangeBtnLocal;
1600        btn_local->set_pointer = this;
1601/*
1602        surface = pparent->Root().NewSurface(btn_local->Pic()->Width(), btn_local->Pic()->Height(), ALPHA_MASK);
1603        DSurfaceFill(surface, Rect(*surface), 0, 0xff, 0, 0x80);
1604        btn_local->Pic()->SetSurface(surface, 0, 0);
1605        btn_local->Pic()->SetSurfaceFreeFlag();
1606*/
1607        menu->item[1] = btn_local;
1608        int i;
1609        for (i=0; i<12; i++)
1610                btn_local->Add("",false);
1611        btn_local->pack();
1612        btn_local->show_all();
1613        menu->pack();
1614
1615        PicBase* local_pic = btn_local->Pic();
1616        int local_x2 = local_pic->PosX() + local_pic->Width();
1617        int local_y2 = local_pic->PosY() + local_pic->Height();
1618        btn_scale = new Scale(*pevent, menu->PicNode(), Rect(local_x2-16, local_pic->PosY(), local_x2, local_y2), Color(0xff, 0x80, 0), true);
1619        btn_scale->SetRange(0, 900);
1620        btn_scale->InitCursor(1024/10);
1621        btn_scale->SetValue(0);
1622        btn_scale->change_func = &ChangeBtnScale;
1623        btn_scale->change_pointer = this;
1624
1625        menu->PicNode()->show_all();
1626}
1627
1628void LoadMenu::InitTitle(const SaveTitle& title_op) {
1629        title.clear();
1630        int i;
1631        for (i=1; i<=100; i++) {
1632                char buf[100];
1633                sprintf(buf,"%2d:",i);
1634                string t = title_op(i);
1635                string s = string(buf) + t;
1636                if (t.length() == 0) {
1637                        string s = string(buf) + "--------";
1638                        title_valid.push_back(0);
1639                } else {
1640                        title_valid.push_back(1);
1641                }
1642                title.push_back(s);
1643        }
1644        if (btn_local==0) return;
1645        for (i=0; i<10; i++) {
1646                TextButton* button = dynamic_cast<TextButton*>(btn_local->item[i]);
1647                if (button) button->SetText(title[i].c_str());
1648        }
1649}
1650
1651void LoadMenu::SetPage(int new_page) {
1652        if (new_page < 0) new_page = 0;
1653        if (new_page > 900) new_page = 900;
1654        if (select_page == new_page) return;
1655        if (in_setpage) return;
1656        in_setpage = true;
1657       
1658        int prev_page = select_page / 10;
1659        int cur_page = new_page / 10;
1660        int prev_point = select_page%10;
1661        int new_point = new_page%10;
1662        select_page = new_page;
1663        if (prev_page != cur_page) {
1664                int i;
1665                for (i=0; i<12; i++) {
1666                        TextButton* button = dynamic_cast<TextButton*>(btn_local->item[i]);
1667                        if (button) {
1668                                if (cur_page+i < title.size()) button->SetText(title[cur_page+i].c_str());
1669                                else button->SetText("----");
1670                        }
1671                }
1672                // ボタンの内容を倉曎する
1673                if (select_value < cur_page || select_value > cur_page+12)
1674                        btn_local->SetValue(-1);
1675                else
1676                        btn_local->SetValue(select_value - cur_page);
1677        }
1678        if (prev_point != new_point) {
1679                int i;
1680                for (i=0; i<12; i++) {
1681                        int old_x = btn_local->item[i]->Pic()->PosX();
1682                        btn_local->item[i]->Pic()->Move(old_x, i*30-new_point*3);
1683                }
1684        }
1685        if (btn_page != NULL) {
1686                if (select_page%100 == 0) btn_page->SetValue(select_page/100);
1687                else btn_page->SetValue(-1);
1688        }
1689        if (btn_scale != NULL) {
1690                btn_scale->SetValue(select_page);
1691        }
1692        in_setpage = false;
1693}
1694
1695void LoadMenu::SetValue(int new_value) {
1696        if (in_setpage) return;
1697        in_setpage = true;
1698
1699        if (new_value < 0 || new_value > title.size() ||
1700            (interface.type == Scn2kMenu::MENU_LOAD && title_valid[new_value] == 0) ) { // 無効な遞択肢
1701                if (select_value < select_page/10 || select_value > select_page/10+12)
1702                        btn_local->SetValue(-1);
1703                else
1704                        btn_local->SetValue(select_value-select_page/10);
1705        } else { // 遞択肢を倉曎する
1706                if (select_value == new_value) {
1707                        PressOk(); // ダブルクリック
1708                } else {
1709                        select_value = new_value;
1710                        if (interface.type == Scn2kMenu::MENU_SAVE && title_valid[select_value] == 0) {
1711                                PressOk(); // 新しいセヌブデヌタなら無条件に遞択
1712                        }
1713                }
1714        }
1715
1716        in_setpage = false;
1717}
1718
1719void LoadMenu::PressOk(void) {
1720        if (select_value == -1) {
1721                btn_set->SetValue(-1); // なにもしない
1722                return;
1723        }
1724        menu->deactivate();
1725        if (interface.type == Scn2kMenu::MENU_LOAD) {
1726                interface.cmd.cmd_type = CMD_LOAD;
1727                interface.cmd.args.push_back(VarInfo(select_value));
1728                awk_dialog = new Dialog(*pevent, pparent, "ファむルをロヌドしたすか", true);
1729                awk_dialog->set_pointer = this;
1730                awk_dialog->set_func = ChangeDialog;
1731        } else {// MENU_SAVE
1732                interface.cmd.cmd_type = CMD_SAVE;
1733                interface.cmd.args.push_back(VarInfo(select_value));
1734                if (title_valid[select_value] == 0) { // 新しいセヌブデヌタ
1735                        interface.status = Scn2kMenu::MenuStatus(Scn2kMenu::MENU_CMD | Scn2kMenu::MENU_DELETE);
1736                } else { // セヌブデヌタを䞊曞き確認
1737                        awk_dialog = new Dialog(*pevent, pparent, "デヌタを䞊曞きしたすか", true);
1738                        awk_dialog->set_pointer = this;
1739                        awk_dialog->set_func = ChangeDialog;
1740                }
1741        }
1742}
1743
1744void LoadMenu::Cancel(void) {
1745        if (awk_dialog != NULL) { // ダむアログのキャンセル
1746                awk_dialog->status = Dialog::CANCEL;
1747                ChangeDialog(this, awk_dialog);
1748        } else { // 䞀般キャンセル
1749                btn_set->SetValue(1);
1750        }
1751}
1752
1753void LoadMenu::Exec(Cmd& cmd) {
1754}
1755
1756void LoadMenu::ChangeBtnPage(void* pointer, MenuItem* widget) {
1757        LoadMenu* instance = (LoadMenu*)pointer;
1758        if (instance->btn_page_val == -1) return;
1759        instance->SetPage(instance->btn_page_val*100);
1760}
1761
1762void LoadMenu::ChangeBtnScale(void* pointer, Scale* from) {
1763        LoadMenu* instance = (LoadMenu*)pointer;
1764        int value = from->GetValue();
1765        instance->SetPage(value);
1766}
1767
1768void LoadMenu::ChangeBtnSet(void* pointer, MenuItem* widget) {
1769        LoadMenu* instance = (LoadMenu*)pointer;
1770        if (instance->btn_set_val == 1) { // cancel
1771                instance->interface.status = Scn2kMenu::MENU_DELETE;
1772                return;
1773        } else if (instance->btn_set_val == 0) { // OK
1774                instance->PressOk();
1775        }
1776}
1777
1778void LoadMenu::ChangeDialog(void* pointer, Dialog* widget) {
1779        LoadMenu* instance = (LoadMenu*)pointer;
1780        if (widget->status == Dialog::CANCEL) {
1781                // ダむアログ消去、OK ボタン埩垰
1782                delete instance->awk_dialog;
1783                instance->awk_dialog = NULL;
1784                instance->menu->activate();
1785                instance->btn_set->SetValue(-1);
1786                return;
1787        } else if (widget->status == Dialog::OK) {
1788                instance->interface.status = Scn2kMenu::MenuStatus(Scn2kMenu::MENU_CMD | Scn2kMenu::MENU_DELETE);
1789                return;
1790        }
1791}
1792
1793void LoadMenu::ChangeBtnLocal(void* pointer, MenuItem* widget) {
1794        LoadMenu* instance = (LoadMenu*)pointer;
1795        if (instance->btn_local_val == -1) return;
1796        instance->SetValue( (instance->select_page/10) + instance->btn_local_val);
1797}
1798
1799struct BacklogMenu : Scn2kMenuImpl {
1800        Scn2k& scn_impl;
1801        Text& text_exec;
1802        bool backlog_update;
1803        int backlog_cnt;
1804        BacklogMenu(Scn2kMenu& _interface, Scn2k& scn_impl, Text& text_exec);
1805        ~BacklogMenu();
1806        void InitPanel(Event::Container& event, PicContainer& parent);
1807        void InitTitle(const SaveTitle&);
1808        void Cancel(void);
1809        void Exec(Cmd& cmd);
1810};
1811
1812BacklogMenu::BacklogMenu(Scn2kMenu& _interface, Scn2k& _scn, Text& parent_text_exec) : Scn2kMenuImpl(_interface), scn_impl(_scn), text_exec(parent_text_exec) {
1813        backlog_cnt = -1;
1814        backlog_update = false;
1815}
1816
1817BacklogMenu::~BacklogMenu() {
1818}
1819
1820void BacklogMenu::InitPanel(Event::Container& event, PicContainer& parent) {
1821        pevent = &event;
1822}
1823
1824void BacklogMenu::InitTitle(const SaveTitle& title_op) {
1825}
1826
1827void BacklogMenu::Cancel(void) {
1828        interface.status = Scn2kMenu::MenuStatus(Scn2kMenu::MENU_DELETE);
1829}
1830
1831void BacklogMenu::Exec(Cmd& cmd) {
1832        int command_direction = 0; // forward
1833        if (cmd.cmd_type == CMD_NOP) text_exec.Wait(0xffffffffUL, cmd);
1834        if (cmd.cmd_type == CMD_BACKLOGREQ || pevent->presscount(MOUSE_UP)) {
1835                if (cmd.cmd_type == CMD_BACKLOGREQ) cmd.clear();
1836                backlog_cnt++;
1837                backlog_update = false;
1838                command_direction = 1;
1839        }
1840        if (cmd.cmd_type == CMD_BACKLOGREQ_FWD || pevent->presscount(MOUSE_DOWN)) {
1841                if (cmd.cmd_type == CMD_BACKLOGREQ_FWD) cmd.clear();
1842                backlog_cnt--;
1843                backlog_update = false;
1844                if (backlog_cnt == -2 || (
1845                   (backlog_cnt == -1 && text_exec.backlog_item.scn == -1 && text_exec.backlog_item.pos == -1)) ){
1846                        Cancel();
1847                        return;
1848                }
1849                command_direction = -1;
1850        }
1851        if (cmd.cmd_type != CMD_NOP) return;
1852        if (backlog_update) return;
1853        // backlog を最新の状態に曎新
1854        cmd.clear();
1855        BacklogItem item;
1856
1857retry:
1858        if (backlog_cnt < -1) backlog_cnt = -1;
1859        if (backlog_cnt >= int(text_exec.backlog.size())) backlog_cnt = text_exec.backlog.size() - 1;
1860
1861        if (backlog_cnt == -1) {
1862                if (text_exec.backlog_item.scn == -1 && text_exec.backlog_item.pos == -1) {
1863                        if (text_exec.backlog.size() == 0 || command_direction < 0) {
1864                                Cancel();
1865                                return;
1866                        }
1867                        item = text_exec.backlog.back();
1868                        backlog_cnt = 0;
1869                } else {
1870                        // item = text_exec.backlog.back();
1871                        item = text_exec.backlog_item;
1872                }
1873        } else {
1874                item = text_exec.backlog[text_exec.backlog.size()-1-backlog_cnt];
1875        }
1876        if (item.scn ==  BacklogItem::SaveSelect) { // select marker ; skip this item
1877                if (command_direction == 0) command_direction = 1;
1878                backlog_cnt += command_direction;
1879                goto retry;
1880        }
1881        if (item.scn == 0 && item.pos == -1) ; // not read cmd
1882        else {
1883                scn_impl.ReadCmdAt(cmd, item.scn, item.pos);
1884        }
1885        text_exec.DrawBacklog(item, cmd);
1886        cmd.clear();
1887        backlog_update = true;
1888}
1889
1890/*******************************************************************************
1891**
1892**
1893*/
1894
1895Scn2kMenu::Scn2kMenu(MenuType _type, Scn2k& scn_impl, const Flags& flags, Text& text_exec, int system_version) :
1896        cmd(flags, system_version), type(_type) {
1897        pimpl = NULL;
1898        status = MENU_CONTINUE;
1899        switch(type) {
1900        case MENU_LOAD: pimpl = new LoadMenu(*this); break;
1901        case MENU_SAVE: pimpl = new LoadMenu(*this); break;
1902        case MENU_BACKLOG: pimpl = new BacklogMenu(*this, scn_impl, text_exec); break;
1903        }
1904}
1905
1906Scn2kMenu::~Scn2kMenu() {
1907        if (pimpl) delete pimpl;
1908        pimpl = NULL;
1909}
1910
1911void Scn2kMenu::InitPanel(Event::Container& event, PicContainer& parent) {
1912        if (pimpl != NULL) pimpl->InitPanel(event, parent);
1913}
1914
1915void Scn2kMenu::InitTitle(const SaveTitle& t) {
1916        if (pimpl != NULL) pimpl->InitTitle(t);
1917}
1918
1919void Scn2kMenu::Cancel(void) {
1920        if (pimpl) pimpl->Cancel();
1921}
1922
1923void Scn2kMenu::Exec(Cmd& ret_cmd) {
1924        if (pimpl == NULL) return;
1925        pimpl->Exec(ret_cmd);
1926        if (pimpl->pevent->presscount(MOUSE_RIGHT)) {
1927                Cancel();
1928        }
1929        if (status & MENU_CMD && cmd.cmd_type != CMD_NOP) {
1930                status = Scn2kMenu::MenuStatus(status & (~Scn2kMenu::MENU_CMD) );
1931                CmdSimplified tmp_cmd;
1932                char cmd_str[32768];
1933                char* tmp_cmd_str = cmd_str;
1934                cmd.write(tmp_cmd, tmp_cmd_str);
1935                ret_cmd.read(tmp_cmd);
1936        }
1937}
1938
1939void Scn2kMenu::activate(void) {
1940        if (pimpl != NULL && pimpl->menu) pimpl->menu->activate();
1941}
1942
1943void Scn2kMenu::deactivate(void) {
1944        if (pimpl != NULL && pimpl->menu) pimpl->menu->deactivate();
1945}
1946
Note: See TracBrowser for help on using the browser.