/* FSPConv Application */
/* A. Southgate 1993 */
/* Originally Compiled with Norcroft C 3.00 */
/* There's a bit of a bodge going on here.
The upshot is that horizontal locking is dodgy unless either all or none
of the h-lock toggles are set */


#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "baricon.h"
#include "event.h"
#include "res.h"
#include "resspr.h"
#include "template.h"
#include "os.h"
#include "dbox.h"
#include "dboxquery.h"
#include "menu.h"
#include "flex.h"
#include "heap.h"
#include "werr.h"
#include "visdelay.h"
#include "xfersend.h"
#include "xferrecv.h"
#include "colourtran.h"
#include "coords.h"
#include "bbc.h"
#include <stdlib.h>
#include <string.h>



#define Prog_name "FSPConv"
#define Prog_spr_name "!FSPConv"
#define Version_string "2.12 (25th April 1995)"
#define Wimp_sprite_area 1

#define XOS_File 0x20008
#define XOS_SpriteOp 0x2002e
#define XWimp_StartTask 0x600de
#define XWimp_DecodeMenu 0x600d5
#define Sprite_filetype 0x0ff9
#define FastSpr_filetype 0x0113
#define FSPConv_max_name 255
#define FSPConv_null_name "Null"
#define Max_frame 16
#define Fastspr_filetype 0x113

#define ModeVars_LineLength 6
#define Modevars_Log2BPP 9

/* Menu Items */

#define Bar_menu_info 0
#define Bar_menu_transp 1
#define Bar_menu_convert 2
#define Bar_menu_help 3
#define Bar_menu_quit 4
#define Sprite_menu_convert 0
#define Sprite_menu_fake 1
#define Sprite_menu_discard 2
#define Sprite_menu_help 3


#define Isprite_number 6
#define Isprite_frame 10
#define Isprite_xsize 13
#define Isprite_ysize 14
#define Isprite_mode 15
#define Isprite_xcent 17
#define Isprite_ycent 21
#define Isprite_sprite 0
#define Isprite_name 29
#define Isprite_type 31
#define Isprite_typemenu 32
#define Isprite_preshift 33
#define Isprite_pre0 34
#define Isprite_pre1 35
#define Isprite_pre2 36
#define Isprite_pre3 37
#define Isprite_premenu 38
#define Isprite_all 39
#define Isprite_hlock 40
#define Isprite_none 41
#define Isprite_str 42
#define Isprite_stm 43

#define Ispeed_preludetime 2
#define Ispeed_stmtime 3
#define Ispeed_strtime 4
#define Ispeed_strbtime 5
#define Ispeed_toaltime 6
#define Ispeed_stmopt 7
#define Ispeed_stropt 8
#define Ispeed_unclipped 9
#define Ispeed_rightclip 10
#define Ispeed_leftclip 11
#define Ispeed_bothclip 12
#define Ispeed_cancel 13
#define Ispeed_calculate 14

#define Iconvert_fileicon 4
#define Iconvert_filename 6
#define Iconvert_filetype 9
#define Iconvert_sourcesize 11
#define Iconvert_outputsize 14
#define Iconvert_cancel 10
#define Iconvert_convert 8
#define Iconvert_message 15
#define Iconvert_getlength 16

#define Itransp_transp 1

#define Ifake_fake 1
#define Ifake_use  2

#define Centre_menu_centre 0
#define Centre_menu_left 1
#define Centre_menu_right 2
#define Centre_menu_top 3
#define Centre_menu_bottom 4
#define Centre_menu_topleft 5
#define Centre_menu_topright 6
#define Centre_menu_bottomleft 7
#define Centre_menu_bottomright 8
#define Centre_menu_all 9
#define Centre_menu_help 10

#define Type_menu_fsp1 0
#define Type_menu_fsp2 1
#define Type_menu_help 2

#define Preshift_menu_all 0
#define Preshift_menu_none 1
#define Preshift_menu_just0 2
#define Preshift_menu_0and2 3
#define Preshift_menu_appall 4
#define Preshift_menu_help 5
#define Preshift_menu_hlock 6 /* Not a real menu item */

#define Smask_0 1
#define Smask_1 2
#define Smask_2 4
#define Smask_3 8
#define Smask_stm 16
#define Smask_str 32



typedef enum {
  M_iconbar,
  M_sprite_number,
  M_sprite_frame,
  M_sprite_centre,
  M_sprite_preshift,
  M_sprite_type,
  M_sprite_normal
} Menu_type;

typedef struct {
  char  id[4];
  int   res1;
  int   basemode;
  int   number;
} FSP1_header;

typedef struct {
  char  id[4];
  int   number;
  int   basemode;
} FSP2_header;

typedef struct {
  int   entry[1];
} FSP2_offset;

typedef struct {
  char  xsize[1];
  char  ysize[1];
  char  xofs[1];
  char  yofs[1];
} FSP1_entry;
#define Sizeof_FSP1_entry 4

typedef struct {
  short ysize;  /* Reversed because of how short works */
  short xsize;
  short yofs;
  short xofs;
  int   stmofs;
  int   strofs;
  int   strbofs;
  int   advofs;
  int   data[1];
} FSP2_entry;
#define Sizeof_FSP2_entry 24


typedef struct {
  FSP1_header   header;
  FSP1_entry    entry[1];
} FSP1_file;

typedef struct {
  FSP2_header   header;
  FSP2_offset   ofs;
  FSP2_entry    entry[1];
} FSP2_file;

typedef struct {
  int   width;
  int   height;
  int   bwidth;
  int   rbytes;
  int   hbytes;
} Conv_det;

typedef struct {
  BOOL          loaded;
  BOOL          saved;
  int           size;
  int           number;
  int           last_number;
  int           last_frame;
  int           frame;
  int           no_frames;
  sprite_area   *area;
  sprite_id     id;
  char          name[12];
  char          filename[FSPConv_max_name];
} Conv_sprites;

typedef struct {
  int           number;
  int           total;
  int           max;
} Conv_info_header;

typedef struct {
  int           number;
  int           frames;
  int           xcent[Max_frame];
  int           ycent[Max_frame];
  int           type;
} Conv_info_entry;

/* Change info_size if you alter this */

typedef struct {
  Conv_info_header      header;
  Conv_info_entry       entry[1];
} Conv_info;

/*  Globals  */
static Conv_sprites conv_sprites;
static Conv_info *conv_info=0;
static wimp_w sprite_whandle;
static dbox sprite_dbox;
static dbox convert_dbox;
static char save_filename[255];
static sprite_pixtrans trans[256];
static menu bar_menu,
            sprite_menu,
            sprite_number_menu,
            sprite_frame_menu,
            sprite_centre_menu,
            sprite_preshift_menu,
            sprite_type_menu;

static Menu_type menu_type;

static int transp = 0;
static int fsp_type = 1;
static int lastcentre = 9;

static int      fsp_ofs;
static int      anim_ofs;
static int      spr_ofs;
static char     *fsp_area_ptr;
static char     **fsp_area = &fsp_area_ptr;

static int      fsp_area_size = 0;
static int      conv_no =-1;
static int 	fakemode=13;
static BOOL	fake_use=FALSE;
static BOOL     convert_dbox_open = FALSE;
static BOOL     conv_info_valid = FALSE;
static BOOL 	convert_getlength;

static char *typestr1 = "FSP1";
static char *typestr2 = "FSP2";
static char *typestr3 = "FSP?";

static int sequence_length[17] =
{0,1,2,4,4,8,8,8,8,16,16,16,16,16,16,16,16};

static char sequence[17][16] =
{
 {0},
 {0},
 {0,1},
 {0,1,2,1},
 {0,1,2,3},
 {0,1,2,3,4,3,2,1},
 {0,1,2,3,4,5,5,5},
 {0,1,2,3,4,5,6,6},
 {0,1,2,3,4,5,6,7},
 {0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1},
 {0,1,2,3,4,5,6,7,8,9,9,9,9,9,9,9},
 {0,1,2,3,4,5,6,7,8,9,10,10,10,10,10,10},
 {0,1,2,3,4,5,6,7,8,9,10,11,11,11,11,11},
 {0,1,2,3,4,5,6,7,8,9,10,11,12,12,12,12},
 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,13},
 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14},
 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
};

/* -----Coding -----*/

static void quad_align(int *ptr, int value)
{
  int inc, i;
  inc = 12 - ((value) & 15);
  for (i=0; i<inc; i+=4)
    *(int *)&(*fsp_area)[value + i] = 0x00120969;
  *ptr += inc;
}

static void start_task(char *filename)
{
  wimpt_complain(os_swi1(XWimp_StartTask, (int) filename));
}

static void limit(int *ptr, int lowlim, int highlim)
{
  if (*ptr < lowlim) *ptr=lowlim;
  if (*ptr > highlim) *ptr=highlim;
}


static void read_trans(int mode)
{
  wimpt_checkmode();
  wimpt_complain(colourtran_select_table(mode, 0, -1,
                    (wimp_paletteword *)-1, trans));
}

static int scan_conv_info(int n)
{
  int i;
  if (!conv_info_valid) return -1;
  for (i=0; i<conv_info->header.number; i++)
    if (conv_info->entry[i].number == n) return i;
  return -1;
}

static sprite_id * current_id(Conv_sprites *s)
{
  int e;

/* return pronto if details already correct */

  if (s->number == s->last_number && s->frame == s->last_frame) return &s->id;

  if (conv_info_valid && (e=scan_conv_info(s->number), e != -1))
  {

    if (conv_info->entry[e].frames > 1)
      sprintf(s->name,"%d_%d",s->number, s->frame);
    else
      sprintf(s->name,"%d",s->number);
  }
  else
  {
      sprintf(s->name,"%d",s->number);
  }

  s->id.s.name=s->name;
  s->id.tag=sprite_id_name;
  s->last_frame=s->frame;
  s->last_number=s->number;
  return &s->id;
}

static sprite_id * conv_id(Conv_sprites *s, Conv_info_entry *e, int f)
{
  if (e->frames > 1)
    sprintf(s->name,"%d_%d",e->number, (int) sequence[e->frames][f]);
  else
    sprintf(s->name,"%d",e->number);

  s->id.s.name=s->name;
  s->id.tag=sprite_id_name;
  return &s->id;
}

static void redraw_sprite_window(Conv_sprites *d)
{
  wimp_redrawstr r;
  coords_cvtstr *c;
  sprite_info sinfo;
  int x,y,div;
  BOOL more = TRUE;
  sprite_factors factors;
  sprite_pixtrans pixtrans[256];
  wimp_icon icon;

/* Set pointer for coords */

  c=(coords_cvtstr *) &r.box;
  r.w = sprite_whandle;
  wimpt_noerr(wimp_redraw_wind(&r, &more));
  if (d->loaded && (sprite_select(d->area, current_id(d)) == 0))
  {
/* Plot a sprite */
/* Get icon infomation */

    wimpt_noerr(wimp_get_icon_info(sprite_whandle, Isprite_sprite, &icon));
    x=16+coords_x_toscreen(icon.box.x0, c);
    y=16+coords_y_toscreen(icon.box.y0, c);
    wimpt_noerr(sprite_readsize(d->area, current_id(d), &sinfo));


    read_trans(sinfo.mode);
    wimpt_noerr(wimp_readpixtrans(d->area, current_id(d), &factors, pixtrans));

    factors.xmag = abs(icon.box.x1-icon.box.x0-32)/wimpt_dx();
    factors.ymag = abs(icon.box.y1-icon.box.y0-32)/wimpt_dy();

    if (sinfo.width>sinfo.height)
    {
      div=sinfo.width;
    }
    else
    {
      div=sinfo.height;
    }
    factors.xdiv=div;
    factors.ydiv=div;

    while (more)
    {
      wimpt_noerr(sprite_put_scaled(conv_sprites.area, &conv_sprites.id, 0,x, y, &factors, trans));

      wimp_get_rectangle(&r, &more);
    }
  }
  else
  {
    while (more)
    {
      wimp_get_rectangle(&r, &more);
    }
  }
}
/*

static BOOL create_window(char *name, wimp_w *handle)
{
  wimp_wind *window;

  window = template_syshandle(name);

  if (window == 0) return FALSE;
  return (wimpt_complain(wimp_create_wind(window, handle)) == 0);
}


static void open_window(wimp_w whandle)
{
  wimp_wstate state;
  if (wimpt_complain(wimp_get_wind_state(whandle, &state)) == 0)
  {
    state.o.behind = -1;
    wimpt_noerr(wimp_open_wind(&state.o));
  }
}

static void redraw_window(wimp_w w)
{
  wimp_redrawstr r;
  r.w=w;
  wimpt_complain(wimp_getwindowoutline(&r));
  r.w=-1;
  wimpt_complain(wimp_force_redraw(&r));
}
*/
static void sprites_dispose(Conv_sprites *s)
{
  if (s->loaded)
  {
    /* Free old memory */
    flex_free((flex_ptr) &s->area);
    s->size = 0;
  }

  /* Cancel loaded flag and clear name */
  s->loaded = FALSE;
  if (conv_info_valid) flex_free((flex_ptr) &conv_info);

  conv_info_valid = FALSE;
  s->last_number=-1;
  win_settitle(sprite_whandle, "No Sprites");
}

static char *type_string(void)
{
  switch (fsp_type)
  {
    case 1:
      return typestr1;

    case 2:
      return typestr2;

    default:
      return typestr3;
  }
}


static void fillin_sprite_window(dbox d, Conv_sprites *s)
{
  int e, type;
  current_id(s);
  dbox_setnumeric(d,Isprite_number,s->number);
  dbox_setfield(d, Isprite_name, s->name);
  dbox_setfield(d, Isprite_type, type_string());
  if (fsp_type == 1)
  {
    dbox_fadefield(d, Isprite_pre0);
    dbox_fadefield(d, Isprite_pre1);
    dbox_fadefield(d, Isprite_pre2);
    dbox_fadefield(d, Isprite_pre3);
    dbox_fadefield(d, Isprite_str);
    dbox_fadefield(d, Isprite_stm);
  }
  else
  {
    dbox_unfadefield(d, Isprite_pre0);
    dbox_unfadefield(d, Isprite_pre1);
    dbox_unfadefield(d, Isprite_pre2);
    dbox_unfadefield(d, Isprite_pre3);
    dbox_unfadefield(d, Isprite_str);
    dbox_unfadefield(d, Isprite_stm);
  }

  e=scan_conv_info(s->number);
  if (s->loaded && e != -1 && (sprite_select(s->area, current_id(s)) == 0))
  {
    sprite_info sinfo;
    sprite_readsize(s->area, current_id(s), &sinfo);
    dbox_setnumeric(d,Isprite_xsize,sinfo.width);
    dbox_setnumeric(d,Isprite_ysize,sinfo.height);
    dbox_setnumeric(d,Isprite_mode,sinfo.mode);

    dbox_setnumeric(d,Isprite_xcent,conv_info->entry[e].xcent[s->frame]);
    dbox_setnumeric(d,Isprite_ycent,conv_info->entry[e].ycent[s->frame]);
    if (conv_info->entry[e].frames > 1)
    {
      dbox_setnumeric(d,Isprite_frame,s->frame);
      dbox_unfadefield(d, Isprite_frame);
    }
    else
    {
      dbox_setfield(d,Isprite_frame,"-");
      dbox_fadefield(d, Isprite_frame);
    }
    if (fsp_type == 1)
     type = 0;
       else
     type = conv_info->entry[e].type;

    dbox_setnumeric(d, Isprite_pre0, (type & Smask_0));
    dbox_setnumeric(d, Isprite_pre1, (type & Smask_1));
    dbox_setnumeric(d, Isprite_pre2, (type & Smask_2));
    dbox_setnumeric(d, Isprite_pre3, (type & Smask_3));
    dbox_setnumeric(d, Isprite_str, (type & Smask_str));
    dbox_setnumeric(d, Isprite_stm, (type & Smask_stm));

  }
  else
  {
    dbox_setfield(d,Isprite_frame,"-");
    dbox_setfield(d,Isprite_xsize,"-");
    dbox_setfield(d,Isprite_ysize,"-");
    dbox_setfield(d,Isprite_mode,"-");
    dbox_setfield(d,Isprite_xcent,"-");
    dbox_setfield(d,Isprite_ycent,"-");
  }
}

static void remake_sprite_window(Conv_sprites *s)
{
  wimp_icon icon;
  wimp_redrawstr r;

  fillin_sprite_window(sprite_dbox, s);

/* Get icon infomation */

  r.w = sprite_whandle;
  wimpt_complain(wimp_get_icon_info(sprite_whandle, Isprite_sprite, &icon));
  r.box.x0=icon.box.x0;
  r.box.y0=icon.box.y0;
  r.box.x1=icon.box.x1;
  r.box.y1=icon.box.y1;
  wimpt_complain(wimp_force_redraw(&r));
}

static void sprite_read_details(dbox d, Conv_sprites *s, int e)
{
  int type;
  if (e != -1)
  {
    conv_info->entry[e].xcent[s->frame]=dbox_getnumeric(d, Isprite_xcent);
    conv_info->entry[e].ycent[s->frame]=dbox_getnumeric(d, Isprite_ycent);
    limit(&conv_info->entry[e].xcent[s->frame], 0, 255);
    limit(&conv_info->entry[e].ycent[s->frame], 0, 255);
    if (fsp_type == 2)
    {
      type = (Smask_0 * dbox_getnumeric(d, Isprite_pre0))
           + (Smask_1 * dbox_getnumeric(d, Isprite_pre1))
           + (Smask_2 * dbox_getnumeric(d, Isprite_pre2))
           + (Smask_3 * dbox_getnumeric(d, Isprite_pre3))
           + (Smask_str * dbox_getnumeric(d, Isprite_str))
           + (Smask_stm * dbox_getnumeric(d, Isprite_stm))
           ;
      conv_info->entry[e].type=type;
    }
  }
}

static void sprite_readfields(dbox d, Conv_sprites *s)
{
  int e, last_number, last_frame;
  last_number=s->number;
  last_frame=s->frame;
  e=scan_conv_info(s->number);
  if (e != -1)
  {
    sprite_read_details(d, s, e);
  }
  s->number=dbox_getnumeric(d, Isprite_number);
  e=scan_conv_info(s->number);
  if (e == -1)
  {
    remake_sprite_window(s);
    return;
  }
  s->frame=dbox_getnumeric(d,Isprite_frame);

  if (last_number != s->number || last_frame != s->frame)
  {
    if (e != -1)
    {
      limit(&s->frame, 0, conv_info->entry[e].frames-1);
      limit(&s->frame, 0, Max_frame);
    }
    remake_sprite_window(s);
  }
}

static void swap_sprite_info(int i, int j)
{
  Conv_info_entry temp;
  memcpy(&temp, &conv_info->entry[i], sizeof(Conv_info_entry));
  memcpy(&conv_info->entry[i], &conv_info->entry[j], sizeof(Conv_info_entry));
  memcpy(&conv_info->entry[j], &temp, sizeof(Conv_info_entry));
}

static void sort_sprite_info(void)
{
  int i;
  for (i=conv_info->header.number-1; i>=1; i--)
  {
    if (conv_info->entry[i].number<conv_info->entry[i-1].number)
    {
      swap_sprite_info(i, i-1);
      i=conv_info->header.number;
    }
  }
}

static void read_sprite_info(Conv_sprites *s)
{
  int i, e, n, number, frame, info_size;
  sprite_area sarea;
  sprite_info sinfo;
  sprite_id id;
  char name[12];
  e=0;
  if (s->loaded)
  {
    visdelay_begin();
    if (conv_info_valid) flex_free((flex_ptr) &conv_info);
    conv_info_valid = FALSE;

    wimpt_complain(sprite_area_readinfo(s->area, &sarea));
    info_size=sizeof(Conv_info_header)+sizeof(Conv_info_entry) * (sarea.number);
    if (flex_alloc((flex_ptr) &conv_info, info_size) == 0)
    {
      visdelay_end();
      werr(0, "Not enough memory for FSPConv");
      sprites_dispose(s);
      return;
    }
    conv_info->header.number=e;
    conv_info->header.max=0;
    conv_info_valid=TRUE;
    for (i=0; i<sarea.number; i++)
    {
      conv_info->entry[i].number = -1;
      conv_info->entry[i].frames = 0;
    }
    for (i=1; i<=sarea.number; i++)
    {
      wimpt_complain(os_swi6(XOS_SpriteOp, 256+13, (int) s->area, (int) name, 12, i, 0));

      switch (sscanf(name, "%d_%d", &number, &frame))
      {
        case 1:
          frame=0;
          break;
        case 2:
          break;
        default:
          number=-1;
          frame=0;
          break;
      }
      if (frame > 15)
      {
        werr(0, "This version of FSPConv cannot cope with more than 16 animation frames");
        sprites_dispose(s);
        return;
      }
      if (number != -1)
      {
        if (number > conv_info->header.max)
          conv_info->header.max = number;
        conv_info->header.number = e;
        if (n = scan_conv_info(number), n < 0)
        {
          n=e;
          id.s.name=name;
          id.tag=sprite_id_name;
          wimpt_complain(sprite_readsize(s->area, &id, &sinfo));
          conv_info->entry[n].number=number;
          conv_info->entry[n].type=Smask_0+Smask_1+Smask_2+Smask_3+Smask_stm+Smask_str;
          conv_info->entry[n].frames = 1;
          e++;
        }
        id.s.name=name;
        id.tag=sprite_id_name;
        wimpt_complain(sprite_readsize(s->area, &id, &sinfo));
        if (conv_info->entry[n].frames < frame+1)
            conv_info->entry[n].frames=frame+1;
        conv_info->entry[n].xcent[frame]=sinfo.width/2;
        conv_info->entry[n].ycent[frame]=sinfo.height/2;
      }
    }
    conv_info->header.number = e;
    sort_sprite_info();
    visdelay_end();
    s->number = conv_info->entry[0].number;
    s->frame = 0;
    remake_sprite_window(s);
  }
}

static void conv_info_total(void)
{
  int i, t;
  t=0;
  for (i=0; i<conv_info->header.number; i++)
  {
    t += 4 * sequence_length[conv_info->entry[i].frames];
  }
  conv_info->header.total = t;
}

/****************************** SAVE FUNCTIONS ****************************/


static void fillin_save_filename(char *filename)
{
  dbox_setfield(convert_dbox, Iconvert_filename, filename);
}

static BOOL save_do_save(char *filename, void *handle)
{
  handle=handle;
  strncpy(save_filename, filename, 255);
  fillin_save_filename(save_filename);
  return TRUE;
}


static void open_textfile(char *filename)
{
  wimp_msgstr msg;
  msg.hdr.size=256;
  msg.hdr.your_ref = 0;
  msg.hdr.action = wimp_MDATAOPEN;
  msg.data.dataopen.w = sprite_whandle;
  msg.data.dataopen.i = 0;
  msg.data.dataopen.x = 0;
  msg.data.dataopen.y = 0;
  msg.data.dataopen.size = 0;
  msg.data.dataopen.type = 0xfff;
  strncpy(msg.data.dataopen.name, filename, 212);
  wimpt_complain(wimp_sendmessage(wimp_ESENDWANTACK, &msg, 0));
}

static void out_of_memory(void)
{
  werr(0, "Not enough memory to perform conversion");
}








/********************** CONVERSION FUNCTIONS  ******************************/

static void discard_fsp_area(void)
{
  if (fsp_area_size != 0)
  {
    flex_free((flex_ptr) fsp_area);
    *fsp_area = 0;
    fsp_area_size = 0;
  }
}
static BOOL extend_if_needed(char **area, int *size, int offset)
{
  if ((*size - offset) < 0x1000) /* arbitrary limit */
  {
    *size += 0x4000;
    if (flex_extend((flex_ptr) area, *size) == 0) return FALSE;
  }
  return TRUE;
}

static void convert_link(void)
{
  event_setmask(wimp_EMPTRENTER | wimp_EMPTRLEAVE);
}

static void convert_unlink(void)
{
  event_setmask(wimp_EMNULL | wimp_EMPTRENTER | wimp_EMPTRLEAVE);
}

static void convert_error(void)
{
  convert_unlink();
  discard_fsp_area();
  remove(save_filename);
  conv_sprites.number=conv_info->entry[0].number;
  conv_sprites.last_number=-1;
  conv_sprites.frame=0;
  remake_sprite_window(&conv_sprites);
}


static void convert_message(char *message)
{
  if (convert_dbox_open)
    dbox_setfield(convert_dbox, Iconvert_message, message);
}

static void convert_size(int size)
{
  if (convert_dbox_open)
    dbox_setnumeric(convert_dbox, Iconvert_outputsize, size);
}

static void sprite_getdetails(sprite_id *id, Conv_det *det)
{
  int mode;
  sprite_header *h;
  h = (sprite_header *) id->s.addr;
  if (fake_use) mode = fakemode;
    else mode=h->mode;
  det->width = h->width;
  det->height = h->height;
  det->bwidth=(32*h->width+(h->rbit+1))/8; /* eight for bytes */
  det->rbytes=4*(h->width+1);
  det->hbytes=bbc_modevar(mode, ModeVars_LineLength);
}

static BOOL copy_sprite(sprite_id *id, void **sprc)
{
  int size;
  sprite_header *h;
  h= (sprite_header *) id->s.addr;
  size =  4*(h->width + 1)*(h->height + 1);
  if (flex_alloc(sprc, size) == 0)
  {
    out_of_memory();
    return FALSE;
  }
  memcpy(*sprc, (char *) &h->next + h->image, size);
  return TRUE;
}


static BOOL fsp1_make_file_header(void)
{
  FSP1_header h;
  strncpy(h.id, "FSP1", 4);
  h.res1 = 0;
  h.basemode = 0;
  h.number = conv_info->header.max + 1;
  memcpy(*fsp_area, &h, sizeof(FSP1_header));
  fsp_ofs += sizeof(FSP1_header);
  spr_ofs = fsp_ofs + 4 * (conv_info->header.max + 1);
  if (!extend_if_needed(fsp_area, &fsp_area_size, spr_ofs)) return FALSE;
  memset(&(*fsp_area)[fsp_ofs], 0, spr_ofs - fsp_ofs);
  return TRUE;
}

static BOOL fsp2_make_file_header(void)
{
  FSP2_header h;
  strncpy(h.id, "FSP2", 4);
  h.number = conv_info->header.max + 1;
  h.basemode = 0;
  memcpy(*fsp_area, &h, sizeof(FSP2_header));
  fsp_ofs += sizeof(FSP2_header);
  conv_info_total();
  anim_ofs = fsp_ofs + 4 * (conv_info->header.max+1);
  spr_ofs = anim_ofs + 4 * conv_info->header.total;
  if (!extend_if_needed(fsp_area, &fsp_area_size, spr_ofs)) return FALSE;
  memset(&(*fsp_area)[fsp_ofs], 0, spr_ofs - fsp_ofs);
  return TRUE;
}



static BOOL convert_make_file_header(void)
{
  if (fsp_type == 1) return fsp1_make_file_header();
  if (fsp_type == 2) return fsp2_make_file_header();
  return FALSE;
}

static int build_stm_block(void **sprc, Conv_det *det, int shift, int base, BOOL *fours, BOOL *eights)
{
  int x,y,g,h,col,local_ofs,start;
  BOOL any = FALSE;
  char **read;
  *eights = TRUE;
  *fours = TRUE;
  read = (char **) sprc;
  start=4-shift;
  if (shift==0) start=0;
  local_ofs = 4*(det->height+2); /*size of vert table*/
  quad_align(&local_ofs, spr_ofs + local_ofs);
  for (y=0; y<=det->height; y++)
  {
    *(int *)&(*fsp_area)[spr_ofs + 4*y] = local_ofs + base;
    for (x=start; x<det->bwidth; x+=4)
    {

      for (g=0; g<7*4 && g+x<det->bwidth; g++)
      {
        col=(*read)[y*det->rbytes + x + g];
        if (col == transp) break;
        (*fsp_area)[spr_ofs + local_ofs + 4 + g] = col;
      }
      h=0;
      if (g >= 3*4) h = 3*4;
      if (g == 7*4) h = 7*4;
      if (h == 3*4) *eights = FALSE;
      if (h == 7*4) *fours = FALSE;
      if (h > 0)
      {
        any = TRUE;
        col=((x + y*det->hbytes)<<8) +(h/4)+1;
        *(int *)&(*fsp_area)[spr_ofs + local_ofs] = col;
        local_ofs += 4+h;
        for (h--;h>=0;h--)
          (*read)[y*det->rbytes + x + h] = transp;
      }
    }
    if (!extend_if_needed(fsp_area, &fsp_area_size, spr_ofs+local_ofs)) return 0;
  }
  *(int *)&(*fsp_area)[spr_ofs + 4*y] = local_ofs + base;
  if (!any) local_ofs = 0;
  return local_ofs;
}

static int build_str_block(void **sprc, Conv_det *det, int shift, int base)
{
  int x,y,g,col,local_ofs,start;
  BOOL any = FALSE;
  char **read;
  read = (char **) sprc;
  start=4-shift;
  if (shift==0) start=0;
  local_ofs = 4*(det->height+2); /*size of vert table*/
  quad_align(&local_ofs, spr_ofs + local_ofs);
  for (y=0; y<=det->height; y++)
  {
    *(int *)&(*fsp_area)[spr_ofs + 4*y] = local_ofs + base;
    for (x=start; x<det->bwidth; x+=4)
    {

      for (g=0; g<4 && g+x<det->bwidth; g++)
      {
        col=(*read)[y*det->rbytes + x + g];
        if (col == transp) break;
        (*fsp_area)[spr_ofs + local_ofs + 4 + g] = col;
      }
      if (g == 4)
      {
        any = TRUE;
        col=((x + y*det->hbytes)<<8) +(g/4)+1;
        *(int *)&(*fsp_area)[spr_ofs + local_ofs] = col;
        local_ofs += 4+g;
        for (g--;g>=0;g--)
          (*read)[y*det->rbytes + x + g] = transp;
      }

    }
    if (!extend_if_needed(fsp_area, &fsp_area_size, spr_ofs+local_ofs)) return 0;
  }
  *(int *)&(*fsp_area)[spr_ofs + 4*y] = local_ofs + base;
  if (!any) local_ofs = 0;
  return local_ofs;
}


static int build_strb_block(void **sprc, Conv_det *det, int shift, int base)
{
  int x,y,col,local_ofs;
  char **read;
  read = (char **) sprc;
  shift = shift;
  local_ofs = 4*(det->height+2); /*size of vert table*/
  for (y=0; y<=det->height; y++)
  {
    *(int *)&(*fsp_area)[spr_ofs + 4*y] = local_ofs + base;
    for (x=0; x<det->bwidth; x++)
    {
      col=(*read)[y*det->rbytes + x];
      if (col != transp)
      {
        col+=(x + y*det->hbytes)<<8;
        *(int *)&(*fsp_area)[spr_ofs + local_ofs] = col;
        local_ofs+=4;
      }
    }
    if (!extend_if_needed(fsp_area, &fsp_area_size, spr_ofs+local_ofs)) return 0;
  }
  *(int *)&(*fsp_area)[spr_ofs + 4*y] = local_ofs + base;
  if (local_ofs == 4*(det->height+2)) local_ofs = 0;
  return local_ofs;
}

/* -- Write data or line table to file */

static int fsp1_convert_data(sprite_id *id, sprite_info *i, Conv_info_entry *e)
{
  int length;
  FSP1_entry fsp;
  Conv_det det;
  void *sprc;
  *(int *)&(*fsp_area)[fsp_ofs + 4*e->number] = spr_ofs;
  if (!copy_sprite(id, &sprc)) return 0;
  fsp.xsize[0]=i->width;
  fsp.ysize[0]=i->height;
  fsp.xofs[0]=e->xcent[0];
  fsp.yofs[0]=e->ycent[0];

  memcpy(&(*fsp_area)[spr_ofs], &fsp, Sizeof_FSP1_entry);
  spr_ofs += Sizeof_FSP1_entry;
  sprite_getdetails(id, &det);
  length = build_strb_block(&sprc, &det, 0, 4);
  spr_ofs+=length;
  flex_free(&sprc);
  return length;
}

static int fsp2_convert_single(sprite_id *id, sprite_info *i, Conv_info_entry *e, int shift, int frame, BOOL *flag)
{
  int temp_spr_ofs, length;
  BOOL fours,eights;

  FSP2_entry fsp;
  Conv_det det;
  void *sprc;
  temp_spr_ofs = spr_ofs;
  if (!copy_sprite(id, &sprc)) return 0;
  wimpt_complain(sprite_readsize(conv_sprites.area, id, i));
  sprite_getdetails(id, &det);
  fsp.xsize     =i->width;
  fsp.ysize     =i->height;
  fsp.xofs      =e->xcent[frame];
  fsp.yofs      =e->ycent[frame];
  fsp.stmofs    =0;
  fsp.strofs    =0;
  fsp.strbofs   =0;
  fsp.advofs    =0;
  spr_ofs += Sizeof_FSP2_entry;
  if ((e->type & Smask_stm) == Smask_stm)
  {
    length = build_stm_block(&sprc, &det, shift, 0, &fours, &eights);
    if (length == 0) fsp.stmofs = 0;
    else
    {
      fsp.stmofs = (spr_ofs - temp_spr_ofs) << 8 ;
      if (fours) fsp.stmofs+=4;
      if (eights) fsp.stmofs+=8;
    }
    spr_ofs += length;
  }

  if ((e->type & Smask_str) == Smask_str)
  {
    length = build_str_block(&sprc, &det, shift, 0);
    if (length == 0) fsp.strofs = 0;
    else fsp.strofs = (spr_ofs - temp_spr_ofs) << 8;
    spr_ofs += length;
  }

  length = build_strb_block(&sprc, &det, shift, 0);
  if (length == 0) fsp.strbofs = 0;
  else fsp.strbofs = (spr_ofs - temp_spr_ofs) << 8;
  spr_ofs += length;

  if (fsp.stmofs == 0 && fsp.strofs == 0)
    *flag = TRUE;
  else
    *flag = FALSE;

  flex_free(&sprc);
  memcpy(&(*fsp_area)[temp_spr_ofs], &fsp, Sizeof_FSP2_entry);
  return spr_ofs - temp_spr_ofs;
}

static int fsp2_block_convert(sprite_id *id, sprite_info *inf, Conv_info_entry *e, int *length)
{
  int i, j, shift, prev_shift, sp_no, anim_base, def_ofs = 0xdead0b0d;
  sprite_ptr s_ptr;
  BOOL flag[4], downgrade = TRUE;
  *length = 0;
  if ((e->type & (Smask_0 + Smask_1 + Smask_2 + Smask_3)) == 0)
    e->type = e->type | Smask_0;
  if ((e->type & (Smask_str + Smask_stm)) == 0)
  {
    e->type = Smask_0;
    downgrade = FALSE;
  }
  *(int *)&(*fsp_area)[fsp_ofs + 4*e->number] = (anim_ofs<<8)+(sequence_length[e->frames]-1);
  anim_base = anim_ofs;
  for (i=0; i<sequence_length[e->frames]; i++)
  {
    sp_no = sequence[e->frames][i];
    if (sp_no < i)
    {
      memcpy(&(*fsp_area)[anim_ofs], &(*fsp_area)[anim_base + 16*sp_no], 16);
    }
    else
    {
      if(wimpt_complain(sprite_select_rp(conv_sprites.area, conv_id(&conv_sprites, e, sp_no), &s_ptr)) != 0) s_ptr = 0;
      if (s_ptr == 0)
      {
        werr(0, "Sprite named %s does not exist", conv_sprites.name);
        *length = 0;
        return FALSE;
      }
      id->s.addr=s_ptr;
      id->tag=sprite_id_addr;
      wimpt_complain(sprite_removewastage(conv_sprites.area, id));
      def_ofs = 0;
      prev_shift = 0;
      for (j=0; j<4; j++)
      {
        flag[j] = TRUE;
        if ((e->type & 1<<j) != 0)
        {
          shift = (j - e->xcent[i]) & 3;
          /* if ((shift > j) && (e->type != Smask_0)) shift |= 4;*/
          /* release h-locking if STRB only */
          def_ofs = (spr_ofs<<8) + shift;
          if (e->type == Smask_0) def_ofs |= 8;
          *(int *)&(*fsp_area)[anim_ofs+j*4] = def_ofs;
          *length+=fsp2_convert_single(id, inf, e, shift & 3, i, &flag[j]);
        }
        else
          *(int *)&(*fsp_area)[anim_ofs+j*4] = def_ofs;
      }
    if (!(flag[0] && flag[1] && flag[2] && flag[3]))
      downgrade = FALSE;
    }
    for (j=3; j>=0; j--)
    {
      if (*(int *)&(*fsp_area)[anim_ofs+j*4] == 0)
        *(int *)&(*fsp_area)[anim_ofs+j*4] = def_ofs;
      else
        def_ofs = *(int *)&(*fsp_area)[anim_ofs+j*4];
    }
    anim_ofs+=16;
  }
  return downgrade;
}

static int fsp2_convert_data(sprite_id *id, sprite_info *inf, Conv_info_entry *e)
{
  int length = 0;
  int temp_spr_ofs, temp_anim_ofs;
  temp_spr_ofs = spr_ofs;
  temp_anim_ofs = anim_ofs;
  if (fsp2_block_convert(id, inf, e, &length))
  {
    e->type = Smask_0;
    spr_ofs = temp_spr_ofs;
    anim_ofs = temp_anim_ofs;
    if (fsp2_block_convert(id, inf, e, &length))
      werr(1, "Internal Inconsistency - FSPConv must leave the Desktop");
  }
  return length;
}


/****- sprite data to file entry point ****/

static BOOL fsp_convert(sprite_id *id,Conv_info_entry *e)
{
  int length = 0;
  sprite_info i;

  wimpt_complain(sprite_readsize(conv_sprites.area, id, &i));

  if (fsp_type == 1) length = fsp1_convert_data(id, &i, e);
  if (fsp_type == 2) length = fsp2_convert_data(id, &i, e);
  if (length == 0) return FALSE;
  convert_size(spr_ofs);
  return TRUE;
}

/****- CONVERT SPRITES TO FILE ENTRY POINT -****/

static BOOL sprite_convert(Conv_sprites *s, Conv_info_entry *e, int i)
{
  char message[128];
  sprite_ptr s_ptr;
  sprite_id id;

  sprintf(message, "Converting sprite %d out of %d", i+1, conv_info->header.number);
  convert_message(message);

  wimpt_complain(sprite_select_rp(s->area, conv_id(s, e, 1), &s_ptr));
  if (s_ptr == 0)
  {
    werr(0, "Cannot get pointer for sprite named %s", s->name);
    return FALSE;
  }

  id.s.addr=s_ptr;
  id.tag=sprite_id_addr;
  wimpt_complain(sprite_removewastage(s->area, &id));

  if (!fsp_convert(&id, e)) return FALSE;
  return TRUE;
}

static BOOL convert_open()
{
  fsp_area_size = 0x4000; /* Get a default size flex block */
  if (flex_alloc((flex_ptr) fsp_area, fsp_area_size) == 0)
  {
    *fsp_area = NULL;
    fsp_area_size = 0;
    werr(0, "Not enough memory to perform conversion");
    convert_error();
    return FALSE;
  }
  fsp_ofs = 0;
  conv_no=0;
  convert_link();
  return TRUE;
}

static void set_filetype(char *filename, int type)
{
  wimpt_complain(os_swi4(XOS_File,18,(int) filename,type,0));
}

static BOOL convert_write()
{
  FILE *f;
  if (!convert_getlength)
  {

    if (f = fopen (save_filename, "w"), f == 0)
    {
      werr(0, "Cannot open file for write access");
      convert_error();
      return FALSE;
    }
    if (fwrite(*fsp_area, 1, spr_ofs, f) != spr_ofs)
    {
      fclose(f);
      werr(0, "Disc full");
      convert_error();
      return FALSE;
    }
    fclose(f);
  set_filetype(save_filename, FastSpr_filetype);
  }
  discard_fsp_area();
  conv_no=-1;
  convert_size(spr_ofs);
  conv_sprites.number=conv_info->entry[0].number;
  conv_sprites.last_number=-1;
  conv_sprites.frame=0;
  remake_sprite_window(&conv_sprites);
  return TRUE;
}

static void convert_event(void)
{
  if (conv_no == -1)
  {
    werr(0, "Conversion event received when no conversion requested");
    convert_unlink();
    return;
  }
  if (conv_no == -2)
  {
    convert_error();
    return;
  }
  if (conv_no == 0)
  {
    if (!convert_make_file_header())
    {
      convert_error();
      return;
    }
  }
  if(!sprite_convert(&conv_sprites, &conv_info->entry[conv_no], conv_no))
  {
    werr(0, "Conversion of sprite named %d failed", conv_info->entry[conv_no].number);
    convert_error();
    return;
  }
  conv_no++;
  if (conv_no >= conv_info->header.number)
  {
    if (!convert_write())
    {
      convert_message("Conversion OK - write to disc failed");
      convert_error();
      return;
    }
    convert_unlink();
    if (convert_getlength)
      convert_message("Length calculation successful");
    else
      convert_message("Conversion successful");
  }
}

static BOOL do_conversion(Conv_info *c)
{
  if (!conv_info_valid || c->header.number == 0)
  {
    werr(0, "There are no sprites to convert");
    return FALSE;
  }
  return convert_open();
}

static int filesize_sprite(sprite_area *sa)
{
  sprite_area result;
  sprite_area_readinfo(sa, &result);
  return result.size;
}

static BOOL save_raw_event_handler(dbox d, void *v, void *handle)
{
  wimp_eventstr *e;
  e = (wimp_eventstr *) v;
  switch (e->e)
  {
    case wimp_EBUT:
      if (e->data.but.m.bbits == wimp_BDRAGLEFT)
      {
        dbox_getfield(d, Iconvert_filename, save_filename, 255);
        xfersend(Fastspr_filetype, save_filename, 0, save_do_save, 0, 0, e, handle);
        fillin_save_filename(save_filename);
        return TRUE;
      }
      else
      return FALSE;

    case wimp_ECLOSE:
      conv_no=-2; /* signal a cancel */
      return FALSE;

    default:
      break;

  }
  return FALSE;
}

static void convert_event_handler(dbox d, void *handle)
{
  BOOL persist;
  dbox_field f;
  handle=handle;
  f=dbox_get(d);
  persist=dbox_persist();
  convert_getlength = FALSE;
  switch (f)
  {
    case Iconvert_cancel:
      conv_no=-2;
      persist=FALSE;
      break;

    case Iconvert_getlength:
      convert_getlength = TRUE; /* drops through */
    case Iconvert_convert:
    case 0:
      if (!conv_sprites.loaded) break;

      sprite_readfields(sprite_dbox, &conv_sprites);
      dbox_getfield(d, Iconvert_filename, save_filename, 255);
      dbox_setnumeric(d, Iconvert_convert, 1);
      do_conversion(conv_info);
      dbox_setnumeric(d, Iconvert_convert, 0);
      persist = TRUE;
      break;





    default:
      break;
  }
  if (!persist)
  {
    dbox_dispose(&d);
    convert_dbox_open = FALSE;
  }
}


static BOOL begin_conversion(Conv_sprites *s)
{
  s=s;

  if (convert_dbox_open)
  {
    dbox_dispose(&convert_dbox);
  }
  if (convert_dbox = dbox_new("Convert"), convert_dbox != NULL)
  {
    convert_dbox_open = TRUE;
    dbox_raw_eventhandler(convert_dbox, save_raw_event_handler, 0);
    dbox_eventhandler(convert_dbox, convert_event_handler, 0);
    dbox_setfield(convert_dbox, Iconvert_filename, save_filename);
    dbox_setfield(convert_dbox, Iconvert_filetype, type_string());
    dbox_setnumeric(convert_dbox, Iconvert_sourcesize, filesize_sprite(s->area));
    if (conv_sprites.loaded)
    {
      if (conv_info->header.number == 0)
        convert_message("No sprites to convert.  Are they named correctly?");
      else
        if (fake_use)
        {
          char message[128];
          sprintf(message,"Ready to Convert...  Faking Mode %d", fakemode);
          convert_message(message);
        }
        else
        convert_message("Ready to Convert...");
    }
    else
      convert_message("No sprite file is loaded");
  dbox_showstatic(convert_dbox);

  }
  return TRUE;
}


/****************************** LOAD FUNCTIONS ****************************/



/*--- Load checks: determines if the file type is valid, and if so deletes
      the old file, then claims memory as necessary. ---*/
static BOOL FSPConv_load_check(int filetype, Conv_sprites *d, int size)
{
  int areasize;

  /* Check validity of file type */
  if (filetype != Sprite_filetype)
  {
    werr(0, "You can only load sprite files");
    return FALSE;
  }

  /* Remove any old file */
  sprites_dispose(d);

  /* Allocate new memory */
  areasize = size+sizeof(sprite_area);
  if (flex_alloc((flex_ptr) &d->area, areasize))
  {
  /* Initialise sprite area */

    sprite_area_initialise(d->area, areasize);
    return TRUE;
  }
  else
  {
    werr(0, "There is not enough free memory to load that file.");
    return FALSE;
  }
}

/*--- Handle loading errors. ---*/
static void FSPConv_load_error(Conv_sprites *d)
{
  sprites_dispose(d);
  strcpy(d->filename, FSPConv_null_name);
  werr(0,"Sprite load has failed");
}

/*--- Complete the loading process. ---*/
static void FSPConv_load_finish(Conv_sprites *d, int size)
{
  int i;
  d->size     = size;
  d->loaded   = TRUE;
  d->saved    = TRUE;
  for (i=strlen(d->filename); i>0; i--)
    if (d->filename[i]=='.') break;

  strncpy(save_filename, d->filename, i+1);
  save_filename[i+1]='\0';
  strcat(save_filename, "FSP");
  strcat(save_filename, &d->filename[i+1]);

  win_settitle(sprite_whandle, d->filename);
  if (convert_dbox_open)
    dbox_setfield(convert_dbox, Iconvert_filename, save_filename);

  read_sprite_info(d);
  /* Redraw the diagram */

  remake_sprite_window(d);
  begin_conversion(d);
}


/*--- Read the size of a file. Return 0 if not a file. ---*/
static int FSPConv_filesize(char *filename)
{
  os_filestr file;

  /* Read the size of the file */
  file.action = 5;    /* Get catalogue info */
  file.name   = filename;
  if (wimpt_complain(os_file(&file)) != 0) return 0;

  switch (file.action)
  {
    case 0: werr(0, "File not found");              return 0;
    case 2: werr(0, "You cannot load a directory"); return 0;
  }

  return file.start;
}


/*--- Load from a file. ---*/
static void load_file(Conv_sprites *d)
{
  char      *filename;
  int       size;

  /* Fetch the type and name of the file */
  int filetype = xferrecv_checkinsert(&filename);

  /* Check the name and make a permanent copy */
  if (strlen(filename) > FSPConv_max_name)
  {
    werr(0, "File name is too long");
  }
  else
  {
    strcpy(d->filename, filename);

    /* Get size of file (and check it is a file) */
    size = FSPConv_filesize(d->filename);

    /* Check file type, get rid of old memory and allocate new */
    if (size > 0 && FSPConv_load_check(filetype, d, size))
    {
      visdelay_begin();

      if (wimpt_complain(sprite_area_load(d->area, d->filename)) == 0)
        FSPConv_load_finish(d, size);
          else
        FSPConv_load_error(d);

      visdelay_end();
    }
    else
    {
      werr(0,"Failed to load file");
    }
  }

  /* Indicate load is completed */
  xferrecv_insertfileok();
}

/**************** --- Sprite handling *********************/

static menu make_number_menu(void)
{
  int i, m;
  menu next_menu = NULL;
  char extendstr[12];
  if (sprite_number_menu != NULL)
    menu_dispose(&sprite_number_menu, 1);
  sprite_number_menu = menu_new("Number","");
  if (conv_info_valid)
  {
    if (conv_info->header.number < 20)
    {
      for (i=0; i<conv_info->header.number; i++)
      {
        sprintf(extendstr, "%d",conv_info->entry[i].number);
        menu_extend(sprite_number_menu, extendstr);
      }
    }
    else
    {
      m=0;
      for (i=0;i<conv_info->header.number; i++)
      {
        if (conv_info->entry[i].number >= m || next_menu == NULL)
        {
          if (next_menu != NULL)
            menu_submenu(sprite_number_menu, m/10, next_menu);
          m=(conv_info->entry[i].number/10)*10;
          sprintf(extendstr, "%d-%d",m,m+9);
          menu_extend(sprite_number_menu, extendstr);
          next_menu = menu_new(extendstr, "");
          m+=10;
        }
        sprintf(extendstr, "%d",conv_info->entry[i].number);
        menu_extend(next_menu, extendstr);
      }
      menu_submenu(sprite_number_menu, m/10, next_menu);
    }
  }
  menu_extend(sprite_number_menu, "|Help");
  return sprite_number_menu;
}

static menu make_frame_menu(void)
{
  int i,e=-1;
  char extendstr[12];
  if (sprite_frame_menu != NULL)
    menu_dispose(&sprite_frame_menu, 0);
  sprite_frame_menu = menu_new("Frame","");
  e = scan_conv_info(conv_sprites.number);
  if (e != -1 && conv_info->entry[e].frames <= 256)
  {
    for (i=0; i<conv_info->entry[e].frames; i++)
    {
      sprintf(extendstr, "%d",i);
      menu_extend(sprite_frame_menu, extendstr);
    }
  }
  menu_extend(sprite_frame_menu, "|Help");
  return sprite_frame_menu;
}

static void open_menu(menu menu, wimp_mousestr *mouse)
{
  wimpt_complain(wimp_create_menu(menu_syshandle(menu), mouse->x-96, mouse->y));
}

static void open_popup_menu(menu menu, wimp_mousestr *mouse)
{
  wimpt_complain(wimp_create_menu(menu_syshandle(menu), mouse->x+24, mouse->y));
}

static void open_sprite_menu(wimp_mousestr *mouse)
{
  switch (mouse->i)
  {
    case Isprite_number+2:
      open_popup_menu(make_number_menu(), mouse);
      menu_type=M_sprite_number;
      break;

    case Isprite_frame+2:
      open_popup_menu(make_frame_menu(), mouse);
      menu_type=M_sprite_frame;
      break;

    case Isprite_xcent+2:
      open_popup_menu(sprite_centre_menu, mouse);
      menu_type=M_sprite_centre;
      break;

    case Isprite_premenu:
      open_popup_menu(sprite_preshift_menu, mouse);
      menu_type=M_sprite_preshift;
      break;

    case Isprite_typemenu:
      open_popup_menu(sprite_type_menu, mouse);
      menu_type=M_sprite_type;
      break;

    default:
      open_menu(sprite_menu, mouse);
      menu_type=M_sprite_normal;
      break;
  }
}

static void check_incdec(dbox d, int f, int g)
{
  int x,i;
  wimp_mousestr mouse;
  wimpt_complain(wimp_get_point_info(&mouse));
  if (mouse.bbits == wimp_BLEFT)
    i=1;
  else
    i=-1;

  if (f == g-1)
  {
    x=dbox_getnumeric(d, g) - i;
    dbox_setnumeric(d, g, x);
  }
  if (f == g+1)
  {
    x=dbox_getnumeric(d, g) + i;
    dbox_setnumeric(d, g, x);
  }
  if ((f == g+2) && (i == 1))
  {
    open_sprite_menu(&mouse);
  }
}

static void check_number_incdec(dbox d, int f, int g)
{
  int x,i,e;
  wimp_mousestr mouse;
  wimpt_complain(wimp_get_point_info(&mouse));
  if (mouse.bbits == wimp_BLEFT)
    i=1;
  else
    i=-1;

  if (f == g-1)
  {
    e = scan_conv_info(conv_sprites.number);
    if (e != -1 && e != 0)
    {
      x=conv_info->entry[e-1].number;
      dbox_setnumeric(d, g, x);
    }
  }
  if (f == g+1)
  {
    e = scan_conv_info(conv_sprites.number);
    if (e != -1 && e+1 < conv_info->header.number)
    {
      x=conv_info->entry[e+1].number;
      dbox_setnumeric(d, g, x);
    }
  }
  if ((f == g+2) && (i == 1))
  {
    open_sprite_menu(&mouse);
  }
}

static void info_about_program(void)
{
  dbox d;
  if (d = dbox_new("Proginfo"), d != NULL)
  {
    dbox_setfield(d,4,Prog_name);
    dbox_setfield(d,7,Version_string);
    dbox_show(d);
    dbox_fillin(d);
    dbox_dispose(&d);
  }
}

static void open_transp_dbox(void)
{
  dbox d;
  if (d = dbox_new("Transp"), d != NULL)
  {
    dbox_setnumeric(d,Itransp_transp,transp);
    dbox_show(d);
    dbox_fillin(d);
    transp = dbox_getnumeric(d,Itransp_transp);
    if (transp<0 || transp>255)
    {
      werr(0, "The transparent colour should range from 0 to 255");
    }
    dbox_dispose(&d);
  }
}

static void open_fake_dbox(void)
{
  dbox d;
  if (d = dbox_new("FakeMode"), d != NULL)
  {
    dbox_setnumeric(d,Ifake_fake,fakemode);
    dbox_setnumeric(d,Ifake_use,fake_use);
    dbox_show(d);
    dbox_fillin(d);
    fakemode = dbox_getnumeric(d,Ifake_fake);
    fake_use = dbox_getnumeric(d,Ifake_use);
    if (fakemode<0 || fakemode>255)
    {
      werr(0, "The fake mode should range from 0 to 255");
    }
    dbox_dispose(&d);
  }
}

static void decode_bar_menu(void *hitlist)
{
  int *hitno;
  hitno = (int *) hitlist;

  switch(hitno[0])
  {
    case Bar_menu_info:
      info_about_program();
      break;

    case Bar_menu_transp:
      open_transp_dbox();
      break;

    case Bar_menu_convert:
      begin_conversion(&conv_sprites);
      break;

    case Bar_menu_help:
      open_textfile("FSPConv:Help.General");
      break;

    case Bar_menu_quit:
      exit(0);

    default:
      break;
  }
}

static void decode_number_menu(void *hitlist)
{
  int args,first,last,value;
  char buffer[256];
  wimpt_complain(wimp_decode_menu(menu_syshandle(sprite_number_menu),
    hitlist, buffer));
  args=sscanf(buffer,"%d-%d.%d",&first,&last,&value);
  switch (args)
  {
    case 0:
      open_textfile("FSPConv:Help.NumberMenu");
      break;

    case 1:
    case 2:
      dbox_setnumeric(sprite_dbox, Isprite_number, first);
      sprite_readfields(sprite_dbox, &conv_sprites);
      break;


    case 3:
      dbox_setnumeric(sprite_dbox, Isprite_number, value);
      sprite_readfields(sprite_dbox, &conv_sprites);
      break;

    default:
      break;
  }
}

static void decode_frame_menu(void *hitlist)
{
  int args,value;
  char buffer[256];
  wimpt_complain(wimp_decode_menu(menu_syshandle(sprite_frame_menu),
    hitlist, buffer));
  args=sscanf(buffer,"%d",&value);
  switch (args)
  {
    case 0:
      open_textfile("FSPConv:Help.FrameMenu");
      break;

    case 1:
      dbox_setnumeric(sprite_dbox, Isprite_frame, value);
      sprite_readfields(sprite_dbox, &conv_sprites);
      break;
  }
}

static void centre_menu_all(void)
{
  int i,j,x,y,num,frame;
  sprite_info sinfo;
  num=conv_sprites.number;
  frame=conv_sprites.frame;
  for (i=0; i<=conv_info->header.number; i++)
  {
    for (j=0; j< conv_info->entry[i].frames; j++)
    {
      conv_sprites.number=conv_info->entry[i].number;
      conv_sprites.frame=j;
      wimpt_complain(sprite_readsize(conv_sprites.area, current_id(&conv_sprites), &sinfo));
      x=0;
      y=0;
      switch (lastcentre)
      {
        case Centre_menu_centre:
          x=sinfo.width/2;
          y=sinfo.height/2;
          break;
    
        case Centre_menu_left:
          x=0;
          y=sinfo.height/2;
          break;
    
        case Centre_menu_right:
          x=sinfo.width-1;
          y=sinfo.height/2;
          break;
    
        case Centre_menu_top:
          x=sinfo.width/2;
          y=0;
          break;
    
        case Centre_menu_bottom:
          x=sinfo.width/2;
          y=sinfo.height-1;
          break;
    
        case Centre_menu_topleft:
          x=0;
          y=0;
          break;
    
        case Centre_menu_topright:
          x=sinfo.width-1;
          y=0;
          break;
    
        case Centre_menu_bottomleft:
          x=0;
          y=sinfo.height-1;
          break;
    
        case Centre_menu_bottomright:
          x=sinfo.width-1;
          y=sinfo.height-1;
          break;
      }
    conv_info->entry[i].xcent[conv_sprites.frame] = x;
    conv_info->entry[i].ycent[conv_sprites.frame] = y;
    }
  }
  conv_sprites.number=num;
  conv_sprites.frame=frame;
  remake_sprite_window(&conv_sprites);
}
    

static void decode_centre_menu(void *hitlist)
{
  int *hitno;
  int hit;
  sprite_info sinfo;
  int x, y, e;
  hitno = (int *) hitlist;
  hit=hitno[0];
  e=scan_conv_info(conv_sprites.number);
  if (e == -1)
  {
    x=0;
    y=0;
    sinfo.width=0;
    sinfo.height=0;
  }
  else
  {
    x=conv_info->entry[e].xcent[conv_sprites.frame];
    y=conv_info->entry[e].ycent[conv_sprites.frame];
    wimpt_complain(sprite_readsize(conv_sprites.area, current_id(&conv_sprites), &sinfo));
  }

  switch (hit)
  {
    case Centre_menu_centre:
      x=sinfo.width/2;
      y=sinfo.height/2;
      break;

    case Centre_menu_left:
      x=0;
      y=sinfo.height/2;
      break;

    case Centre_menu_right:
      x=sinfo.width-1;
      y=sinfo.height/2;
      break;

    case Centre_menu_top:
      x=sinfo.width/2;
      y=0;
      break;

    case Centre_menu_bottom:
      x=sinfo.width/2;
      y=sinfo.height-1;
      break;

    case Centre_menu_topleft:
      x=0;
      y=0;
      break;

    case Centre_menu_topright:
      x=sinfo.width-1;
      y=0;
      break;

    case Centre_menu_bottomleft:
      x=0;
      y=sinfo.height-1;
      break;

    case Centre_menu_bottomright:
      x=sinfo.width-1;
      y=sinfo.height-1;
      break;
      
    case Centre_menu_all:
      centre_menu_all();
      break;  

    case Centre_menu_help:
      open_textfile("FSPConv:Help.Centremenu");
      return;
      break;
  }
  lastcentre=hit;
  if (e != -1 && hit != Centre_menu_all)
  {
    conv_info->entry[e].xcent[conv_sprites.frame] = x;
    conv_info->entry[e].ycent[conv_sprites.frame] = y;
    remake_sprite_window(&conv_sprites);
  }
}

static void decode_type_menu(void *hitlist)
{
  int *hitno;
  hitno = (int *) hitlist;
  switch(hitno[0])
  {
    case Type_menu_fsp1:
      fsp_type = 1;
      remake_sprite_window(&conv_sprites);
      if (convert_dbox_open) begin_conversion (&conv_sprites);
      break;

    case Type_menu_fsp2:
      fsp_type = 2;
      remake_sprite_window(&conv_sprites);
      if (convert_dbox_open) begin_conversion (&conv_sprites);
      break;

    case Type_menu_help:
      open_textfile("FSPConv:Help.TypeMenu");
      break;

    default:
      break;
  }
}

static void decode_preshift_menu(void *hitlist)
{
  int *hitno;
  int e;
  hitno = (int *) hitlist;
  e=scan_conv_info(conv_sprites.number);
  switch (hitno[0])
  {
    case Preshift_menu_just0:
      if (e == -1 | fsp_type != 2) break;
      conv_info->entry[e].type=conv_info->entry[e].type & (Smask_str + Smask_stm) | Smask_0;
      fillin_sprite_window(sprite_dbox, &conv_sprites);
      break;

    case Preshift_menu_hlock:
      if (e == -1 | fsp_type != 2) break;
      conv_info->entry[e].type=Smask_str + Smask_stm + Smask_0;
      fillin_sprite_window(sprite_dbox, &conv_sprites);
      break;

    case Preshift_menu_0and2:
      if (e == -1 | fsp_type != 2) break;
      conv_info->entry[e].type=conv_info->entry[e].type & (Smask_str + Smask_stm) | (Smask_0+Smask_2);
      fillin_sprite_window(sprite_dbox, &conv_sprites);
      break;

    case Preshift_menu_all:
      if (e == -1 | fsp_type != 2) break;
      conv_info->entry[e].type=conv_info->entry[e].type |
        Smask_0+Smask_1+Smask_2+Smask_3+Smask_str+Smask_stm;
      fillin_sprite_window(sprite_dbox, &conv_sprites);
      break;

    case Preshift_menu_none:
      if (e == -1 | fsp_type != 2) break;
      conv_info->entry[e].type=0;
      fillin_sprite_window(sprite_dbox, &conv_sprites);
      break;

    case Preshift_menu_appall:
      if (e == -1 | fsp_type != 2) break;
      {
        int i;
        for (i=0; i<=conv_info->header.number; i++)
        {
          conv_info->entry[i].type = conv_info->entry[e].type;
        }
      }break;


    case Preshift_menu_help:
      open_textfile("FSPConv:Help.PreMenu");
      break;

    default:
      break;
  }
}

static void decode_sprite_menu(void *hitlist)
{
  int *hitno;
  hitno=(int *) hitlist;
  switch (hitno[0])
  {
    case Sprite_menu_convert:
      begin_conversion(&conv_sprites);
      break;

    case Sprite_menu_help:
      open_textfile("FSPConv:Help.SpriteHelp");
      break;

    case Sprite_menu_fake:
      open_fake_dbox();
      break;

    case Sprite_menu_discard:
      if (!conv_sprites.loaded) break;
      if (!conv_sprites.saved)
        if (dboxquery("Sprites are unsaved.  Do you still wish to discard them?") != dboxquery_YES) break;
      sprites_dispose(&conv_sprites);
      strcpy(conv_sprites.filename,"No Sprites");
      remake_sprite_window(&conv_sprites);
      break;

  }
}

static void generic_menuproc(void *hitlist)
{
  wimp_mousestr mouse;
  wimpt_complain(wimp_get_point_info(&mouse));

  switch (menu_type)
  {
    case M_iconbar:
      decode_bar_menu(hitlist);
      break;


    case M_sprite_number:
      if (mouse.bbits == wimp_BRIGHT)
        wimp_create_menu(menu_syshandle(sprite_number_menu), 0, 0);
      decode_number_menu(hitlist);
      break;

    case M_sprite_frame:
      if (mouse.bbits == wimp_BRIGHT)
        wimp_create_menu(menu_syshandle(sprite_frame_menu), 0, 0);
      decode_frame_menu(hitlist);
      break;

    case M_sprite_centre:
      if (mouse.bbits == wimp_BRIGHT)
        wimp_create_menu(menu_syshandle(sprite_centre_menu), 0, 0);
      decode_centre_menu(hitlist);
      break;

    case M_sprite_normal:
      if (mouse.bbits == wimp_BRIGHT)
        wimp_create_menu(menu_syshandle(sprite_menu), 0, 0);
      decode_sprite_menu(hitlist);
      break;

    case M_sprite_type:
      if (mouse.bbits == wimp_BRIGHT)
        wimp_create_menu(menu_syshandle(sprite_type_menu), 0, 0);
      decode_type_menu(hitlist);
      break;

    case M_sprite_preshift:
      if (mouse.bbits == wimp_BRIGHT)
        wimp_create_menu(menu_syshandle(sprite_preshift_menu), 0, 0);
      decode_preshift_menu(hitlist);
      break;


    default:
      break;
  }
}

static void forge_preshift(int i)
{
  decode_preshift_menu(&i);
}

static void sprite_event_handler(dbox d, void *handle)
{
  int f;
  wimp_mousestr mouse;
  wimpt_complain(wimp_get_point_info(&mouse));
  handle=handle;
  f=(int) dbox_get(d);
  check_number_incdec(d, f, Isprite_number);
  check_incdec(d, f, Isprite_frame);
  check_incdec(d, f, Isprite_xcent);
  check_incdec(d, f, Isprite_ycent);

  if (((f == Isprite_premenu) | (f == Isprite_typemenu)) && (mouse.bbits == 4))
    open_sprite_menu(&mouse);

  if (f == Isprite_all) forge_preshift(Preshift_menu_all);
  if (f == Isprite_none) forge_preshift(Preshift_menu_none);
  if (f == Isprite_hlock) forge_preshift(Preshift_menu_hlock);

  sprite_readfields(d, &conv_sprites);

}


static BOOL sprite_raw_event_handler(dbox d, void *v,  void *handle)
{
  wimp_eventstr *e;
  handle = handle;
  d=d;

  e= (wimp_eventstr *) v;
  switch (e->e)
  {
    case wimp_EREDRAW:
      redraw_sprite_window(&conv_sprites);
      return TRUE;

    case wimp_EOPEN:
      wimp_open_wind(&e->data.o);
      return TRUE;

    case wimp_ECLOSE:
      dbox_hide(sprite_dbox);
      if (convert_dbox_open)
      {
        dbox_dispose(&convert_dbox);
        convert_dbox_open = FALSE;
      }
      return FALSE;


    case wimp_EKEY:
      if (e->data.key.chcode == 13)
      {
        sprite_readfields(d, &conv_sprites);
      }
      return FALSE;

    case wimp_EBUT:
      if (e->data.but.m.bbits == wimp_BMID)
      {
        open_sprite_menu(&e->data.but.m);
        return TRUE;
      }
      return FALSE;

    case wimp_EMENU:
        generic_menuproc(&e->data.menu);
    return TRUE;

    case wimp_ESEND:
    case wimp_ESENDWANTACK:
    {
      switch (e->data.msg.hdr.action)
      {
        case wimp_MDATALOAD:   /* insert data */
          load_file(&conv_sprites);
          return TRUE;

        default: return FALSE;        /* ignore other messages */
      }

    }

    case wimp_EACK:

          if (e->data.msg.hdr.action == wimp_MDATAOPEN)
            start_task(e->data.msg.data.dataopen.name);
          return TRUE;


    case wimp_ENULL:
      convert_event();
      return TRUE;

    default:
      return FALSE;
  }
}

static void open_bar_menu(wimp_mousestr *m)
{
  menu_type=M_iconbar;
  wimpt_complain(wimp_create_menu(menu_syshandle(bar_menu), m->x-80, m->y+6*44));
}

static void bar_icon_handler(wimp_eventstr *e, void *handle)
{
  handle=handle;
  switch (e->e)
  {
    case wimp_EBUT:
      if (e->data.but.m.bbits == wimp_BMID)
      {
        open_bar_menu(&e->data.but.m);
        break;
      }
      if (e->data.but.m.bbits == wimp_BLEFT)
      {
        fillin_sprite_window(sprite_dbox, &conv_sprites);
        dbox_hide(sprite_dbox);
        dbox_showstatic(sprite_dbox);
        break;
      }
      if (e->data.but.m.bbits == wimp_BRIGHT)
      {
        begin_conversion(&conv_sprites);
        break;
      }
    break;

    case wimp_ESEND:
    case wimp_ESENDWANTACK:
    {
      switch (e->data.msg.hdr.action)
      {
        case wimp_MDATALOAD:   /* insert data */
          dbox_hide(sprite_dbox);
          dbox_showstatic(sprite_dbox);
          load_file(&conv_sprites);
          break;

        default: break;        /* ignore other messages */
      }
      break;

      default:
        break;

    }
  }
}




static void set_default_file(void)
{
  conv_info_valid=FALSE;
  conv_sprites.number=0;
  conv_sprites.loaded=FALSE;
  conv_sprites.area=0;
  conv_sprites.last_number=-1;
  strcpy(conv_sprites.name, "-");
  strcpy(save_filename,"null:FSPSprites");
}

/*  ----- Initialisation -----  */

static BOOL initialise(void)
{
  flex_init();
  heap_init(TRUE);
  wimpt_init(Prog_name);
  res_init(Prog_name);
  resspr_init();
  template_init();
  dbox_init();

/*-- Create window */

  if (sprite_dbox = dbox_new("Sprite"), sprite_dbox==0) return FALSE;

  dbox_raw_eventhandler(sprite_dbox, sprite_raw_event_handler, 0);
  dbox_eventhandler(sprite_dbox, sprite_event_handler, 0);

  sprite_whandle = dbox_syshandle(sprite_dbox);

/*-- Create Menu */

  if (bar_menu = menu_new(Prog_name,">Info,>Transparent,Convert...,Help,Quit"), bar_menu == NULL) return FALSE;
  if (sprite_menu = menu_new("Sprite","Convert...,>Fake Mode|Discard|Help"), sprite_menu == NULL) return FALSE;
  if (sprite_number_menu = menu_new("Number","1,2,3,4"), sprite_number_menu == NULL) return FALSE;
  if (sprite_frame_menu = menu_new("Frame","1,2,3,4"), sprite_frame_menu == NULL) return FALSE;
  if (sprite_centre_menu = menu_new("Centre","Centre,Left,Right,Top,Bottom,Top Left,Top Right,Bottom Left,Bottom Right|Apply to All|Help"), sprite_centre_menu == NULL) return FALSE;
  if (sprite_type_menu = menu_new("Type","FSP1,FSP2|Help"), sprite_type_menu == NULL) return FALSE;
  if (sprite_preshift_menu = menu_new("Preshift","All,None,Just 0,0 and 2|Apply to All|Help"), sprite_preshift_menu == NULL) return FALSE;


 /*-- Create Bar Icon */

  baricon(Prog_spr_name, Wimp_sprite_area, 0);

/* -- Attach everything -- */

  win_register_event_handler(win_ICONBAR, bar_icon_handler, 0);
  win_register_event_handler(win_ICONBARLOAD, bar_icon_handler, 0);
  win_claim_unknown_events(sprite_whandle);

  event_setmask(wimp_EMNULL | wimp_EMPTRENTER | wimp_EMPTRLEAVE);

  set_default_file();

  return TRUE;
}

int main()
{
   if (initialise())
  {
    while (TRUE)
      event_process();
  }
  return 0;
}
