context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Windows.Forms;
  using Fomm.Properties;
  using Fomm.SharpZipLib.Zip.Compression;
  
  namespace Fomm.Games.Fallout3.Tools.BSA
  {
    internal partial class BSACreator : Form
    {
      internal static ulong GenHash(string s)
      {
        var extension = "";
        var i = s.LastIndexOf('.');
        if (i != -1)
        {
          extension = s.Substring(i);
          s = s.Remove(i);
        }
        return GenHash(s, extension);
      }
  
      internal static ulong GenHash(string file, string ext)
      {
        file = file.ToLower();
        ext = ext.ToLower();
        ulong hash = 0;
        if (file.Length > 0)
        {
          hash = (ulong) (
            (((byte) file[file.Length - 1])*0x1) +
            ((file.Length > 2 ? (byte) file[file.Length - 2] : 0)*0x100) +
            (file.Length*0x10000) +
            (((byte) file[0])*0x1000000)
            );
        }
        if (file.Length > 3)
        {
          hash += (ulong) (GenHashInternal(file.Substring(1, file.Length - 3))*0x100000000);
        }
        if (ext.Length > 0)
        {
          hash += (ulong) (GenHashInternal(ext)*0x100000000);
          byte i = 0;
          switch (ext)
          {
            case ".nif":
              i = 1;
              break;
            case ".kf":
              i = 2;
              break;
            case ".dds":
              i = 3;
              break;
            case ".wav":
              i = 4;
              break;
          }
          if (i != 0)
          {
            var a = (byte) (((i & 0xfc) << 5) + (byte) ((hash & 0xff000000) >> 24));
            var b = (byte) (((i & 0xfe) << 6) + (byte) (hash & 0xff));
            var c = (byte) ((i << 7) + (byte) ((hash & 0xff00) >> 8));
            hash -= hash & 0xFF00FFFF;
            hash += (uint) ((a << 24) + b + (c << 8));
          }
        }
        return hash;
      }
  
      private static uint GenHashInternal(string s)
      {
        uint hash = 0;
        foreach (var c in s)
        {
          hash *= 0x1003f;
          hash += (byte) c;
        }
        return hash;
      }
  
      private class FolderRecord
      {
        internal readonly List<FileRecord> files;
        internal readonly string name;
        internal readonly ulong hash;
        internal long offsetpos;
  
        internal FolderRecord(string _name)
        {
          name = _name;
          hash = GenHash(name, "");
          files = new List<FileRecord>();
        }
  
        //internal override string ToString() { return name; }
      }
  
      private class FileRecord
      {
        internal readonly string filepath;
        internal readonly uint size;
        internal readonly string name;
        internal readonly ulong hash;
        internal long offsetpos;
        internal bool DoCompress;
  
        internal FileRecord(ListViewItem lvi)
        {
          name = Path.GetFileName(lvi.Text);
          hash = GenHash(name);
          filepath = lvi.SubItems[1].Text;
          size = (uint) (new FileInfo(filepath)).Length;
          DoCompress = lvi.Checked;
        }
      }
  
      private class FolderRecordComparer : IComparer<FolderRecord>
      {
        public int Compare(FolderRecord a, FolderRecord b)
        {
          if (a.hash == b.hash)
          {
            return 0;
          }
          if (a.hash > b.hash)
          {
            return 1;
          }
          return -1;
        }
      }
  
      private class FileRecordComparer : IComparer<FileRecord>
      {
        public int Compare(FileRecord a, FileRecord b)
        {
          if (a.hash == b.hash)
          {
            return 0;
          }
          if (a.hash > b.hash)
          {
            return 1;
          }
          return -1;
        }
      }
  
      private class ListViewSorter : IComparer
      {
        public int Compare(object oa, object ob)
        {
          var a = (ListViewItem) oa;
          var b = (ListViewItem) ob;
          var i = string.Compare(Path.GetDirectoryName(a.Text), Path.GetDirectoryName(b.Text));
          if (i != 0)
          {
            return i;
          }
          return string.Compare(Path.GetFileName(a.Text), Path.GetFileName(b.Text));
        }
      }
  
      private List<FolderRecord> folders;
      private List<FileRecord> files;
      private readonly ListViewSorter sorter = new ListViewSorter();
  
      private void CreateFileRecords()
      {
        folders = new List<FolderRecord>();
        files = new List<FileRecord>();
        string folder = null;
        FolderRecord currentfolder = null;
        foreach (ListViewItem lvi in lvFiles.Items)
        {
          var newfolder = Path.GetDirectoryName(lvi.Text);
          if (newfolder != folder)
          {
            currentfolder = new FolderRecord(newfolder);
            folders.Add(currentfolder);
            folder = newfolder;
          }
          var fr = new FileRecord(lvi);
          if (fr.size >= (1 << 30))
          {
            MessageBox.Show("Error: File '" + fr.filepath + "' is too big to store in a BSA archive");
            continue;
          }
          files.Add(fr);
          currentfolder.files.Add(fr);
        }
        var frc1 = new FolderRecordComparer();
        var frc2 = new FileRecordComparer();
        folders.Sort(frc1);
        foreach (var fr in folders)
        {
          fr.files.Sort(frc2);
        }
      }
  
      internal BSACreator()
      {
        InitializeComponent();
        Icon = Resources.fomm02;
        lvFiles.ListViewItemSorter = sorter;
      }
  
      private void comboBox1_KeyPress(object sender, KeyPressEventArgs e)
      {
        e.Handled = true;
      }
  
      private void bAddFile_Click(object sender, EventArgs e)
      {
        openFileDialog1.Title = "Select files to include";
        openFileDialog1.Filter = "any file|*.*";
        if (openFileDialog1.ShowDialog() != DialogResult.OK)
        {
          return;
        }
        if (openFileDialog1.FileNames.Length > 1)
        {
          lvFiles.Sorting = SortOrder.None;
        }
        foreach (var s in openFileDialog1.FileNames)
        {
          var path = s.ToLower();
          /*if(path.StartsWith(Program.CurrentDir+"data\\")) {
                      path=path.Substring((Program.CurrentDir+"data\\").Length);
                  } else*/
          path = path.StartsWith("data\\") ? path.Substring(5) : Path.GetFileName(path);
          var lvi = new ListViewItem(new[]
          {
            path, s
          });
          lvFiles.Items.Add(lvi);
        }
        if (openFileDialog1.FileNames.Length > 1)
        {
          lvFiles.Sorting = SortOrder.Ascending;
          lvFiles.ListViewItemSorter = sorter;
        }
        ValidateFiles();
      }
  
      private void ValidateFiles()
      {
        //Remove missing files
        for (var i = 0; i < lvFiles.Items.Count; i++)
        {
          if (!File.Exists(lvFiles.Items[i].SubItems[1].Text))
          {
            lvFiles.Items.RemoveAt(i--);
          }
        }
        //Remove duplicate or absolute relative paths
        var previous = lvFiles.Items[0].Text.ToLower();
        for (var i = 1; i < lvFiles.Items.Count; i++)
        {
  
gt:         var next = lvFiles.Items[i].Text.ToLower();