/*
    TiMidity++ -- MIDI to WAVE converter and player
    Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
    Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>

    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

    WRD reader - Written by Masanao Izumo <mo@goice.co.jp>
		 Modified by Takaya Nogami <t-nogami@happy.email.ne.jp> for
			Sherry WRD.

 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#ifndef NO_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>

#include "timidity.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "readmidi.h"
#include "controls.h"
#include "wrd.h"
#include "wrdi.h"
#include "strtab.h"

/*#define DEBUG 1*/

#if  defined(JAPANESE) || defined(__MACOS__)
#define IS_MULTI_BYTE(c)	( ((c)&0x80) && ((0x1 <= ((c)&0x7F) && ((c)&0x7F) <= 0x1f) ||\
				 (0x60 <= ((c)&0x7F) && ((c)&0x7F) <= 0x7c)))
#define IS_SJIS_ZENKAKU_SPACE(p) ((p)[0] == 0x81 && (p)[1] == 0x40)
#else
#define IS_MULTI_BYTE(c)	0
#endif /* JAPANESE */

#define WRDENDCHAR 26 /* ^Z */
#define MAXTIMESIG 256

#ifdef DEBUG
#define WRD_BUGEMUINFO(code) ctl->cmsg(c, CMSG_WARNING, VERB_VERBOSE, \
    "WRD: Try to emulate bug of MIMPI at line %d (code=%d)", c->wrd_lineno, code)
#else
#define WRD_BUGEMUINFO(code) ctl->cmsg(c, CMSG_WARNING, VERB_NOISY, \
    "WRD: Try to emulate bug of MIMPI at line %d", c->wrd_lineno)
#endif /* DEBUG */
/* Current max code: 13 */

#define FADE_SPEED_BASE 24 /* 24 or 48?? */

struct wrd_delayed_event
{
    int32 waittime;
    int cmd, arg;
    struct wrd_delayed_event* next;
};

struct wrd_step_tracer
{
    int32 at;		/* total step count */
    int32 last_at;	/* estimated maxmum steps */
    int32 step_inc;	/* increment steps per newline or character */
    int bar;		/* total bar count */
    int step;		/* step in current bar */
    int barstep;	/* step count of current bar */
    MidiEvent timesig[MAXTIMESIG]; /* list of time signature code */
    int timeidx;	/* index of current timesig */
    int ntimesig;	/* number of time signatures */
    int timebase;	/* divisions */
    int offset;		/* @OFFSET */
    int wmode0, wmode1;	/* @WMODE */

    /* Delayed MIDI event list */
    struct wrd_delayed_event *de;
    struct wrd_delayed_event *free_de;

    MBlockList pool;	/* memory buffer */
};

#ifdef ENABLE_SHERRY
#define DEFAULT_DATAPACKET_LEN 16384
static int import_sherrywrd_file(struct timiditycontext_t *c, const char * );
#endif /* ENABLE_SHERRY */

static uint8 cmdlookup(uint8 *cmd);
static int wrd_nexttok(struct timiditycontext_t *c, struct timidity_file *tf);
static void wrd_readinit(struct timiditycontext_t *c);
static struct timidity_file *open_wrd_file(struct timiditycontext_t *c, char *fn);
static int wrd_hexval(char *hex);
static int wrd_eint(char *hex);
static int wrd_atoi(char *val, int default_value);
static void wrd_add_lyric(struct timiditycontext_t *c, int32 at, char *lyric, int len);
static int wrd_split(char* arg, char** argv, int maxarg);
static void wrdstep_inc(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep, int32 inc);
static void wrdstep_update_forward(struct wrd_step_tracer *wrdstep);
static void wrdstep_update_backward(struct wrd_step_tracer *wrdstep);
static void wrdstep_nextbar(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep);
static void wrdstep_prevbar(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep);
static void wrdstep_wait(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep, int bar, int step);
static void wrdstep_rest(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep, int bar, int step);
static struct wrd_delayed_event *wrd_delay_cmd(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep,
					int32 waittime, int cmd, int arg);

#define WRD_ADDEVENT(at, cmd, arg) \
    { MidiEvent e; e.time = (at); e.type = ME_WRD; e.channel = (cmd); \
      e.a = (uint8)((arg) & 0xFF); e.b = (uint8)(((arg) >> 8) & 0xFF); \
      if(c->mimpi_bug_emulation_level > 0){ if(at < c->wrd_last_event_time){ e.time = \
      c->wrd_last_event_time; }else{ c->wrd_last_event_time = e.time; }} \
      readmidi_add_event(c, &e); }

#define WRD_ADDSTREVENT(at, cmd, str) \
    { MidiEvent e; readmidi_make_string_event(c, ME_WRD, (str), &e, 0); \
      e.channel = (cmd); e.time = (at); \
      if(c->mimpi_bug_emulation_level > 0){ if(at < c->wrd_last_event_time){ e.time = \
      c->wrd_last_event_time; }else{ c->wrd_last_event_time = e.time; }} \
      readmidi_add_event(c, &e); }

#define SETMIDIEVENT(e, at, t, ch, pa, pb) \
    { (e).time = (at); (e).type = (t); \
      (e).channel = (uint8)(ch); (e).a = (uint8)(pa); (e).b = (uint8)(pb); }
#define MIDIEVENT(at, t, ch, pa, pb) \
    { MidiEvent event; SETMIDIEVENT(event, at, t, ch, pa, pb); \
      readmidi_add_event(c, &event); }

#ifdef DEBUG
static char *wrd_name_string(int cmd);
#endif /* DEBUG */

int import_wrd_file(struct timiditycontext_t *c, char *fn)
{
    struct timidity_file *tf;
    char *args[WRD_MAXPARAM], *arg0;
    int argc;
    int32 i, num;
    struct wrd_step_tracer wrdstep;
#define step_at wrdstep.at

    char *wfn; /* opened WRD filename */
    StringTableNode *stn; /* Chain list of string */

    if(!c->import_wrd_file_initflag) /* Initialize at once */
    {
	char *read_opts[WRD_MAXPARAM];

	c->import_wrd_file_initflag = 1;
	for(stn = c->wrd_read_opts.head; stn; stn = stn->next)
	{
	    int nopts;

	    nopts = wrd_split(stn->string, read_opts, WRD_MAXPARAM);
	    for(i = 0; i < nopts; i++)
	    {
		char *a, *b;
		a = read_opts[i];
		if((b = strchr(a, '=')) != NULL)
		    *b++ = '\0';
		if(strcmp(a, "d") == 0)
		    c->mimpi_bug_emulation_level = (b ? atoi(b) : 0);
		else if(strcmp(a, "f") == 0)
		{
		    if(c->import_wrd_file_default_wrd_file1 != NULL)
			free(c->import_wrd_file_default_wrd_file1);
		    c->import_wrd_file_default_wrd_file1 = (b ? safe_strdup(c, b) : NULL);
		}
		else if(strcmp(a, "F") == 0)
		{
		    if(c->import_wrd_file_default_wrd_file2 != NULL)
			free(c->import_wrd_file_default_wrd_file2);
		    c->import_wrd_file_default_wrd_file2 = (b ? safe_strdup(c, b) : NULL);
		}
		else if(strcmp(a, "p") == 0)
		{
		    if(b != NULL)
			wrd_add_default_path(c, b);
		}
	    }
	}
    }

    if(c->datapacket == NULL)
	init_mblock(&c->sry_pool);
    else
    {
	free(c->datapacket);
	c->datapacket = NULL;
	reuse_mblock(c, &c->sry_pool);
    }

    wrd_init_path(c);
    if(c->import_wrd_file_default_wrd_file2 != NULL)
	tf = open_file(c, (wfn = c->import_wrd_file_default_wrd_file2), 0, OF_NORMAL);
    else
	tf = open_wrd_file(c, wfn = fn);
    if(tf == NULL && c->import_wrd_file_default_wrd_file1 != NULL)
	tf = open_file(c, (wfn = c->import_wrd_file_default_wrd_file1), 0, OF_NORMAL);
    if(tf == NULL)
    {
	c->import_wrd_file_default_wrd_file1 = c->import_wrd_file_default_wrd_file2 = NULL;
#ifdef ENABLE_SHERRY
	if(import_sherrywrd_file(c, fn))
	    return WRD_TRACE_SHERRY;
#endif
	return WRD_TRACE_NOTHING;
    }

    wrd_readinit(c);

    memset(&wrdstep, 0, sizeof(wrdstep));
    init_mblock(&wrdstep.pool);
    wrdstep.de = wrdstep.free_de = NULL;
    wrdstep.timebase = c->current_file_info->divisions;
    wrdstep.ntimesig = dump_current_timesig(c, wrdstep.timesig, MAXTIMESIG - 1);
    if(wrdstep.ntimesig > 0)
    {
	wrdstep.timesig[wrdstep.ntimesig] =
	    wrdstep.timesig[wrdstep.ntimesig - 1];
	wrdstep.timesig[wrdstep.ntimesig].time = 0x7fffffff; /* stopper */
#ifdef DEBUG
	fprintf(stderr, "Time signatures:\n");
	for(i = 0; i < wrdstep.ntimesig; i++)
	    printf("  %d: %d/%d\n",
		   wrdstep.timesig[i].time,
		   wrdstep.timesig[i].a,
		   wrdstep.timesig[i].b);
#endif /* DEBUG */
	wrdstep.barstep =
	    wrdstep.timesig[0].a * wrdstep.timebase * 4 / wrdstep.timesig[0].b;
    }
    else
	wrdstep.barstep = 4 * wrdstep.timebase;
    wrdstep.step_inc = wrdstep.barstep;
    wrdstep.last_at = readmidi_set_track(c, 0, 0);

    readmidi_set_track(c, 0, 1);

#ifdef DEBUG
    fprintf(stderr, "Timebase: %d\n", wrdstep.timebase);
    fprintf(stderr, "Step: %d\n", wrdstep.step_inc);
#endif /* DEBUG */

    while(!c->readmidi_error_flag && wrd_nexttok(c, tf))
    {
	if(c->wrd_version == -1 &&
	   (c->wrd_tok != WRD_COMMAND || c->wrd_tokval[0] != WRD_STARTUP))
	{
	    /* WRD_STARTUP must be first */
	    ctl->cmsg(c, CMSG_WARNING, VERB_VERBOSE, "WRD: No @STARTUP");
	    c->wrd_version = 0;
	    WRD_ADDEVENT(0, WRD_STARTUP, 0);
	}

#ifdef DEBUG
	fprintf(stderr, "%d: [%d,%d]/%d %s: ",
	       c->wrd_lineno,
	       wrdstep.bar,
	       wrdstep.step,
	       c->wrd_bugstatus,
	       wrd_name_string(c->wrd_tok));
	if(c->wrd_tok == WRD_COMMAND)
	    printf("%s(%s)", wrd_name_string(c->wrd_tokval[0]), c->wrd_tokval + 1);
	else if(c->wrd_tok == WRD_LYRIC)
	    printf("<%s>", c->wrd_tokval);
	printf("\n");
	fflush(stdout);
#endif /* DEBUG */

	switch(c->wrd_tok)
	{
	  case WRD_COMMAND:
	    arg0 = (char *)c->wrd_tokval + 1;
	    switch(c->wrd_tokval[0])
	    {
	      case WRD_COLOR:
		num = atoi(arg0);
		WRD_ADDEVENT(step_at, WRD_COLOR, num);
		break;
	      case WRD_END:
		while(step_at < wrdstep.last_at)
		    wrdstep_nextbar(c, &wrdstep);
		break;
	      case WRD_ESC:
		WRD_ADDSTREVENT(step_at, WRD_ESC, arg0);
		break;
	      case WRD_EXEC:
		WRD_ADDSTREVENT(step_at, WRD_EXEC, arg0);
		break;
	      case WRD_FADE:
		argc = wrd_split(arg0, args, 3);
		for(i = 0; i < 2; i++)
		{
		    num = atoi(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}

		num = wrd_atoi(args[i], 1);
		WRD_ADDEVENT(step_at, WRD_FADE, num);

		if(num >= 1)
		{
		    int32 delay, fade_speed;

		    fade_speed = (num + 1) * wrdstep.timebase / FADE_SPEED_BASE;
		    for(i = 1; i < WRD_MAXFADESTEP; i++)
		    {
			delay =	(int32)((double)fade_speed *
				       i / WRD_MAXFADESTEP);
			wrd_delay_cmd(c, &wrdstep, delay, WRD_ARG,
				      i);
			wrd_delay_cmd(c, &wrdstep, delay, WRD_FADESTEP,
				      WRD_MAXFADESTEP);
		    }
		    wrd_delay_cmd(c, &wrdstep, fade_speed, WRD_ARG,
				  WRD_MAXFADESTEP);
		    wrd_delay_cmd(c, &wrdstep, fade_speed, WRD_FADESTEP,
				  WRD_MAXFADESTEP);
		}
		break;
	      case WRD_GCIRCLE:
		argc = wrd_split(arg0, args, 6);
		if(argc < 5)
		{
		    /* Error : Too few argument */
		    break;
		}
		for(i = 0; i < 5; i++)
		{
		    num = atoi(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = atoi(args[i]);
		WRD_ADDEVENT(step_at, WRD_GCIRCLE, num);
		break;
	      case WRD_GCLS:
		num = atoi(arg0);
		WRD_ADDEVENT(step_at, WRD_GCLS, num);
		break;
	      case WRD_GINIT:
		WRD_ADDEVENT(step_at, WRD_GINIT, 0);
		break;
	      case WRD_GLINE:
		argc = wrd_split(arg0, args, 7);
		if(argc < 4)
		{
		    /* Error: Too few arguments */
		    break;
		}
		for(i = 0; i < 4; i++)
		{
		    num = atoi(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num)
		}
		WRD_ADDEVENT(step_at, WRD_ARG,   atoi(args[4]));
		WRD_ADDEVENT(step_at, WRD_ARG,   atoi(args[5]));
		WRD_ADDEVENT(step_at, WRD_GLINE, atoi(args[6]));
		break;
	      case WRD_GMODE:
		num = atoi(arg0);
		WRD_ADDEVENT(step_at, WRD_GMODE, num)
		break;
	      case WRD_GMOVE:
		argc = wrd_split(arg0, args, 9);
		if(argc < 6)
		{
		    /* Error: Too few arguments */
		    break;
		}
		for(i = 0; i < 8; i++)
		{
		    num = atoi(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = atoi(args[i]);
		WRD_ADDEVENT(step_at, WRD_GMOVE, num)
		break;
	      case WRD_GON:
		num = atoi(arg0);
		WRD_ADDEVENT(step_at, WRD_GON, num);
		break;
	      case WRD_GSCREEN:
		if(c->mimpi_bug_emulation_level >= 1)
		{
		    for(i = 0; arg0[i]; i++)
			if(arg0[i] == '.')
			{
			    WRD_BUGEMUINFO(110);
			    arg0[i] = ',';
			}
		}
		argc = wrd_split(arg0, args, 2);
		if(argc != 2)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}
		num = atoi(args[0]);
		WRD_ADDEVENT(step_at, WRD_ARG, num);
		num = atoi(args[1]);
		WRD_ADDEVENT(step_at, WRD_GSCREEN, num);
		break;
	      case WRD_INKEY: /* FIXME */
		num = atoi(arg0);
		if(num < wrdstep.bar)
		{
		    /* Error */
		    break;
		}
		WRD_ADDEVENT(step_at, WRD_INKEY, WRD_NOARG);
		num = (num - wrdstep.bar) * wrdstep.barstep;
		wrd_delay_cmd(c, &wrdstep, num, WRD_OUTKEY, WRD_NOARG);
		break;
	      case WRD_LOCATE:
		if(strchr(arg0, ';') != NULL)
		    i = 1; /* Swap argument */
		else
		    i = 0;
		argc = wrd_split(arg0, args, 2);
		if(argc != 2)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}
		if(i)
		{
		    num = atoi(args[1]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		    num = atoi(args[0]);
		    WRD_ADDEVENT(step_at, WRD_LOCATE, num);
		}
		else
		{
		    num = atoi(args[0]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		    num = atoi(args[1]);
		    WRD_ADDEVENT(step_at, WRD_LOCATE, num);
		}
		break;
	      case WRD_LOOP: /* Not supported */
		break;
	      case WRD_MAG:
		argc = wrd_split(arg0, args, 5);
		if(!*args[0])
		{
		    /* Error: @MAG No file name */
		    break;
		}
		WRD_ADDSTREVENT(step_at, WRD_ARG, args[0]);
		for(i = 1; i < 3; i++)
		    WRD_ADDEVENT(step_at, WRD_ARG,
				 wrd_atoi(args[i], WRD_NOARG));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[3], 1));
		WRD_ADDEVENT(step_at, WRD_MAG, atoi(args[4]));
		break;
	      case WRD_MIDI:
		argc = wrd_split(arg0, args, WRD_MAXPARAM);
		for(i = 0; i < argc; i++)
		    WRD_ADDEVENT(step_at, WRD_ARG, wrd_hexval(args[i]));
		WRD_ADDEVENT(step_at, WRD_MIDI, atoi(args[i]));
		break;
	      case WRD_OFFSET:
		wrdstep.offset = atoi(arg0);
		break;
	      case WRD_PAL:
		argc = wrd_split(arg0, args, 17);
		if(argc != 16 && argc != 17)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}

		if(argc == 16)
		{
		    WRD_ADDEVENT(step_at, WRD_ARG, 0);
		    i = 0;
		}
		else
		{
		    if(*args[0] == '#')
			num = atoi(args[0] + 1);
		    else
			num = atoi(args[0]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		    i = 1;
		}
		for(; i < argc - 1; i++)
		{
		    if(!*args[i])
			num = 0;
		    else
			num = wrd_hexval(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		if(!*args[i])
		    num = 0;
		else
		    num = wrd_hexval(args[i]);
		WRD_ADDEVENT(step_at, WRD_PAL, num)
		break;
	      case WRD_PALCHG:
		WRD_ADDSTREVENT(step_at, WRD_PALCHG, arg0);
		break;
	      case WRD_PALREV:
		num = atoi(arg0);
		WRD_ADDEVENT(step_at, WRD_PALREV, num)
		break;
	      case WRD_PATH:
		WRD_ADDSTREVENT(step_at, WRD_PATH, arg0);
		break;
	      case WRD_PLOAD:
		WRD_ADDSTREVENT(step_at, WRD_PLOAD, arg0);
		break;
	      case WRD_REM:
		WRD_ADDSTREVENT(step_at, WRD_REM, arg0);
		break;
	      case WRD_REMARK:
		WRD_ADDSTREVENT(step_at, WRD_REMARK, arg0);
		break;
	      case WRD_REST:
		argc = wrd_split(arg0, args, 2);
		num = atoi(args[0]);
		if(c->mimpi_bug_emulation_level >= 9 && /* For testing */
		   num == 5 &&
		   wrdstep.wmode0 == 1)
		{
		    WRD_BUGEMUINFO(901);
		    num--; /* Why??? */
		}
		wrdstep_rest(c, &wrdstep, num, atoi(args[1]));
		break;
	      case WRD_SCREEN: /* Not supported */
		break;
	      case WRD_SCROLL:
		argc = wrd_split(arg0, args, 7);
		/*for(i = 0; i < 6; i++)
		{
		    num = atoi(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = atoi(args[i]);
		WRD_ADDEVENT(step_at, WRD_SCROLL, num);*/
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[0], 1));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[1], 1));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[2], 80));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[3], 25));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[4], 0));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[5], 0));
		WRD_ADDEVENT(step_at, WRD_SCROLL,wrd_atoi(args[6], 32));
		break;
	      case WRD_STARTUP:
		c->wrd_version = atoi(arg0);
		WRD_ADDEVENT(step_at, WRD_STARTUP, c->wrd_version);
		break;
	      case WRD_STOP: {
		  MidiEvent e;
		  e.time = step_at;
		  e.type = ME_EOT;
		  e.channel = e.a = e.b = 0;
		  readmidi_add_event(c, &e);
		}
		break;
	      case WRD_TCLS:
		argc = wrd_split(arg0, args, 6);
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[0], 1));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[1], 1));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[2], 80));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[3], 25));
		WRD_ADDEVENT(step_at, WRD_ARG, wrd_atoi(args[4], 0));
		i = wrd_atoi(args[5], ' ');
		if(i == 0)
		    i = ' ';
		WRD_ADDEVENT(step_at, WRD_TCLS,i);
		break;
	      case WRD_TON:
		num = atoi(arg0);
		WRD_ADDEVENT(step_at, WRD_TON, num);
		break;
	      case WRD_WAIT:
		argc = wrd_split(arg0, args, 2);
		wrdstep_wait(c, &wrdstep, atoi(args[0]), atoi(args[1]));
		break;
	      case WRD_WMODE:
		argc = wrd_split(arg0, args, 2);
		wrdstep.wmode0 = wrd_atoi(args[0], wrdstep.wmode0); /* n */
		wrdstep.wmode1 = wrd_atoi(args[1], wrdstep.wmode1); /* mode */
		if(c->mimpi_bug_emulation_level >= 1 &&
		   (c->wrd_version <= 0 || c->wrd_version == 400))
		    c->wrd_wmode_prev_step = wrdstep.step_inc;
		if(argc == 1 && wrdstep.wmode0 == 0)
		    wrdstep.step_inc = wrdstep.barstep;
		else
		{
		    if(wrdstep.wmode0 <= 0 || wrdstep.wmode0 >= 256)
		    {
			ctl->cmsg(c, CMSG_WARNING, VERB_NORMAL,
				  "WRD: Out of value range: "
				  "@WMODE(%d,%d) at line %d",
				  wrdstep.wmode0,wrdstep.wmode1,c->wrd_lineno);
			wrdstep.step_inc = wrdstep.barstep;
		    } else
			wrdstep.step_inc =
			    (wrdstep.wmode0 + 1) * wrdstep.timebase / 24;
		}
#ifdef DEBUG
		fprintf(stderr, "Step change: wmode=%s, step=%d\n",
		       wrdstep.wmode1 ? "char" : "line", wrdstep.step_inc);
#endif /* DEBUG */
		break;
	    }
	    break;
	  case WRD_ECOMMAND:
	    arg0 = (char *)c->wrd_tokval + 1;
	    switch(c->wrd_tokval[0])
	    {
	      case WRD_eFONTM:
		num = wrd_eint(arg0);
		WRD_ADDEVENT(step_at, WRD_eFONTM, num);
		break;
	      case WRD_eFONTP:
		argc = wrd_split(arg0, args, 4);
		if(argc != 4)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}
		for(i = 0; i < 3; i++)
		{
		    num = wrd_eint(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = wrd_eint(args[i]);
		WRD_ADDEVENT(step_at, WRD_eFONTP, num);
		break;
	      case WRD_eFONTR:
		argc = wrd_split(arg0, args, 17);
		if(argc != 17)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}
		for(i = 0; i < 16; i++)
		{
		    num = wrd_eint(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = wrd_eint(args[i]);
		WRD_ADDEVENT(step_at, WRD_eFONTR, num);
		break;
	      case WRD_eGSC:
		num = wrd_eint(arg0);
		WRD_ADDEVENT(step_at, WRD_eGSC, num);
		break;
	      case WRD_eLINE:
		num = wrd_eint(arg0);
		WRD_ADDEVENT(step_at, WRD_eLINE, num);
		break;
	      case WRD_ePAL:
		argc = wrd_split(arg0, args, 2);
		if(argc != 2)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}
		num = wrd_eint(args[0]);
		WRD_ADDEVENT(step_at, WRD_ARG, num);
		num = wrd_eint(args[1]);
		WRD_ADDEVENT(step_at, WRD_ePAL, num);
		break;
	      case WRD_eREGSAVE:
		argc = wrd_split(arg0, args, 17);
		if(argc < 2)
		{
		    /* Error: Too few arguments */
		    break;
		}
		for(i = 0; i < 16; i++)
		{
		    num = wrd_eint(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = wrd_eint(args[i]);
		WRD_ADDEVENT(step_at, WRD_eREGSAVE, num);
		break;
	      case WRD_eSCROLL:
		argc = wrd_split(arg0, args, 2);
		if(argc != 2)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}
		num = wrd_eint(args[0]);
		WRD_ADDEVENT(step_at, WRD_ARG, num);
		num = wrd_eint(args[1]);
		WRD_ADDEVENT(step_at, WRD_eSCROLL, num);
		break;
	      case WRD_eTEXTDOT:
		num = wrd_eint(arg0);
		WRD_ADDEVENT(step_at, WRD_eTEXTDOT, num);
		break;
	      case WRD_eTMODE:
		num = wrd_eint(arg0);
		WRD_ADDEVENT(step_at, WRD_eTMODE, num);
		break;
	      case WRD_eTSCRL:
		num = wrd_eint(arg0);
		WRD_ADDEVENT(step_at, WRD_eTSCRL, num);
		break;
	      case WRD_eVCOPY:
		argc = wrd_split(arg0, args, 9);
		if(argc != 9)
		{
		    /* Error: Number of arguments miss match */
		    break;
		}
		for(i = 0; i < 8; i++)
		{
		    num = wrd_eint(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = wrd_eint(args[i]);
		WRD_ADDEVENT(step_at, WRD_eVCOPY, num);
		break;
	      case WRD_eVSGET:
		argc = wrd_split(arg0, args, 4);
		if(argc < 1)
		{
		    /* Error: Too few arguments */
		    break;
		}
		for(i = 0; i < 3; i++)
		{
		    num = wrd_eint(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = wrd_eint(args[i]);
		WRD_ADDEVENT(step_at, WRD_eVSGET, num);
		break;
	      case WRD_eVSRES:
		WRD_ADDEVENT(step_at, WRD_eVSRES, WRD_NOARG);
		break;
	      case WRD_eXCOPY:
		argc = wrd_split(arg0, args, 14);
		if(argc < 9)
		{
		    /* Error: Too few arguments */
		    break;
		}
		for(i = 0; i < 13; i++)
		{
		    num = wrd_eint(args[i]);
		    WRD_ADDEVENT(step_at, WRD_ARG, num);
		}
		num = wrd_eint(args[i]);
		WRD_ADDEVENT(step_at, WRD_eXCOPY, num);
		break;
	      default:
		ctl->cmsg(c, CMSG_WARNING, VERB_NORMAL,
			  "WRD: Unknown WRD command at line %d (Ignored)",
			  c->wrd_lineno);
		break;
	    }
	    break;
	  case WRD_STEP:
	    if(c->wrd_wmode_prev_step == 0 ||
	       c->wrd_wmode_prev_step == wrdstep.barstep ||
	       wrdstep.step_inc == wrdstep.barstep)
		wrdstep_inc(c, &wrdstep, wrdstep.step_inc);
	    else
	    {
		if(c->wrd_wmode_prev_step != wrdstep.step_inc)
		    WRD_BUGEMUINFO(103);
		wrdstep_inc(c, &wrdstep, c->wrd_wmode_prev_step);
	    }
	    c->wrd_wmode_prev_step = 0;
	    break;
	  case WRD_LYRIC:
	    if(wrdstep.wmode1 == 0)
	    {
		i = (int32)strlen((char *)c->wrd_tokval);
		if(i > 0 && c->wrd_tokval[i - 1] == ';')
		    wrd_add_lyric(c, step_at, (char *)c->wrd_tokval, i - 1);
		else
		{
		    wrd_add_lyric(c, step_at, (char *)c->wrd_tokval, i);
		    WRD_ADDEVENT(step_at, WRD_NL, WRD_NOARG);
		    wrdstep_inc(c, &wrdstep, wrdstep.step_inc);
		}
	    }
	    else
	    {
		unsigned char *val, *lyric;
		int barcheck;

		val = (unsigned char *)c->wrd_tokval;
		barcheck = 0;
		for(;;)
		{
		    if(*val == ';' && *(val + 1) == '\0')
			break;
		    if(*val == '\0')
		    {
			WRD_ADDEVENT(step_at, WRD_NL, WRD_NOARG);
			wrdstep_inc(c, &wrdstep, wrdstep.step_inc);
			break;
		    }

		    if(*val == '\\')
		    {
			lyric = ++val;
			wrd_add_lyric(c, step_at, (char *) lyric, 1);
			wrdstep_inc(c, &wrdstep, wrdstep.step_inc);
		    }
		    else if(*val == '|')
		    {
			lyric = ++val;
			while(*val && *val != '|')
			{
			    if( IS_MULTI_BYTE(*val) || *val == '\\')
			    {
				val++;
				if(!*val)
				    break;
			    }
			    val++;
			}
			i = val - lyric;
			if(*val == '|')
			    val++;
			wrd_add_lyric(c, step_at, (char *) lyric, i);

			/* Why does /^\|[^\|]+\|$/ takes only one waiting ? */
			if(c->mimpi_bug_emulation_level >= 2 &&
			   c->wrd_version == 427 &&
			   barcheck == 0 && *val == '\0')
			{
			    WRD_BUGEMUINFO(204);
			    WRD_ADDEVENT(step_at, WRD_NL, WRD_NOARG);
			    wrdstep_inc(c, &wrdstep, wrdstep.step_inc);
			    break;
			}

			wrdstep_inc(c, &wrdstep, wrdstep.step_inc);
			barcheck++;
		    }
		    else
		    {
			lyric = val;
			if( IS_MULTI_BYTE(*val) )
			    val++;
			if(*val++ == '\0')
			    break;
			i = val - lyric;
			if(*lyric != '_')
			    wrd_add_lyric(c, step_at, (char *) lyric, i);
			wrdstep_inc(c, &wrdstep, wrdstep.step_inc);
		    }
		}
	    }
	    break;
	  case WRD_EOF:
	    goto end_of_wrd;
	  default:
	    break;
	}
    }

  end_of_wrd:
    while(wrdstep.de)
	wrdstep_nextbar(c, &wrdstep);
    reuse_mblock(c, &wrdstep.pool);
    close_file(c, tf);
#ifdef DEBUG
    fflush(stderr);
#endif /* DEBUG */

    return WRD_TRACE_MIMPI;

#undef step_at
}

static struct wrd_delayed_event *wrd_delay_cmd(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep,
					int32 waittime, int cmd, int arg)
{
    struct wrd_delayed_event *p;
    struct wrd_delayed_event *insp, *prev;

    if(wrdstep->free_de != NULL)
    {
	p = wrdstep->free_de;
	wrdstep->free_de = wrdstep->free_de->next;
    }
    else
	p = (struct wrd_delayed_event *)
	    new_segment(c, &wrdstep->pool, sizeof(struct wrd_delayed_event));
    p->waittime = waittime;
    p->cmd = cmd;
    p->arg = arg;

    prev = NULL;
    for(insp = wrdstep->de;
	insp != NULL && insp->waittime <= waittime;
	prev = insp, insp = insp->next)
	;
    if(prev == NULL)
    {
	p->next = wrdstep->de;
	wrdstep->de = p;
    }
    else
    {
	prev->next = p;
	p->next = insp;
    }

#ifdef DEBUG
    fprintf(stderr, "Delay events:");
    for(insp = wrdstep->de; insp != NULL; insp = insp->next)
	fprintf(stderr, " @%s/%d", wrd_name_string(insp->cmd), insp->waittime);
    fprintf(stderr, "\n");
#endif /* DEBUG */

    return p;
}

static void wrdstep_update_forward(struct wrd_step_tracer *wrdstep)
{
    int lastidx;
    lastidx = wrdstep->timeidx;
    while(wrdstep->timeidx < wrdstep->ntimesig &&
	  wrdstep->timesig[wrdstep->timeidx + 1].time <= wrdstep->at)
	wrdstep->timeidx++;
    if(lastidx != wrdstep->timeidx)
    {
	wrdstep->barstep =
	    wrdstep->timesig[wrdstep->timeidx].a * wrdstep->timebase * 4
		/ wrdstep->timesig[wrdstep->timeidx].b;
#ifdef DEBUG
	fprintf(stderr, "Time signature is changed: %d/%d barstep=%d\n",
	       wrdstep->timesig[wrdstep->timeidx].a,
	       wrdstep->timesig[wrdstep->timeidx].b,
	       wrdstep->barstep);
#endif /* DEBUG */
	/* if(wrdstep->barstep < wrdstep->step) { WRD ERROR?? } */
    }
}

static void wrdstep_update_backward(struct wrd_step_tracer *wrdstep)
{
    int lastidx;

    lastidx = wrdstep->timeidx;
    while(wrdstep->timeidx > 0 &&
	  wrdstep->timesig[wrdstep->timeidx].time > wrdstep->at)
	wrdstep->timeidx--;
    if(lastidx != wrdstep->timeidx)
    {
	wrdstep->barstep =
	    wrdstep->timesig[wrdstep->timeidx].a * wrdstep->timebase * 4
		/ wrdstep->timesig[wrdstep->timeidx].b;
#ifdef DEBUG
	fprintf(stderr, "Time signature is changed: %d/%d barstep=%d\n",
	       wrdstep->timesig[wrdstep->timeidx].a,
	       wrdstep->timesig[wrdstep->timeidx].b,
	       wrdstep->barstep);
#endif /* DEBUG */
	/* if(wrdstep->barstep < wrdstep->step) { WRD ERROR?? } */
    }
}

static void wrdstep_nextbar(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep)
{
    wrdstep_inc(c, wrdstep, wrdstep->barstep - wrdstep->step);
}

static void wrdstep_prevbar(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep)
{
    if(wrdstep->bar == 0)
	return;
    wrdstep_inc(c, wrdstep, -wrdstep->step);
    wrdstep_inc(c, wrdstep, -wrdstep->barstep);
}

static void wrdstep_setstep(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep, int step)
{
    if(step > wrdstep->barstep) /* Over step! */
	step = wrdstep->barstep;
    wrdstep_inc(c, wrdstep, step - wrdstep->step);
}

static void wrdstep_inc(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep, int32 inc)
{
    int inc_save = inc;

    do
    {
	if(wrdstep->de == NULL)
	{
	    wrdstep->at += inc;
	    break;
	}
	else
	{
	    struct wrd_delayed_event *p, *q, *qtail;
	    int32 w;

	    w = inc;	/* Note that: if inc < 0, w always equals inc. */
	    for(p = wrdstep->de; p; p = p->next)
		if(w > p->waittime)
		    w = p->waittime;
	    q = qtail = NULL;
	    p = wrdstep->de;
	    while(p)
	    {
		struct wrd_delayed_event *next;

		p->waittime -= w;
		next = p->next;
		if(p->waittime <= 0)
		{
		    WRD_ADDEVENT(wrdstep->at, p->cmd, p->arg);
		    p->next = wrdstep->free_de;
		    wrdstep->free_de = p;
		}
		else
		{
		    p->next = NULL;
		    if(qtail == NULL)
			q = qtail = p;
		    else
			qtail = qtail->next = p;
		}
		p = next;
	    }

	    wrdstep->de = q;
	    inc -= w;
	    wrdstep->at += w;
	}
    } while(inc > 0);

    wrdstep->step += inc_save;
    if(inc_save >= 0)
    {
	while(wrdstep->step >= wrdstep->barstep)
	{
	    wrdstep->step -= wrdstep->barstep;
	    wrdstep->bar++;
	    wrdstep_update_forward(wrdstep);
	}
    }
    else
    {
	while(wrdstep->step < 0)
	{
	    wrdstep->step += wrdstep->barstep;
	    wrdstep->bar--;
	    wrdstep_update_backward(wrdstep);
	}
    }
}

static void wrdstep_wait(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep, int bar, int step)
{
    bar = bar + wrdstep->offset - 1;
    step = wrdstep->timebase * step / 48;

    if(c->mimpi_bug_emulation_level >= 2 && wrdstep->bar > bar)
    {
	/* ignore backward bar */
	WRD_BUGEMUINFO(213);
    }
    else
    {
	while(wrdstep->bar > bar)
	    wrdstep_prevbar(c, wrdstep);
    }

    while(wrdstep->bar < bar)
	wrdstep_nextbar(c, wrdstep);
    wrdstep_setstep(c, wrdstep, step);
}

static void wrdstep_rest(struct timiditycontext_t *c, struct wrd_step_tracer *wrdstep, int bar, int step)
{
    while(bar-- > 0)
	wrdstep_nextbar(c, wrdstep);
    wrdstep_setstep(c, wrdstep, wrdstep->timebase * step / 48);
}

static void wrd_add_lyric(struct timiditycontext_t *c, int32 at, char *lyric, int len)
{
    MBlockList pool;
    char *str;

    init_mblock(&pool);
    str = (char *)new_segment(c, &pool, len + 1);
    memcpy(str, lyric, len);
    str[len] = '\0';
    WRD_ADDSTREVENT(at, WRD_LYRIC, str);
    reuse_mblock(c, &pool);
}

static int wrd_hexval(char *hex)
{
    int val, neg;

    if(!*hex)
	return WRD_NOARG;

    if(*hex != '-')
	neg = 0;
    else
    {
	neg = 1;
	hex++;
    }
    val = 0;
    for(;;)
    {
	if('0' <= *hex && *hex <= '9')
	    val = (val << 4) | (*hex - '0');
	else if('a' <= *hex && *hex <= 'f')
	    val = (val << 4) | (*hex - 'a' + 10);
	else if('A' <= *hex && *hex <= 'F')
	    val = (val << 4) | (*hex - 'A' + 10);
	else
	    break;
	hex++;
    }
    return neg ? -val : val;
}

static int wrd_eint(char *s)
{
    if(*s == '\0')
	return WRD_NOARG;
    if(*s != '$')
	return atoi(s);
    return wrd_hexval(s + 1);
}

static int wrd_atoi(char *val, int default_value)
{
    while(*val && (*val < '0' || '9' < *val))
	val++;
    return !*val ? default_value : atoi(val);
}

static struct timidity_file *open_wrd_file(struct timiditycontext_t *c, char *fn)
{
    char *wrdfile, *p;
    MBlockList pool;
    struct timidity_file *tf;

    init_mblock(&pool);
    wrdfile = (char *)new_segment(c, &pool, strlen(fn) + 5);
    strcpy(wrdfile, fn);
    if((p = strrchr(wrdfile, '.')) == NULL)
    {
	reuse_mblock(c, &pool);
	return NULL;
    }
    if('A' <= p[1] && p[1] <= 'Z')
	strcpy(p + 1, "WRD");
    else
	strcpy(p + 1, "wrd");

    tf = open_file(c, wrdfile, 0, OF_NORMAL);
    reuse_mblock(c, &pool);
    return tf;
}

static void wrd_readinit(struct timiditycontext_t *c)
{
    wrd_nexttok(c, NULL);
    c->wrd_version = -1;
    c->wrd_lineno = 0;
    c->wrd_last_event_time = 0;
}

/* return 1 if line is modified */
static int connect_wrd_line(char *line)
{
    int len;

    len = strlen(line);
    if(len > 1 && line[len - 2] != ';')
    {
	line[len - 1] = ';';
	line[len] = '\n';
	line[len + 1] = '\0';
	return 1;
    }
    return 0;
}

static void mimpi_bug_emu(struct timiditycontext_t *c, int cmd, char *linebuf)
{
    if(c->mimpi_bug_emulation_level >= 1 && c->wrd_version <= 0)
    {
	switch(c->wrd_bugstatus)
	{
	  case 0:  /* Normal state (0) */
	  bugstate_0:
	    if(cmd == WRD_WAIT)
	    {
		if(connect_wrd_line(linebuf))
		    WRD_BUGEMUINFO(105);
		c->wrd_bugstatus = 2; /* WRD_WAIT shift */
	    }
	    else if(c->mimpi_bug_emulation_level >= 2 &&
		    cmd == WRD_REST)
	    {
		if(connect_wrd_line(linebuf))
		    WRD_BUGEMUINFO(206);
		c->wrd_bugstatus = 4; /* REST shift */
	    }
	    else if(c->mimpi_bug_emulation_level >= 8 && /* For testing */
		    cmd == WRD_WMODE)
		c->wrd_bugstatus = 3; /* WMODE shift */
	    break;

	  case 2: /* WRD_WAIT shift */
	    if(c->mimpi_bug_emulation_level >= 2)
	    {
		if(connect_wrd_line(linebuf))
		    WRD_BUGEMUINFO(212);
	    }
	    else if(cmd == WRD_WMODE)
	    {
		if(connect_wrd_line(linebuf))
		    WRD_BUGEMUINFO(107);
	    }
	    c->wrd_bugstatus = 0;
	    goto bugstate_0;

	  case 3: /* Testing */
	    if(cmd > 0 && connect_wrd_line(linebuf))
		WRD_BUGEMUINFO(808);
	    c->wrd_bugstatus = 0;
	    goto bugstate_0;

	  case 4: /* WRD_REST shift */
	    if(connect_wrd_line(linebuf))
		WRD_BUGEMUINFO(209);
	    c->wrd_bugstatus = 0;
	    goto bugstate_0;
	}
    }
}

static int wrd_nexttok(struct timiditycontext_t *c, struct timidity_file *tf)
{
    int ch, len;

    if(tf == NULL)
    {
	c->wrd_nexttok_waitflag = 0;
	c->wrd_nexttok_tokp = 0;
	c->wrd_nexttok_linebuf[0] = '\0';
	c->wrd_bugstatus = 0;
	c->wrd_wmode_prev_step = 0;
	return 1;
    }

    if(c->wrd_nexttok_waitflag)
    {
	c->wrd_nexttok_waitflag = 0;
	c->wrd_tok = WRD_STEP;
	return 1;
    }

  retry_read:
    if(!c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp])
    {
	c->wrd_nexttok_tokp = 0;
	c->wrd_wmode_prev_step = 0;
	c->wrd_lineno++;
	if(tf_gets(c, (char *)c->wrd_nexttok_linebuf, MAXTOKLEN, tf) == NULL)
	{
	    c->wrd_tok = WRD_EOF;
	    return 0;
	}

	len = strlen((char *)c->wrd_nexttok_linebuf); /* 0 < len < MAXTOKLEN */
	if(c->wrd_nexttok_linebuf[len - 1] != '\n') /* c->wrd_nexttok_linebuf must be terminated '\n' */
	{
	    c->wrd_nexttok_linebuf[len] = '\n';
	    c->wrd_nexttok_linebuf[len++] = '\0';
	}
	else if(len > 1 &&
		c->wrd_nexttok_linebuf[len - 2] == '\r' && c->wrd_nexttok_linebuf[len - 1] == '\n')
	{
	    /* CRLF => LF */
	    c->wrd_nexttok_linebuf[len - 2] = '\n';
	    c->wrd_nexttok_linebuf[len - 1] = '\0';
	    len--;
	}
    }

  retry_parse:
    if(c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == WRDENDCHAR)
    {
	c->wrd_tok = WRD_EOF;
	return 0;
    }

    if(c->wrd_nexttok_tokp == 0 && c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] != '@' && c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] != '^') /* Lyric */
    {
	len = 0;
	ch = 0; /* Shut gcc-Wall up! */

	while(len < MAXTOKLEN)
	{
	    ch = c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp++];
	    if(ch == '\n' || ch == WRDENDCHAR)
		break;
	    c->wrd_tokval[len++] = ch;
	}
	c->wrd_tokval[len] = '\0';
	c->wrd_tok = WRD_LYRIC;
	if(ch == WRDENDCHAR)
	{
	    c->wrd_nexttok_tokp = 0;
	    c->wrd_nexttok_linebuf[0] = WRDENDCHAR;
	}
	return 1;
    }

    /* Command */

    if(c->wrd_nexttok_tokp == 0)
    {
	int i;
	/* tab to space */
	for(i = 0; c->wrd_nexttok_linebuf[i]; i++)
	    if(c->wrd_nexttok_linebuf[i] == '\t')
		c->wrd_nexttok_linebuf[i] = ' ';
    }

    /* Skip white space */
    for(;;)
    {
	if(c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == ' ')
	    c->wrd_nexttok_tokp++;
#ifdef IS_SJIS_ZENKAKU_SPACE
	else if(IS_SJIS_ZENKAKU_SPACE(c->wrd_nexttok_linebuf + c->wrd_nexttok_tokp))
	    c->wrd_nexttok_tokp += 2;
#endif /* IS_SJIS_ZENKAKU_SPACE */
	else
	    break;
    }

    ch = c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp++];

    if(ch == '\n')
    {
	c->wrd_tok = WRD_STEP;
	return 1;
    }

    if(ch == ';')
    {
	if(c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == '\n')
	{
	    c->wrd_nexttok_tokp = 0;
	    c->wrd_nexttok_linebuf[0] = '\0';
	}
	goto retry_read;
    }

    if(ch == '@' || ch == '^') /* command */
    {
	int cmd, save_tokp;

	c->wrd_tok = (ch == '@' ? WRD_COMMAND : WRD_ECOMMAND);
	save_tokp = c->wrd_nexttok_tokp;

	len = 0;
#ifdef IS_SJIS_ZENKAKU_SPACE
	if(IS_SJIS_ZENKAKU_SPACE(c->wrd_nexttok_linebuf + c->wrd_nexttok_tokp)) {
	    /* nop */
	    mimpi_bug_emu(c, -1, (char *) c->wrd_nexttok_linebuf);
	    c->wrd_nexttok_tokp += 2;
	    goto retry_parse;
	}
#endif /* IS_SJIS_ZENKAKU_SPACE */

	if(c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == ' ' ||
	   c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == '\n' ||
	   c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == WRDENDCHAR ||
	   c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == ';')
	{
	    /* nop */
	    mimpi_bug_emu(c, -1, (char *) c->wrd_nexttok_linebuf);
	    goto retry_parse;
	}

	while(len < MAXTOKLEN)
	{
	    ch = c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp++];
	    if(!isalpha(ch))
		break;
	    c->wrd_tokval[len++] = toupper(ch);
	}
	c->wrd_tokval[len] = '\0';
	cmd = c->wrd_tokval[0] = cmdlookup(c->wrd_tokval);

	if(ch != '(' || cmd == 0)
	{
	    len = 1;
	    if(cmd == 0) {
		/* REM */
		c->wrd_nexttok_tokp = save_tokp;
		cmd = c->wrd_tokval[0] = WRD_REM;
	    } else {
		c->wrd_nexttok_linebuf[--c->wrd_nexttok_tokp] = ch;	/* Putback advanced char */
		/* skip spaces */
		while(c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == ' ')
		    c->wrd_nexttok_tokp++;
	    }

	    if(cmd == WRD_STARTUP)
	    {
		while(len < MAXTOKLEN)
		{
		    ch = c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp++];
		    if((ch == ';' && c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == '\n') ||
		       ch == '\n' ||
		       ch == WRDENDCHAR ||
		       ch == '@' ||
		       ch == ' ')
			break;
		    c->wrd_tokval[len++] = ch;
		}
	    }
	    else
	    {
		while(len < MAXTOKLEN)
		{
		    ch = c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp++];
		    if((ch == ';' && c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == '\n') ||
		       ch == '\n' ||
		       ch == WRDENDCHAR)
			break;
		    c->wrd_tokval[len++] = ch;
		}
	    }
	    c->wrd_nexttok_linebuf[--c->wrd_nexttok_tokp] = ch;	/* Putback advanced char */
	    c->wrd_tokval[len] = '\0';
	    return 1;
	}

	if(c->wrd_tok == WRD_ECOMMAND)
	{
	    if(cmd == WRD_PAL)
		c->wrd_tokval[0] = WRD_ePAL;
	    else if(cmd == WRD_SCROLL)
		c->wrd_tokval[0] = WRD_eSCROLL;
	}

	len = 1;
	while(len < MAXTOKLEN)
	{
	    ch = c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp++];
	    if(ch == ')' || ch == '\n' || ch == WRDENDCHAR)
		break;
	    c->wrd_tokval[len++] = ch;
	}
	c->wrd_tokval[len] = '\0';
	mimpi_bug_emu(c, c->wrd_tokval[0], (char *) c->wrd_nexttok_linebuf);

	if(ch == WRDENDCHAR)
	{
	    c->wrd_nexttok_tokp = 0;
	    c->wrd_nexttok_linebuf[0] = WRDENDCHAR;
	}
	return 1;
    }

    /* This is quick hack for informal WRD file */
    if(ch == ':' && c->mimpi_bug_emulation_level >= 1)
    {
	WRD_BUGEMUINFO(111);
	goto retry_parse;
    }

    /* Convert error line to @REM format */
    c->wrd_nexttok_linebuf[--c->wrd_nexttok_tokp] = ch;	/* Putback advanced char */
    len = 0;
    c->wrd_tok = WRD_COMMAND;
    c->wrd_tokval[len++] = WRD_REM;

    while(len < MAXTOKLEN)
    {
	ch = c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp++];
	if((ch == ';' && c->wrd_nexttok_linebuf[c->wrd_nexttok_tokp] == '\n') ||
	   ch == '\n' ||
	   ch == WRDENDCHAR)
	    break;
	c->wrd_tokval[len++] = ch;
    }
    c->wrd_nexttok_linebuf[--c->wrd_nexttok_tokp] = ch;	/* Putback advanced char */
    c->wrd_tokval[len] = '\0';
    return 1;
}

static uint8 cmdlookup(uint8 *cmd)
{
    switch(cmd[0])
    {
      case 'C':
	return WRD_COLOR;
      case 'E':
	if(cmd[1] == 'N')
	    return WRD_END;
	if(cmd[1] == 'S')
	    return WRD_ESC;
	return WRD_EXEC;
      case 'F':
	if(cmd[1] == 'A')
	    return WRD_FADE;
	if(cmd[4] == 'M')
	    return WRD_eFONTM;
	if(cmd[4] == 'P')
	    return WRD_eFONTP;
	return WRD_eFONTR;
      case 'G':
	if(cmd[1] == 'O')
	    return WRD_GON;
	if(cmd[1] == 'S')
	{
	    if(cmd[3] == 'R')
		return WRD_GSCREEN;
	    return WRD_eGSC;
	}
	if(cmd[3] == 'R')
	    return WRD_GCIRCLE;
	if(cmd[3] == 'S')
	    return WRD_GCLS;
	if(cmd[3] == 'I')
	    return WRD_GINIT;
	if(cmd[3] == 'N')
	    return WRD_GLINE;
	if(cmd[3] == 'D')
	    return WRD_GMODE;
	return WRD_GMOVE;
      case 'I':
	return WRD_INKEY;
      case 'L':
	if(cmd[2] == 'C')
	    return WRD_LOCATE;
	if(cmd[2] == 'N')
	    return WRD_eLINE;
	return WRD_LOOP;
      case 'M':
	if(cmd[1] == 'A')
	    return WRD_MAG;
	return WRD_MIDI;
      case 'O':
	return WRD_OFFSET;
      case 'P':
	if(cmd[3] == '\0')
	    return WRD_PAL;
	if(cmd[1] == 'L')
	    return WRD_PLOAD;
	if(cmd[2] == 'T')
	    return WRD_PATH;
	if(cmd[3] == 'C')
	    return WRD_PALCHG;
	return WRD_PALREV;
      case 'R':
	if(cmd[2] == 'G')
	    return WRD_eREGSAVE;
	if(cmd[2] == 'S')
	    return WRD_REST;
	if(cmd[3] == 'A')
	    return WRD_REMARK;
	return WRD_REM;
      case 'S':
	if(cmd[3] == 'E')
	    return WRD_SCREEN;
	if(cmd[3] == 'O')
	    return WRD_SCROLL;
	if(cmd[3] == 'R')
	    return WRD_STARTUP;
	return WRD_STOP;
      case 'T':
	if(cmd[1] == 'C')
	    return WRD_TCLS;
	if(cmd[1] == 'E')
	    return WRD_eTEXTDOT;
	if(cmd[1] == 'M')
	    return WRD_eTMODE;
	if(cmd[1] == 'O')
	    return WRD_TON;
	return WRD_eTSCRL;
      case 'V':
	if(cmd[2] == 'O')
	    return WRD_eVCOPY;
	if(cmd[2] == 'G')
	    return WRD_eVSGET;
	return WRD_eVSRES;
      case 'W':
	if(cmd[1] == 'A')
	    return WRD_WAIT;
	return WRD_WMODE;
      case 'X':
	return WRD_eXCOPY;
    }
    return 0;
}

static int wrd_split(char* arg, char** argv, int maxarg)
{
    int i, j;

#if defined(ABORT_AT_FATAL) || defined(DEBUG)
    if(maxarg < 2) {
	fprintf(stderr,
		"wrd_read.c: wrd_split(): maxarg must be more than 1.\n");
	abort();
    }
#endif

    for(i = 0; *arg && i < maxarg; i++)
    {
	argv[i] = arg;
	while(*arg && *arg != ',' && *arg != ';')
	    arg++;
	if(!*arg)
	{
	    i++;
	    break;
	}
	*arg++ = '\0';
    }
    for(j = i; j < maxarg; j++)
	argv[j] = "";
    return i;
}

#ifdef DEBUG
static char *wrd_name_string(int cmd)
{
    switch(cmd)
    {
#define WRDCASE(cmd) case WRD_ ## cmd: return #cmd
	WRDCASE(COMMAND);
	WRDCASE(ECOMMAND);
	WRDCASE(STEP);
	WRDCASE(LYRIC);
	WRDCASE(EOF);
	WRDCASE(COLOR);
	WRDCASE(END);
	WRDCASE(ESC);
	WRDCASE(EXEC);
	WRDCASE(FADE);
	WRDCASE(GCIRCLE);
	WRDCASE(GCLS);
	WRDCASE(GINIT);
	WRDCASE(GLINE);
	WRDCASE(GMODE);
	WRDCASE(GMOVE);
	WRDCASE(GON);
	WRDCASE(GSCREEN);
	WRDCASE(INKEY);
	WRDCASE(LOCATE);
	WRDCASE(LOOP);
	WRDCASE(MAG);
	WRDCASE(MIDI);
	WRDCASE(OFFSET);
	WRDCASE(PAL);
	WRDCASE(PALCHG);
	WRDCASE(PALREV);
	WRDCASE(PATH);
	WRDCASE(PLOAD);
	WRDCASE(REM);
	WRDCASE(REMARK);
	WRDCASE(REST);
	WRDCASE(SCREEN);
	WRDCASE(SCROLL);
	WRDCASE(STARTUP);
	WRDCASE(STOP);
	WRDCASE(TCLS);
	WRDCASE(TON);
	WRDCASE(WAIT);
	WRDCASE(WMODE);
	WRDCASE(eFONTM);
	WRDCASE(eFONTP);
	WRDCASE(eFONTR);
	WRDCASE(eGSC);
	WRDCASE(eLINE);
	WRDCASE(ePAL);
	WRDCASE(eREGSAVE);
	WRDCASE(eSCROLL);
	WRDCASE(eTEXTDOT);
	WRDCASE(eTMODE);
	WRDCASE(eTSCRL);
	WRDCASE(eVCOPY);
	WRDCASE(eVSGET);
	WRDCASE(eVSRES);
	WRDCASE(eXCOPY);
	WRDCASE(ARG);
	WRDCASE(FADESTEP);
	WRDCASE(OUTKEY);
	WRDCASE(NL);
	WRDCASE(MAGPRELOAD);
	WRDCASE(PHOPRELOAD);
	WRDCASE(START_SKIP);
	WRDCASE(END_SKIP);
	WRDCASE(NOARG);
#undef WRDCASE
    }
    return "Unknown";
}
#endif /* DEBUG */

#ifdef ENABLE_SHERRY
/*******************************************************************************/
#if 0
/* for mac only */
#pragma mark -
#endif

static int32 sry_getVariableLength(struct timidity_file	*tf)
{
  int32 value= 0;
  int tmp;
  do
    {
      tmp = tf_getc(tf);
      if(tmp == EOF)
	  return -1;
      value = (value << 7) + (tmp&0x7f);
    }while ((tmp&0x80) != 0);
  return value;
}

static int sry_check_head(struct timiditycontext_t *c, struct timidity_file	*tf)
{
	char	magic[12];
	uint8	version[4];

	tf_read(c, magic, 12,1,tf);
	if( memcmp(magic, "Sherry WRD\0\0", 12) ){
		ctl->cmsg(c, CMSG_WARNING, VERB_NORMAL,
			  "Sherry open::Header NG." );
		return 1;
	}
	tf_read(c, version, 1, 4, tf);
	ctl->cmsg(c, CMSG_INFO, VERB_VERBOSE,
		  "Sherry WRD version: %02x %02x %02x %02x",
		  version[0], version[1], version[2], version[3]);

	return 0;	/*good*/
}

struct sry_drawtext_{
	char	op;
	short	v_plane;
	char	mask;
	char	mode;
	char	fore_color;
	char	back_color;
	short	x;
	short	y;
	char	*text;
};
typedef struct sry_drawtext_ sry_drawtext;

static void sry_regist_datapacket(struct timiditycontext_t *c, struct wrd_step_tracer* wrdstep,
					const sry_datapacket* packet)
{
    int A, B, C;
    int err = 0;

    if(c->datapacket == NULL)
    {
	c->datapacket = (sry_datapacket *)safe_malloc(DEFAULT_DATAPACKET_LEN *
						   sizeof(sry_datapacket));
	c->datapacket_len = DEFAULT_DATAPACKET_LEN;
	c->datapacket_cnt = 0;
    }
    else
    {
	if(c->datapacket_cnt == (1<<24)-1) /* Over flow */
	    err = 1;
	else
	{
	    if(c->datapacket_cnt >= c->datapacket_len)
	    {
		c->datapacket_len *= 2;
		c->datapacket = (sry_datapacket *)
		    safe_realloc(c->datapacket,
				 c->datapacket_len * sizeof(sry_datapacket));
	    }
	}
    }

    A = c->datapacket_cnt & 0xff;
    B = (c->datapacket_cnt >> 8) & 0xff;
    C = (c->datapacket_cnt >> 16) & 0xff;
    c->datapacket[c->datapacket_cnt] = *packet;
    MIDIEVENT(wrdstep->at, ME_SHERRY, A, B, C);

    if(err)
	c->datapacket[c->datapacket_cnt].data[0] = 0xff;
    else
	c->datapacket_cnt++;
}

static int sry_read_datapacket(struct timiditycontext_t *c, struct timidity_file *tf, sry_datapacket* packet)
{
	int	len;
	uint8	*data;

	do
	{
	    len = sry_getVariableLength(tf);
	    if(len < 0)
	    {
		ctl->cmsg(c, CMSG_ERROR, VERB_NORMAL,
			  "Warning: Too shorten Sherry WRD file.");
		return 1;
	    }
	} while(len == 0);
	data =	(uint8 *)new_segment(c, &c->sry_pool, len + 1);
	if(tf_read(c, data, 1, len, tf) < len)
	{
	    ctl->cmsg(c, CMSG_ERROR, VERB_NORMAL,
		      "Warning: Too shorten Sherry WRD file.");
	    return 1;
	}
	data[len]=0;
	packet->len= len;
	packet->data= data;
	return 0;
}

static void sry_timebase21(struct timiditycontext_t *c, struct wrd_step_tracer* wrdstep, int timebase)
{
    c->sherry_started=0;
    memset(wrdstep, 0, sizeof(struct wrd_step_tracer));
    init_mblock(&wrdstep->pool);
    wrdstep->de = wrdstep->free_de = NULL;
    wrdstep->timebase = timebase; /* current_file_info->divisions; */
    wrdstep->ntimesig = dump_current_timesig(c, wrdstep->timesig, MAXTIMESIG - 1);
    if(wrdstep->ntimesig > 0)
    {
	wrdstep->timesig[wrdstep->ntimesig] =
	    wrdstep->timesig[wrdstep->ntimesig - 1];
	wrdstep->timesig[wrdstep->ntimesig].time = 0x7fffffff; /* stopper */
#ifdef DEBUG
	{
	int i;
	fprintf(stderr, "Time signatures:\n");
	for(i = 0; i < wrdstep->ntimesig; i++)
	    fprintf(stderr, "  %d: %d/%d\n",
		   wrdstep->timesig[i].time,
		   wrdstep->timesig[i].a,
		   wrdstep->timesig[i].b);
	}
#endif /* DEBUG */
	wrdstep->barstep =
	    wrdstep->timesig[0].a * wrdstep->timebase * 4 / wrdstep->timesig[0].b;
    }
    else
	wrdstep->barstep = 4 * wrdstep->timebase;
    wrdstep->step_inc = wrdstep->barstep;
    wrdstep->last_at = readmidi_set_track(c, 0, 0);

    readmidi_set_track(c, 0, 1);
    /* wrdstep.step_inc = wrdstep.timebase; */

#ifdef DEBUG
    fprintf(stderr, "Timebase: %d, divisions:%d\n",
		wrdstep->timebase, current_file_info->divisions);
    fprintf(stderr, "Step: %d\n", wrdstep->step_inc);
#endif /* DEBUG */
}

static void sry_timebase22(struct timiditycontext_t *c, struct wrd_step_tracer* wrdstep, int mode)
{
    c->sry_timebase_mode = mode;
    if(c->sry_timebase_mode)
	ctl->cmsg(c, CMSG_WARNING, VERB_NORMAL,
		  "Sherry time synchronize mode is not supported");
}

static void sry_wrdinfo(struct timiditycontext_t *c, uint8 *info, int len)
{
    uint8 *info1, *info2, *desc;
    int i;

    /* "info1\0info2\0desc\0" */
    /* FIXME: Need to convert SJIS to "output_text_code" */

    i = 0;
    info1 = info;
    while(i < len && info[i])
	i++;
    i++; /* skip '\0' */
    if(i >= len)
	return;
    info2 = info + i;
    while(i < len && info[i])
	i++;
    i++; /* skip '\0' */
    if(i >= len)
	return;
    desc = info + i;

    ctl->cmsg(c, CMSG_INFO, VERB_VERBOSE,
	      "Sherry WRD: %s: %s: %s", info1, info2, desc);
}

static void sry_show_debug(struct timiditycontext_t *c, uint8 *data)
{
    switch(data[0])
    {
      case 0x71: /* Compiler name */
	if(data[1])
	    ctl->cmsg(c, CMSG_INFO, VERB_NOISY,
		      "Sherry WRD Compiler: %s", data + 1);
	break;
      case 0x72: /* Source Name */
	if(data[1])
	    ctl->cmsg(c, CMSG_INFO, VERB_NOISY,
		      "Sherry WRD Compiled from %s", data + 1);
	break;
      case 0x7f: /* Compiler Private */
	break;
    }
}

static void sry_read_headerblock(struct timiditycontext_t *c, struct wrd_step_tracer* wrdstep,
					struct timidity_file *tf)
{
	sry_datapacket	packet;
	int err;

	packet.len = 1;
	packet.data = (uint8 *)new_segment(c, &c->sry_pool, 1);
	packet.data[0] = 0x01;
	sry_regist_datapacket(c, wrdstep , &packet);

	for(;;){
		err= sry_read_datapacket(c, tf, &packet);
		if( err ) break;
		sry_regist_datapacket(c, wrdstep , &packet);
		switch(packet.data[0])
		{
		  case 0x00: /* end of header */
		    return;
		  case 0x20:
		    if( c->mimpi_bug_emulation_level >= 1 ){
		      sry_timebase21(c, wrdstep, SRY_GET_SHORT(packet.data+1));
		    }
		    break;
		  case 0x21:
		    sry_timebase21(c, wrdstep, SRY_GET_SHORT(packet.data+1));
		    break;
		  case 0x22:
		    sry_timebase22(c, wrdstep, packet.data[1]);
		    break;
		  case 0x61:
		    sry_wrdinfo(c, packet.data + 1, packet.len - 1);
		    break;
		  default:
		    if((packet.data[0] & 0x70) == 0x70)
			sry_show_debug(c, packet.data);
		    break;
		}
	}
}

static void sry_read_datablock(struct timiditycontext_t *c, struct wrd_step_tracer* wrdstep,
				  struct timidity_file	*tf)
{
    sry_datapacket  packet;
    int		    delta_time; /*, cur_time=0; */
    int		    err;
    int		    need_update;

    need_update = 0;
    for(;;){
		delta_time= sry_getVariableLength(tf);
		if(delta_time > 0 && need_update)
		{
		    WRD_ADDEVENT(wrdstep->at, WRD_SHERRY_UPDATE, WRD_NOARG);
		    need_update = 0;
		}
		err = sry_read_datapacket(c, tf, &packet);
		if( err ) break;
		/* cur_time =+ delta_time;*/
		/* wrdstep_wait(wrdstep, delta_time,0); */
		/* wrdstep_setstep(wrdstep, delta_time); */

		if( c->sherry_started && delta_time ){
		    wrdstep_inc(c, wrdstep,
				delta_time*current_file_info->divisions
				/wrdstep->timebase);
		}

		if( packet.data[0]==0x01 ){
			c->sherry_started=1;
			continue;
		} else if( (packet.data[0]&0x70) == 0x70) {
		    sry_show_debug(c, packet.data);
		}

		sry_regist_datapacket(c, wrdstep , &packet);
		if(packet.data[0] == 0x31 ||
		   packet.data[0] == 0x35 ||
		   packet.data[0] == 0x36)
		    need_update = 1;

		if( packet.data[0] == 0x00 ) break;
    }
    if(need_update)
    {
	WRD_ADDEVENT(wrdstep->at, WRD_SHERRY_UPDATE, WRD_NOARG);
	need_update = 0;
    }
}

static int import_sherrywrd_file(struct timiditycontext_t *c, const char * fn)
{
	char	sry_fn[256];
	char	*cp;
	struct timidity_file	*tf;
    struct wrd_step_tracer wrdstep;

	strncpy(sry_fn, fn, sizeof(sry_fn));
	cp=strrchr(sry_fn, '.');
	if( cp==0 ) return 0;

	strncpy(cp+1, "sry", sizeof(sry_fn) - (cp - sry_fn) - 1);
	tf= open_file(c, sry_fn, 0, OF_NORMAL);
	if( tf==NULL ) return 0;
	if( sry_check_head(c, tf)!=0 ) return 0;
	ctl->cmsg(c, CMSG_INFO, VERB_NORMAL,
		  "%s: reading sherry data...", sry_fn);

	wrd_readinit(c);
	memset(&wrdstep, 0, sizeof(wrdstep));

/**********************/
/*    MIDIEVENT(0, ME_SHERRY_START, 0, 0, 0); */
    sry_read_headerblock(c, &wrdstep, tf);
    sry_read_datablock(c, &wrdstep, tf);

/*  end_of_wrd: */
    while(wrdstep.de)
    {
	wrdstep_nextbar(c, &wrdstep);
    }
    reuse_mblock(c, &wrdstep.pool);
    close_file(c, tf);
#ifdef DEBUG
    fflush(stderr);
#endif /* DEBUG */
    return 1;
}

#endif /*ENABLE_SHERRY*/
