--------------------------------------------------------------------------------
--                                                                            --
-- Copyright (C) 2004, RISC OS Ada Library (RASCAL) developers.               --
--                                                                            --
-- This library is free software; you can redistribute it and/or              --
-- modify it under the terms of the GNU Lesser General Public                 --
-- License as published by the Free Software Foundation; either               --
-- version 2.1 of the License, or (at your option) any later version.         --
--                                                                            --
-- This library is distributed in the hope that it will be useful,            --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of             --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU           --
-- Lesser General Public License for more details.                            --
--                                                                            --
-- You should have received a copy of the GNU Lesser General Public           --
-- License along with this library; if not, write to the Free Software        --
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA    --
--                                                                            --
--------------------------------------------------------------------------------

-- $Author$
-- $Date$
-- $Revision$

with Ada.IO_Exceptions;
with Ada.Characters.Handling;
with Ada.Strings.Fixed;
with Ada.Strings.Unbounded;    use Ada.Strings.Unbounded;

with System.Storage_Elements;

with Interfaces.C;             use Interfaces.C;


with RASCAL.OS;                use RASCAL.OS;
with RASCAL.FileExternal;
with RASCAL.FileInternal;
with RASCAL.Memory;

package body RASCAL.Sprite is

   use RASCAL.Utility,System.Storage_Elements,Kernel,Interfaces.C;

   OS_SpriteOp        : constant := 16#2E#;
   Wimp_SpriteOp      : constant := 16#400E9#;
   Wimp_ReadSysInfo   : constant := 16#400F2#;
   Wimp_BaseOfSprites : constant := 16#400EA#;

   --
   -- Returns true if there is a priority spritepool.
   --
   function Is_PriorityPool return Boolean is
   
      Register   : aliased Kernel.SWI_Regs;
      Error      : Kernel.OSError_Access;
   begin
      Register.R (0) := 19;
      Error := Kernel.SWI (Wimp_ReadSysInfo,register'Access,register'Access);
      return Error = null;
   end Is_PriorityPool;

   --
   -- Get wimp pool addresses.
   --
   procedure Get_WimpPools (Priority : out Address;
                            High     : out Address;
                            Low      : out Address) is

      Register   : aliased Kernel.SWI_Regs;
      Error      : Kernel.OSError_Access;
      NotFound   : Address := Integer_To_adr(0);
   begin
      Priority := NotFound; High := NotFound; Low :=NotFound;
      Register.R (0) := 19;
      Error := Kernel.SWI (Wimp_ReadSysInfo,register'Access,register'Access);
      if Error /= null then
         Register.R (0) := 16;
         Error := Kernel.SWI (Wimp_ReadSysInfo,register'Access,register'Access);
         if Error /= null then
            Error := Kernel.SWI (Wimp_BaseOfSprites,register'Access,register'Access);
            High     := Int_To_Adr(Register.R(0));
            Low      := Int_To_Adr(Register.R(1));
         else
            High     := Int_To_Adr(Register.R(1));
            Low      := Int_To_Adr(Register.R(0));
         end if;
      else
         Priority := Int_To_Adr(Register.R(0));
         High     := Int_To_Adr(Register.R(1));
         Low      := Int_To_Adr(Register.R(2));
      end if;
   end Get_WimpPools;

   --
   -- Loads Sprite into spritearea, without regarding any previous content.
   --
   procedure Load (SpriteArea : in out Sprite_Area_Type;
                   Filename   : in string) is

      Exists     : Boolean := FileExternal.Exists (Filename);
      Filename_0 : String  := Filename & ASCII.NUL;
      FileSize   : Positive;
      Register   : aliased Kernel.SWI_Regs;
      Error      : Kernel.OSError_Access;
      New_Size   : Positive;
   begin
      if Exists then
         FileSize := FileExternal.Get_Size(Filename);
         New_Size := SpriteArea.Area_Size + FileSize;
         Heap.Extend (SpriteArea.Heap,New_Size);
         Memory.PutWord (New_Size,Heap.Get_Address(SpriteArea.Heap));
         SpriteArea.Area_Size := New_Size;
         Register.R (0) := 16#A# +256;
         Register.R (1) := Adr_To_Int (Heap.Get_Address(SpriteArea.Heap));
         Register.R (2) := Adr_To_Int (Filename_0'address);

         Error := Kernel.SWI (OS_SpriteOp,register'Access,register'Access);

         if Error /= null then
            pragma Debug(Report("Sprite - Load: " & Interfaces.C.To_Ada(Error.errmess)));
            OS.Raise_Error(Error);
         end if;
      else
         raise Ada.IO_Exceptions.Name_Error;
      end if;
   end Load;

   --
   -- Returns the number of sprites in the SpriteArea.
   --
   function Count (SpriteArea : Sprite_Area_Type) return Natural is
   begin
      if Adr_To_Integer(Heap.Get_Address(SpriteArea.Heap)) = -1 then
         return 0;
      end if;
      return Memory.GetWord(Heap.Get_Address(SpriteArea.Heap),4);
   end Count;

   --
   -- Returns the number of sprites in the SpriteArea.
   --
   function Count (SpriteArea : Address) return Natural is
   begin
      if Adr_To_Integer(SpriteArea) = -1 then
         return 0;
      end if;
      return Memory.GetWord(SpriteArea,4);
   end Count;

   --
   -- Returns the name of the sprite SpriteNr in the SpriteArea.
   --
   function Get_Name (SpriteArea : in Sprite_Area_Type;
                      SpriteNr   : in Positive) return String is
   begin
      return Get_Name (Heap.Get_Address(SpriteArea.Heap),SpriteNr);
   end Get_Name;

   --
   -- Returns the name of the sprite SpriteNr in the SpriteArea.
   --
   function Get_Name (SpriteArea : in Address;
                      SpriteNr   : in Positive) return String is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.SWI_Regs;
      Buffer     : array(1..13) of Integer;
   begin
      if Adr_To_Integer(SpriteArea) = -1 then
         raise Invalid_SpriteArea;
      end if;

      Register.R(0) := 16#D# + 256;
      Register.R(1) := Adr_To_Int (SpriteArea);
      Register.R(2) := Adr_To_Int (Buffer'Address);
      Register.R(3) := 13;
      Register.R(4) := Interfaces.C.Int (SpriteNr);
      Error := Kernel.swi (OS_SpriteOp,Register'access,Register'access);

      if Error /= null then
         pragma Debug(Report ("Sprite - Get_Name: " & Interfaces.C.To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
      return Memory.MemoryToString (Buffer'address,0,Integer (Register.R(3)));
   end Get_Name;

   --
   -- Returns an array with the names of all sprites in the SpriteArea.
   --
   function Get_List (SpriteArea : in Sprite_Area_Type) return Sprite_List_Type is

      Sprites     : Natural := Count(SpriteArea);
      Sprite_List : Sprite_List_Type(1..Sprites);
      Name        : Utility.UString;
   begin
      for i in Sprite_List'Range loop
         Name := U(Get_Name(SpriteArea,i));
         Sprite_List(i)(1..Length(Name)) := S(Name);
      end loop;
      return Sprite_List;
   end Get_List;

   --
   -- Returns an array with the names of all sprites in all wimppools.
   --
   function Get_List return Sprite_List_Type is

      Priority, High, Low : Address;
      Name                : Utility.UString;
      Len                 : Natural;
   begin
      Get_WimpPools(Priority,High,Low);
      declare
         Priority_Count : Natural := Count(Priority);
         High_Count     : Natural := Count(High);
         Low_Count      : Natural := Count(Low);
         Sprite_List    : Sprite_List_Type(1..Priority_Count+High_Count+Low_Count);
      begin
         for i in 1..Priority_Count loop
            Name := U(Get_Name(Priority,i));
            Len  := Length(Name);
            if Len = 12 then
               Sprite_List(i):= S(Name);
            else
               Sprite_List(i):= S(Name & (12-Len)*ASCII.NUL);
            end if;
         end loop;
         for i in 1..High_Count loop
            Name := U(Get_Name(High,i));
            Len  := Length(Name);
            if Len = 12 then
               Sprite_List(i+Priority_Count):= S(Name);
            else
               Sprite_List(i+Priority_Count):= S(Name & (12-Len)*ASCII.NUL);
            end if;
         end loop;
         for i in 1..Low_Count loop
            Name := U(Get_Name(Low,i));
            Len  := Length(Name);
            if Len = 12 then
               Sprite_List(i+Priority_Count+High_Count):= S(Name);
            else
               Sprite_List(i+Priority_Count+High_Count):= S(Name & (12-Len)*ASCII.NUL);
            end if;
         end loop;
         return Sprite_List;
      end;
   end Get_List;

   --
   -- Creates a new blank sprite in SpriteArea
   --
   procedure Create (SpriteArea : in out Sprite_Area_Type;
                     PixelWidth : in Positive := 640;
                     PixelHeight: in Positive := 480;
                     ModeNumber : in System.Unsigned_Types.Unsigned := 2**5;
                     Name       : in String   := "Untitled";
                     Palette    : in Boolean  := True) is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.SWI_Regs;
      New_Area   : Sprite_Area_Type;
      New_Size   : Positive := PixelWidth * PixelHeight * 4 + New_Area.Area_Size;
      Name_0     : String   := Name & ASCII.NUL;
   begin
      -- Enlarge area
      Resize_Area(New_Area,New_Size);

      -- Create Sprite in new area
      Register.R(0) := 16#F# + 256;
      Register.R(1) := Adr_To_Int (Heap.Get_Address (New_Area.Heap));
      Register.R(2) := Adr_To_Int (Name_0'address);
      Register.R(3) := Interfaces.C.Int (boolean'Pos(Palette));
      Register.R(4) := Interfaces.C.Int (PixelWidth);
      Register.R(5) := Interfaces.C.Int (PixelHeight);
      Register.R(6) := Interfaces.C.Int (ModeNumber);
      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);
      if Error /= null then
         pragma Debug(Report ("Sprite - Create: " & To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;

      -- Copy sprite from new area to target area
      Copy(New_Area,1,SpriteArea);

   end Create;

   --
   -- Save the SpriteArea as a spritefile to Path
   --
   procedure Save_SpriteArea (SpriteArea : in Sprite_Area_Type;
                              Path       : in string) is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.SWI_Regs;
      Path_0     : String := Path & ASCII.NUL;
   begin
      if Adr_To_Integer(Heap.Get_Address(SpriteArea.Heap)) = -1 then
         raise Invalid_SpriteArea;
      end if;

      Register.R(0) := 16#C# + 256;
      Register.R(1) := Adr_To_Int (Heap.Get_Address(SpriteArea.Heap));
      Register.R(2) := Adr_To_Int (Path_0'address);

      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);

      if Error /= null then
         pragma Debug(Report ("Sprite - Save_SpriteArea: " & To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
   end Save_SpriteArea;

   --
   -- Does the sprite (Name) exist in the SpriteArea ?
   --
   function Is_Sprite (SpriteArea : in Sprite_Area_Type;
                       Name       : in string) return boolean is
   begin
      return Is_Sprite(Heap.Get_Address(SpriteArea.Heap),Name);
   end Is_Sprite;

   --
   -- Does the sprite (Name) exist in the SpriteArea ?
   --
   function Is_Sprite (SpriteArea : in Address;
                       Name       : in string) return boolean is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.SWI_Regs;
      Name_0     : String  := Name & ASCII.NUL;
   begin
      if Adr_To_Integer(SpriteArea) = -1 then
         raise Invalid_SpriteArea;
      end if;
   
      Register.R(0) := 40 + 256;
      Register.R(1) := Adr_To_Int (SpriteArea);
      Register.R(2) := Adr_To_Int (Name_0'address);

      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);
      if Error /= null then
         return false;
      else
         return true;
      end if;
   end Is_Sprite;

   --
   -- Does the sprite (Name) exist in the Wimp sprite pool ?
   --
   function Is_Sprite (Name : in string) return boolean is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.SWI_Regs;
      Name_0     : string := Name & ASCII.NUL;
   begin
      Register.R(0) := 40;
      Register.R(1) := 0;
      Register.R(2) := Adr_To_Int (Name_0'address);

      Error := Kernel.SWI (Wimp_SpriteOp,Register'access,Register'access);

      if Error /= null then
         return false;
      else
         return true;
      end if;
   end Is_Sprite;


   --
   -- Return information about a sprite.
   --
   procedure Get_Info (SpriteArea : in Address;
                       Name       : in String;
                       Width      : out Natural;
                       Height     : out Natural;
                       ModeOrType : out Integer) is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.swi_regs;
      Name_0     : string := Name & ASCII.NUL;
   begin
      if Adr_To_Integer(SpriteArea) = -1 then
         raise Invalid_SpriteArea;
      end if;
      Register.R(0) := 40 + 256;
      Register.R(1) := Adr_To_Int (SpriteArea);
      Register.R(2) := Adr_To_Int (Name_0'address);
      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);
      if Error /= null then
         pragma Debug(Report ("Sprite - Get_info: " & To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
      Width      := Natural(Register.R(3));
      Height     := Natural(Register.R(4));
      ModeOrType := Integer(Register.R(6));
   end Get_Info;

   --
   -- Plot sprite (Name) in SpriteArea on to the screen
   --with its bottom left hand corner at the graphics cursor.
   --
   procedure Plot (SpriteArea : in Sprite_Area_Type;
                   Name       : in String) is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.swi_regs;
      Name_0     : string := Name & ASCII.NUL;
   begin
      if Adr_To_Integer(Heap.Get_Address(SpriteArea.Heap)) = -1 then
         raise Invalid_SpriteArea;
      end if;

      Register.R(0) := 28 + 256;
      Register.R(1) := Adr_To_Int (Heap.Get_Address(SpriteArea.Heap));
      Register.R(2) := Adr_To_Int (Name_0'address);
      Register.R(5) := 0;

      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);

      if Error /= null then
         pragma Debug(Report ("Sprite - Plot I: " & To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
   end Plot;

   --
   -- Plot sprite (Name) in SpriteArea on to the screen at X_Position, Y_Position.
   --Do not use if not in the same mode style (colours/resolution) as the sprite.
   --
   procedure Plot (SpriteArea : in Sprite_Area_Type;
                   Name       : in string;
                   X_Position : in integer;
                   Y_Position : in integer) is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.swi_regs;
      Name_0     : string := Name & ASCII.NUL;
   begin
      Register.R(0) := 34 + 256;
      Register.R(1) := Adr_To_Int (Heap.Get_Address(SpriteArea.Heap));
      Register.R(2) := Adr_To_Int (Name_0'address);
      Register.R(3) := Interfaces.C.Int (X_Position);
      Register.R(4) := Interfaces.C.Int (Y_Position);
      Register.R(5) := 16#08#;

      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);

      if Error /= null then
         pragma Debug(Report ("Sprite - Plot II: " & To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
   end Plot;

   --
   -- Renames sprite in SpriteArea.
   --
   procedure Rename (SpriteArea : in Sprite_Area_Type;
                     Old_Name   : in string;
                     New_Name   : in string) is

      New_Name_12: string    := Ada.Strings.Fixed.Head(
                                Ada.Characters.Handling.To_Lower (New_Name),11,ASCII.NUL);
      offset     : integer   := 0;
      sprites    : integer   := Count(SpriteArea);
      Terminator : character := ASCII.NUL;
   begin
      if sprites > 0 then
         if New_Name'Length >= 11 then
            Terminator := Ada.Characters.Handling.To_Lower (New_Name (New_Name'First+11));
         end if;

         offset := Memory.GetWord (Heap.Get_Address(SpriteArea.Heap),8);

         for s in 1..sprites loop
            if Get_Name (SpriteArea,s) = Old_Name then
               Memory.StringToMemory (New_Name_12,
                                      Heap.Get_Address(SpriteArea.Heap),offset+4,0,Terminator);
            end if;
            offset := offset + Memory.GetWord (Heap.Get_Address(SpriteArea.Heap),offset);
         end loop;
      end if;
   end Rename;

   --
   -- Deletes the named (Name) sprite in the SpriteArea.
   --
   procedure Delete (SpriteArea : in Sprite_Area_Type;
                     Name       : in String) is

      Error      : Kernel.OSError_Access;
      Register   : aliased Kernel.SWI_Regs;
      Name_0     : String := Name & ASCII.NUL;
   begin
      if Adr_To_Integer(Heap.Get_Address(SpriteArea.Heap)) = -1 then
         raise Invalid_SpriteArea;
      end if;

      Register.R(0) := 16#19# + 256;
      Register.R(1) := Adr_To_Int (Heap.Get_Address(SpriteArea.Heap));
      Register.R(2) := Adr_To_Int (Name_0'Address);
      Error := Kernel.swi (OS_SpriteOp,Register'access,Register'access);

      if Error /= null then
         pragma Debug(Report ("Sprite - Delete_Sprite: " & Interfaces.C.To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
   end Delete;

   --
   -- Save a single sprite (SpriteNr) from a SpriteArea as a spritefile (Path).
   --
   procedure Save (SpriteArea  : in Sprite_Area_Type;
                   SpriteNr    : in Positive;
                   Path        : in String) is

      New_Area    : Sprite_Area_Type;
   begin
      if Adr_To_Integer(Heap.Get_Address(SpriteArea.Heap)) = -1 then
         raise Invalid_SpriteArea;
      end if;
      Copy(SpriteArea,SpriteNr,New_Area);
      Save_SpriteArea (New_Area,Path);
   end Save;

   --
   -- Copy one (spritenr) sprite from one Spritearea (source) to another (target).
   --
   procedure Copy (Source   : in Sprite_Area_Type;
                   SpriteNr : in Positive;
                   Target   : in out Sprite_Area_Type) is

      Source_Offset : Positive := Memory.GetWord (Heap.Get_Address(Source.Heap),8);
      Target_Offset : Positive := Memory.GetWord (Heap.Get_Address(Target.Heap),12);
      Sprite_Size   : Positive;
      x             : Positive := 1;
      Source_Adr    : Memory.Mem_Adr_Type;
      Target_Adr    : Memory.Mem_Adr_Type;
   begin
      -- Locate sprite in spritearea
      while x /= SpriteNr loop
         x := x + 1;
         Source_Offset := Source_Offset+Memory.GetWord (Heap.Get_Address(Source.Heap),Source_Offset);
      end loop;

      Sprite_Size := Memory.GetWord (Heap.Get_Address(Source.Heap),Source_Offset);

      -- Resize target spritearea
      Heap.Extend (Target.Heap,Target.Area_Size+Sprite_Size);
      Target.Area_Size := Target.Area_Size+Sprite_Size;
      Memory.PutWord(Target.Area_Size+Sprite_Size,Heap.Get_Address(Target.Heap));

      Source_Adr  := To_Address (Integer_Address (Source_Offset+integer (Adr_To_Int (Heap.Get_Address(Source.Heap)))));
      Target_Adr  := To_Address (Integer_Address (Target_Offset+integer (Adr_To_Int (Heap.Get_Address(Target.Heap)))));

      -- Copy sprite to target spritearea
      Memory.MemCopy (Source_Adr,Target_Adr,0,Sprite_Size);

      -- Update header of target spritearea
      Memory.PutWord (Count(Target)+1,Heap.Get_Address(Target.Heap),4); -- Number of sprites in area
      Memory.PutWord (Target_Offset+Sprite_Size,Heap.Get_Address(Target.Heap),12);-- Offset to first free word
   end Copy;

   --
   -- Adds sprites from spritefile pointed to by Path to existing SpriteArea.
   --
   procedure Add (SpriteArea : in out Sprite_Area_Type;
                  Path       : in String) is

      New_Sprite        : Sprite_Area_Type;
      Nr_Of_Sprites     : Positive;
   begin
      if Count(SpriteArea) = 0 then
         Load(SpriteArea,Path);
      else
         Load(New_Sprite,Path);
         Nr_Of_Sprites := Count(New_Sprite);

         for x in 1..Nr_Of_Sprites loop
            Copy (New_Sprite,x,SpriteArea);
         end loop;
      end if;
   end Add;

   --
   -- Switches output to sprite.
   --
   procedure Output_To_Sprite (SpriteArea : in Sprite_Area_Type;
                               SpriteName : in String;
                               Save_Area  : in Integer := 0) is

      Error        : Kernel.OSError_Access;
      Register     : aliased Kernel.SWI_Regs;
      SpriteName_0 : String := SpriteName & ASCII.NUL;
   begin
      if Adr_To_Integer(Heap.Get_Address(SpriteArea.Heap)) = -1 then
         raise Invalid_SpriteArea;
      end if;

      Register.R(0) := 16#3c#+256;
      Register.R(1) := Adr_To_Int (Heap.Get_Address (SpriteArea.Heap));
      Register.R(2) := Adr_To_Int (SpriteName_0'address);
      Register.R(3) := Int(Save_Area);
      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);

      if Error /= null then
         pragma Debug(Report ("Sprite.Output_To_Sprite: " & To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
   end Output_To_Sprite;

   --

   procedure Output_To_Mask (SpriteArea : in Sprite_Area_Type;
                             SpriteName : in String;
                             Save_Area  : in Integer := 0) is

      Error        : Kernel.OSError_Access;
      Register     : aliased Kernel.SWI_Regs;
      SpriteName_0 : String := SpriteName & ASCII.NUL;
   begin
      if Adr_To_Integer(Heap.Get_Address(SpriteArea.Heap)) = -1 then
         raise Invalid_SpriteArea;
      end if;

      Register.R(0) := 16#3D#+256;
      Register.R(1) := Adr_To_Int (Heap.Get_Address (SpriteArea.Heap));
      Register.R(2) := Adr_To_Int (SpriteName_0'address);
      Register.R(3) := Int(Save_Area);
      Error := Kernel.SWI (OS_SpriteOp,Register'access,Register'access);

      if Error /= null then
         pragma Debug(Report ("Sprite.Output_To_Mask: " & To_Ada (Error.errmess)));
         OS.Raise_Error(Error);
      end if;
   end Output_To_Mask;

   --

   procedure Resize_Area (SpriteArea : in out Sprite_Area_Type;
                          Change     : in Integer) is
      New_Size : Integer;
   begin
      New_Size := SpriteArea.Area_Size + Change;
      Heap.Extend (SpriteArea.Heap,New_Size);
      Memory.PutWord (New_Size,Heap.Get_Address(SpriteArea.Heap));
      SpriteArea.Area_Size := New_Size;
   end Resize_Area;

   --

   procedure Initialize (The : in out Sprite_Area_Type) is
   begin
      Memory.PutWord (20,Heap.Get_Address(The.Heap),0);
      Memory.PutWord (0,Heap.Get_Address(The.Heap),4);
      Memory.PutWord (16#10#,Heap.Get_Address(The.Heap),8);
      Memory.PutWord (16#10#,Heap.Get_Address(The.Heap),12);
      The.Area_Size := 20;
   end Initialize;

   --

   function Get_Address (SpriteArea : in Sprite_Area_Type) return Address is   
   begin
      return Heap.Get_Address(SpriteArea.Heap);
   end Get_Address;

   --

end RASCAL.Sprite;
