private void RescanButton_Click(object sender, RoutedEventArgs e)
 {
     if (lockTracks)
     {
         return;
     }
     Task.Run(() =>
     {
         Track[] data = tracks.ToArray();
         this.Dispatcher.Invoke(() =>
         {
             lockTracks                  = true;
             ProgBar.Visibility          = Visibility.Visible;
             ProgBarText.Visibility      = Visibility.Visible;
             trackList.AllowDrop         = false;
             dragTracksMessage.AllowDrop = false;
             tracks.Clear();
         });
         for (int i = 0; i < data.Length; i++)
         {
             TrackUtilities.AddFileToTracks(data[i].Path, ref tracks);
             this.Dispatcher.Invoke(() =>
             {
                 ProgBar.Value    = ((double)i / (double)data.Length) * 100;
                 ProgBarText.Text = "" + i + " / " + data.Length;
             });
         }
         this.Dispatcher.Invoke(() =>
         {
             ((ListCollectionView)trackList.ItemsSource).Refresh();
             trackList.Items.Refresh();
             lockTracks                  = false;
             ProgBar.Visibility          = Visibility.Hidden;
             ProgBarText.Visibility      = Visibility.Hidden;
             ProgBar.Value               = 0;
             ProgBarText.Text            = "";
             trackList.AllowDrop         = true;
             dragTracksMessage.AllowDrop = true;
             if (data.Length != tracks.Count)
             {
                 MessageBox.Show("" + (data.Length - tracks.Count) + " files could not be found and were removed.");
             }
         });
     });
 }
        private void WriteFiles_Click(object sender, RoutedEventArgs e)
        {
            if (lockTracks)
            {
                return;
            }
            if (MessageBox.Show("Are you sure you want to overwrite the database?", "Confirm Overwrite", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes)
            {
                return;
            }
            if (!File.Exists(dbFile.Text))
            {
                MessageBox.Show("Please select a valid database file.");
                return;
            }
            if (File.GetAttributes(dbFile.Text).HasFlag(FileAttributes.ReadOnly))
            {
                MessageBox.Show("\"" + dbFile.Text + "\" is Read-only.", "Write Failed");
                return;
            }
            if (File.GetAttributes(dbsFile.Text).HasFlag(FileAttributes.ReadOnly))
            {
                MessageBox.Show("\"" + dbsFile.Text + "\" is Read-only.", "Write Failed");
                return;
            }

            bool setDbHidden  = false;
            bool setDbsHidden = false;

            if (File.GetAttributes(dbFile.Text).HasFlag(FileAttributes.Hidden))
            {
                setDbHidden = true;
                File.SetAttributes(dbFile.Text, File.GetAttributes(dbsFile.Text) & ~FileAttributes.Hidden);
            }

            if (File.GetAttributes(dbsFile.Text).HasFlag(FileAttributes.Hidden))
            {
                setDbsHidden = true;
                File.SetAttributes(dbsFile.Text, File.GetAttributes(dbsFile.Text) & ~FileAttributes.Hidden);
            }

            FileStream fs;
            FileStream fsdbs;

            try
            {
                fs = new FileStream(dbFile.Text, FileMode.Create, FileAccess.ReadWrite);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Could not write to \"" + dbFile.Text + "\": " + "\n" + ex.Message);
                return;
            }

            try
            {
                fsdbs = new FileStream(dbsFile.Text, FileMode.Create, FileAccess.ReadWrite);
            }
            catch (Exception ex)
            {
                fs.Close();
                MessageBox.Show("Could not write to \"" + dbsFile.Text + "\": " + "\n" + ex.Message);
                return;
            }

            BinaryWriter bw            = new BinaryWriter(fs);
            BinaryWriter bwdbs         = new BinaryWriter(fsdbs);
            string       userMusicPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Rockstar Games\GTA V\User Music";

            foreach (Track t in tracks)
            {
                byte[] header         = BitConverter.GetBytes(t.ID);
                byte[] header2        = { 0x00, 0x00, 0x00, 0x00 };
                byte[] chunkSeparator = { 0x00 };
                byte[] chunkTitle     = TrackUtilities.StrToPaddedChunk(t.Title, 31, 0x00, Encoding.Default);
                byte[] chunkArtist    = TrackUtilities.StrToPaddedChunk(t.Artist, 31, 0x00, Encoding.Default);
                byte[] duration       = BitConverter.GetBytes((int)t.Duration.TotalMilliseconds);
                byte[] unknown        = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                byte[] footer         = { 0x02, 0x00, 0x00, 0x00 };

                string final_path = t.Path;
                if (final_path.Contains(userMusicPath))
                {
                    final_path = final_path.Insert(userMusicPath.Length, @"\"); //GTA wants a double slash after User Music for some reason
                }
                else
                {
                    final_path = TrackUtilities.GetShortPath(final_path);
                }
                byte[] pathLength = BitConverter.GetBytes(final_path.Length);

                byte[] chunk = header.Concat(header2).Concat(chunkArtist).Concat(chunkSeparator).Concat(chunkTitle).Concat(chunkSeparator).Concat(duration).Concat(unknown).Concat(footer).Concat(pathLength).ToArray();

                bw.Write(chunk);
                bwdbs.Write(Encoding.Unicode.GetBytes(final_path));
            }

            bw.Close();
            bwdbs.Close();
            fs.Close();
            fsdbs.Close();

            if (setDbHidden)
            {
                File.SetAttributes(dbFile.Text, File.GetAttributes(dbFile.Text) | FileAttributes.Hidden);
            }
            if (setDbsHidden)
            {
                File.SetAttributes(dbsFile.Text, File.GetAttributes(dbsFile.Text) | FileAttributes.Hidden);
            }
            MessageBox.Show("Be sure to set the files to read-only prior to launching the game.\n\nAlternatively, disable the \"Auto-scan for Music\" setting in-game.", "Database files written");
        }
        private void TrackList_Drop(object sender, DragEventArgs e)
        {
            if (lockTracks)
            {
                return;
            }
            Task.Run(() =>
            {
                this.Dispatcher.Invoke(() =>
                {
                    lockTracks                  = true;
                    ProgBar.Visibility          = Visibility.Visible;
                    ProgBarText.Visibility      = Visibility.Visible;
                    trackList.AllowDrop         = false;
                    dragTracksMessage.AllowDrop = false;
                });
                if (e.Data.GetDataPresent(DataFormats.FileDrop))
                {
                    string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

                    int numFiles = files.Length;

                    bool showFailMessage      = false;
                    bool showInvalidExtension = false;
                    bool showMissingMessage   = false;
                    int i = 0;
                    foreach (string f in files)
                    {
                        string fileName = f;
                        if (fileName.EndsWith(".lnk"))
                        {
                            try
                            {
                                IWshRuntimeLibrary.WshShell wsh          = new IWshRuntimeLibrary.WshShell();
                                IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)wsh.CreateShortcut(fileName);
                                fileName = shortcut.TargetPath;
                            }
                            catch (Exception)
                            {
                                showFailMessage = true;
                                continue;
                            }
                        }
                        if (!File.Exists(fileName) && !Directory.Exists(fileName))
                        {
                            showMissingMessage = true;
                            continue;
                        }
                        if (Directory.Exists(fileName)) //is Folder
                        {
                            string[][] sub = { Directory.GetFiles(fileName, "*.mp3"), Directory.GetFiles(fileName, "*.m4a"), Directory.GetFiles(fileName, "*.aac"), Directory.GetFiles(fileName, "*.wma") };
                            numFiles      += sub[0].Length + sub[1].Length + sub[2].Length + sub[3].Length;
                            foreach (string[] type in sub)
                            {
                                foreach (string trk in type)
                                {
                                    this.Dispatcher.Invoke(() =>
                                    {
                                        ProgBar.Value    = ((double)++i / (double)numFiles) * 100;
                                        ProgBarText.Text = "" + i + " / " + numFiles;
                                    });
                                    if (!TrackUtilities.AddFileToTracks(trk, ref tracks))
                                    {
                                        showFailMessage = true;
                                    }
                                }
                            }
                        }
                        else if (TrackUtilities.IsValidAudioExt(fileName))
                        {
                            if (!TrackUtilities.AddFileToTracks(fileName, ref tracks))
                            {
                                showFailMessage = true;
                            }
                        }
                        else
                        {
                            showInvalidExtension = true;
                            continue;
                        }
                        this.Dispatcher.Invoke(() =>
                        {
                            ProgBar.Value    = ((double)++i / (double)numFiles) * 100;
                            ProgBarText.Text = "" + i + " / " + numFiles;
                        });
                    }
                    if (showFailMessage)
                    {
                        MessageBox.Show("Failed to parse tags in one or more files");
                    }
                    if (showInvalidExtension)
                    {
                        MessageBox.Show("One or more files were skipped due to an invalid extension.");
                    }
                    if (showMissingMessage)
                    {
                        MessageBox.Show("One or more files were missing, so they were skipped.");
                    }

                    this.Dispatcher.Invoke(() =>
                    {
                        ((ListCollectionView)trackList.ItemsSource).Refresh();
                        trackList.Items.Refresh();
                    });
                }
                this.Dispatcher.Invoke(() =>
                {
                    lockTracks                  = false;
                    ProgBar.Visibility          = Visibility.Hidden;
                    ProgBarText.Visibility      = Visibility.Hidden;
                    ProgBar.Value               = 0;
                    ProgBarText.Text            = "";
                    trackList.AllowDrop         = true;
                    dragTracksMessage.AllowDrop = true;
                });
            });
        }
        private void LoadButton_Click(object sender, RoutedEventArgs e)
        {
            if (lockTracks)
            {
                return;
            }
            if (!File.Exists(dbFile.Text))
            {
                MessageBox.Show("Please select a valid database file.");
                return;
            }

            if (File.GetAttributes(dbFile.Text).HasFlag(FileAttributes.ReadOnly) && MessageBox.Show("\"" + dbFile.Text + "\" is Read-only! File cannot be modified. Load anyway?", "Warning", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes)
            {
                return;
            }
            if (!File.GetAttributes(dbFile.Text).HasFlag(FileAttributes.ReadOnly) && File.GetAttributes(dbsFile.Text).HasFlag(FileAttributes.ReadOnly) && MessageBox.Show("\"" + dbsFile.Text + "\" is Read-only! File cannot be modified. Load anyway?", "Warning", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes)
            {
                return;
            }
            FileStream fs;
            FileStream fsdbs;

            try
            {
                fs = new FileStream(dbFile.Text, FileMode.Open, FileAccess.Read);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Could not open \"" + dbFile.Text + "\": " + "\n" + ex.Message);
                return;
            }

            try
            {
                fsdbs = new FileStream(dbsFile.Text, FileMode.Open, FileAccess.Read);
            }
            catch (Exception ex)
            {
                fs.Close();
                MessageBox.Show("Could not open \"" + dbsFile.Text + "\": " + "\n" + ex.Message);
                return;
            }

            if (tracks.Count > 0)
            {
                if (MessageBox.Show("Are you sure you want to clear the track list? Any unsaved data will be lost.", "Confirm Clear", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes)
                {
                    return;
                }
                tracks.Clear();
            }

            if (fs.Length % 96 != 0)
            {
                if (MessageBox.Show("\"" + dbsFile.Text + "\" seems to be corrupt (incorrect length). Try to parse anyway?", "Incorrect Length", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes)
                {
                    fs.Close();
                    fsdbs.Close();
                    return;
                }
            }

            BinaryReader br      = new BinaryReader(fs);
            BinaryReader brdbs   = new BinaryReader(fsdbs);
            int          counter = 0;

            while (fs.CanRead && fs.Position < fs.Length)
            {
                if (fs.Length - fs.Position < 96)
                {
                    break;
                }
                byte[] header          = br.ReadBytes(8);
                byte[] chunkArtist     = br.ReadBytes(31);
                byte   chunkSeparator  = br.ReadByte();
                byte[] chunkTitle      = br.ReadBytes(31);
                byte   chunkSeparator2 = br.ReadByte();
                byte[] duration        = br.ReadBytes(4);
                byte[] unknown         = br.ReadBytes(12);
                byte[] footer          = br.ReadBytes(4);
                byte[] pathByteLength  = br.ReadBytes(4);
                int    pathLength      = BitConverter.ToInt32(pathByteLength) * 2;

                string path = Encoding.Unicode.GetString(brdbs.ReadBytes(pathLength));

                string artist = "";
                string title  = "";
                foreach (byte b in chunkArtist)
                {
                    if (b == 0x00)
                    {
                        break;
                    }
                    artist += (char)b;
                }
                foreach (byte b in chunkTitle)
                {
                    if (b == 0x00)
                    {
                        break;
                    }
                    title += (char)b;
                }
                tracks.Add(new Track()
                {
                    ID = counter, Title = title, Artist = artist, ShortPath = TrackUtilities.GetShortPath(path.Replace(@"\\", @"\")), Path = System.IO.Path.GetFullPath(path.Replace(@"\\", @"\")), Duration = TimeSpan.FromMilliseconds(BitConverter.ToInt32(duration))
                });
                counter++;
            }

            br.Close();
            brdbs.Close();
            fs.Close();
            fsdbs.Close();

            ((ListCollectionView)trackList.ItemsSource).Refresh();
            trackList.Items.Refresh();
        }