context: using System;
  using System.IO;
  using System.Collections;
  using System.Drawing;
  using System.Drawing.Imaging;
  using System.Windows.Forms;
  
  namespace CUODesktop.PatchLib
  {
  	/// <summary>
  	/// Contains translation tables used for mapping body values to file subsets.
  	/// <seealso cref="Animations" />
  	/// </summary>
  	public class BodyConverter
  	{
  		private static int[] m_Table1;
  		private static int[] m_Table2;
  		private static int[] m_Table3;
  		private static int[] m_Table4;
  
  		private BodyConverter()
  		{
  		}
  
  		static BodyConverter()
  		{
  			string path = Client.GetFilePath( "bodyconv.def" );
  
  			if ( path == null )
  				return;
  
  			ArrayList list1 = new ArrayList(), list2 = new ArrayList(), list3 = new ArrayList(), list4 = new ArrayList();
  			int max1 = 0, max2 = 0, max3 = 0, max4 = 0;
  
  			using ( StreamReader ip = new StreamReader( path ) )
  			{
  				string line;
  
  				while ( (line = ip.ReadLine()) != null )
  				{
  					if ( (line=line.Trim()).Length == 0 || line.StartsWith("#") )
  						continue;
  
  					try
  					{
  						string[] split = line.Split( '\t' );
  
  						int original = System.Convert.ToInt32( split[0] );
  						int anim2 = System.Convert.ToInt32( split[1] );
  						int anim3;
  						int anim4;
  						int anim5;
  
  						try
  						{
  							anim3 = System.Convert.ToInt32( split[2] );
  						}
  						catch
  						{
  							anim3 = -1;
  						}
  
  						try
  						{
  							anim4 = System.Convert.ToInt32( split[ 3 ] );
  						} catch
  						{
  							anim4 = -1;
  						}
  						
  						try
  						{
  							anim5 = System.Convert.ToInt32( split[4] );
  						}
  						catch
  						{
  							anim5= -1;
  						}
  
  						if ( anim2 != -1 )
  						{
  							if ( anim2 == 68 )
  								anim2 = 122;
  
  							if ( original > max1 )
  								max1 = original;
  
  							list1.Add( original );
  							list1.Add( anim2 );
  						}
  
  						if ( anim3 != -1 )
  						{
  							if ( original > max2 )
  								max2 = original;
  
  							list2.Add( original );
  							list2.Add( anim3 );
  						}
  						
  						if ( anim4 != -1 )
  						{
  							if ( original > max3 )
  								max3 = original;
  
  							list3.Add( original );
  							list3.Add( anim4 );
  						}
  
  						if( anim5 != -1 )
  						{
  							if( original > max4 )
  								max4 = original;
  
  							list4.Add( original );
  							list4.Add( anim5 );
  						}
  					}
  					catch
  					{
  					}
  				}
  			}
  
  			m_Table1 = new int[max1+1];
  
  			for ( int i = 0; i < m_Table1.Length; ++i )
  				m_Table1[i] = -1;
  
  			for ( int i = 0; i < list1.Count; i += 2 )
  				m_Table1[(int)list1[i]] = (int)list1[i + 1];
  
  			m_Table2 = new int[max2+1];
  
  			for ( int i = 0; i < m_Table2.Length; ++i )
  				m_Table2[i] = -1;
  
  			for ( int i = 0; i < list2.Count; i += 2 )
  				m_Table2[(int)list2[i]] = (int)list2[i + 1];
  				
  			m_Table3 = new int[max3+1];
  
  			for ( int i = 0; i < m_Table3.Length; ++i )
  				m_Table3[i] = -1;
  
  			for ( int i = 0; i < list3.Count; i += 2 )
  				m_Table3[(int)list3[i]] = (int)list3[i + 1];
  
  			m_Table4 = new int[ max4 + 1 ];
  
  			for( int i = 0; i < m_Table4.Length; ++i )
  				m_Table4[ i ] = -1;
  
  			for( int i = 0; i < list4.Count; i += 2 )
  				m_Table4[ (int)list4[ i ] ] = (int)list4[ i + 1 ];
  		}
  
  		/// <summary>
  		/// Checks to see if <paramref name="body" /> is contained within the mapping table.
  		/// </summary>
  		/// <returns>True if it is, false if not.</returns>
  		public static bool Contains( int body )
  		{
  			if ( m_Table1 != null && body >= 0 && body < m_Table1.Length && m_Table1[body] != -1 )
  				return true;
  
  			if ( m_Table2 != null && body >= 0 && body < m_Table2.Length && m_Table2[body] != -1 )
  				return true;
  				
  			if ( m_Table3 != null && body >= 0 && body < m_Table3.Length && m_Table3[body] != -1 )
  				return true;
  
  			if ( m_Table4 != null && body >= 0 && body < m_Table4.Length && m_Table4[ body ] != -1 )
  				return true;
  
  			return false;
  		}
  
  		/// <summary>
  		/// Attempts to convert <paramref name="body" /> to a body index relative to a file subset, specified by the return value.
  		/// </summary>
  		/// <returns>A value indicating a file subset:
  		/// <list type="table">
  		/// <listheader>
  		/// <term>Return Value</term>
  		/// <description>File Subset</description>
  		/// </listheader>
  		/// <item>
  		/// <term>1</term>
  		/// <description>Anim.mul, Anim.idx (Standard)</description>
  		/// </item>
  		/// <item>
  		/// <term>2</term>
  		/// <description>Anim2.mul, Anim2.idx (LBR)</description>
  		/// </item>
  		/// <item>
  		/// <term>3</term>
  		/// <description>Anim3.mul, Anim3.idx (AOS)</description>
  		/// </item>
  		/// </list>
  		/// </returns>
  		public static int Convert( ref int body )
  		{
  			if ( m_Table1 != null && body >= 0 && body < m_Table1.Length )
  			{
  				int val = m_Table1[body];
  
  				if ( val != -1 )
  				{
  					body = val;
  					return 2;
  				}
  			}
  
  			if ( m_Table2 != null && body >= 0 && body < m_Table2.Length )
  			{
  				int val = m_Table2[body];
  
  				if ( val != -1 )
  				{
  					body = val;
  					return 3;
  				}
  			}
  			
  			if ( m_Table3 != null && body >= 0 && body < m_Table3.Length )
  			{
  				int val = m_Table3[body];
  
  				if ( val != -1 )
  				{
  					body = val;
  					return 4;
  				}
  			}
  
  			if( m_Table4 != null && body >= 0 && body < m_Table4.Length )
  			{
  				int val = m_Table4[ body ];
  
  				if( val != -1 )
  				{
  					body = val;
  					return 5;
  				}
  			}
  
  			return 1;
  		}
  	}
  
  	public class Animations
  	{
  		private static FileIndex m_FileIndex = new FileIndex( "Anim.idx", "Anim.mul", 0x40000 );
  		public static FileIndex FileIndex{ get{ return m_FileIndex; } }
  
  		private static FileIndex m_FileIndex2 = new FileIndex( "Anim2.idx", "Anim2.mul", 0x10000 );
  		public static FileIndex FileIndex2{ get{ return m_FileIndex2; } }
  
  		private static FileIndex m_FileIndex3 = new FileIndex( "Anim3.idx", "Anim3.mul", 0x20000 );
  		public static FileIndex FileIndex3{ get{ return m_FileIndex3; } }
  		
  		private static FileIndex m_FileIndex4 = new FileIndex( "Anim4.idx", "Anim4.mul", 0x20000 );
  		public static FileIndex FileIndex4{ get{ return m_FileIndex4; } }
  
  		private static FileIndex m_FileIndex5 = new FileIndex( "Anim5.idx", "Anim5.mul", 0x20000 );
  		public static FileIndex FileIndex5 { get { return m_FileIndex5; } }
  
  		private Animations()
  		{
  		}
  
  		public static Frame[] GetAnimation( int body, int action, int direction, int hue, bool preserveHue )
  		{
  			if ( preserveHue )
  				Translate( ref body );
  			else
  				Translate( ref body, ref hue );
  
  			int fileType = BodyConverter.Convert( ref body );
  			FileIndex fileIndex;
  
  			int index;
  
  			switch( fileType )
  			{
  				default:
  				case 1:
  					{
  						fileIndex = m_FileIndex;
  
  						if( body < 200 )
  							index = body * 110;
  						else if( body < 400 )
  							index = 22000 + ( ( body - 200 ) * 65 );
  						else
  							index = 35000 + ( ( body - 400 ) * 175 );
  
  						break;
  					}
  				case 2:
  					{
  						fileIndex = m_FileIndex2;
  
  						if( body < 200 )
  							index = body * 110;
  						else
  							index = 22000 + ( ( body - 200 ) * 65 );
  
  						break;
  					}
  				case 3:
  					{
  						fileIndex = m_FileIndex3;
  
  						if( body < 300 )
  							index = body * 65;
  						else if( body < 400 )
  							index = 33000 + ( ( body - 300 ) * 110 );
  						else
  							index = 35000 + ( ( body - 400 ) * 175 );
  
  						break;
  					}
  				case 4:
  					{
  						fileIndex = m_FileIndex4;
  
  						if( body < 200 )
  							index = body * 110;
  						else if( body < 400 )
  							index = 22000 + ( ( body - 200 ) * 65 );
  						else
  							index = 35000 + ( ( body - 400 ) * 175 );
  
  						break;
  					}
  				case 5:
  					{
  						fileIndex = m_FileIndex5;
  
  						if( body < 200 && body != 34 ) // looks strange, though it works.
  							index = body * 110;
  						else
  							index = 35000 + ( ( body - 400 ) * 65 );
  
  						break;
  					}
  			}
  
  			index += action * 5;
  
  			if ( direction <= 4 )
  				index += direction;
  			else
  				index += direction - (direction - 4) * 2;
  
  			int length, extra;
  			bool patched;
  			Stream stream = fileIndex.Seek( index, out length, out extra, out patched );
  
  			if( stream == null )
  			{
  				return null;
  			}
  
  			bool flip = ( direction > 4 );
  
  			BinaryReader bin = new BinaryReader( stream );
  
  			ushort[] palette = new ushort[0x100];
  
  			for ( int i = 0; i < 0x100; ++i )
  				palette[i] = (ushort)(bin.ReadUInt16() ^ 0x8000);
  
  			int start = (int)bin.BaseStream.Position;
  			int frameCount = bin.ReadInt32();
  
  			int[] lookups = new int[frameCount];
  
  			for ( int i = 0; i < frameCount; ++i )
  				lookups[i] = start + bin.ReadInt32();
  
  			bool onlyHueGrayPixels = ( (hue & 0x8000) == 0 );
  
  			hue = (hue & 0x3FFF) - 1;
  
  			Hue hueObject = null;
  
  			if ( hue >= 0 && hue < Hues.List.Length )
  				hueObject = Hues.List[hue];
  
  			Frame[] frames = new Frame[frameCount];
  
  			for ( int i = 0; i < frameCount; ++i )
  			{
  				bin.BaseStream.Seek( lookups[i], SeekOrigin.Begin );
  				frames[i] = new Frame( palette, bin, flip );
  
  				if ( hueObject != null )
  					hueObject.ApplyTo( frames[i].Bitmap, onlyHueGrayPixels );
  			}
  
  			return frames;
  		}
  
  		private static int[] m_Table;
  
  		public static void Translate( ref int body )
  		{
  			if ( m_Table == null )
  				LoadTable();
  
  			if ( body <= 0 || body >= m_Table.Length )
  			{
  				body = 0;
  				return;
  			}
  
  			body = ( m_Table[body] & 0x7FFF );
  		}
  
  		public static void Translate( ref int body, ref int hue )
  		{
  			if ( m_Table == null )
  				LoadTable();
  
  			if ( body <= 0 || body >= m_Table.Length )
  			{
  				body = 0;
  				return;
  			}
  
  			int table = m_Table[body];
  
  			if ( (table & (1 << 31)) != 0 )
  			{
  				body = table & 0x7FFF;
  
  				int vhue = (hue & 0x3FFF) - 1;
  
  				if ( vhue < 0 || vhue >= Hues.List.Length )
  					hue = (table >> 15) & 0xFFFF;
  			}
  		}
  
  		private static void LoadTable()
  		{
  			int count = 400 + ((m_FileIndex.Length - 35000) / 175);
  
  			m_Table = new int[count];
  
  			for ( int i = 0; i < count; ++i )
  			{
  				object o = BodyTable.m_Entries[i];
  
  				if ( o == null || BodyConverter.Contains( i ) )
  				{
  					m_Table[i] = i;
  				}
  				else
  				{
  					BodyTableEntry bte = (BodyTableEntry)o;
  
  					m_Table[i] = bte.m_OldID | (1 << 31) | (((bte.m_NewHue ^ 0x8000) & 0xFFFF) << 15);
  				}
  			}
  		}
  	}
  
  	public class Frame
  	{
  		private Point m_Center;
  		private Bitmap m_Bitmap;
  
  		public Point Center{ get{ return m_Center; } }
  		public Bitmap Bitmap{ get{ return m_Bitmap; } }
  
  		private const int DoubleXor = (0x200 << 22) | (0x200 << 12);
  
  		public static readonly Frame Empty = new Frame();
  		public static readonly Frame[] EmptyFrames = new Frame[1]{ Empty };
  
  		private Frame()
  		{
  			m_Bitmap = new Bitmap(1,1);
  		}
  
  		public unsafe Frame( ushort[] palette, BinaryReader bin, bool flip )
  		{
  			int xCenter = bin.ReadInt16();
  			int yCenter = bin.ReadInt16();
  
  			int width = bin.ReadUInt16();
  			int height = bin.ReadUInt16();
  
  			Bitmap bmp = new Bitmap( width, height, PixelFormat.Format16bppArgb1555 );
  			BitmapData bd = bmp.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.WriteOnly, PixelFormat.Format16bppArgb1555 );
  
  			ushort *line = (ushort *)bd.Scan0;
  			int delta = bd.Stride >> 1;
  
  			int header;
  
  			int xBase = xCenter - 0x200;
  
gt: 			int yBase = (yCenter + height) - 0x200;