namespace SHNDecrypt
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Forms;

    public class SHNFile
    {
        private uint ColumnCount;
        private int[] ColumnLengths;
        private string[] ColumnNames;
        public List<SHNColumn> columns;
        private uint[] ColumnTypes;
        public byte[] CryptHeader;
        public byte[] data;
        private uint DefaultRecordLength;
        public Dictionary<int, int> displayToReal;
        public uint Header;
        public bool isTextData;
        public string Path;
        private uint RecordCount;
        public DataTable table;

        public SHNFile()
        {
            this.table = new DataTable();
            this.displayToReal = new Dictionary<int, int>();
            this.columns = new List<SHNColumn>();
        }

        public SHNFile(string path)
        {
            this.table = new DataTable();
            this.displayToReal = new Dictionary<int, int>();
            this.columns = new List<SHNColumn>();
            try
            {
                BinaryReaderEx ex;
                this.columns.Clear();
                this.Path = path;
                if (System.IO.Path.GetFileNameWithoutExtension(path).ToLower().Contains("textdata"))
                {
                    this.isTextData = true;
                }
                using (ex = new BinaryReaderEx(File.OpenRead(path)))
                {
                    if (path.EndsWith(".shn"))
                    {
                        this.CryptHeader = ex.ReadBytes(0x20);
                        this.data = ex.ReadBytes(ex.ReadInt32() - 0x24);
                    }
                    else
                    {
                        this.data = ex.ReadBytes((int) ex.Length);
                    }
                }
                this.Decrypt(this.data, 0, this.data.Length - 0x24);
                ex = new BinaryReaderEx(new MemoryStream(this.data));
                this.Header = ex.ReadUInt32();
                this.RecordCount = ex.ReadUInt32();
                this.DefaultRecordLength = ex.ReadUInt32();
                this.ColumnCount = ex.ReadUInt32();
                this.ColumnNames = new string[this.ColumnCount];
                this.ColumnTypes = new uint[this.ColumnCount];
                this.ColumnLengths = new int[this.ColumnCount];
                int num = 2;
                int num2 = 0;
                for (uint i = 0; i < this.ColumnCount; i++)
                {
                    string str = ex.ReadString(0x30);
                    uint num4 = ex.ReadUInt32();
                    int num5 = ex.ReadInt32();
                    SHNColumn item = new SHNColumn();
                    if ((str.Length == 0) || string.IsNullOrWhiteSpace(str))
                    {
                        str = "UnkCol" + num2.ToString();
                        num2++;
                    }
                    item.name = str;
                    item.Type = num4;
                    item.Lenght = num5;
                    this.columns.Add(item);
                    this.ColumnNames[i] = str;
                    this.ColumnTypes[i] = num4;
                    this.ColumnLengths[i] = num5;
                    num += num5;
                }
                if (num != this.DefaultRecordLength)
                {
                    throw new Exception("Wrong record length!");
                }
                this.GenerateColumns(this.table, this.columns);
                this.ReadRows(ex, this.table);
            }
            catch (Exception)
            {
            }
        }

        public void CreateColumn(string name, int len, uint type, string defaultval)
        {
            SHNColumn item = new SHNColumn {
                name = name,
                Lenght = len,
                Type = type
            };
            this.columns.Add(item);
            DataColumn column = new DataColumn {
                ColumnName = name,
                DefaultValue = defaultval,
                DataType = this.GetType(item)
            };
            this.table.Columns.Add(column);
        }

        public void CreateDefaultLayout()
        {
            this.CryptHeader = new byte[0x20];
            this.SetCryptHeader("3B 02 00 00 32 30 30 35 2D 30 38 2D 32 36 20 BF C0 C8 C4 20 32 3A 33 00 6A 7F 00 00 01 00 00 00");
            this.Header = 0;
            this.CreateColumn("Empty Column", 0xff, 0x18, "");
            this.Path = "New File.shn";
        }

        public void CreateSQL(out string Output, bool drop)
        {
            Output = string.Empty;
            string name = "data_" + System.IO.Path.GetFileNameWithoutExtension(this.Path);
            Output = Output + SQLConv.CreateHeader(name, drop) + "\r\n";
            for (int i = 0; i < this.table.Columns.Count; i++)
            {
                string str2 = "";
                string str6 = str2;
                str2 = str6 + "`" + this.table.Columns[i].Caption + "` " + SQLConv.GetSQLType(this.table.Columns[i].DataType);
                if ((i + 1) != this.table.Columns.Count)
                {
                    str2 = str2 + ",";
                }
                str2 = str2 + "\r\n";
                Output = Output + str2;
            }
            Output = Output + "); \r\n\r\n";
            int num2 = 0;
            int count = 0x7d0;
            if (this.table.Rows.Count < 0x7d0)
            {
                count = this.table.Rows.Count;
            }
            while (num2 < (this.table.Rows.Count - 1))
            {
                Output = Output + SQLConv.InsterInto(name, this.table.Columns);
                int num4 = this.table.Columns.Count;
                for (int j = num2; j < (count + num2); j++)
                {
                    string str3 = "(";
                    for (int k = 0; k < num4; k++)
                    {
                        string prefix = SQLConv.GetPrefix(this.table.Columns[k].DataType);
                        string str5 = Convert.ToString(this.table.Rows[j][k]).Replace("'", @"\'");
                        str3 = str3 + prefix + str5 + prefix;
                        if ((k + 1) != num4)
                        {
                            str3 = str3 + ",";
                        }
                    }
                    str3 = str3 + ")";
                    if ((j + 1) != (count + num2))
                    {
                        str3 = str3 + ",";
                    }
                    else
                    {
                        str3 = str3 + ";";
                    }
                    Output = Output + str3 + "\r\n";
                }
                num2 += count;
                if ((this.table.Rows.Count - num2) < 0x7d0)
                {
                    count = this.table.Rows.Count - num2;
                }
            }
        }

        private void Decrypt(byte[] data, int index, int length)
        {
            if (((index < 0) | (length < 1)) | ((index + length) > data.Length))
            {
                throw new IndexOutOfRangeException();
            }
            byte num = (byte) length;
            for (int i = length - 1; i >= 0; i--)
            {
                data[i] = (byte) (data[i] ^ num);
                byte num3 = (byte) i;
                num3 = (byte) (num3 & 15);
                num3 = (byte) (num3 & 0x55);
                num3 = (byte) (num3 ^ ((byte) (((byte) i) * 11)));
                num3 = (byte) (num3 ^ num);
                num3 = (byte) (num3 ^ 170);
                num = num3;
            }
        }

        public void DeleteColumn(string name)
        {
            this.columns.Remove(this.GetColByName(name));
            this.table.Columns.Remove(this.GetDataColByName(name));
        }

        public void Dispose()
        {
            this.table = null;
            this.CryptHeader = null;
        }

        public void EditColumnName(string from, string to)
        {
            this.GetDataColByName(from).ColumnName = to;
            this.GetColByName(from).name = to;
        }

        public void exportCVS(string path)
        {
            TextWriter writer = new StreamWriter(path);
            foreach (DataColumn column in this.table.Columns)
            {
                SHNColumn colByName = this.GetColByName(column.ColumnName);
                writer.Write(colByName.name + ", ");
            }
            writer.Write(writer.NewLine);
            foreach (DataRow row in this.table.Rows)
            {
                for (int i = 0; i < this.table.Columns.Count; i++)
                {
                    string source = row[i].ToString();
                    if (source.Contains<char>('"'))
                    {
                        source = source.Replace('"', ' ');
                    }
                    writer.Write("\"" + source + "\"");
                    if ((i + 1) == this.table.Columns.Count)
                    {
                        writer.Write(writer.NewLine);
                    }
                    else
                    {
                        writer.Write(',');
                    }
                }
            }
            writer.Close();
        }

        private void GenerateColumns(DataTable table, List<SHNColumn> cols)
        {
            for (int i = 0; i < cols.Count; i++)
            {
                DataColumn column = new DataColumn {
                    ColumnName = cols[i].name,
                    DataType = this.GetType(cols[i])
                };
                table.Columns.Add(column);
            }
        }

        public SHNColumn GetColByName(string name)
        {
            foreach (SHNColumn column in this.columns)
            {
                if (column.name.ToLower() == name.ToLower())
                {
                    return column;
                }
            }
            return null;
        }

        public int getColIndex(string name)
        {
            for (int i = 0; i < this.table.Columns.Count; i++)
            {
                if (this.table.Columns[i].ColumnName == name)
                {
                    return i;
                }
            }
            return -1;
        }

        public string GetCryptString()
        {
            string str = "";
            for (int i = 0; i < this.CryptHeader.Length; i++)
            {
                if (i != (this.CryptHeader.Length - 1))
                {
                    str = str + this.CryptHeader[i].ToString("X2") + " ";
                }
                else
                {
                    str = str + this.CryptHeader[i].ToString("X2");
                }
            }
            return str;
        }

        public DataColumn GetDataColByName(string name)
        {
            for (int i = 0; i < this.table.Columns.Count; i++)
            {
                if (this.table.Columns[i].ColumnName == name)
                {
                    return this.table.Columns[i];
                }
            }
            return null;
        }

        public uint GetDefaultRecLen()
        {
            uint num = 2;
            foreach (DataColumn column in this.table.Columns)
            {
                SHNColumn colByName = this.GetColByName(column.ColumnName);
                num += (uint) colByName.Lenght;
            }
            return num;
        }

        public int GetRowByIndex(int ColIndex, string RowInput)
        {
            for (int i = 0; i < this.table.Rows.Count; i++)
            {
                if (this.table.Rows[i][ColIndex].ToString().ToLower() == RowInput.ToLower())
                {
                    return i;
                }
            }
            return -1;
        }

        public System.Type GetType(SHNColumn col)
        {
            switch (col.Type)
            {
                case 1:
                case 12:
                    return typeof(byte);

                case 2:
                    return typeof(ushort);

                case 3:
                case 11:
                    return typeof(uint);

                case 5:
                    return typeof(float);

                case 9:
                case 0x18:
                case 0x1a:
                    return typeof(string);

                case 13:
                case 0x15:
                    return typeof(short);

                case 0x10:
                    return typeof(byte);

                case 0x11:
                    return typeof(string);

                case 0x12:
                case 0x1b:
                    return typeof(uint);

                case 20:
                    return typeof(sbyte);

                case 0x16:
                    return typeof(int);
            }
            return typeof(object);
        }

        public void LoadMe(string path)
        {
            BinaryReaderEx ex = new BinaryReaderEx(File.OpenRead(path));
            if (path.EndsWith(".shn"))
            {
                this.CryptHeader = ex.ReadBytes(0x20);
                this.data = ex.ReadBytes(ex.ReadInt32() - 0x48);
            }
            else
            {
                this.data = ex.ReadBytes((int) ex.Length);
            }
            ex.Close();
            this.Decrypt(this.data, 0, this.data.Length);
        }

        private void ReadRows(BinaryReaderEx r, DataTable table)
        {
            object[] values = new object[this.columns.Count];
            for (uint i = 0; i < this.RecordCount; i++)
            {
                r.ReadUInt16();
                for (int j = 0; j < this.columns.Count; j++)
                {
                    switch (this.columns[j].Type)
                    {
                        case 1:
                            values[j] = r.ReadByte();
                            break;

                        case 2:
                            values[j] = r.ReadUInt16();
                            break;

                        case 3:
                            values[j] = r.ReadUInt32();
                            break;

                        case 5:
                            values[j] = r.ReadSingle();
                            break;

                        case 9:
                            values[j] = r.ReadString(this.ColumnLengths[j]);
                            break;

                        case 11:
                            values[j] = r.ReadUInt32();
                            break;

                        case 12:
                            values[j] = r.ReadByte();
                            break;

                        case 13:
                            values[j] = r.ReadInt16();
                            break;

                        case 0x10:
                            values[j] = r.ReadByte();
                            break;

                        case 0x11:
                            values[j] = r.ReadString(0x20);
                            break;

                        case 0x12:
                            values[j] = r.ReadUInt32();
                            break;

                        case 20:
                            values[j] = r.ReadSByte();
                            break;

                        case 0x15:
                            values[j] = r.ReadInt16();
                            break;

                        case 0x16:
                            values[j] = r.ReadInt32();
                            break;

                        case 0x18:
                            values[j] = r.ReadString(this.ColumnLengths[j]);
                            break;

                        case 0x1a:
                            values[j] = r.ReadString();
                            break;

                        case 0x1b:
                            values[j] = r.ReadUInt32();
                            break;
                    }
                }
                table.Rows.Add(values);
            }
        }

        public bool Save(string file)
        {
            MemoryStream output = new MemoryStream();
            BinaryWriter w = new BinaryWriter(output);
            try
            {
                w.Write(this.Header);
                w.Write(this.table.Rows.Count);
                w.Write(this.GetDefaultRecLen());
                w.Write(this.table.Columns.Count);
                for (int i = 0; i < this.table.Columns.Count; i++)
                {
                    SHNColumn colByName = this.GetColByName(this.table.Columns[this.displayToReal[i]].ColumnName);
                    if (colByName.name.Contains("UnkCol"))
                    {
                        w.Write(new byte[0x30]);
                    }
                    else
                    {
                        this.WriteString(w, colByName.name, 0x30);
                    }
                    w.Write(colByName.Type);
                    w.Write(colByName.Lenght);
                }
                this.WriteRows(w);
                byte[] sourceArray = output.GetBuffer();
                long length = output.Length;
                byte[] destinationArray = new byte[length];
                Array.Copy(sourceArray, destinationArray, length);
                this.Decrypt(destinationArray, 0, destinationArray.Length - 0x24);
                w.Close();
                w = new BinaryWriter(File.Create(file));
                w.Write(this.CryptHeader);
                w.Write((int) (destinationArray.Length + 0x24));
                w.Write(destinationArray);
                w.Close();
                this.Path = file;
                return true;
            }
            catch (Exception exception)
            {
                w.Close();
                MessageBox.Show("Could not save file: " + exception.Message, "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
        }

        public void SetCryptHeader(string hexString)
        {
            string[] strArray = hexString.Split(new char[] { ' ' });
            if (strArray.Length != 0x20)
            {
                throw new Exception("Incorrect header length!");
            }
            for (int i = 0; i < strArray.Length; i++)
            {
                this.CryptHeader[i] = byte.Parse(strArray[i], NumberStyles.HexNumber);
            }
        }

        private void WriteRows(BinaryWriter w)
        {
            for (int i = 0; i < this.table.Rows.Count; i++)
            {
                DataRow row = this.table.Rows[i];
                long position = w.BaseStream.Position;
                w.Write((ushort) 0);
                for (int j = 0; j < this.table.Columns.Count; j++)
                {
                    object obj2 = row.ItemArray[this.displayToReal[j]];
                    if (obj2 == null)
                    {
                        obj2 = "0";
                    }
                    SHNColumn colByName = this.GetColByName(this.table.Columns[this.displayToReal[j]].ColumnName);
                    switch (colByName.Type)
                    {
                        case 1:
                        {
                            if (obj2 is string)
                            {
                                obj2 = byte.Parse((string) obj2);
                            }
                            w.Write((byte) obj2);
                            continue;
                        }
                        case 2:
                        {
                            if (obj2 is string)
                            {
                                obj2 = ushort.Parse((string) obj2);
                            }
                            w.Write((ushort) obj2);
                            continue;
                        }
                        case 3:
                        {
                            if (obj2 is string)
                            {
                                obj2 = uint.Parse((string) obj2);
                            }
                            w.Write((uint) obj2);
                            continue;
                        }
                        case 4:
                        case 6:
                        case 7:
                        case 8:
                        case 10:
                        case 14:
                        case 15:
                        case 0x13:
                        case 0x17:
                        case 0x19:
                        {
                            continue;
                        }
                        case 5:
                        {
                            if (obj2 is string)
                            {
                                obj2 = float.Parse((string) obj2);
                            }
                            w.Write((float) obj2);
                            continue;
                        }
                        case 9:
                        {
                            if (!string.IsNullOrWhiteSpace(obj2.ToString()))
                            {
                                break;
                            }
                            this.WriteString(w, obj2.ToString(), colByName.Lenght);
                            continue;
                        }
                        case 11:
                        {
                            if (obj2 is string)
                            {
                                obj2 = uint.Parse((string) obj2);
                            }
                            w.Write((uint) obj2);
                            continue;
                        }
                        case 12:
                        {
                            if (obj2 is string)
                            {
                                obj2 = byte.Parse((string) obj2);
                            }
                            w.Write((byte) obj2);
                            continue;
                        }
                        case 13:
                        {
                            if (obj2 is string)
                            {
                                obj2 = short.Parse((string) obj2);
                            }
                            w.Write((short) obj2);
                            continue;
                        }
                        case 0x10:
                        {
                            if (obj2 is string)
                            {
                                obj2 = byte.Parse((string) obj2);
                            }
                            w.Write((byte) obj2);
                            continue;
                        }
                        case 0x11:
                        {
                            this.WriteString(w, (string) obj2, 0x20);
                            continue;
                        }
                        case 0x12:
                        {
                            if (obj2 is string)
                            {
                                obj2 = uint.Parse((string) obj2);
                            }
                            w.Write((uint) obj2);
                            continue;
                        }
                        case 20:
                        {
                            if (obj2 is string)
                            {
                                obj2 = sbyte.Parse((string) obj2);
                            }
                            w.Write((sbyte) obj2);
                            continue;
                        }
                        case 0x15:
                        {
                            if (obj2 is string)
                            {
                                obj2 = short.Parse((string) obj2);
                            }
                            w.Write((short) obj2);
                            continue;
                        }
                        case 0x16:
                        {
                            if (obj2 is string)
                            {
                                obj2 = int.Parse((string) obj2);
                            }
                            w.Write((int) obj2);
                            continue;
                        }
                        case 0x18:
                        {
                            this.WriteString(w, (string) obj2, colByName.Lenght);
                            continue;
                        }
                        case 0x1a:
                        {
                            this.WriteString(w, (string) obj2, -1);
                            continue;
                        }
                        case 0x1b:
                        {
                            if (obj2 is string)
                            {
                                obj2 = uint.Parse((string) obj2);
                            }
                            w.Write((uint) obj2);
                            continue;
                        }
                        default:
                        {
                            continue;
                        }
                    }
                    this.WriteString(w, (string) obj2, colByName.Lenght);
                }
                long num4 = w.BaseStream.Position - position;
                long offset = w.BaseStream.Position;
                w.BaseStream.Seek(position, SeekOrigin.Begin);
                w.Write((ushort) num4);
                w.BaseStream.Seek(offset, SeekOrigin.Begin);
            }
        }

        private void WriteString(BinaryWriter w, string s, int length)
        {
            byte[] bytes = Encoding.GetEncoding(Program.eT).GetBytes(s);
            if (length == -1)
            {
                w.Write(bytes);
                w.Write((byte) 0);
            }
            else
            {
                byte[] destinationArray = new byte[length];
                Array.Copy(bytes, destinationArray, Math.Min(length, bytes.Length));
                w.Write(destinationArray);
            }
        }
    }
}

