Files
kichikuou_xsystem35-sdl2/src/scenario.c
T
kichikuou aec06f775a Support the save format of original System3.x (except System3.5 v1)
Now xsystem35 can load ASD files created by System3.5 (v2+) - System3.9.
The format for saving can be configured with the -saveformat option.
2023-02-23 19:03:23 +09:00

764 lines
17 KiB
C

/*
* scenario.c シナリオ管理実行
*
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
* 1998- <masaki-c@is.aist-nara.ac.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* $Id: scenario.c,v 1.30 2003/04/25 17:23:55 chikama Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "portab.h"
#include "ald_manager.h"
#include "variable.h"
#include "scenario.h"
#include "LittleEndian.h"
#include "xsystem35.h"
const uint8_t *sl_sco;
int sl_page;
int sl_index;
static void pop_state(uint8_t tag);
static uint8_t *stack_buf;
static uint8_t *stack_top;
static int stack_size = 1024;
static char strbuf[512];
static dridata *dfile; // dri object for current page
static dridata *datatbl; // dri object for data table
static void stack_reserve(int size) {
if (stack_top + size < stack_buf + stack_size)
return;
int n = stack_top - stack_buf;
stack_size *= 2;
stack_buf = realloc(stack_buf, stack_size);
if (!stack_buf)
NOMEMERR();
stack_top = stack_buf + n;
}
static void stack_push_byte(uint8_t v) {
*stack_top++ = v;
}
static void stack_push_word(uint16_t v) {
LittleEndian_putW(v, stack_top, 0);
stack_top += 2;
}
static void stack_push_dword(uint32_t v) {
LittleEndian_putDW(v, stack_top, 0);
stack_top += 4;
}
static void stack_drop(int n) {
stack_top -= n;
if (stack_top < stack_buf)
SYSERROR("stack underflow");
}
static uint8_t stack_pop_byte(void) {
stack_drop(1);
return *stack_top;
}
static int stack_pop_word(void) {
stack_drop(2);
return LittleEndian_getW(stack_top, 0);
}
static int stack_pop_dword(void) {
stack_drop(4);
return LittleEndian_getDW(stack_top, 0);
}
/* initilize stack and load first scenario data */
boolean sl_init(void) {
stack_buf = malloc(stack_size);
if (stack_buf == NULL)
NOMEMERR();
stack_top = stack_buf;
sl_jmpFar(0);
return TRUE;
}
/* UD 0 command, reinitilized scenario loader */
boolean sl_reinit(void) {
free(stack_buf);
return sl_init();
}
int sl_getw(void) {
int c0 = sl_getc();
int c1 = sl_getc();
return c0 + (c1 << 8);
}
int sl_getcAt(int adr) {
return sl_sco[adr];
}
int sl_getwAt(int adr) {
int c0 = sl_sco[adr];
int c1 = sl_sco[adr + 1];
return c0 + (c1 << 8);
}
int sl_getdAt(int adr) {
int c0 = sl_getwAt(adr);
int c1 = sl_getwAt(adr + 2);
return c0 + (c1 << 16);
}
int sl_getaddr(void) {
int c0 = sl_getw();
int c1 = sl_getw();
return c0 + (c1 << 16);
}
void sl_ungetc(void) {
sl_index--;
}
const char *sl_getString(char term) {
int c0;
char *index = strbuf;
while ((c0 = sl_getc()) != (int)term) {
*index++ = c0;
}
*index = '\0';
return strbuf;
}
const char *sl_getConstString(void) {
char *index = strbuf;
int c0 = sl_getc();
while ((c0 = sl_getc()) != 0) {
*index++ = ((c0 & 0xf0) >> 4) + ((c0 & 0x0f) << 4);
}
*index = '\0';
return strbuf;
}
/* @address */
void sl_jmpNear(int address) {
sl_index = address;
}
/*
#page
page = 0~
*/
boolean sl_jmpFar(int page) {
if (dfile) {
ald_freedata(dfile);
}
dfile = ald_getdata(DRIFILE_SCO, page);
if (dfile == NULL) {
return FALSE;
}
sl_sco = dfile->data;
sl_page = page;
sl_index = LittleEndian_getDW(sl_sco, 4);
return TRUE;
}
/*
~page,address
page = 1~
*/
boolean sl_jmpFar2(int page, int address) {
if (dfile) {
ald_freedata(dfile);
}
dfile = ald_getdata(DRIFILE_SCO, page);
if (dfile == NULL) {
DEBUG_MESSAGE("ald_getdata fail\n");
return FALSE;
}
sl_sco = dfile->data;
sl_page = page;
sl_index = address;
return TRUE;
}
void sl_callNear(int address) {
stack_reserve(4 + 1);
stack_push_dword(sl_index);
stack_push_byte(STACK_NEARCALL);
sl_jmpNear(address);
}
void sl_retNear(void) {
uint8_t tag;
while ((tag = stack_pop_byte()) != STACK_NEARCALL) {
switch (tag) {
case STACK_FARCALL:
SYSERROR("label return from page call");
break;
default:
pop_state(tag);
break;
}
}
int address = stack_pop_dword();
sl_jmpNear(address);
}
void sl_callFar(int page) {
stack_reserve(2 + 4 + 1);
stack_push_word(sl_page + 1);
stack_push_dword(sl_index);
stack_push_byte(STACK_FARCALL);
if (!sl_jmpFar(page))
sl_retFar();
}
void sl_callFar2(int page, int address) {
stack_reserve(2 + 4 + 1);
stack_push_word(sl_page + 1);
stack_push_dword(sl_index);
stack_push_byte(STACK_FARCALL);
if (!sl_jmpFar2(page, address))
sl_retFar();
}
void sl_retFar(void) {
uint8_t tag;
while ((tag = stack_pop_byte()) != STACK_FARCALL) {
switch (tag) {
case STACK_NEARCALL:
stack_drop(4);
break;
default:
pop_state(tag);
break;
}
}
int address = stack_pop_dword();
int page = stack_pop_word() - 1;
sl_jmpFar2(page, address);
}
/* UC0 */
void sl_clearStack(bool restore) {
if (!restore) {
stack_top = stack_buf;
return;
}
while (stack_top > stack_buf) {
uint8_t tag = stack_pop_byte();
switch (tag) {
case STACK_NEARCALL:
stack_drop(4);
break;
case STACK_FARCALL:
stack_drop(4 + 2);
break;
default:
pop_state(tag);
break;
}
}
}
/* UC 2 */
void sl_dropPageCalls(int cnt) {
while (cnt > 0) {
uint8_t tag = stack_pop_byte();
switch (tag) {
case STACK_NEARCALL:
stack_drop(4);
break;
case STACK_FARCALL:
stack_drop(4 + 2);
cnt--;
break;
default:
pop_state(tag);
break;
}
}
}
/* UC 1 */
void sl_dropLabelCalls(int cnt) {
while (cnt > 0) {
uint8_t tag = stack_pop_byte();
switch (tag) {
case STACK_NEARCALL:
stack_drop(4);
cnt--;
break;
case STACK_FARCALL:
stack_push_byte(tag);
return;
default:
pop_state(tag);
break;
}
}
}
/* US */
void sl_pushVar(struct VarRef *vref, int cnt) {
stack_reserve((2 + 2 + 2 + 1) * cnt);
int *val = v_resolveRef(vref);
for (int i = 0; i < cnt; i++) {
stack_push_word(vref->index + i);
stack_push_word(vref->page);
stack_push_word(val[i]);
stack_push_byte(STACK_VARIABLE);
}
}
/* UG */
void sl_popVar(struct VarRef *vref, int cnt) {
int *val = v_resolveRef(vref) + cnt;
while (cnt-- > 0) {
if (stack_pop_byte() != STACK_VARIABLE)
SYSERROR("stack top is not a variable push");
*--val = stack_pop_word();
stack_drop(2 + 2);
}
}
enum save_stack_frame_type {
SAVE_NEARJMP = 1,
SAVE_FARJMP = 2,
SAVE_VARIABLE = 3,
SAVE_TXXSTATE = 4
};
enum txx_type {
TxxTEXTCOLOR = 1,
TxxTEXTSIZE = 2,
TxxTEXTLOC = 3
};
struct save_stack_frame {
struct save_stack_frame *next;
int len;
int buf[];
};
struct save_stack_frame *push_save_stack_frame(int size, struct save_stack_frame *next) {
struct save_stack_frame *f =
malloc(sizeof(struct save_stack_frame) + sizeof(int) * size);
f->next = next;
f->len = size;
return f;
}
uint8_t *sl_saveStack(enum save_format format, int *size_out) {
if (format != SAVEFMT_XSYS35) {
int size = stack_top - stack_buf;
uint8_t *buf = malloc(size);
memcpy(buf, stack_buf, size);
*size_out = size;
return buf;
}
// Serialize to the stack format used in old versions of xsystem35.
struct save_stack_frame *frame = NULL;
uint8_t *sp = stack_top;
while (sp > stack_buf) {
switch (sp[-1]) {
case STACK_FARCALL:
sp -= 7;
frame = push_save_stack_frame(4, frame);
frame->buf[3] = SAVE_FARJMP;
frame->buf[2] = 2;
frame->buf[1] = LittleEndian_getW(sp, 0) - 1; // page
frame->buf[0] = LittleEndian_getDW(sp, 2); // addr
break;
case STACK_NEARCALL:
sp -= 5;
frame = push_save_stack_frame(3, frame);
frame->buf[2] = SAVE_NEARJMP;
frame->buf[1] = 1;
frame->buf[0] = LittleEndian_getDW(sp, 0); // addr
break;
case STACK_VARIABLE:
sp -= 7;
{
int index = LittleEndian_getW(sp, 0);
int page = LittleEndian_getW(sp, 2);
int value = LittleEndian_getW(sp, 4);
if (frame && frame->buf[frame->len - 1] == SAVE_VARIABLE) {
int count = frame->buf[frame->len - 2];
int fpage = frame->buf[frame->len - 3];
int base = frame->buf[frame->len - 4];
// Merge into existing SAVE_VARIABLE frame.
if (page == fpage && index == base - 1) {
frame = realloc(frame, sizeof(struct save_stack_frame) + sizeof(int) * (frame->len + 1));
frame->len++;
frame->buf[frame->len - 1] = SAVE_VARIABLE;
frame->buf[frame->len - 2] = count + 1;
frame->buf[frame->len - 3] = page;
frame->buf[frame->len - 4] = base - 1;
frame->buf[frame->len - 5] = value;
break;
}
}
frame = push_save_stack_frame(5, frame);
frame->buf[4] = SAVE_VARIABLE;
frame->buf[3] = 3;
frame->buf[2] = page;
frame->buf[1] = index;
frame->buf[0] = value;
}
break;
case STACK_TEXTCOLOR:
sp -= 3;
frame = push_save_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTCOLOR;
frame->buf[1] = sp[0]; // type
frame->buf[0] = sp[1]; // color
break;
case STACK_TEXTSIZE:
sp -= 6;
frame = push_save_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTSIZE;
frame->buf[1] = sp[0]; // type
frame->buf[0] = LittleEndian_getDW(sp, 1); // size
break;
case STACK_TEXTLOC:
sp -= 9;
frame = push_save_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTLOC;
frame->buf[1] = LittleEndian_getDW(sp, 0); // x
frame->buf[0] = LittleEndian_getDW(sp, 4); // y
break;
default:
SYSERROR("broken stack");
}
}
int total_len = 0;
for (struct save_stack_frame *f = frame; f; f = f->next)
total_len += f->len;
int *buf = malloc(total_len * sizeof(int));
int *p = buf;
for (struct save_stack_frame *f = frame; f;) {
memcpy(p, f->buf, f->len * sizeof(int));
p += f->len;
struct save_stack_frame *next = f->next;
free(f);
f = next;
}
*size_out = total_len * sizeof(int);
#if 0
// Testing code. Call sl_loadStack with the serialized stack, and compare
// the deserialized stack with the original stack.
{
int orig_size = stack_top - stack_buf;
uint8_t *orig_stack = malloc(orig_size);
memcpy(orig_stack, stack_buf, orig_size);
sl_loadStack(format, (uint8_t *)buf, *size_out);
if (stack_top - stack_buf != orig_size || memcmp(stack_buf, orig_stack, orig_size)) {
FILE *fp = fopen("orig_stack", "wb");
fwrite(orig_stack, 1, orig_size, fp);
fclose(fp);
fp = fopen("deserialized_stack", "wb");
fwrite(stack_buf, 1, stack_top - stack_buf, fp);
fclose(fp);
SYSERROR("stack mismatch");
}
}
#endif
return (uint8_t *)buf;
}
void sl_loadStack(enum save_format format, uint8_t *unaligned_data, int size) {
if (format != SAVEFMT_XSYS35) {
if (size > stack_size) {
while (size > stack_size)
stack_size *= 2;
free(stack_buf);
stack_buf = malloc(stack_size);
if (!stack_buf)
NOMEMERR();
}
memcpy(stack_buf, unaligned_data, size);
stack_top = stack_buf + size;
return;
}
// Deserialize from the stack format used in old versions of xsystem35.
int *data = malloc(size);
memcpy(data, unaligned_data, size);
struct save_stack_frame *frame = NULL;
for (int *p = data + size / sizeof(int); p > data;) {
int len = p[-2] + 2;
p -= len;
frame = push_save_stack_frame(len, frame);
memcpy(frame->buf, p, len * sizeof(int));
}
free(data);
stack_top = stack_buf;
for (struct save_stack_frame *f = frame; f;) {
switch (f->buf[f->len - 1]) {
case SAVE_NEARJMP:
if (f->len != 3)
SYSERROR("broken stack data");
stack_reserve(4 + 1);
stack_push_dword(f->buf[0]);
stack_push_byte(STACK_NEARCALL);
break;
case SAVE_FARJMP:
if (f->len != 4)
SYSERROR("broken stack data");
stack_reserve(2 + 4 + 1);
stack_push_word(f->buf[1] + 1);
stack_push_dword(f->buf[0]);
stack_push_byte(STACK_FARCALL);
break;
case SAVE_VARIABLE:
if (f->len < 5) {
SYSERROR("broken stack data");
} else {
int count = f->len - 4;
int page = f->buf[f->len - 3];
int base = f->buf[f->len - 4];
stack_reserve((2 + 2 + 2 + 1) * count);
for (int i = 0; i < count; i++) {
stack_push_word(base + i);
stack_push_word(page);
stack_push_word(f->buf[count - 1 - i]);
stack_push_byte(STACK_VARIABLE);
}
}
break;
case SAVE_TXXSTATE:
if (f->len != 5)
SYSERROR("broken stack data");
switch (f->buf[2]) {
case TxxTEXTCOLOR:
stack_reserve(1 + 1 + 1);
stack_push_byte(f->buf[1]); // type
stack_push_byte(f->buf[0]); // color
stack_push_byte(STACK_TEXTCOLOR);
break;
case TxxTEXTSIZE:
stack_reserve(1 + 4 + 1);
stack_push_byte(f->buf[1]); // type
stack_push_dword(f->buf[0]); // size
stack_push_byte(STACK_TEXTSIZE);
break;
case TxxTEXTLOC:
stack_reserve(4 + 4 + 1);
stack_push_dword(f->buf[1]); // x
stack_push_dword(f->buf[0]); // y
stack_push_byte(STACK_TEXTLOC);
break;
default:
SYSERROR("broken stack data");
}
break;
default:
SYSERROR("broken stack data");
}
struct save_stack_frame *next = f->next;
free(f);
f = next;
}
}
void sl_getStackInfo(struct stack_info *info) {
memset(info, 0, sizeof(struct stack_info));
struct stack_frame_info *sfi = NULL;
while ((sfi = sl_next_stack_frame(sfi)) != NULL) {
switch (sfi->tag) {
case STACK_NEARCALL:
info->label_calls++;
if (!info->page_calls)
info->label_calls_after_page_call++;
break;
case STACK_FARCALL:
info->page_calls++;
break;
case STACK_VARIABLE:
info->var_pushes++;
if (info->page_calls + info->label_calls == 0)
info->var_pushes_after_call++;
break;
default:
break;
}
}
}
/* TPC */
void sl_pushTextColor(uint8_t type, uint8_t color) {
stack_reserve(1 + 1 + 1);
stack_push_byte(type);
stack_push_byte(color);
stack_push_byte(STACK_TEXTCOLOR);
}
/* TPS */
void sl_pushTextSize(uint8_t type, int size) {
stack_reserve(1 + 4 + 1);
stack_push_byte(type);
stack_push_dword(size);
stack_push_byte(STACK_TEXTSIZE);
}
/* TPP */
void sl_pushTextLoc(int x, int y) {
stack_reserve(4 + 4 + 1);
stack_push_dword(x);
stack_push_dword(y);
stack_push_byte(STACK_TEXTLOC);
}
/* TOx */
void sl_popState(uint8_t expected_type) {
uint8_t tag = stack_pop_byte();
if (tag != expected_type) {
// No-op if the stack top is of an unexpected type.
stack_push_byte(tag);
WARNING("unexpected stack top type");
return;
}
pop_state(tag);
}
static void pop_state(uint8_t tag) {
switch (tag) {
case STACK_VARIABLE:
{
int value = stack_pop_word();
int page = stack_pop_word();
int index = stack_pop_word();
varPage[page].value[index] = value;
}
break;
case STACK_TEXTCOLOR:
{
uint8_t color = stack_pop_byte();
uint8_t type = stack_pop_byte();
if (type)
nact->sel.MsgFontColor = color;
else
nact->msg.MsgFontColor = color;
}
break;
case STACK_TEXTSIZE:
{
int size = stack_pop_dword();
uint8_t type = stack_pop_byte();
if (type)
nact->sel.MsgFontSize = size;
else
nact->msg.MsgFontSize = size;
}
break;
case STACK_TEXTLOC:
{
int x = stack_pop_dword();
int y = stack_pop_dword();
msg_setMessageLocation(x, y);
}
break;
default:
SYSERROR("broken stack");
}
}
struct stack_frame_info *sl_next_stack_frame(struct stack_frame_info *sfi) {
static struct stack_frame_info frame_info;
if (!sfi) {
sfi = &frame_info;
sfi->p = stack_top;
}
if (sfi->p <= stack_buf)
return NULL;
sfi->tag = sfi->p[-1];
switch (sfi->tag) {
case STACK_NEARCALL:
sfi->p -= 5;
sfi->addr = LittleEndian_getDW(sfi->p, 0);
break;
case STACK_FARCALL:
sfi->p -= 7;
sfi->page = LittleEndian_getW(sfi->p, 0) - 1;
sfi->addr = LittleEndian_getDW(sfi->p, 2);
break;
case STACK_VARIABLE: sfi->p -= 7; break;
case STACK_TEXTCOLOR: sfi->p -= 3; break;
case STACK_TEXTSIZE: sfi->p -= 6; break;
case STACK_TEXTLOC: sfi->p -= 9; break;
default:
SYSERROR("broken stack");
}
return sfi;
}
void *sl_setDataTable(int page, int index) {
if (datatbl) {
ald_freedata(datatbl);
}
datatbl = ald_getdata(DRIFILE_SCO, page);
return (void *)(datatbl->data + index);
}
void sl_returnGoto(int address) {
uint8_t tag;
while ((tag = stack_pop_byte()) != STACK_FARCALL && tag != STACK_NEARCALL)
pop_state(tag);
if (tag == STACK_FARCALL) {
stack_drop(4);
int page = stack_pop_word() - 1;
sl_jmpFar2(page, address);
} else {
stack_drop(4);
sl_jmpNear(address);
}
}