/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* c.psprite
 *
 *  Paint: Arthur 2 sprite editor
 *   Sprite handling
 *
 *  Author: A.P. Thompson
 *  Others:
 *     DAHE David Elworthy
 *     JRC  Jonathan Coxhead
 *     JSR  Jonathan Roach
 *     CDP  Christopher Partington (Cambridge Systems Design)
 *     TMD  Tim Dobson
 *
 *  Upgraded to RISC_OSlib - DAHE - 16 Aug 1989
 *  DAHE, 28 Aug 89 - internationalisation
 *  DAHE, 13 Sept 89 - get round static data init. problem for module
 *                        version
 *  JSR,  25 Oct  89 - Fix up (*= 0x11) wimp palette after reading it.
 *                     Don't fixup 256 colour palettes when reading them for
 *                         generating translation table
 *                     Filename changed to avoid conflict with RISCOSlib
 *  CDP,  20 Feb  92
 *     Added FIX9544: part of fix of G-RO-9544 (large shear factor causes
 *     arithmetic exception). Checks for float becoming too big for an int.
 *     See also Menus.c.
 *     Added FIX8482: fixes RO-8482 (crash when rotating doesn't cause change
 *     of size) by not calling SpriteExtend when size change = 0 which would
 *     cause problems.
 *     Added FIX8490: fixes RO-8482 (crash when scale factor < 1) by
 *     calculating deletion row/column correctly. Also avoids calling
 *     SpriteExtend when count = 0 as for FIX8482.
 *  CDP,  27 Feb  92
 *     Added FIX0553: fixes RP-0553 (create sprite, new sprite not shown in
 *     summary in full-info mode).
 *  TMD,  12 Mar  92
 *     Check in psprite_scale, psprite_rotate that x- or y-size does not
 *     become zero.
 *  TMD,  16 Mar  92
 *     Fix psprite_size to work correctly for modes where log2bpc > 3 (eg
 *     mode 10)
 *  TMD,  23 Mar  92
 *     Fix psprite_rotate to delete columns/rows from sensible position, and
 *     optimise order for space. Removed use of FIX8482, since code
 *     rewritten.
 */

#include <limits.h>
#include <math.h>
#include <stddef.h> /*font.h needs it, but not all get it*/
#include <swis.h>
#include "Global/VduExt.h"

#include "bbc.h"
#include "colourtran.h"
#include "flex.h"
#include "help.h"
#include "resspr.h"
#include "visdelay.h"
#include "werr.h"
#include "wimpt.h"

#include "ftrace.h"
#include "m.h"
#include "main.h"
#include "SprWindow.h"
#include "PSprite.h"
#include "ToolWindow.h"
#include "Tools.h"
#include "Colours.h"
#include "Menus.h"
#include "MenuD.h"

#if TRACE
  /*#define XTRACE for extended tracing*/
#endif

/*#define DUMPING*/ /*dump tables when put sprite scaled fails*/

/************************************
 *   Static variables               *
 ************************************/

static char ecfstring [] =
  { /*VDU*/ 23, 17, 4, 1, 0, 0, 0, 0, 0, 0, /*set native E C F's*/
    /*VDU*/ 23,  5, 0, 0, 0, 0, 0, 0, 0, 0  /*clear E C F pattern 4*/
  };

static main_ttab *tbchain = NULL;

/*static main_ttab *default_translations [4];*/ /*default palette
    translations*/

/*!!!static main_ttab *icon_translations [4];*/ /*translations if want WIMP
    colours*/
/*!!!static main_ttab *brush_translations [8];*/ /*for brush sprite*/

  /*copies of the current palette*/
static int wimp_colours [20], twowimp_colours [2], fourwimp_colours [4];

/* statics for sprite create stuff */
static dbox Create;
static win_event_handler Old_Create_Handler;
static void *Old_Create_Handle;
static wimp_w Source; /* handle of the source window */
static unsigned Mode; /*just a cache to avoid flicker*/

/*mode numbers to use for x x y modes (indexed by lb_bpp)*/
static int
  modes_1x2 [] = {0, 8, 12, 15},
  modes_2x2 [] = {-1, 1, 9, 13},
  modes_1x1 [] = {25, 26, 27, 28};

/********************************
 * Has the sprite got a palette *
 ********************************/

sprite_palflag psprite_haspal (main_sprite *sprite)

{ sprite_header *addr = psprite_address (sprite);

  ftracef2 ("psprite_haspal \"%.12s\": %s\n", addr->name,
      WHETHER ((char *) addr + addr->image !=
      (char *) &addr->mode + sizeof (int)));
  return (sprite_palflag) ((char *) addr + addr->image !=
      (char *) &addr->mode + sizeof (int));
}

/************************************************
 * Has the sprite got a palette of true colours *
 ************************************************/

BOOL psprite_hastruecolpal (main_sprite *sprite)

{ sprite_header *addr = psprite_address (sprite);
  int truecolpalsize = 2*4*colours_count (sprite);
    /*i e, 2048 for 256-colour sprites*/

  ftracef2 ("psprite_hastruecolpal \"%.12s\": %s\n", addr->name,
      WHETHER (MIN (addr->image, addr->mask) == truecolpalsize + 44));
  return MIN (addr->image, addr->mask) == truecolpalsize + 44;
}

/********************************
 * Has the sprite got a mask    *
 ********************************/

BOOL psprite_hasmask (main_sprite *sprite)

{ sprite_header *addr = psprite_address (sprite);

  ftracef2 ("psprite_hasmask \"%.12s\": %s\n", addr->name,
      WHETHER (addr->image != addr->mask));
  return addr->image != addr->mask;
}

/********************************
 * Calculate sizeof a sprite    *
 ********************************/

int psprite_size (int width, int height, int mode, int mask, int palette)

{ int log2bpp = bbc_modevar (mode, bbc_Log2BPC), bpr, spritesize,
    palsize = 0;

  /*Modifed 4th Nov 1993 J R C so that |palette| may be
        0 => no palette
        1 => colour palette
        2 => mono palette
    colour and mono palettes are the same except in 256-colour modes, where
    a 'mono' palette has 256 entries rather than 16.
  */

  if (palette != 0)
  { int nc = bbc_modevar (mode, bbc_NColour) + 1;

    ftracef1 ("nc %d\n", nc);
    if (nc == 64 && palette == 2) nc = 256;

    palsize = 2*sizeof (int)*nc;
  }

  /*how many bytes per row?*/
  bpr = (width << log2bpp) + 31 >> 5 << 2; /*round up to word boundary*/

  if (!(bpr < ((INT_MAX & ~3) - sizeof (sprite_header) -
      palsize)/height))
  { ftracef1("psprite_size: bytes per row %d too big\n", bpr);
    return INT_MAX & ~3;
  }

  spritesize = bpr*height;

  if (mask)
  { if (!(spritesize < ((INT_MAX & ~3) - sizeof (sprite_header) -
        palsize)/2))
    { ftracef1("psprite_size: got mask and spritesize %d too big\n",
               spritesize);
      return INT_MAX & ~3;
    }

    if ((unsigned) mode < 256u)
    {
      /*Old-format sprite.*/
      spritesize += spritesize;
    }
    else if (mode & 0x80000000)
    {
      /*8bpp alpha mask.*/
      spritesize += height*((width+3)&~3);
    }
    else
    {
      /*New format sprite - 1bpp mask.*/
      spritesize += height*(width + 31 >> 5 << 2);
    }
  }
  spritesize += sizeof (sprite_header) + palsize;

  #if TRACE
    ftracef (__FILE__, __LINE__,
        "psprite_size (width %d, height %d, mode %d, mask %s, "
        "palette %d) -> %d\n",
        width, height, mode, WHETHER (mask), palette, spritesize);
  #endif
  return spritesize;
}

/********************************
 * Get full info on a sprite    *
 ********************************/

void psprite_read_full_info (main_sprite *sprite,
    psprite_info *sinfo)

{ sprite_id  sid;
  int i;
  sprite_header *header = psprite_address (sprite);

  ftracef1 ("read_psprite_info: (spriteno + 1) %d\n",
      sprite->spriteno + 1);

  sprite_getname (sprite->file->spritearea, sinfo->name,
      (i = NAME_LIMIT + 1, &i), sprite->spriteno + 1);
  sinfo->name [i] = '\0';
  ftracef1 ("name is \"%.12s\"\n", sinfo->name);
  sid.tag    = sprite_id_name;
  sid.s.name = sinfo->name;

  if (sprite_readsize (sprite->file->spritearea, &sid,
      (sprite_info *) sinfo) != NULL)
  { sinfo->width  = -1;
    sinfo->height = -1;
  }

  sinfo->mode = header->mode;
  sinfo->mask = psprite_hasmask (sprite);
  sinfo->size = header->next;
  sinfo->palette = psprite_haspal (sprite);
  sinfo->truepalette = sinfo->palette && psprite_hastruecolpal (sprite);
}

/************************************************
 *  Sprite iteration: get first sprite offset   *
 ************************************************/

int psprite_first (sprite_area **spritearea)

{ ftracef0 ("psprite_first\n");
  if ((*spritearea)->number == 0) return 0;
  return (*spritearea)->sproff;
}

/***********************************************
 *  Sprite iteration: get next sprite offset   *
 ***********************************************/

int psprite_next (sprite_area **spritearea, int sprite)

{ ftracef0 ("psprite_next\n");
  sprite += ((sprite_header *) ((char *) *spritearea + sprite))->next;
  if (sprite >= (*spritearea)->freeoff) sprite = 0;
  return sprite;
}

/********************************************************
 *  Recalculate sprite offsets in a sprite area.        *
 *  There are already enough sprite info blocks, so     *
 *  just zap the offsets and numbers.                   *
 ********************************************************/

void psprite_recalculate_offsets (main_file *file)

{ main_sprite *sprite;
  int sproff;
  sprite_area *sarea = file->spritearea;
  int spriteno = 0;

  ftracef0 ("psprite_recalculate_offsets\n");
  for (sprite = file->sprites, sproff = sarea->sproff;
      sprite != NULL && sproff < sarea->freeoff;
      sprite = sprite->link,
      sproff += ((sprite_header *) ((char *) sarea + sproff))->next)
    sprite->offset = sproff, sprite->spriteno = spriteno++;

  spencer2
  ( if (sprite != NULL || sproff < file->spritearea->freeoff)
    { ftracef0 ("****ERROR**** mismatch between sprites and info blocks\n");
      ftrace_off ();
    }
  )

  ftracef0 ("psprite_recalculate_offsets: main_set_modified\n");
  main_set_modified (file);
  ftracef0 ("psprite_recalculate_offsets -> EMPTY\n");
}

/********************************************************
 *  Delete a sprite info block from its list            *
 ********************************************************/

void psprite_delete (main_window *window, main_sprite *sprite)

{ main_sprite_window *spritewindow;
  main_file *file = sprite->file;
  main_sprite **prevsprite;
  int i;

  ftracef0 ("psprite_delete\n");
  /* panic if this is the current brush */
  if (sprite == tools_brushsprite_ptr)
  { if (toolwindow_current_tool == &tools_brushpaint)
    { toolwindow_stop_all_tools ();
      psprite_free_brush_blocks ();
    }
    tools_brushsprite_ptr = NULL;
  }

  /* free any brush translation table */
  if (toolwindow_current_tool == &tools_brushpaint)
    psprite_drop_translation ((main_ttab **) &sprite->toolspace [2]);

  /* free normal translation table */
  psprite_drop_translation (&sprite->transtab);

  /* close all windows onto the sprite */
  for (spritewindow = sprite->windows; spritewindow != NULL;
      spritewindow = sprite->windows)
    sprwindow_delete (spritewindow->window);

  /* delete any ECFs set up */
  for (i = 0; i < 4; i++)
    if (sprite->ECFs [i].sarea != NULL)
    { ftracef2 ("ECF %d freeing: 0x%X\n", i, sprite->ECFs [i].sarea);
      flex_free ((flex_ptr) &sprite->ECFs [i].sarea);
    }

  /* now find it on the chain, delink it and free */
  for (prevsprite = &file->sprites; *prevsprite != NULL;
      prevsprite = &(*prevsprite)->link)
    if (*prevsprite == sprite)
    { *prevsprite = sprite->link;
      break;
    }

  if (sprite->flags & MSF_SELECTED) window->selection.count--;

  m_FREE (sprite, sizeof (main_sprite)); /*always free it! J R C 6th Dec
      1993*/
}

/***************************************************
 *  Add a sprite info block to the list indicated  *
 ***************************************************/

main_sprite *psprite_new (int offset, int spriteno, main_file *parent)

{ main_sprite *sprite;
  int i;

  ftracef0 ("psprite_new\n");
  if ((sprite = m_ALLOC (sizeof (main_sprite))) == NULL)
  { main_NO_ROOM ("main_sprite");
    return 0; /* return false to abort any adding of more sprites */
  }

  sprite->link         = NULL;
  sprite->offset       = offset;
  sprite->spriteno     = spriteno;
  sprite->file         = parent;
  sprite->windows      = NULL;
  sprite->colourhandle = 0;
  sprite->gcol.colour  = colours_count (sprite) - 1;
  sprite->gcol.alpha   = 255;
  sprite->gcol.ecf     = FALSE;
  sprite->flags        = 0;
  ftracef2 ("initialised sprite \"%.12s\" GCOL 0x%X\n",
      psprite_address (sprite)->name, sprite->gcol);
  sprite->gcol2.colour = 0;
  sprite->gcol2.alpha  = 255;
  sprite->gcol2.ecf    = FALSE;

  for (i = 0; i < toolspacesize; i++) sprite->toolspace [i] = 0;
  ftracef2 ("0x%X->toolspace [0]: 0x%X\n", sprite, sprite->toolspace [0]);
  for (i = 0; i < 4; i++) sprite->ECFs [i].sarea = NULL;

  sprite->colourdialogue = 0;
  sprite->needsnull  = toolwindow_current_tool == &tools_textpaint ||
                       toolwindow_current_tool == &tools_brushpaint;
  sprite->transtab   = NULL;
  sprite->coloursize = main_current_options.colours.small_colours?
      colours_SIZE/2: colours_SIZE;

  return sprite;
}

/* Make a table - just a wrapper for 2 calls to ColourTrans_GenerateTable. An
 * error has happened if (*size != 0 && *table == NULL) on exit. The returned
 * value must be flex_free()d at some point.
 */

static void table_new (void *a0, void *a1, int dmode, int *dpal, int flags,
      int *size, char **table)

{ ftracef0 ("table_new\n");
  os_swi6r (ColourTrans_GenerateTable,
            a0, a1, dmode, dpal, 0 /*read size*/, flags,
            NULL, NULL, NULL, NULL, size, NULL);
  ftracef1 ("size %d\n", *size);

  if (*size == 0)
    *table = NULL;
  else
  { if (!flex_alloc ((flex_ptr) table, *size))
    { ftracef0 ("*** failed to find memory\n");
      *table = NULL;
      return;
    }

    os_swi6 (ColourTrans_GenerateTable, a0, a1, dmode, dpal, *table,
             flags);
  }
}

/*********************************************
 * Call the SWI to build a translation table *
 *********************************************/

static const unsigned int modesixteen [] =     /* actual colour values */
{ 0,             /* black */
  0xFF00,        /* red */
  0xFF0000,      /* green */
  0xFFFF00,      /* yellow */
  0xFF000000,    /* blue */
  0xFF00FF00,    /* magenta */
  0xFFFF0000,    /* cyan */
  0xFFFFFF00,    /* white */
  0,             /* black - flashing */
  0xFF00,        /* red */
  0xFF0000,      /* green */
  0xFFFF00,      /* yellow */
  0xFF000000,    /* blue */
  0xFF00FF00,    /* magenta */
  0xFFFF0000,    /* cyan */
  0xFFFFFF00     /* white */
};

static const unsigned int hardmode_defpal [] = /* default palette for 256 colour modes */
{ 0,            /* 0000 */
  0x10101000,   /* 0001 */
  0x20202000,   /* 0010 */
  0x30303000,   /* 0011 */
  0x00004000,   /* 0100 */
  0x10105000,   /* 0101 */
  0x20206000,   /* 0110 */
  0x30307000,   /* 0111 */
  0x40000000,   /* 1000 */
  0x50101000,   /* 1001 */
  0x60202000,   /* 1010 */
  0x70303000,   /* 1011 */
  0x40004000,   /* 1100 */
  0x50105000,   /* 1101 */
  0x60206000,   /* 1110 */
  0x70307000    /* 1111 */
};

static const unsigned int black_and_white [] =
{ 0,            /* black */
  0xFFFFFF00    /* white */
};

static const unsigned int fourcolourpal [] =
{ 0,            /* black */
  0xFF00,       /* red */
  0xFFFF00,     /* yellow */
  0xFFFFFF00    /* white */
};

int *psprite_std_palettes [2] [4];

static void init_palettes (void)

{ /* Required to generate relocatable code */
 ftracef0 ("init_palettes\n");
  psprite_std_palettes [0] [0] = twowimp_colours;
  psprite_std_palettes [0] [1] = fourwimp_colours;
  psprite_std_palettes [0] [2] = wimp_colours;
  psprite_std_palettes [0] [3] = (int *)hardmode_defpal;
  psprite_std_palettes [1] [0] = (int *)black_and_white;
  psprite_std_palettes [1] [1] = (int *)fourcolourpal;
  psprite_std_palettes [1] [2] = (int *)modesixteen;
  psprite_std_palettes [1] [3] = (int *)hardmode_defpal;
}

static int Current_Palette [256];

#ifdef XTRACE
  static void Trace_Ttab (main_ttab *ttab)

  { int i;

    ftracef1 ("Trace_Ttab: refcount %d table1 {", ttab->refcount);
    for (i = 0; i < ttab->ttab_size; i++)
      ftracef (NULL, 0, i == 0? "%d": ", %d", ttab->table [i]);
    ftracef (NULL, 0, "} sbpp %d, table2 {", ttab->sbpp);
    for (i = 0; i < ttab->ttab2_size; i++)
      ftracef (NULL, 0, i == 0? "%d": ", %d", ttab->table2 [i]);
    ftracef (NULL, 0, "} dbpp %d\n", ttab->dbpp);
  }
#else
  #define Trace_Ttab(ttab) ((void) 0)
#endif

#ifdef DUMPING
  static void Dump_Ttab (main_ttab *ttab)

  { FILE *f;

    ftracef0 ("Dump_Ttab\n");

    if ((f = fopen ("$.table", "wb")) != NULL)
    { fwrite (ttab->table, 1, ttab->ttab_size, f);
      fclose (f);
    }

    if ((f = fopen ("$.table2", "wb")) != NULL)
    { fwrite (ttab->table2, 1, ttab->ttab2_size, f);
      fclose (f);
    }

  }
#endif

main_ttab *psprite_ttab_for_sprite (main_sprite *sprite, int dmode,
    int *dpal)

{ main_ttab *tb = NULL;
  int smode, slb_bpp, dlb_bpp, sbpp, dbpp, spal [256 /*no palette can be
      larger than this*/], *destpal, spalsize, dncolour, dmodeflags;
  os_error *error;
  char *msg = NULL; /*this is our error indicator*/

  ftracef3 ("psprite_ttab_for_sprite \"%.12s\", dmode %d, dpal 0x%X\n",
      psprite_address (sprite)->name, dmode, dpal);

  /*Get smode and spal.*/
  smode = psprite_address (sprite)->mode;
  slb_bpp = bbc_modevar (smode, bbc_Log2BPP);
  dlb_bpp = bbc_modevar (dmode, bbc_Log2BPP);
  dncolour = bbc_modevar (dmode, bbc_NColour);
  dmodeflags = bbc_modevar (dmode, bbc_ModeFlags);
  ftracef3 ("smode 0x%X, slb_bpp %d, dlb_bpp %d\n",
      smode, slb_bpp, dlb_bpp);

  /*Catch attempts to use sprites of unknown mode.*/
  if (slb_bpp != -1 && dlb_bpp != -1)
  { sbpp = 1 << slb_bpp;
    dbpp = 1 << dlb_bpp;

    if (sbpp <= 8)
    { if (psprite_haspal (sprite))
      { /*Extract the (expanded) source palette from the sprite.*/
        ftracef0 ("ColourTrans_ReadPalette\n");
        spalsize = 4 << sbpp;
        if ((error = os_swix5 (ColourTrans_ReadPalette,
            sprite->file->spritearea, psprite_address (sprite), spal,
            spalsize, 1 << 0 /*R1 -> sprite*/)) != NULL)
        { msg = error->errmess;
          goto finish;
        }
      }
      else
      { /*Copy the relevant standard palette.*/
        spalsize = sbpp != 8? 4 << sbpp: 4*16;
        memcpy (spal, psprite_std_palettes
            [sprite->file->use_current_palette? 0: 1] [slb_bpp], spalsize);
      }
    }
    else
      spalsize = 0; /**/

    if (dmode == -1 && dbpp <= 8)
    { ftracef0 ("using the current palette\n");
      destpal = Current_Palette;
    }
    else
    { ftracef0 ("using the given palette\n");
      destpal = dpal;
    }

  #ifdef XTRACE
  { int i;

    ftracef1 ("source palette: %d entries\n", spalsize/4);
    if (sbpp <= 8)
      for (i = 0; i < spalsize/4; i++)
        ftracef2 ("%d: 0x%X\n", i, spal [i]);
    ftracef1 ("destination palette: %d entries\n", 1 << dbpp);
    if (dbpp <= 8)
      for (i = 0; i < 1 << dbpp; i++)
        ftracef2 ("%d: 0x%X\n", i, destpal [i]);
  }
  #endif

    /*If the table already exists, reuse it.*/
    ftracef0 ("looking for table\n");
    for (tb = tbchain; tb != NULL; tb = tb->link)
      if (tb->smode == smode && tb->dlog2bpp == dlb_bpp && tb->dncolour == dncolour && tb->dmodeflags == dmodeflags && tb->spalsize == spalsize &&
          (sbpp <= 8? memcmp (tb->spal, spal,    4 << sbpp) == 0: TRUE) &&
          (dbpp <= 8? memcmp (tb->dpal, destpal, 4 << dbpp) == 0: TRUE))
      { tb->refcount++;
        break;
      }

    if (tb == NULL)
    { if ((tb = psprite_ttab_new (sprite, dmode, dpal)) == NULL)
      { msg = msgs_lookup ("PntEG");
        goto finish;
      }

      /*Link this block into the list.*/
      tb->link = tbchain;
      tbchain  = tb;

      ftracef0 ("Made new translation table\n");
    }
    else
    { ftracef0 ("using preexisting table\n");
    }
  }

finish:
  if (msg != NULL)
  { ftracef1 ("psprite_ttab_for_sprite got error \"%s\"\n", msg);
    return NULL;
  }
  else
  { Trace_Ttab (tb);
    ftracef0 ("psprite_ttab_for_sprite got no error\n");
    return tb;
  }
}

main_ttab *psprite_ttab_new (main_sprite *sprite, int dmode, int *dpal)

{ main_ttab *tb = NULL;
  int smode, slb_bpp, dlb_bpp, sbpp, dbpp, spal [256 /*no palette can
      be larger than this*/], *destpal, spalsize, dncolour, dmodeflags;
  os_error *error;
  char *msg = NULL; /*this is our error indicator*/
  BOOL done_tb = FALSE, done_spal = FALSE, done_dpal = FALSE,
     done_table = FALSE, done_table2 = FALSE;

/* Returns NULL if something goes wrong, but in practice this can only be
 * NoMem.
 */

/* J R C 14th Jun 1994: rewritten again for Black: uses the fact that wide
 *    and shallow entries are the same if dlb_bpp <= 3. Also ...
 *    |ttab| is the translation table in the current mode (byte-, short- or
 *       int-wide). This is used by both Sprite Extend and OS_SetColour for the
 *       colours window.
 *    |ttab2| is not used.
 *
 * Older operating systems are not supported - Paint uses OS_SetColour
 *    extensively now.
 */

  ftracef3 ("psprite_ttab_new (\"%.12s\", dmode %d, dpal 0x%X)\n",
      psprite_address (sprite)->name, dmode, dpal);

  /*Get smode and spal.*/
  smode = psprite_address (sprite)->mode;
  slb_bpp = bbc_modevar (smode, bbc_Log2BPP);
  dlb_bpp = bbc_modevar (dmode, bbc_Log2BPP);
  dncolour = bbc_modevar (dmode, bbc_NColour);
  dmodeflags = bbc_modevar (dmode, bbc_ModeFlags);
  ftracef3 ("smode 0x%X, slb_bpp %d, dlb_bpp %d\n",
      smode, slb_bpp, dlb_bpp);

  /*Catch attempts to use sprites of unknown mode.*/
  if (slb_bpp != -1 && dlb_bpp != -1)
  { sbpp = 1 << slb_bpp;
    dbpp = 1 << dlb_bpp;

    if (sbpp <= 8)
    { if (psprite_haspal (sprite))
      { /*Extract the (expanded) source palette from the sprite.*/
        ftracef0 ("ColourTrans_ReadPalette\n");
        spalsize = 4 << sbpp;
        if ((error = os_swix5 (ColourTrans_ReadPalette,
            sprite->file->spritearea, psprite_address (sprite), spal,
            4 << sbpp, 1 << 0 /*R1 -> sprite*/)) != NULL)
        { msg = error->errmess;
          goto finish;
        }
      }
      else
      { /*Copy the relevant standard palette.*/
        spalsize = sbpp != 8? 4 << sbpp: 4*16;
        memcpy (spal, psprite_std_palettes
            [sprite->file->use_current_palette? 0: 1] [slb_bpp], spalsize);
      }
    }
    else
      spalsize = 0; /**/

    if (dmode == -1 && dbpp <= 8)
    { ftracef0 ("using the current palette\n");
      destpal = Current_Palette;
    }
    else
    { ftracef0 ("using the given palette\n");
      destpal = dpal;
    }

  #ifdef XTRACE
  { int i;

    ftracef1 ("source palette: %d entries\n", spalsize/4);
    if (sbpp <= 8)
     for (i = 0; i < spalsize/4; i++)
          ftracef2 ("%d: 0x%X\n", i, spal [i]);
    ftracef1 ("destination palette: %d entries\n", 1 << dbpp);
    if (dbpp <= 8)
       for (i = 0; i < 1 << dbpp; i++)
          ftracef2 ("%d: 0x%X\n", i, destpal [i]);
  }
  #endif

    ftracef0 ("Allocate a new translation table block\n");
    if ((tb = m_ALLOC (sizeof *tb)) == NULL)
    { msg = msgs_lookup ("PntEG");
      goto finish;
    }
    done_tb = TRUE;

    tb->refcount = 1;

    ftracef0 ("Copy the palettes into the block\n");
    tb->smode = smode;
    tb->spalsize = spalsize;
    if (sbpp <= 8)
    { if (!flex_alloc ((flex_ptr) &tb->spal, spalsize))
      { tb->spal = 0;
        msg = msgs_lookup ("PntEG");
        goto finish;
      }
      done_spal = TRUE;

      memcpy (tb->spal, spal, spalsize);
    }
    else
      tb->spal = 0;

    tb->dlog2bpp = dlb_bpp;
    tb->dncolour = dncolour;
    tb->dmodeflags = dmodeflags;
    if (dbpp <= 8)
    { if (!flex_alloc ((flex_ptr) &tb->dpal, 4 << dbpp))
      { tb->dpal = 0;
        msg = msgs_lookup ("PntEG");
        goto finish;
      }
      done_dpal = TRUE;

      memcpy (tb->dpal, destpal, 4 << dbpp);
    }
    else
      tb->dpal = 0;

    /*Black*/
    if (slb_bpp > 3 || psprite_hastruecolpal (sprite))
    { /*Use the sprites own palette if it has one, or if it is a deep
        sprite in the "new format" (in which case it's not allowed a
        palette at all, and ColourTrans understands this).*/
      table_new (sprite->file->spritearea, psprite_address (sprite), dmode,
          dpal, 1 << 0 /*R1 is sprite*/ | 1 << 4 /*wide entries for deep
          modes*/, &tb->ttab_size, &tb->table);
      if (tb->ttab_size != 0 && tb->table == NULL)
      {  msg = msgs_lookup ("PntEG");
         goto finish;
      }
      done_table = TRUE;
    }
    else
    { /*Use one of the standard palettes for sprites without one.*/
      table_new ((void *) psprite_address (sprite)->mode,
          psprite_std_palettes [sprite->file->use_current_palette? 0: 1]
          [slb_bpp], dmode, dpal, 1 << 4 /*wide entries for deep modes*/,
          &tb->ttab_size, &tb->table);
      if (tb->ttab_size != 0 && tb->table == NULL)
      {  msg = msgs_lookup ("PntEG");
         goto finish;
      }
      done_table = TRUE;
    }

    tb->table2 = NULL;
    tb->ttab2_size = 0;
  }

finish:
  ftracef2 ("table 0x%X, table2 0x%X\n", tb->table, tb->table2);

  if (msg != NULL)
  { if (done_table2 && tb->ttab2_size != 0)
      flex_free ((flex_ptr) &tb->table2);
    if (done_table && tb->ttab_size != 0)
      flex_free ((flex_ptr) &tb->table);
    if (done_dpal)
      flex_free ((flex_ptr) &tb->dpal);
    if (done_spal)
      flex_free ((flex_ptr) &tb->spal);
    if (done_tb)
      m_FREE (tb, sizeof *tb);

    ftracef1 ("psprite_ttab_new got error \"%s\"\n", msg);
    werr (FALSE, msg);
    return NULL;
  }
  else
  { spencer (Trace_Ttab (tb))

    ftracef0 ("psprite_ttab_new got no error\n");
    return tb;
  }
}

void psprite_drop_translation (main_ttab **tb)

{ ftracef0 ("psprite_drop_translation\n");

  if (*tb != NULL && --(*tb)->refcount == 0)
  { main_ttab **prev;

    for (prev = &tbchain; *prev != NULL; prev = &(*prev)->link)
      if (*prev == *tb)
      { ftracef1 ("ttab block @ 0x%X dies\n", *tb);
        *prev = (*tb)->link;
        psprite_ttab_delete (*tb);
        *tb = NULL;
        break;
      }
  }
}

void psprite_ttab_delete (main_ttab *tb)

{ ftracef0 ("psprite_ttab_delete\n");

  ftracef0 ("psprite_ttab_delete: deleting tb->table2\n");
  if (tb->table2 != 0) flex_free ((flex_ptr) &tb->table2);
  ftracef0 ("psprite_ttab_delete: deleting tb->table\n");
  if (tb->table  != 0) flex_free ((flex_ptr) &tb->table);
  ftracef0 ("psprite_ttab_delete: deleting tb->dpal\n");
  if (tb->dpal   != 0) flex_free ((flex_ptr) &tb->dpal);
  ftracef0 ("psprite_ttab_delete: deleting tb->spal\n");
  if (tb->spal   != 0) flex_free ((flex_ptr) &tb->spal);
  ftracef0 ("psprite_ttab_delete: deleting tb\n");
  m_FREE (tb, sizeof *tb);
}

/***************************************************
 *  Calculate the default translation tables for   *
 *   the current mode.                             *
 ***************************************************/

os_error *psprite_set_default_translations (void)

{ int i, c1, c2, bitaddr, mode_bpp;
  wimp_palettestr palette_str;
  unsigned int mode_mask;
  os_error *error = NULL;

  ftracef0 ("psprite_set_default_translations\n");

  init_palettes ();

  mode_bpp = 1 << bbc_modevar (-1, bbc_Log2BPP);
  ftracef1 ("mode_bpp %d\n", mode_bpp);

  mode_mask = ~0u >> 32 - mode_bpp;
  ftracef1 ("mode_mask %d\n", mode_mask);

  if (mode_bpp <= 8)
    os_swi5 (ColourTrans_ReadPalette, -1, -1, Current_Palette,
        sizeof Current_Palette, 0);
  /*otherwise, this is just untrustworthy*/

  wimpt_noerr (wimp_readpalette (&palette_str));

  /* Fix up the wimp palette */
  for (i = 0; i < 20; i++)
    wimp_colours [i] = palette_str.c [i].word & 0xFFFFFF00;
        /*take out low byte*/

  fourwimp_colours [0] = twowimp_colours [0] = wimp_colours [0];
  fourwimp_colours [1] =                       wimp_colours [2/*was 1*/];
  fourwimp_colours [2] =                       wimp_colours [4/*was 3*/];
  fourwimp_colours [3] = twowimp_colours [1] = wimp_colours [7];

  /*!!!for (i = 0; i < 4; i++)
  { psprite_drop_translation (&default_translations [i]);
    psprite_drop_translation (&icon_translations [i]);
  }*/

  /* now set up an appropriate ECF string */
  if (mode_bpp <= 8)
  { c1 = palette_str.c [0].bytes.gcol;
    c2 = palette_str.c [7].bytes.gcol;
    ftracef2 ("GCOL's are %d, %d\n", c1, c2);

    /* these should really be demunged if in 256 colour mode; see if anybody
       ever notices! */
    for (i = 12; i < 20; i++) ecfstring [i] = '\0';
    for (bitaddr = 0; bitaddr < 64; bitaddr += mode_bpp)
    { int temp, bytenum = 12 + (bitaddr >> 3), shift = bitaddr & 7,
        currmask = ~(mode_mask << shift);

    #if TRACE
      if (!(bytenum < 20)) werr (TRUE, "BUG!");
    #endif
      ecfstring [bytenum] = (ecfstring [bytenum] & currmask) | c1 << shift;

      temp = c1; c1 = c2; c2 = temp;
    }
  }
  else
  {
    int black,white;
    os_swi3r(ColourTrans_ReturnColourNumberForMode,0,-1,-1,&black,NULL,NULL);
    os_swi3r(ColourTrans_ReturnColourNumberForMode,0xffffff00,-1,-1,&white,NULL,NULL);
    if (mode_bpp == 16)
    { *(short *) &ecfstring [12] = *(short *) &ecfstring [16] = white;
      *(short *) &ecfstring [14] = *(short *) &ecfstring [18] = black;
    }
    else /*mode_bpp == 32*/
    { *(int *) &ecfstring [12] = white;
      *(int *) &ecfstring [16] = black;
    }
  }
  #if TRACE
    ftracef (__FILE__, __LINE__,
        "ecfstring %d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
        ecfstring [0], ecfstring [1], ecfstring [2], ecfstring [3],
        ecfstring [4], ecfstring [5], ecfstring [6], ecfstring [7],
        ecfstring [8], ecfstring [9]);
    ftracef (__FILE__, __LINE__,
        "%d,%d,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X\n",
        ecfstring [10], ecfstring [11], ecfstring [12], ecfstring [13],
        ecfstring [14], ecfstring [15], ecfstring [16], ecfstring [17],
        ecfstring [18], ecfstring [19]);
  #endif

  return error;
}

/****************************************************
 * (Re)Calculate scaling needed for icon of sprite; *
 *  needs mode scale already set                    *
 ****************************************************/

void psprite_set_icon_scale (main_sprite *sprite)

{ sprite_info infoblock;
  sprite_id sid;
  int iconwidth, iconheight, fullinfo;
  sprite_ptr sinfo = (sprite_ptr) psprite_address (sprite);

  ftracef0 ("psprite_set_icon_scale\n");
  fullinfo = sprite->file->fullinfo;
  iconwidth = fullinfo? 3*main_FILER_TextHeight-main_FILER_Border:
                         main_FILER_XSize;
  iconheight = fullinfo? iconwidth: main_FILER_YSize;
  ftracef2 ("iconwidth %d, iconheight %d\n", iconwidth, iconheight);

  sid.s.addr = sinfo;
  sid.tag = sprite_id_addr;

  /*can't do anything if mode invalid*/
  if (sprite_readsize (sprite->file->spritearea, &sid, &infoblock) != NULL)
    return; /*But shut up about it! J R C 15th Oct 1993*/
  ftracef2 ("spritewidth %d, spriteheight %d\n",
      infoblock.width, infoblock.height);

  /* do scaling to get sprite a fixed size in "filer" window */
  sprite->iconsize.scale_xmul =
  sprite->iconsize.scale_xdiv =
  sprite->iconsize.scale_ymul =
  sprite->iconsize.scale_ydiv = 1;

  if (sprite->mode.scale_xmul*infoblock.width > iconwidth)
  { sprite->iconsize.scale_ymul =
    sprite->iconsize.scale_xmul = iconwidth;
    sprite->iconsize.scale_ydiv =
    sprite->iconsize.scale_xdiv = sprite->mode.scale_xmul*infoblock.width;
  }

  if (sprite->mode.scale_ymul*infoblock.height*sprite->iconsize.scale_ymul
       > sprite->iconsize.scale_ydiv*iconheight)
   /* Y dimension larger - it governs scale */
  { sprite->iconsize.scale_ymul =
    sprite->iconsize.scale_xmul = iconheight;

    sprite->iconsize.scale_ydiv =
    sprite->iconsize.scale_xdiv = sprite->mode.scale_ymul*infoblock.height;
  }
}

/*****************************************************
 *  Calculate the scaling tables for the given file  *
 *****************************************************/

os_error *psprite_set_plot_info (main_file *file)

{ main_sprite *sprite;
  int
    mode_eig_x = bbc_modevar (-1, bbc_XEigFactor),
    mode_eig_y = bbc_modevar (-1, bbc_YEigFactor);
  os_error *error = NULL;

  ftracef0 ("psprite_set_plot_info\n");

  for (sprite = file->sprites; sprite != NULL; sprite = sprite->link)
  { int mode = psprite_address (sprite)->mode,
      sprite_eig_x = bbc_modevar (mode, bbc_XEigFactor),
      sprite_eig_y = bbc_modevar (mode, bbc_YEigFactor);

    ftracef4 ("sprite 0x%X, mode %d, x eig %d, y eig %d\n",
        sprite, mode, sprite_eig_x, sprite_eig_y);

    sprite->mode.scale_xmul = 1 << sprite_eig_x;
    sprite->mode.scale_xdiv = 1 << mode_eig_x;
    sprite->mode.scale_ymul = 1 << sprite_eig_y;
    sprite->mode.scale_ydiv = 1 << mode_eig_y;

    ftracef0 ("calling psprite_set_icon_scale\n");
    psprite_set_icon_scale (sprite);
    ftracef0 ("called psprite_set_icon_scale\n");

    ftracef1 ("link is 0x%X\n", sprite->link);
  }

  return error;
}

/**********************************************
 *  Calculate the colour translation tables   *
 *   for the sprites in the given file.       *
 **********************************************/

int psprite_palsize (main_sprite *sprite)

{ sprite_header *header = psprite_address (sprite);
  int size, lb_bpp = bbc_modevar (header->mode, bbc_Log2BPP);

  /*Find out the palette size*/
  if (psprite_haspal (sprite))
    os_swi6r (ColourTrans_ReadPalette,
        sprite->file->spritearea, header, 0 /*read size*/, 0, 1 << 0 /*sprite
        in R1*/, 0, NULL, NULL, NULL, &size, NULL, NULL);
  else
    size = ENTRIES (lb_bpp)*sizeof (int);
  ftracef1 ("psprite_palsize: palette size is %d\n", size);
  return size;
}

/*New code - reallocs *palptr to point at the palette for the sprite. Sets
  to NULL (and frees the block) if it can't.*/

os_error *psprite_build_palette (main_sprite *sprite, int **palptr)

{ int size;
  os_error *error = NULL;

  ftracef0 ("psprite_build_palette\n");

  m_FREE (*palptr, 0);
  *palptr = NULL;

  /*Find out the palette size*/
  size = psprite_palsize (sprite);

  if (size != 0)
  { /*Get the store for the palette.*/
    if ((*palptr = m_ALLOC (size)) == NULL)
    { error = main_error ("PntEG");
      goto finish;
    }

    if (psprite_haspal (sprite))
    { if ((error = os_swix6 (ColourTrans_ReadPalette,
          sprite->file->spritearea, psprite_address (sprite), *palptr, size,
          1 << 0 /*sprite in R1*/, 0)) != NULL)
        goto finish;
    }
    else
      memcpy (*palptr, psprite_std_palettes
          [sprite->file->use_current_palette? 0: 1]
          [bbc_modevar (psprite_address (sprite)->mode, bbc_Log2BPP)], size);
  }

finish:
  return error;
}

os_error *psprite_set_colour (main_sprite *sprite)

{ os_error *error = NULL;

  ftracef0 ("psprite_set_colour\n");
  /*Not if the sprite hasn't got a sensible mode. J R C 20th Jun 1994*/
  if (bbc_modevar (psprite_address (sprite)->mode, bbc_Log2BPP) != -1)
  { psprite_drop_translation (&sprite->transtab);

    ftracef0 ("calling psprite_ttab_for_sprite()\n");
    if ((sprite->transtab = psprite_ttab_for_sprite (sprite, -1, (int *) -1))
        == NULL)
    { error = main_error ("PntEG");
      goto finish;
    }
  }

finish:
  return error;
}

os_error *psprite_set_colour_info (main_file *file)

{ main_sprite *sprite;
  os_error *error = NULL;
  BOOL done_begin = FALSE;

  ftracef0 ("psprite_set_colour_info\n");
  visdelay_begin ();
  done_begin = TRUE;
  for (sprite = file->sprites; sprite != NULL; sprite = sprite->link)
    if ((error = psprite_set_colour (sprite)) != NULL)
      goto finish;

finish:
  if (done_begin) visdelay_end ();
  return error;
}

/***************************************************
 *  Plot sprite at coordinate given                *
 *   using sprites colour table, and scaled by     *
 *   sprites modescale*given scaling               *
 ***************************************************/

os_error *psprite_plot_scaled (int x, int y, main_sprite *sprite,
    main_scaling_block *scale, int mode)

  /*Recoded to work with the new psprite_ttab_new(). J R C 17th Jun 1994*/

{ main_scaling_block scaling;
  os_error *error;
  os_regset regs;

  ftracef1 ("psprite_plot_scaled \"%.12s\"\n",
      psprite_address (sprite)->name);
  scaling.scale_xmul = sprite->mode.scale_xmul*scale->scale_xmul;
  scaling.scale_xdiv = sprite->mode.scale_xdiv*scale->scale_xdiv;
  scaling.scale_ymul = sprite->mode.scale_ymul*scale->scale_ymul;
  scaling.scale_ydiv = sprite->mode.scale_ydiv*scale->scale_ydiv;

  Trace_Ttab (sprite->transtab);

  ftracef0 ("putting sprite scaled ...\n");
  ftracef2 ("area 0x%X, sprite 0x%X\n", sprite->file->spritearea,
     psprite_address (sprite));
  ftracef3 ("pos (%d, %d), action 0x%X\n", x, y, mode | 8 |
      psprite_haspal (sprite) << 4 | 1 << 5);
  ftracef4 ("factors (%d:%d, %d:%d)\n", scaling.scale_xmul,
      scaling.scale_xdiv, scaling.scale_ymul, scaling.scale_ydiv);
  ftracef1 ("table at 0x%X\n", sprite->transtab->table);
  /*Black*/
  regs.r[0] = 512 | 52;
  regs.r[1] = (int)sprite->file->spritearea;
  regs.r[2] = (int)psprite_address (sprite);
  regs.r[3] = x;
  regs.r[4] = y;
  regs.r[5] = mode | 8 /*use mask if present*/ | 1 << 5 /*ttab is wide*/;
  regs.r[6] = (int)&scaling;
  regs.r[7] = (int)sprite->transtab->table;
  error = os_swix (OS_SpriteOp, &regs);
  ftracef0 ("putting sprite scaled ... done\n");

#ifdef DUMPING
  if (error != NULL)
  { Dump_Ttab (sprite->transtab);
  }
#endif

  return error;
}

/***************************************************
 *   Read sprite size in os units                  *
 ***************************************************/

BOOL psprite_read_size (main_sprite *sprite, sprite_info *infoblock)

{ sprite_id sid;
  os_error *error;

  ftracef0 ("psprite_read_size\n");
  sid.s.addr = (sprite_ptr) psprite_address (sprite);
  sid.tag = sprite_id_addr;

  if ((error = sprite_readsize (sprite->file->spritearea, &sid, infoblock))
      != NULL)
  { ftracef1 ("sprite_readsize give error %s\n", error->errmess);
    return TRUE;
  }

  infoblock->width  *= sprite->mode.scale_xmul;
  infoblock->height *= sprite->mode.scale_ymul;

  return FALSE;
}

/*****************************************************
 *  Plot sprite at coordinate given                  *
 *   using sprites colour table, and scaled by       *
 *   sprites modescale*given scaling.                *
 *   same parameters as ppsprite_plot_scaled_centred *
 *   new 2007 CG                                     *
 ****************************************************/

os_error *psprite_plot_scaled_m (int x, int y, main_sprite *sprite,
    main_scaling_block *scale, int mode, int maskreally)

{
  main_scaling_block scaling;
  os_error *error = NULL;
  os_regset regs;
  int op, flags = 0;
  char *table = NULL;

  ftracef1 ("psprite_plot_scaled_m \"%.12s\"\n",
      psprite_address (sprite)->name);

  scaling.scale_xmul = sprite->mode.scale_xmul*scale->scale_xmul;
  scaling.scale_xdiv = sprite->mode.scale_xdiv*scale->scale_xdiv;
  scaling.scale_ymul = sprite->mode.scale_ymul*scale->scale_ymul;
  scaling.scale_ydiv = sprite->mode.scale_ydiv*scale->scale_ydiv;

  Trace_Ttab (sprite->transtab);

  if (!maskreally)
  { op = 512 | 52;

    flags = mode | 1 << 3 /*use mask if present*/ |
        1 << 5 /*ttab is wide*/; /*JRC 30th Jan 1995*/
    table = sprite->transtab->table;
  }
  else
  { op = 512 | 50;
    flags = mode;
    table = NULL;
  }

  ftracef0 ("putting sprite scaled ...\n");
  ftracef1 ("R0 0x%X\n", op);
  ftracef1 ("R1 0x%X\n", sprite->file->spritearea);
  ftracef1 ("R2 0x%X\n", psprite_address (sprite));
  ftracef1 ("R3 0x%X\n", x);
  ftracef1 ("R4 0x%X\n", y);
  ftracef1 ("R5 0x%X\n", flags);
  ftracef1 ("R6 0x%X\n", &scaling);
  ftracef1 ("R7 0x%X\n", table);
  ftracef4 ("factors (%d:%d, %d:%d)\n", scaling.scale_xmul,
      scaling.scale_xdiv, scaling.scale_ymul, scaling.scale_ydiv);

  regs.r[0] = op;
  regs.r[1] = (int)sprite->file->spritearea;
  regs.r[2] = (int)psprite_address (sprite);
  regs.r[3] = x;
  regs.r[4] = y;
  regs.r[5] = flags;
  regs.r[6] = (int)&scaling;
  regs.r[7] = (int)table;
  error = os_swix (OS_SpriteOp, &regs);
  ftracef0 ("putting sprite scaled ... done\n");

#ifdef DUMPING
  if (error != NULL)
  { Dump_Ttab (sprite->transtab);
  }
#endif

  return error;
}

/***************************************************
 *  Plot sprite centred at coordinate given        *
 *   using sprites colour table, and scaled by     *
 *   sprites modescale*given scaling               *
 ***************************************************/

os_error *psprite_plot_scaled_centred (int x, int y, main_sprite *sprite,
    main_scaling_block *scale, int mode, int maskreally)

{ sprite_info infoblock;
  main_scaling_block scaling;
  os_error *error = NULL;
  os_regset regs;
  int op, flags = 0;
  char *table = NULL;

  ftracef1 ("psprite_plot_scaled_centred \"%.12s\"\n",
      psprite_address (sprite)->name);
  psprite_read_size (sprite, &infoblock);

  scaling.scale_xmul = sprite->mode.scale_xmul*scale->scale_xmul;
  scaling.scale_xdiv = sprite->mode.scale_xdiv*scale->scale_xdiv;
  scaling.scale_ymul = sprite->mode.scale_ymul*scale->scale_ymul;
  scaling.scale_ydiv = sprite->mode.scale_ydiv*scale->scale_ydiv;

  infoblock.width  *= scale->scale_xmul;
  infoblock.height *= scale->scale_ymul;
  infoblock.width  /= scale->scale_xdiv*2;
  infoblock.height /= scale->scale_ydiv*2;

  Trace_Ttab (sprite->transtab);

  if (!maskreally)
  { op = 512 | 52;

    flags = mode | 1 << 3 /*use mask if present*/ |
        1 << 5 /*ttab is wide*/; /*JRC 30th Jan 1995*/
    table = sprite->transtab->table;
  }
  else
  { op = 512 | 50;
    flags = mode;
    table = NULL;
  }

  ftracef0 ("putting sprite scaled ...\n");
  ftracef1 ("R0 0x%X\n", op);
  ftracef1 ("R1 0x%X\n", sprite->file->spritearea);
  ftracef1 ("R2 0x%X\n", psprite_address (sprite));
  ftracef1 ("R3 0x%X\n", x - infoblock.width);
  ftracef1 ("R4 0x%X\n", y - infoblock.height);
  ftracef1 ("R5 0x%X\n", flags);
  ftracef1 ("R6 0x%X\n", &scaling);
  ftracef1 ("R7 0x%X\n", table);
  ftracef4 ("factors (%d:%d, %d:%d)\n", scaling.scale_xmul,
      scaling.scale_xdiv, scaling.scale_ymul, scaling.scale_ydiv);

  regs.r[0] = op;
  regs.r[1] = (int)sprite->file->spritearea;
  regs.r[2] = (int)psprite_address (sprite);
  regs.r[3] = x - infoblock.width;
  regs.r[4] = y - infoblock.height;
  regs.r[5] = flags;
  regs.r[6] = (int)&scaling;
  regs.r[7] = (int)table;
  error = os_swix (OS_SpriteOp, &regs);
  ftracef0 ("putting sprite scaled ... done\n");

#ifdef DUMPING
  if (error != NULL)
  { Dump_Ttab (sprite->transtab);
  }
#endif

  return error;
}

/************************************
 * Return sprite offset of a sprite *
 ************************************/

sprite_header *psprite_address (main_sprite *sprite)

{ return (sprite_header *) ((char *) sprite->file->spritearea +
      sprite->offset);
}

/*********************************************
 * Set fore/back GCOL and ECF to stipple     *
 *********************************************/

void psprite_ecf (int where)

{ ftracef0 ("psprite_ecf\n");
  #if TRACE
    ftracef (__FILE__, __LINE__,
        "VDU %d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
        ecfstring [0], ecfstring [1], ecfstring [2], ecfstring [3],
        ecfstring [4], ecfstring [5], ecfstring [6], ecfstring [7],
        ecfstring [8], ecfstring [9]);
    ftracef (__FILE__, __LINE__,
        "VDU %d,%d,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X\n",
        ecfstring [10], ecfstring [11], ecfstring [12], ecfstring [13],
        ecfstring [14], ecfstring [15], ecfstring [16], ecfstring [17],
        ecfstring [18], ecfstring [19]);
  #endif
  os_swix2 (OS_WriteN, (int) ecfstring, 20);
  bbc_gcol (64, where);
}

/*********************************************
 * Set fore/back GCOL and ECF to sprite ECF  *
 *********************************************/

void psprite_set_ecf (main_sprite *sprite, int ECF, int where)

{ int i;

  ftracef0 ("psprite_set_ecf\n");
  os_swix2 (OS_WriteN, (int) sprite->ECFs [ECF].set, 20);
  ftracef0 ("ecf:\n");
  for (i = 12; i < 20; i++)
    ftracef1 (" 0x%X\n", sprite->ECFs [ECF].set [i]);
  bbc_gcol (64 + toolwindow_current_mode, where<<7);
}

/******************************************************
 * Stuff dealing with brush sprite colour translation *
 ******************************************************/

static os_error *Set_Brush_Translation (main_sprite *sprite)

{ os_error *error = NULL;

  static int *Palette = NULL;

  ftracef0 ("Set_Brush_Translation\n");
  /*amended to do nothing if a sprite has no windows open*/
  if (sprite->windows != NULL)
  { /*sprite->needsnull = 1;*/ /* brush tool is now active */
      /*now done in sprwindow__munge_window*/

    /*Get the palette of the destination sprite.*/
    ftracef1 ("building palette for \"%.12s\"\n",
        psprite_address (sprite)->name);
    if ((error = psprite_build_palette (sprite, &Palette)) != NULL)
      goto finish;
    ftracef1 ("Palette 0x%X\n", Palette);

    ftracef0 ("calling psprite_ttab_for_sprite()\n");
    if ((sprite->toolspace [2] = (int) psprite_ttab_for_sprite
        (tools_brushsprite_ptr, psprite_address (sprite)->mode,
        Palette != NULL? Palette: (int *) -1)) == NULL)
    { error = main_error ("PntEG");
      goto finish;
    }
  }

finish:
  return error;
}

os_error *psprite_set_brush_translations (main_file *file)

{ BOOL done_begin = FALSE;
  os_error *error = NULL;

  ftracef0 ("psprite_set_brush_translations\n");

  if (toolwindow_current_tool == &tools_brushpaint &&
      tools_brushsprite_ptr != NULL)
  { main_sprite *sprite;

    visdelay_begin ();
    done_begin = TRUE;
    for (sprite = file->sprites; sprite != NULL; sprite = sprite->link)
      if ((error = Set_Brush_Translation (sprite)) != NULL)
        goto finish;
  }

finish:
  if (done_begin) visdelay_end ();
  return error;
}

os_error *psprite_set_brush_translation (main_sprite *sprite)

{ os_error *error = NULL;

  ftracef0 ("psprite_set_brush_translation\n");

  if (toolwindow_current_tool == &tools_brushpaint &&
      tools_brushsprite_ptr != NULL)
    if ((error = Set_Brush_Translation (sprite)) != NULL)
      goto finish;

finish:
  return error;
}

os_error *psprite_free_brush_blocks (void) /*free any sprite specific block*/

{ os_error *error = NULL;

  ftracef0 ("psprite_free_brush_blocks\n");
  if (tools_brushsprite_ptr != NULL)
  { main_window *window;
    /*int i;*/

    /*!!!for (i = 0; i < 8; i++)
    { psprite_drop_translation (&brush_translations [i]);
      brush_translations [i] = NULL;
    }*/

    for (window = main_windows; window != NULL; window = window->link)
      if (window->tag == main_window_is_file)
      { main_sprite *sprite;
        for (sprite = window->data->file.sprites; sprite != NULL;
             sprite = sprite->link)
        { psprite_drop_translation ((main_ttab **) &sprite->toolspace [2]);
          /*sprite->needsnull = 0;*/
            /*not done here*/
        }
      }
  }

  return error;
}

os_error *psprite_set_brush_colour_translations (void)

{ os_error *error = NULL;
  BOOL done_begin = FALSE;

  ftracef0 ("psprite_set_brush_colour_translations\n");

  if (tools_brushsprite_ptr != NULL)
  { main_window *window;

    ftracef0 ("Do complete brush colour calculation\n");
    visdelay_begin ();
    done_begin = TRUE;

    for (window = main_windows; window != NULL; window = window->link)
      if (window->tag == main_window_is_file)
        if ((error = psprite_set_brush_translations (&window->data->file)) !=
            NULL)
          goto finish;
  }

finish:
  if (done_begin) visdelay_end ();
  return error;
}

/************************************************
 * Find a sprite of the given name - somewhere! *
 ************************************************/

main_sprite *psprite_find (char *sname, char *estring)

{ main_window *windows;
  sprite_id sid;
  main_sprite *spriteptr = NULL;

  ftracef0 ("psprite_find\n");
  sid.tag = sprite_id_name;
  sid.s.name = sname;

  windows = main_windows;

  while (windows != NULL)
  { if (windows->tag == main_window_is_file)
    { sprite_area *area = windows->data->file.spritearea;
      sprite_ptr oursprite;

      ftracef1 ("try psprite_find in area 0x%X\n", area);

      if (!sprite_select_rp (area, &sid, &oursprite)) /* found it!! */
      { ftracef2 ("Select sprite in area 0x%X, address 0x%X\n",
            area, oursprite);

        for (spriteptr = windows->data->file.sprites;
            spriteptr != NULL && psprite_address (spriteptr) != oursprite;
            spriteptr = spriteptr->link)
          ;

        ftracef1 ("set spriteptr to 0x%X\n", spriteptr);
        break;
      }
    }
    windows = windows->link;
  }

  if (spriteptr == NULL && estring != NULL)
    werr (FALSE, msgs_lookup (estring));

  return spriteptr;
}

/*************************************************************
 * Extract an ECF from a sprite and attach to another sprite *
 *************************************************************/

void psprite_setup_ecf (main_sprite *sprite, int ECF,
    main_sprite *ECFsprite)

{ sprite_area *ecfarea;
  sprite_id ecfid;
  main_ttab *ttab;
  int spbpp, i, spritemode, ecfmode, splb_bpp, ecflb_bpp;
  main_ecf *ECFblock = &sprite->ECFs [ECF];
  sprite_colour colour;
  BOOL ecfsmallpalette;

  static int *Palette = NULL;

  ftracef0 ("psprite_setup_ecf\n");
  spritemode = psprite_address (sprite)->mode;
  ecfmode = psprite_address (ECFsprite)->mode;

  splb_bpp = bbc_modevar (spritemode, bbc_Log2BPP);
  ecflb_bpp = bbc_modevar (ecfmode, bbc_Log2BPP);
  ecfsmallpalette = ecflb_bpp == 3 && psprite_haspal (ECFsprite) &&
      !psprite_hastruecolpal (ECFsprite);
  ftracef1 ("ecfsmallpalette: %s\n", ecfsmallpalette? "TRUE": "FALSE");

  if (splb_bpp == -1 || ecflb_bpp == -1)
  { werr (FALSE, msgs_lookup ("PntEJ"));
    return;
  }

  psprite_build_palette (sprite, &Palette);

  ftracef0 ("calling psprite_ttab_for_sprite ...\n");
  ttab = psprite_ttab_for_sprite (ECFsprite, spritemode, Palette);
  ftracef0 ("calling psprite_ttab_for_sprite ... done\n");
  for (i = 0; i < 1 << (1 << ecflb_bpp); i++)
    ftracef2 ("%d: 0x%X\n", i, ttab->table [i]);

  spbpp = 1 << splb_bpp;

  ecfid.tag    = sprite_id_addr;
  ecfid.s.addr = psprite_address (ECFsprite);
  ecfarea      = ECFsprite->file->spritearea;

  memcpy (ECFblock->set, ecfstring, 20);

  if (ecflb_bpp <= 3)
    for (i = 0; i < 8; i++)
    { int j, shift, byte = 0;

      for (shift = 0, j = 0; shift < 8; shift += spbpp, j++)
      { if (main_read_pixel (ecfarea, &ecfid, j, 7 - i, &colour))
        { colour.colour = 0;
          colour.tint = 0;   /* ECFsprite may be too small really */
        }
        ftracef3 ("pixel (%d, %d) = 0x%X\n",
            j, 7 - i, colour.colour);

        if (ecfsmallpalette)
          /*rotate inner bitfield thrice for inverse transform, add in tint*/
          colour.colour = colours_gcol_ttab [colours_gcol_ttab
              [colours_gcol_ttab [colour.colour]]] << 2 | colour.tint >> 6;

        byte |= ttab->table [colour.colour] << shift;
        ftracef3 ("row %d, bit %d, byte 0x%X\n",
            7 - i, shift, ttab->table [colour.colour]);
      }
      ftracef1 ("0x%X\n", byte);
      ECFblock->set [i + 12] = byte;
    }
  else if (ecflb_bpp == 4)
    for (i = 0; i < 4; i++) /*can only use 4 rows*/
    { ftracef1 ("guard %.4s\n", ttab->table);

      main_read_pixel (ecfarea, &ecfid, 0, 3 - i, &colour);
      ftracef1 ("pixel value is 0x%X\n", colour.colour);

      *(short *) &ECFblock->set [2*i + 12] =
          ((char *) (((int *) ttab->table) [1])) [colour.colour];
      ftracef2 ("row %d, byte 0x%X\n", 3 - i,
          *(short *) &ECFblock->set [2*i + 12]);
    }
  else /*ecflb_bpp == 5*/
    for (i = 0; i < 2; i++) /*only 2 rows*/
    { int c;

      ftracef1 ("guard %.4s\n", ttab->table);

      main_read_pixel (ecfarea, &ecfid, 0, 1 - i, &colour);
      ftracef1 ("pixel value is 0x%X\n", colour.colour);
      c = (colour.colour & 0xFF)*31/0xFF |
          (colour.colour >> 8 & 0xFF)*31/0xFF << 5 |
          (colour.colour >> 16 & 0xFF)*31/0xFF << 10;
      ftracef1 ("15-bit value is 0x%X\n", c);
      *(int *) &ECFblock->set [4*i + 12] =
          ((char *) (((int *) ttab->table) [1])) [c];
      ftracef2 ("row %d, byte 0x%X\n", 1 - i,
          *(int *) &ECFblock->set [4*i + 12]);
    }

  /* now need to build a sprite of the right size and fill it */
  /* Need a sprite that will appear colours_SIZE square on screen,
     after mode scaling */
  { int spritesize, cmode;
    os_regset reg_set;

    /*For this to work the ECF sprite must have the same palette as the sprite
      in which it is to be painted; otherwise, it stands no chance of being
      rendered on screen correctly. Note that at this point we know that
      this must be a shallow sprite. JRC 10th Feb 1995*/
    spritesize = psprite_size (colours_SIZE/sprite->mode.scale_xmul,
        colours_SIZE/sprite->mode.scale_ymul, spritemode, /*mask?*/ FALSE,
        /*palette?*/ 2) + sizeof (sprite_area);
    if (ECFblock->sarea == NULL)
    { flex_alloc ((flex_ptr) &ECFblock->sarea, spritesize);
      if (ECFblock->sarea == NULL) main_NO_ROOM ("ECF sprite");
    }

    sprite_area_initialise (ECFblock->sarea, spritesize);
    sprite_create (ECFblock->sarea, "", sprite_nopalette,
        colours_SIZE/sprite->mode.scale_xmul,
        colours_SIZE/sprite->mode.scale_ymul, spritemode);
    if (os_swix4 (OS_SpriteOp, 37 | 0x200 /*create/remove palette*/,
        ECFblock->sarea, (int) ECFblock->sarea + sizeof (sprite_area),
        1 /*create*/ | 1U << 31) != NULL) main_NO_ROOM ("ECF palette");
    os_swix5 (ColourTrans_ReadPalette,
        sprite->file->spritearea, psprite_address (sprite),
        (int) ECFblock->sarea + sizeof (sprite_area) +
        sizeof (sprite_header), 8 << (1 << splb_bpp), (1 << 1) + 1);

    reg_set.r [0] = 0x23C /*switch output to sprite*/;
    reg_set.r [1] = (int) ECFblock->sarea;
    reg_set.r [2] = reg_set.r [1] + sizeof (sprite_area);
           /* address of first & only sprite in area */
    reg_set.r [3] = 0;
    os_swix (OS_SpriteOp, &reg_set);

    cmode = toolwindow_current_mode; toolwindow_current_mode = 0;
    psprite_set_ecf (sprite, ECF, /*bg?*/ 1);   /* set background to ECF */

    /* now need to set ECF origin so that bottom line of sprite
       has bottom line of ECF */
    os_swix2 (OS_SetECFOrigin, 0, sprite->mode.scale_ymul*7);
        /* align top left of ECF with point 8 rows up */

    bbc_clg ();                          /* fill the sprite with the ECF */
    toolwindow_current_mode = cmode;

    os_swix (OS_SpriteOp, &reg_set) /*unswitch output*/;
  }

  if (sprite->colourdialogue == 0) colours_set_extent (sprite);
}

/***************************************************
 *  Plot ECF sprite at coordinate given            *
 *   using sprites colour table, and scaled by     *
 *   sprites modescale                             *
 ***************************************************/

os_error *psprite_plot_ecf_sprite (main_sprite *sprite, int ECF, int x,
    int y)

{ os_error *error;
  os_regset regs;
  main_scaling_block scaling;

  ftracef0 ("psprite_plot_ecf_sprite\n");
  scaling = sprite->mode;
  if (sprite->coloursize == colours_SIZE/2)
    scaling.scale_xdiv *= 2, scaling.scale_ydiv *= 2;

  ftracef0 ("putting ecf sprite scaled ...\n");
  /*bad translation a pain*/

  regs.r[0] = 512 | 52;
  regs.r[1] = (int)sprite->ECFs [ECF].sarea;
  regs.r[2] = (int)(sprite->ECFs [ECF].sarea + 1);
  regs.r[3] = x;
  regs.r[4] = y;
  regs.r[5] = psprite_haspal (sprite) << 4 /*use ttab if there is no palette*/ |
              1 << 5 /*ttab is wide*/;
  regs.r[6] = (int)&scaling;
  regs.r[7] = (int)sprite->transtab->table;
  error = os_swix (OS_SpriteOp, &regs);
  ftracef0 ("putting ecf sprite scaled ... done\n");

#ifdef DUMPING
  if (error != NULL)
  { Dump_Ttab (sprite->transtab);
  }
#endif

  return error;
}

/***********************************************************
 *  Merge a sprite area into another; an in-core version   *
 *  of MOS's file merging.                                 *
 ***********************************************************/

BOOL psprite_merge_area (main_window *window, main_file *file, sprite_area **area)

{ sprite_area area_info;
  char sname [NAME_LIMIT + 1];
  sprite_id sid;
  int i, len, no_sprites;
  main_sprite **endptr;
  char *msg = NULL;
  os_error *error;

  ftracef0 ("psprite_merge_area\n");
  for (endptr = &file->sprites; *endptr != NULL; endptr = &(*endptr)->link)
    ;

  sid.tag    = sprite_id_name;
  sid.s.name = sname;

  ftracef2 ("psprite_merge_area: sprite area 0x%X -> file area 0x%X\n",
      *area, file->spritearea);

  if ((error = sprite_area_readinfo (*area, &area_info)) != NULL)
  { msg = error->errmess;
    goto finish;
  }
  no_sprites = area_info.number;
  ftracef1 ("area has %d sprites\n", no_sprites);

  ftracef0 ("psprite_merge_area: now loop checking the sprites\n");
  for (i = 1; i <= no_sprites; i++)
  { main_sprite **sprptr = NULL;
    sprite_header *header;
    BOOL newsprite;

    if ((error = sprite_getname (*area, sname, (len = NAME_LIMIT + 1, &len),
        1)) != NULL) /*always looking at the first sprite in the area*/
    { msg = error->errmess;
      goto finish;
    }
    sname [len] = '\0';
    ftracef1 ("psprite_merge_area: Sprite called %.12s\n", sname);

    newsprite = sprite_select_rp (file->spritearea, &sid,
        (sprite_ptr) &header) != NULL;

    if (!newsprite)
    { ftracef0 ("psprite_merge_area: sprite exists; move its information "
          "block to the end of the chain\n");
      for (sprptr = &file->sprites;
          *sprptr != NULL && psprite_address (*sprptr) != header;
          sprptr = &(*sprptr)->link)
        ;

      sprite_delete (file->spritearea, &sid);
    #if TRACE
      if (*sprptr == NULL)
        werr (TRUE, "Failed to find sprite info block");
    #endif
      *endptr = *sprptr;          /* point chain end at block */
      endptr  = &(*sprptr)->link; /* step end */
      *sprptr = *endptr;          /* delink block */
      *endptr = NULL;             /* terminate chain */
    }

    if ((error = sprite_select_rp (*area, &sid, (sprite_ptr) &header)) != NULL)
    { msg = error->errmess;
      goto finish;
    }

    if (!menus_ensure_size (&file->spritearea, header->next))
    { if (!newsprite && *sprptr != NULL)
        psprite_delete (window,*sprptr);
      psprite_recalculate_offsets (file);
      msg = msgs_lookup ("PntEG");
      goto finish;
    }

    sprite_select_rp (*area, &sid, (sprite_ptr) &header); /*might have moved */
    memmove ((char *) file->spritearea + file->spritearea->freeoff,
        header, header->next);

    if (newsprite)
    { ftracef0 ("created new sprite; make a new block at the chain end\n");
      if ((*endptr = psprite_new (file->spritearea->freeoff, 0, file)) == NULL)
      { sprite_delete (file->spritearea, &sid);
        psprite_recalculate_offsets (file);
        msg = msgs_lookup ("PntEG");
        goto finish;
      }
      endptr = &(*endptr)->link;
    }

    sprite_select_rp (*area, &sid, (sprite_ptr) &header); /*might have moved*/

    file->spritearea->freeoff += header->next;
    file->spritearea->number++;

    ftracef0 ("sprite_delete\n");
    sprite_delete (*area, &sid);
    ftracef0 ("menus_ensure_size\n");
    menus_ensure_size (area, 0);
    ftracef0 ("psprite_recalculate_offsets\n");
    psprite_recalculate_offsets (file);
  }

finish:
  if (msg != NULL)
  { werr (FALSE, msg);
    return FALSE;
  }
  else
    return TRUE;
}

/**************************************
 * Rotate a sprite through n degrees. *
 **************************************/

void Temporary_Name (sprite_area *sarea, char *name)

{ int i;
  sprite_id sid;

  ftracef0 ("Temporary_Name\n");
  sid.tag = sprite_id_name;
  sid.s.name = name;

  for (i = 0;; i++)
  { sprintf (name, "%d", i);
    if (sprite_select (sarea, &sid))
    { ftracef1 ("temp sprite name generated is \"%s\"\n", name);
      return;
    }
  }
}

void psprite_rotate (main_sprite *sprite, int degrees)

{ ftracef0 ("psprite_rotate\n");

  if ((degrees %= 360) != 0) /* optimise 0! */
  { int ldx, ldy, dx, dy, nw, nh;
    double x, y, xmax = 0, ymax = 0, xmin = 0, ymin = 0,
      rads = (double) degrees/360.0*2*3.141592653589793, c = cos (rads),
      s = sin (rads);
    psprite_info sinfo;
    main_sprite_window *sprw;
    sprite_id sid, sid2;
    sprite_transmat trans_mat;
    char tempname [NAME_LIMIT + 1], realname [NAME_LIMIT + 1], *msg = NULL;
    BOOL created_sprite = FALSE, hourglass = FALSE, switched_output = FALSE;
    os_error *error;

    sprintf (realname, "%.12s", psprite_address (sprite)->name);

    ftracef5 ("psprite_rotate: \"%s\" %d deg = %g rad; cos %g sin %g\n",
        realname, degrees, rads, c, s);
    sprwindow_remove_wastage (sprite);

    psprite_read_full_info (sprite, &sinfo);
    dx = 1 << (ldx = bbc_modevar (sinfo.mode, bbc_XEigFactor));
    dy = 1 << (ldy = bbc_modevar (sinfo.mode, bbc_YEigFactor));
    sinfo.width  <<= ldx;
    sinfo.height <<= ldy;    /* size in OS units */

    /* Now inspect all corners of the sprite to find new bounding box */
    x = sinfo.width*c;
    y = sinfo.width*s;
    ftracef3 ("0, %d -> %g, %g\n", sinfo.width, x, y);
    if (x > xmax) xmax = x;
    if (x < xmin) xmin = x;
    if (y > ymax) ymax = y;
    if (y < ymin) ymin = y;

    x = -(sinfo.height*s); /*funny brackets*/
    y =   sinfo.height*c;
    ftracef3 ("%d, 0 -> %g, %g\n", sinfo.height, x, y);
    if (x > xmax) xmax = x;
    if (x < xmin) xmin = x;
    if (y > ymax) ymax = y;
    if (y < ymin) ymin = y;

    x = sinfo.width*c - sinfo.height*s;
    y = sinfo.width*s + sinfo.height*c;
    ftracef4 ("%d, %d -> %g, %g\n", sinfo.width, sinfo.height, x, y);
    if (x > xmax) xmax = x;
    if (x < xmin) xmin = x;
    if (y > ymax) ymax = y;
    if (y < ymin) ymin = y;

    nw = (int) ((xmax - xmin)/dx + 0.5);
    nh = (int) ((ymax - ymin)/dy + 0.5); /*in pixels*/

    ftracef4 ("old sprite %dx%d; new %dx%d (O S units)\n",
        sinfo.width, sinfo.height, nw << ldx, nh << ldy);

    /*Disallow zero width or height*/
    if (nw == 0 || nh == 0)
    { msg = msgs_lookup ("PntEA");
      return;
    }

    /*Now know limits of sprite: ensure room for copy*/
    ftracef4 ("psprite_rotate: changing size from (%d, %d) to (%d, %d) "
        "(O S units)\n", sinfo.width, sinfo.height, nw << ldx, nh << ldy);
    sinfo.width  >>= ldx;
    sinfo.height >>= ldy; /*convert back to pixels*/

    if (!menus_ensure_size (&sprite->file->spritearea, sinfo.size))
    { msg = msgs_lookup ("PntEG");
      goto finish;
    }

    sid.tag    = sprite_id_name;
    sid.s.name = realname;

    Temporary_Name (sprite->file->spritearea, tempname);
    ftracef0 ("copying sprite\n");
    if ((error = sprite_copy (sprite->file->spritearea, &sid, tempname)) !=
        NULL)
    { ftracef0 ("ERROR\n");
      msg = error->errmess;
      goto finish;
    }
    created_sprite = TRUE;

    sid2.tag    = sprite_id_name;
    sid2.s.name = tempname;

    /* Make sure there is enough space for the rotated sprite.
     * We used to need MAX (nw, sinfo.width) and MAX (nh, sinfo.height), but
     * we don't any more because we always do any deletion of rows/columns
     * before insertion of columns/rows.
     */

    if (!menus_ensure_size (&sprite->file->spritearea,
        psprite_size (nw, nh, sinfo.mode, sinfo.mask,
        !sinfo.palette? 0: !sinfo.truepalette? 1: 2) - sinfo.size))
    { ftracef0 ("ERROR\n");
      msg = msgs_lookup ("PntEG");
      goto finish;
    }

    visdelay_begin ();
    hourglass = TRUE;

    ftracef2 ("adding %d columns and %d rows to source sprite\n",
        nw - sinfo.width, nh - sinfo.height);

    /*If reducing width then do (width, height) else do (height, width)*/
    if (nw - sinfo.width < 0)
      if ((error = sprite_change_size (sprite->file->spritearea, &sid,
          /*rows?*/ FALSE, /*at*/ nw, nw - sinfo.width)) != NULL)
      { ftracef0 ("ERROR\n");
        msg = error->errmess;
        goto finish;
      }

    if (nh - sinfo.height != 0)
      if ((error = sprite_change_size (sprite->file->spritearea, &sid,
          /*rows?*/ TRUE, /*at*/ nh > sinfo.height? sinfo.height: nh,
          nh - sinfo.height)) != NULL)
      { ftracef0 ("ERROR\n");
        msg = error->errmess;
        goto finish;
      }

    if (nw - sinfo.width > 0)
      if ((error = sprite_change_size (sprite->file->spritearea, &sid,
          /*rows?*/ FALSE, /*at*/ sinfo.width, nw - sinfo.width)) != NULL)
      { ftracef0 ("ERROR\n");
        msg = error->errmess;
        goto finish;
      }
    ftracef0 ("done\n");

    c *= 0x10000; /* Scaled coordinates from now on */
    s *= 0x10000;

    /*Now uses Fishface's new OS_SpriteOp SWI reason code to rotate the
      sprite. Infinitely faster. JRC 30th Nov '89*/
    trans_mat [0] =  (int) c;
    trans_mat [1] =  (int) s;
    trans_mat [2] = -(int) s;
    trans_mat [3] =  (int) c;
    trans_mat [4] = -(int) (0x100*xmin);
    trans_mat [5] = -(int) (0x100*ymin);

    ftracef2 ("int c = %d, int s = %d\n", (int) c, (int) s);

    sprwindow_swap_output_to_sprite (sprite);
    switched_output = TRUE;
    ftracef0 ("Plotting copy back to original, rotated\n");
    os_swi2 (OS_SetColour, 1 << 4, 0);
  #if 0
    /*replaces the following, avoiding need to check full-palette bit*/
    bbc_gcol (0, 0); /*shurely this was shupposhed to set the background?*/
    bbc_tint (0, 0);
  #endif
    bbc_clg ();
    if ((error = sprite_put_trans (sprite->file->spritearea, &sid2, 0, NULL,
        &trans_mat, NULL)) != NULL)
    { ftracef0 ("ERROR\n");
      msg = error->errmess;
      goto finish;
    }

    if (sprwindow_swap_output_to_mask (sprite, TRUE))
    { ftracef0 ("Same for mask\n");
      os_swi2 (OS_SetColour, 1 << 4, 0);
    #if 0
      /*replaces the following, avoiding need to check full-palette bit*/
      bbc_gcol (0, 0); /*shurely this was shupposhed to set the background?*/
      bbc_tint (0, 0);
    #endif
      bbc_clg ();
      os_swi2 (OS_SetColour, 1 << 4, -1);
    #if 0
      /*replaces the following, avoiding need to check full-palette bit*/
      bbc_gcol (0, 0x80 | 63);
      bbc_tint (3, 3);
    #endif
      if ((error = sprite_put_mask_trans (sprite->file->spritearea, &sid2,
          NULL, &trans_mat)) != NULL)
      { ftracef0 ("ERROR\n");
        msg = error->errmess;
        goto finish;
      }
    }

finish:
    if (switched_output) sprwindow_swap_output_to_screen ();
    if (hourglass) visdelay_end ();
    if (created_sprite) sprite_delete (sprite->file->spritearea, &sid2);
    menus_ensure_size (&sprite->file->spritearea, 0);

    sprwindow_invalidate (sprite); /*J R C 8th Dec 1993*/
    psprite_set_icon_scale (sprite);
    /*psprite_recalculate_offsets (sprite->file); Done in Menus.c.*/

    for (sprw = sprite->windows; sprw != NULL; sprw = sprw->link)
      sprwindow_set_work_extent (sprw->window, TRUE);

    ftracef0 ("reporting error for psprite_rotate()\n");
    if (msg != NULL) werr (FALSE, msg);
  }
}

void psprite_scale (main_sprite *sprite, double scale_x, double scale_y)

  /*Do not die on errors - report them and continue. J R C 25th Nov 1993*/

{ psprite_info sinfo;
  int ldx, ldy, dx, dy, nw, nh;
  sprite_id sid, sid2;
  char tempname [NAME_LIMIT + 1], realname [NAME_LIMIT + 1], *msg = NULL;
  main_sprite_window *sprw;
  sprite_transmat trans_mat;
  BOOL switched_output = FALSE, created_sprite = FALSE, hourglass = FALSE;
  os_error *error;

  ftracef0 ("psprite_scale\n");
  ftracef2 ("scaling by (%f, %f)\n", scale_x, scale_y);

  strcpy (realname, psprite_address (sprite)->name);
  sprwindow_remove_wastage (sprite);

  psprite_read_full_info (sprite, &sinfo);
  dx = 1 << (ldx = bbc_modevar (sinfo.mode, bbc_XEigFactor));
  dy = 1 << (ldy = bbc_modevar (sinfo.mode, bbc_YEigFactor));
  sinfo.width  <<= ldx;
  sinfo.height <<= ldy;    /* size in OS units */

  nw = (int) (sinfo.width*scale_x/dx  + 0.5);
  nh = (int) (sinfo.height*scale_y/dy + 0.5); /*in pixels*/

  /* disallow zero size sprites */
  if (nw == 0 || nh == 0)
  { msg = msgs_lookup ("PntEA");
    goto finish;
  }

  sinfo.width  >>= ldx;
  sinfo.height >>= ldy;

  /*ensure room for copy*/
  if (!menus_ensure_size (&sprite->file->spritearea, sinfo.size))
    { msg = msgs_lookup ("PntEG");
      goto finish;
    }

  sid.tag    = sprite_id_name;
  sid.s.name = realname;

  Temporary_Name (sprite->file->spritearea, tempname);
  if ((error = sprite_copy (sprite->file->spritearea, &sid, tempname)) !=
      NULL)
  { msg = error->errmess;
    goto finish;
  }
  created_sprite = TRUE;

  sid2.tag    = sprite_id_name;
  sid2.s.name = tempname;

  /*Make sure there is enough room for the scaled sprite*/
  if (!menus_ensure_size (&sprite->file->spritearea,
      psprite_size (MAX (nw, sinfo.width), MAX (nh, sinfo.height),
      sinfo.mode, sinfo.mask, !sinfo.palette? 0: !sinfo.truepalette? 1: 2) -
      sinfo.size))
        /*Need MAX() because intermediate sprites may be bigger.*/
  { msg = msgs_lookup ("PntEG");
    goto finish; /*was return; J R C 3rd Mar 1994*/
  }

  visdelay_begin ();
  hourglass = TRUE;

  ftracef2 ("psprite_scale: changing x size from %d to %d\n",
      sinfo.width, nw);
  if (nw - sinfo.width != 0)
    if ((error = sprite_change_size (sprite->file->spritearea, &sid,
        /*rows?*/ FALSE,
        /*at*/ nw > sinfo.width? sinfo.width: nw, nw - sinfo.width)) != NULL)
    { msg = error->errmess;
      goto finish;
    }

  ftracef2 ("psprite_scale: changing y size from %d to %d\n",
      sinfo.height, nh);
  if (nh - sinfo.height != 0)
    if ((error = sprite_change_size (sprite->file->spritearea, &sid,
        /*rows?*/ TRUE, /*at*/ nh > sinfo.height? sinfo.height: nh,
        nh - sinfo.height)) != NULL)
    { msg = error->errmess;
      goto finish;
    }

  trans_mat [0] = (int) (scale_x*0x10000);
  trans_mat [1] = 0;
  trans_mat [2] = 0;
  trans_mat [3] = (int) (scale_y*0x10000);
  trans_mat [4] = 0;
  trans_mat [5] = 0;

  sprwindow_swap_output_to_sprite (sprite);
  switched_output = TRUE;
  /*Clear the output to colour 0.*/
  os_swi2 (OS_SetColour, 1 << 4, 0);
#if 0
  /*replaces the following, avoiding need to check full-palette bit*/
  bbc_gcol (0, 0); /*shurely this was shupposhed to set the background?*/
  bbc_tint (0, 0);
#endif
  bbc_clg ();
  if ((error = sprite_put_trans (sprite->file->spritearea, &sid2, 0,
      NULL, &trans_mat, NULL)) != NULL)
  { msg = error->errmess;
    goto finish;
  }

  if (sprwindow_swap_output_to_mask (sprite, TRUE))
  { os_swi2 (OS_SetColour, 1 << 4, 0);
  #if 0
    /*replaces the following, avoiding need to check full-palette bit*/
    bbc_gcol (0, 0); /*shurely this was shupposhed to set the background?*/
    bbc_tint (0, 0);
  #endif
    bbc_clg ();
    os_swi2 (OS_SetColour, 1 << 4, -1);
  #if 0
    /*replaces the following, avoiding need to check full-palette bit*/
    bbc_gcol (0, 0x80 | 63);
    bbc_tint (3, 3);
  #endif
    if ((error = sprite_put_mask_trans (sprite->file->spritearea,
        &sid2, NULL, &trans_mat)) != NULL)
    { msg = error->errmess;
      goto finish;
    }
  }

finish:
  if (switched_output) sprwindow_swap_output_to_screen ();
  if (hourglass) visdelay_end ();
  if (created_sprite) sprite_delete (sprite->file->spritearea, &sid2);
  menus_ensure_size (&sprite->file->spritearea, 0);

  sprwindow_invalidate (sprite); /*J R C 8th Dec 1993*/
  psprite_set_icon_scale (sprite);
  /*psprite_recalculate_offsets (sprite->file);*/

  for (sprw = sprite->windows; sprw != NULL; sprw = sprw->link)
    sprwindow_set_work_extent (sprw->window, TRUE);

  if (msg != NULL) werr (FALSE, msg);
}

void psprite_shear (main_sprite *sprite, double factor)

  /*Do not die on errors - report them and continue. J R C 25th Nov 1993*/

{ psprite_info sinfo;
  int ldx, ldy, dx, dy, nw, nh, shift;
  main_sprite_window *sprw;
  sprite_id sid, sid2;
  sprite_transmat trans_mat;
  char tempname [NAME_LIMIT + 1], realname [NAME_LIMIT + 1], *msg = NULL;
  float tmp_f;
  BOOL switched_output = FALSE, hourglass = FALSE, created_sprite = FALSE;
  os_error *error;

  ftracef0 ("psprite_shear\n");
  ftracef1 ("factor %f\n", factor);

  strcpy (realname, psprite_address (sprite)->name);
  sprwindow_remove_wastage (sprite);

  psprite_read_full_info (sprite, &sinfo);
  dx = 1 << (ldx = bbc_modevar (sinfo.mode, bbc_XEigFactor));
  dy = 1 << (ldy = bbc_modevar (sinfo.mode, bbc_YEigFactor));
  sinfo.width  <<= ldx;
  sinfo.height <<= ldy;    /* size in OS units */

  ftracef2 ("width %d, height %d OS units\n", sinfo.width, sinfo.height);
  ftracef4 ("ceil ((%f + %f)/%d) = %f\n",
      (double) sinfo.width, fabs (sinfo.height * factor), dx,
      ceil (((double) sinfo.width + fabs (sinfo.height*factor))/dx));
  tmp_f = (float) ceil (((double) sinfo.width +
      fabs (sinfo.height*factor))/dx);
  if (tmp_f > INT_MAX)
  { /* new width too large to handle so cause error */
    msg = msgs_lookup ("PntEM");
    goto finish;;
  }
  nw = (int) tmp_f;

  ftracef3 ("ceil (%f/%d) = %f\n", sinfo.height, dy,
           ceil ((double) sinfo.height/dy));
  /* don't need to check for height going out of range */
  nh = (int) ceil ((double) sinfo.height/dy);

  sinfo.width  >>= ldx;
  sinfo.height >>= ldy; /*convert back to pixels*/

  /*ensure room for copy*/
  if (!menus_ensure_size (&sprite->file->spritearea, sinfo.size))
  { msg = msgs_lookup ("PntEG");
    goto finish;
  }

  sid.tag    = sprite_id_name;
  sid.s.name = realname;

  Temporary_Name (sprite->file->spritearea, tempname);
  if ((error = sprite_copy (sprite->file->spritearea, &sid, tempname))
      != NULL)
  { msg = error->errmess;
    goto finish;
  }
  created_sprite = TRUE;

  sid2.tag    = sprite_id_name;
  sid2.s.name = tempname;

  /*Make sure there is enough space for the rotated sprite.*/
  if (!menus_ensure_size (&sprite->file->spritearea,
      psprite_size (MAX (nw, sinfo.width), MAX (nh, sinfo.height),
      sinfo.mode, sinfo.mask, !sinfo.palette? 0: !sinfo.truepalette? 1: 2) -
      sinfo.size))
        /*Need MAX() because intermediate sprites may be bigger.*/
  { msg = msgs_lookup ("PntEG");
    goto finish;
  }

  visdelay_begin ();
  hourglass = TRUE;

  ftracef2 ("psprite_shear: changing x size from %d to %d\n",
      sinfo.width, nw);
  if ((error = sprite_change_size (sprite->file->spritearea, &sid,
      /*rows?*/ FALSE, /*at*/ sinfo.width /*shearing can never make the
      sprite smaller*/, nw - sinfo.width)) != NULL)
  { msg = error->errmess;
    goto finish;
  }

  ftracef2 ("psprite_shear: changing y size from %d to %d\n",
      sinfo.height, nh);
  if ((error = sprite_change_size (sprite->file->spritearea, &sid,
      /*rows?*/ TRUE, /*at*/ sinfo.height /*shearing can never make the
      sprite smaller*/, nh - sinfo.height)) != NULL)
  { msg = error->errmess;
    goto finish;
  }

  shift = (int) ((factor*dy/dx)*sinfo.height);

  trans_mat [0] = 1*0x10000;
  trans_mat [1] = 0;
  trans_mat [2] = (int) (factor*0x10000);
  trans_mat [3] = 1*0x10000;
  trans_mat [4] = ((shift < 0? -shift: 0) << ldx)*0x100;
  trans_mat [5] = 0;

  sprwindow_swap_output_to_sprite (sprite);
  switched_output = TRUE;
  /*Clear the output to colour 0.*/
  os_swi2 (OS_SetColour, 1 << 4, 0);
#if 0
  /*replaces the following, avoiding need to check full-palette bit*/
  bbc_gcol (0, 0); /*shurely this was shupposhed to set the background?*/
  bbc_tint (0, 0);
#endif
  bbc_clg ();
  if ((error = sprite_put_trans (sprite->file->spritearea, &sid2, 0, NULL,
      &trans_mat, NULL)) != NULL)
  { msg = error->errmess;
    goto finish;
  }

  if (sprwindow_swap_output_to_mask (sprite, TRUE))
  { ftracef0 ("Same for mask\n");
    os_swi2 (OS_SetColour, 1 << 4, 0);
  #if 0
    /*replaces the following, avoiding need to check full-palette bit*/
    bbc_gcol (0, 0); /*shurely this was shupposhed to set the background?*/
    bbc_tint (0, 0);
  #endif
    bbc_clg ();
    os_swi2 (OS_SetColour, 1 << 4, -1);
  #if 0
    /*replaces the following, avoiding need to check full-palette bit*/
    bbc_gcol (0, 0x80 | 63);
    bbc_tint (3, 3);
  #endif
    if ((error = sprite_put_mask_trans (sprite->file->spritearea, &sid2,
        NULL, &trans_mat)) != NULL)
    { msg = error->errmess;
      goto finish;
    }
  }

finish:
  if (switched_output) sprwindow_swap_output_to_screen ();
  if (hourglass) visdelay_end ();
  if (created_sprite) sprite_delete (sprite->file->spritearea, &sid2);
  menus_ensure_size (&sprite->file->spritearea, 0);

  sprwindow_invalidate (sprite); /*J R C 8th Dec 1993*/
  psprite_set_icon_scale (sprite);
  /*psprite_recalculate_offsets (sprite->file);*/
  for (sprw = sprite->windows; sprw != NULL; sprw = sprw->link)
    sprwindow_set_work_extent (sprw->window, TRUE);

  if (msg != NULL) werr (FALSE, msg);
}

/**********************************
 *  Generate/do Create sprite     *
 **********************************/

static void Decode (int *lb_bpp_out, unsigned int *mode_out)

{ wimp_which_block which_block;
  wimp_i results [7]; /*only 2 would be needed if function keys couldn't
      wierdly set multiple icons in the same E S G*/
  int x_eig = 0, y_eig = 0, lb_bpp = 0, cols = 0;
  unsigned int mode;

  ftracef0 ("Decode\n");

  which_block.window   = dbox_syshandle (Create);
  which_block.bit_mask = wimp_ISELECTED | 31 << 16;
  which_block.bit_set  = wimp_ISELECTED | 2 << 16; /*colour E S G is 2*/
  wimpt_noerr (wimp_which_icon (&which_block, results));
  ftracef1 ("colours icon %d\n", results [0]);
  switch (results [0])
  { case d_Create_Colours_2:   lb_bpp = 0; break;
    case d_Create_Colours_4:   lb_bpp = 1; break;
    case d_Create_Colours_16:  lb_bpp = 2; break;
    case d_Create_Colours_256: lb_bpp = 3; break;
    case d_Create_Colours_4k:  lb_bpp = 4, cols = 1<<12; break;
    case d_Create_Colours_32k: lb_bpp = 4; cols = 1<<15; break;
    case d_Create_Colours_64k: lb_bpp = 4; cols = 1<<16; break;
    case d_Create_Colours_16M: lb_bpp = 5; break;
  }
  ftracef1 ("lb_bpp %d\n", lb_bpp);

  which_block.bit_set = wimp_ISELECTED | 3 << 16; /*x eig E S G is 3*/
  wimpt_noerr (wimp_which_icon (&which_block, results));
  ftracef1 ("x dpi icon %d\n", results [0]);
  switch (results [0])
  { case d_Create_XEIG_0: x_eig = 0; break;
    case d_Create_XEIG_1: x_eig = 1; break;
    case d_Create_XEIG_2: x_eig = 2; break;
  }
  ftracef1 ("x_eig %d\n", x_eig);

  which_block.bit_set = wimp_ISELECTED | 4 << 16; /*y eig E S G is 4*/
  wimpt_noerr (wimp_which_icon (&which_block, results));
  ftracef1 ("y dpi icon %d\n", results [0]);
  switch (results [0])
  { case d_Create_YEIG_0: y_eig = 0; break;
    case d_Create_YEIG_1: y_eig = 1; break;
    case d_Create_YEIG_2: y_eig = 2; break;
  }
  ftracef1 ("y_eig %d\n", y_eig);

  /*So, is this a mode?*/
  mode = ~0u;
  if (lb_bpp <= 3)
  { if (x_eig == 1 && y_eig == 1)
      mode = modes_1x1 [lb_bpp];
    else if (x_eig == 1 && y_eig == 2)
      mode = modes_1x2 [lb_bpp];
    else if (x_eig == 2 && y_eig == 2)
      mode = modes_2x2 [lb_bpp];
  }

  if ((mode == ~0u) && (lb_bpp == 4) && (cols != 1<<15))
  { /*Special handling of 16bpp.Always create TBGR for now.*/
    if (cols == 1<<12)
      mode = (15 << 27) | (16 << 20) | 0 | y_eig << 6 | x_eig << 4 | 1u;
    else
      mode = (10 << 27) | 180u >> y_eig << 14 | 180u >> x_eig << 1 | 1u;
    ftracef1 ("mode 0x%X\n", mode);
  }

  if (mode == ~0u)
  { mode = lb_bpp + 1 << 27 | 180u >> y_eig << 14 | 180u >> x_eig << 1 | 1u;
    ftracef1 ("mode 0x%X\n", mode);
  }
  else
    ftracef1 ("mode %d\n", mode);

  if (mode_out   != NULL) *mode_out   = mode;
  if (lb_bpp_out != NULL) *lb_bpp_out = lb_bpp;
}

/* this function will close the create dbox if it the window
   handle passed is the same as the source                   */

extern BOOL psprite_close_createbox (wimp_w w)

{ ftracef2 ("psprite_close_createbox: closing 0x%x, Source 0x%x\n",
      w, Source);
  if (w == Source)
  { if (Create != NULL)
      dbox_dispose (&Create);
    Create = NULL, Source = 0;
    ftracef0 ("Closed Create box due to sprite filer window closing\n");
    return TRUE;
  }
  return FALSE;
}

static void create_create_sprite (dbox d, main_window *window)

{ char name [NAME_LIMIT + 2];
  int width, height, sprite_size, white /*GCOL for white pixels*/,
    lb_bpp, *new_pal, e;
  unsigned mode;
  BOOL want_palette, want_mask, mono;
  sprite_id sid;
  main_sprite *sprite;
  psprite_info info;
  sprite_area **sarea = &window->data->file.spritearea;
  wimp_paletteword palette_white;
  sprite_header *addr;
  char *msg = NULL;
  os_error *error;

  ftracef0 ("create_create_sprite\n");
  dbox_getfield (d, d_Create_Name, name, NAME_LIMIT + 1);
  if (name [0] == '\0')
  { msg = msgs_lookup ("PntE7");
    goto finish;
  }
  if (menus_sprite_exists (*sarea, name))
   goto finish;

  width  = dbox_getnumeric (d, d_Create_Width);
  height = dbox_getnumeric (d, d_Create_Height);
  if (width <= 0 || height <= 0)
  { msg = msgs_lookup ("PntEA");
    goto finish;
  }

  Decode (&lb_bpp, &mode);
  want_palette = lb_bpp <= 3 && /*was mode < 256u JRC 5th Dec 1994*/
      !dbox_getnumeric (d, d_Create_Palette_None);
  mono = want_palette && dbox_getnumeric (d, d_Create_Palette_Mono);
  want_mask = !dbox_getnumeric (d, d_Create_Mask);

  #if TRACE
    ftracef (__FILE__, __LINE__,
        "Creating Sprite '%s': width=%d height=%d mask=%d palette=%d "
        "Mode=%d\n",
        name, width, height, want_mask, want_palette, mode);
  #endif

  /* extend sprite area so that creation is guaranteed */
  sprite_size = psprite_size (width, height, mode, want_mask,
      !want_palette? 0: !mono? 1: 2);

  if (!menus_ensure_size (sarea, sprite_size))
  { msg = msgs_lookup ("PntEG");
    goto finish;
  }

  ftracef0 ("creating sprite\n");
  if ((error = sprite_create_rp (*sarea, name, sprite_nopalette, width,
      height, mode, (sprite_ptr *) &addr)) != NULL)
  { msg = error->errmess;
    goto finish;
  }

  /*Just checking ...*/
  ftracef1 ("new sprite mode is %d\n", addr->mode);

  if (want_palette)
  { /*Create the sprite without a palette, then give it one of the right
      mode. JRC 17 Sept 1991*/
    int i, *p;

    static int N [] = {0xFFFFFF, 0x555555, 0x111111, 0x010101};

    /*this is the palette we might give to the sprite*/
    new_pal = !mono? psprite_std_palettes
        [window->data->file.use_current_palette? 0: 1] [lb_bpp]: NULL;

    if ((error = os_swix4 (OS_SpriteOp, 37 | 0x200 /*create/remove palette*/,
        (int) *sarea, (int) addr, 1 /*create*/ | (lb_bpp == 3 && mono?
        1U << 31: 0))) != NULL)
    { msg = error->errmess;
      goto finish;
    }
      /*That palette is not the one we want though.*/

    p = &addr->mode + 1;
    if (!mono)
    { e = ENTRIES (lb_bpp);
      for (i = 0; i < e; i++)
        p [2*i] = p [2*i + 1] = new_pal [i];
    }
    else
    { e = 1 << (1 << lb_bpp);
      for (i = 0; i < e; i++)
        p [2*i] = p [2*i + 1] = i*N [lb_bpp] << 8;
    }

    #ifdef XTRACE
      /*Trace the palette ...*/
      for (i = 0; i < e; i++)
      { char mode1, r1, g1, b1;
        BOOL s1;

        mode1 = p [2*i];
        s1 = (mode1 & 0x80) != 0;
        if (s1) mode1 &= ~0x80;
        r1 = p [2*i] >> 8;
        g1 = p [2*i] >> 16;
        b1 = p [2*i] >> 24;

        ftracef (__FILE__, __LINE__,
            "%-24d(0x%X, 0x%X, 0x%X), mode %d%s\n",
            i, r1, g1, b1, mode1, s1? "S": "");
      }
    #endif
  }

  sid.s.name = name;
  sid.tag    = sprite_id_name;
  if (want_mask)
    if ((error = sprite_create_mask (*sarea, &sid)) != NULL)
    { msg = error->errmess;
      goto finish;
    }

  menus_sprite_new (window, /*hack palette?*/ FALSE);
  /*psprite_set_brush_translations (&window->data->file); No point - done in
    menus_sprite_new(). JRC 11 September 1991*/

  /*find correct info block */
  for (sprite = window->data->file.sprites;
      sprite->link != NULL; sprite = sprite->link)
    ;

  /*if (want_palette) menus_hack_palette (sprite); Now done in
      menus_sprite_new(). JRC 12 September 1991*/

  psprite_read_full_info (sprite, &info);

  /*JRC 27 May 1991 Make the sprite white (rather than 0-pixels).*/
  palette_white.word = (int)0xFFFFFF00;
  sprwindow_swap_output_to_sprite (sprite);

  if (want_palette || lb_bpp > 3)
  { /*If the sprite has a palette or is deep, life is easy.*/
    wimpt_noerr (colourtran_setGCOL (palette_white, 1 << 7, 0, &white));
    bbc_clg ();
  }
  else
  { /*If not, we have to resort to ...*/
    wimpt_noerr (colourtran_return_colourformode (palette_white, mode,
        (wimp_paletteword *) psprite_std_palettes
        [window->data->file.use_current_palette? 0: 1] [lb_bpp], &white));
    ftracef1 ("giving nearest colour number of %d\n", white);
    os_swi2 (OS_SetColour, 1 << 4, white);
    bbc_clg ();
  }
  sprwindow_swap_output_to_screen ();

  /* Having filled with white, find the furthest colour from white and select it */
  palette_white.word = 0;
  wimpt_noerr (colourtran_return_colourformode (palette_white, mode,
      (wimp_paletteword *) psprite_std_palettes
      [window->data->file.use_current_palette? 0: 1] [lb_bpp], (int *) &sprite->gcol.colour));
  sprite->gcol.alpha = 255;
  sprite->gcol.ecf = FALSE;

  /* Auto open sprite window here */
  sprwindow_new (sprite);
  if (main_current_options.tools.show_tools)
     toolwindow_display (/*at pointer?*/ FALSE);

  /* force summary window to be updated */
  window->data->file.lastwidth = 0;

  /*JRC 12 June 1991*/
  main_set_extent (window);
  main_force_redraw (window->handle);

finish:
  if (msg != NULL)
    werr (FALSE, msg);
}

static void create_event (main_window *window, wimp_i i)

{ int
    height = dbox_getnumeric (Create, d_Create_Height),
    width  = dbox_getnumeric (Create, d_Create_Width);
  wimp_mousestr mouse_str;
  BOOL adjust;

  ftracef0 ("create_event\n");

  wimpt_noerr (wimp_get_point_info (&mouse_str));
  adjust = (mouse_str.bbits & wimp_BRIGHT) != 0;

  ftracef0 ("create_event\n");
  switch (i)
  { case d_Create_Ok:
      create_create_sprite (Create, window);
      ftracef0 ("Tried to create sprite\n");
    break;

    case d_Create_Down:
      adjust = !adjust;
    /*fall through*/

    case d_Create_Up:
      if (adjust)
      { if (height > 1)
          dbox_setnumeric (Create, d_Create_Height, height - 1);
      }
      else
        dbox_setnumeric (Create, d_Create_Height, height + 1);
    break;

    case d_Create_Left:
      adjust = !adjust;
    /*fall through*/

    case d_Create_Right:
      if (adjust)
      { if (width > 1)
          dbox_setnumeric (Create, d_Create_Width, width - 1);
      }
      else
        dbox_setnumeric (Create, d_Create_Width, width + 1);
    break;
  }
}

static void Create_Cb (dbox d, void *handle)

{ wimp_i i = dbox_get (d);

  ftracef1 ("Create_Cb: event on icon %d\n", i);
  create_event ((main_window *) handle, i);

  if (i == d_Create_Ok || i == d_Create_Cancel || i == dbox_CLOSE)
  { dbox_dispose (&Create);
    Create = NULL, Source = 0;
  }
}

static BOOL Grey_Scale (int lb_bpp)

{ int colour_count = 1 << (1 << lb_bpp), palette [256], i, colour;
  os_regset swi_regs;
  os_error *oserror;
  BOOL incomplete;

  /*Given that the current mode is 256 colours or less, is it a grey-scale
    mode?*/

  swi_regs.r [0] = 0;
  swi_regs.r [1] = colour_count | 17 << 24;
  swi_regs.r [2] = (int) palette;
  swi_regs.r [3] = 0;
  swi_regs.r [4] = 7 /*read block of palette entries*/;
  swi_regs.r [9] = 35 /*PaletteV*/;

  if ((oserror = os_swix (OS_CallAVector, &swi_regs)) != NULL)
  { ftracef1 ("error %s\n", oserror->errmess);
    return FALSE;
  }

  incomplete = swi_regs.r [4];

  if (!incomplete)
  { for (i = 0; i < colour_count; i++)
      if ((palette [i] ^ palette [i] << 8) >> 16 != 0)
      { ftracef1 ("colour %d not grey\n", i);
        return FALSE;
  }   }
  else
    /*Couldn't read a block of colours - try reading singly.*/
    for (i = 0; i < colour_count; i++)
    { swi_regs.r [0] = i;
      swi_regs.r [1] = 17;
      swi_regs.r [4] = 1 /*read palette entry*/;
      swi_regs.r [9] = 35 /*PaletteV*/;

      if ((oserror = os_swix (OS_CallAVector, &swi_regs)) !=
          NULL)
      { ftracef1 ("error %s\n", oserror->errmess);
        return FALSE;
      }

      colour = swi_regs.r [2];
      incomplete = swi_regs.r [4];

      if (incomplete)
      { /*Resort to OS_ReadPalette*/
        if ((oserror = os_swix3r (OS_ReadPalette, i, 17, 0, NULL, NULL, &colour)) !=
            NULL)
        { ftracef1 ("error %s\n", oserror->errmess);
          return FALSE;
        }
      }

      if ((colour ^ colour << 8) >> 16 != 0)
      { ftracef1 ("colour %d not grey\n", i);
        return FALSE;
      }
    }

  ftracef0 ("all colours grey\n");
  return TRUE;
}

static BOOL Have4k64k (void)

{
  /*True if the OS can plot 4k and 64k TBGR, else false*/
  /*The fallback for unknown sprite types in the kernel is 32bpp, so if
    asking about 4k & 64k we get back 32bpp as the answer, the kernel
    has used the fallback since it should be 16bpp*/
  return (bbc_modevar (0x79004051u /*  4k TBGR */, bbc_Log2BPP) != 5 &&
          bbc_modevar (0x501680B5u /* 64k TBGR */, bbc_Log2BPP) != 5);
}

static void Create_Handler (wimp_eventstr *e, void *handle)

{ int lb_bpp;
  unsigned mode;
  BOOL extd_16bpp = Have4k64k();

  handle = handle;

  ftracef0 ("Create_Handler\n");

  /*Let the proper handler do its stuff first ...*/
  (*Old_Create_Handler) (e, Old_Create_Handle);

  /*Now we have a look in, in order to update the mode display field.
    Sneaky! J R C 2nd Nov 1993*/
  if (Create != NULL)
  { /*don't proceed if the window's gone*/
    Decode (&lb_bpp, &mode);

    if (dbox_getnumeric (Create, d_Create_Palette_Colour) ||
        dbox_getnumeric (Create, d_Create_Palette_Mono))
    { dbox_fadefield (Create, d_Create_Colours_4k);
      dbox_fadefield (Create, d_Create_Colours_32k);
      dbox_fadefield (Create, d_Create_Colours_64k);
      dbox_fadefield (Create, d_Create_Colours_16M);
    }
    else
    { if (extd_16bpp) dbox_unfadefield (Create, d_Create_Colours_4k);
      dbox_unfadefield (Create, d_Create_Colours_32k);
      if (extd_16bpp) dbox_unfadefield (Create, d_Create_Colours_64k);
      dbox_unfadefield (Create, d_Create_Colours_16M);
    }

    if (mode != Mode)
    { /*This code altered to reflect new-format shallow sprites with
        palette. JRC 5th Dec 1994*/
      if (mode < 256u)
        dbox_setnumeric  (Create, d_Create_Mode, mode),
        dbox_unfadefield (Create, d_Create_Mode_LabelL),
        dbox_unfadefield (Create, d_Create_Mode),
        dbox_unfadefield (Create, d_Create_Mode_LabelR);
      else
        /*No corresponding old-style mode.*/
        dbox_setfield  (Create, d_Create_Mode, ""),
        dbox_fadefield (Create, d_Create_Mode_LabelL),
        dbox_fadefield (Create, d_Create_Mode),
        dbox_fadefield (Create, d_Create_Mode_LabelR);

      if (lb_bpp <= 3)
        /*After Black, all shallow sprites can have palettes.*/
        dbox_unfadefield (Create, d_Create_Palette_Colour),
        dbox_unfadefield (Create, d_Create_Palette_Mono);
      else
        dbox_fadefield (Create, d_Create_Palette_Colour),
        dbox_fadefield (Create, d_Create_Palette_Mono);

      Mode = mode;
    }
  }
}

void psprite_create_show (main_window *window, BOOL auto_open,
    char *sprite_name)

{ int width, height, x_eig, y_eig, lb_bpp, ncolour;
  BOOL grey_scale, full_palette, extd_16bpp = Have4k64k();
  wimp_w d_w;

  ftracef0 ("psprite_create_show\n");
  if (Create != NULL)
  { ftracef0 ("Killing Create box because already open\n");
    dbox_dispose (&Create);
    Create = NULL, Source = 0;
  }

  if ((Create = dbox_new ("create")) == NULL)
    return;
  /*Intercept events on this dbox.*/
  d_w = dbox_syshandle (Create);
  win_read_eventhandler (d_w, &Old_Create_Handler, &Old_Create_Handle);
  win_register_event_handler (d_w, Create_Handler, NULL);

  Source = window->handle;
  dbox_raw_eventhandler (Create, &help_dboxrawevents, (void *) "PntH7");

  width  = bbc_modevar (-1, bbc_XWindLimit);
  height = bbc_modevar (-1, bbc_YWindLimit);
  if (width == -1 || height == -1)
  { werr (FALSE, msgs_lookup ("PntEJ"));
    return;
  }
  width++, height++;

  x_eig  = bbc_modevar (-1, bbc_XEigFactor);
  y_eig  = bbc_modevar (-1, bbc_YEigFactor);
  lb_bpp = bbc_modevar (-1, bbc_Log2BPP);
  ncolour = bbc_modevar (-1, bbc_NColour);
  full_palette = (bbc_modevar (-1, bbc_ModeFlags) & 0x80) != 0;
  grey_scale = lb_bpp <= 3 && Grey_Scale (lb_bpp);

  ftracef4 ("x_eig %d, y_eig %d, lb_bpp %d, grey_scale %s\n",
      x_eig, y_eig, lb_bpp, grey_scale? "TRUE": "FALSE");

  dbox_setfield   (Create, d_Create_Name,        sprite_name);
  dbox_setnumeric (Create, d_Create_Mask,        FALSE);
  dbox_setnumeric (Create, d_Create_Width,       width);
  dbox_setnumeric (Create, d_Create_XEIG_2,      x_eig == 2);
  dbox_setnumeric (Create, d_Create_XEIG_1,      x_eig == 1);
  dbox_setnumeric (Create, d_Create_XEIG_0,      x_eig == 0);
  dbox_setnumeric (Create, d_Create_Height,      height);
  dbox_setnumeric (Create, d_Create_YEIG_2,      y_eig == 2);
  dbox_setnumeric (Create, d_Create_YEIG_1,      y_eig == 1);
  dbox_setnumeric (Create, d_Create_YEIG_0,      y_eig == 0);
  dbox_setnumeric (Create, d_Create_Palette_Colour,
                                                 lb_bpp <= 3 && !grey_scale);
  dbox_setnumeric (Create, d_Create_Palette_Mono,
                                                 lb_bpp <= 3 && grey_scale);
  dbox_setnumeric (Create, d_Create_Palette_None,
                                                 lb_bpp > 3);
  dbox_setnumeric (Create, d_Create_Colours_2,   lb_bpp == 0);
  dbox_setnumeric (Create, d_Create_Colours_4,   lb_bpp == 1);
  dbox_setnumeric (Create, d_Create_Colours_16,  lb_bpp == 2);
  dbox_setnumeric (Create, d_Create_Colours_256, lb_bpp == 3);
  dbox_setnumeric (Create, d_Create_Colours_4k,  lb_bpp == 4 && (ncolour == 4095));
  dbox_setnumeric (Create, d_Create_Colours_32k, lb_bpp == 4 && !full_palette);
  dbox_setnumeric (Create, d_Create_Colours_64k, lb_bpp == 4 && full_palette);
  dbox_setnumeric (Create, d_Create_Colours_16M, lb_bpp == 5);

  ftracef1 ("bbc_modevar (6 << 27 | 1, bbc_Log2BPP) %d\n",
      bbc_modevar (6 << 27 | 1, bbc_Log2BPP));
  if (bbc_modevar (6 << 27 | 1, bbc_Log2BPP) == -1)
  { /*This is not Medusa hardware - shade colours > 256.*/
    dbox_fadefield (Create, d_Create_Colours_32k);
    dbox_fadefield (Create, d_Create_Colours_16M);
  }
  if (!extd_16bpp)
  { /*This OS can't do 4k and 64k.*/
    dbox_fadefield (Create, d_Create_Colours_4k);
    dbox_fadefield (Create, d_Create_Colours_64k);
  }

  Decode (&lb_bpp, &Mode);

  if ((unsigned) Mode < 256u)
  { /*There is a mode, so we could put a palette on*/
    dbox_setnumeric  (Create, d_Create_Mode, Mode);
    dbox_unfadefield (Create, d_Create_Mode_LabelL),
    dbox_unfadefield (Create, d_Create_Mode),
    dbox_unfadefield (Create, d_Create_Mode_LabelR),
    dbox_unfadefield (Create, d_Create_Palette_Colour);
    dbox_unfadefield (Create, d_Create_Palette_Mono);
  }
  else
  { /*256 colours or less: may still be new-style mode though.*/
    dbox_setfield  (Create, d_Create_Mode, "");
    dbox_fadefield (Create, d_Create_Mode_LabelL),
    dbox_fadefield (Create, d_Create_Mode),
    dbox_fadefield (Create, d_Create_Mode_LabelR),
    dbox_fadefield (Create, d_Create_Palette_Colour);
    dbox_fadefield (Create, d_Create_Palette_Mono);
  }

  if (auto_open == -1) /* open from menu branch */
  { BOOL open;

    dbox_show (Create);

    open = TRUE;
    while (open)
    { wimp_i i = dbox_fillin (Create);

      ftracef1 ("event on icon %d\n", i);
      create_event (window, i);

      if (i == d_Create_Ok)
        open = dbox_persist ();
      else if (i == d_Create_Cancel || i == dbox_CLOSE)
        open = FALSE;
    }

    dbox_dispose (&Create);
    Create = NULL, Source = 0;
  }
  else
  { dbox_showstatic (Create);
    dbox_eventhandler (Create, &Create_Cb, (void *) window);
  }
}

char *psprite_get_colours(int mode)
{
  int ncolour,modeflags;
  ncolour = bbc_modevar(mode, bbc_NColour);
  modeflags = bbc_modevar(mode, bbc_ModeFlags);

  /* Assuming RGB */
  char *ncol;
  switch(ncolour)
  {
  case 1: ncol = "2"; break;
  case 3: ncol = "4"; break;
  case 15: ncol = "16"; break;
  case 63: case 255: ncol = "256"; break;
  case 4095: ncol = "4k"; break;
  case 65535:
    if(modeflags & ModeFlag_64k)
      ncol = "64k";
    else
      ncol = "32k";
    break;
  case 16777215:
  case -1:
    ncol = "16M";
    break;
  default:
    ncol = "?";
    break;
  }

  return ncol;
}

transparency_type psprite_transparency_type(main_sprite *spr)
{
  int mode = psprite_address(spr)->mode;
  if(psprite_hasmask(spr))
  {
    if(mode & 0x80000000)
      return transparency_type_alphamask;
    return transparency_type_onoffmask;
  }
  int modeflags = bbc_modevar(mode, bbc_ModeFlags);
  if((modeflags != -1) && (modeflags & ModeFlag_DataFormatSub_Alpha))
    return transparency_type_alphachannel;
  return transparency_type_none;
}