md.mclama.com.ModManager.java Source code

Java tutorial

Introduction

Here is the source code for md.mclama.com.ModManager.java

Source

package md.mclama.com;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;

import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

public class ModManager extends JFrame {

    /**
     * 
     */
    public static final long serialVersionUID = 1L;

    static final String McVersion = "0.4.6"; //Build 22
    static final String McCheckVersionPath = "http://mclama.com/McLauncher/McLauncher%20Version.txt";
    static final String McLauncherPath = "http://mclama.com/McLauncher/Downloads/McLauncher.jar";
    static final String McUpdaterPath = "http://mclama.com/McLauncher/Downloads/McLauncher.jar";
    private final boolean testBtnEnabled = false; //a test button that is to be removed on release

    public ModManager McLauncher = this;
    public Utility util;

    private JPanel contentPane;
    private DefaultListModel<String> listModel;
    String gamePath;
    private JTextField txtGamePath;
    private String[] modDir;
    private List<Profile> profiles = new ArrayList<Profile>();
    private Profile selProfile;
    JButton go;
    private int trow = 0;

    JFileChooser chooser;
    String choosertitle;
    private JLabel lblAvailableMods;
    private int modCount;
    private JTextField txtProfile;
    private JButton btnNewProfile;
    private DefaultListModel<String> profileListMdl;
    private JList<String> profileList;
    private JLabel lblModsEnabled;
    private JList<String> enabledModsList;
    private DefaultListModel<String> ModListModel;
    private JList<String> modsList;

    private JLabel lblModVersion;

    private JLabel lblRequiredMods;
    private String reqModsStr = "None";

    private final JButton btnUpdate = new JButton("Update Launcher");

    private JLabel lblModRequires;

    private JButton btnLaunchIgnore;
    private JPanel panelLauncher;
    protected JProgressBar pBarDownloadMod;
    protected JProgressBar pBarExtractMod;
    protected JLabel lblDownloadModInfo;
    private JScrollPane scrollPane_3;
    private JButton btnDownload;
    private JButton btnGotoMod;
    private JTable tableDownloads;
    private BufferedImage modImg;
    private JPanel panelModImg;
    private JTextField txtFilterText;
    protected String[] dlModList;
    private DefaultTableModel dlModel;
    private JLabel lblModDlCounter;
    private JTextArea txtrDMModDescription;
    private JTextArea lblDMModTags;
    private JTextArea lblDMRequiredMods;

    private TableRowSorter<DefaultTableModel> tSorter;

    private String dlModListSelected;

    protected boolean canDownloadMod = false;
    private JLabel lblDLModLicense;

    private URL modPageUrl;
    private JPanel panelOptions;
    private final JScrollPane scrollPane_4 = new JScrollPane();
    private JPanel panel;
    private JLabel lblCloseMclauncherAfter;
    private JLabel lblCloseMclauncherAfter_1;
    private JLabel lblSortNewestDownloadable;
    protected JToggleButton tglbtnNewModsFirst;
    protected JToggleButton tglbtnCloseAfterUpdate;
    protected JToggleButton tglbtnCloseAfterLaunch;
    protected JToggleButton tglbtnDisplayon;
    protected JToggleButton tglbtnDisplayoff;
    private JLabel lblInfo;
    private JComboBox comboBox;
    private JLabel lblNeedrestart;
    protected JToggleButton tglbtnSendAnonData;
    private JLabel lblSendAnonymousUse;
    private JLabel lblWipmod;
    private JLabel lblDeleteOldMod;
    protected JToggleButton tglbtnDeleteBeforeUpdate;
    protected String modPath;

    public static Console con;
    private JSeparator separator_4;
    protected JToggleButton tglbtnAlertOnModUpdateAvailable;
    private JPanel panelChangelog;
    private JTextArea textChangelog;
    private JScrollPane scrollPane_6;

    protected long lastClickTime = 0;

    protected Download CurrentDownload;

    public boolean CurrentlyDownloading = false;;
    protected static FileLock lock = null;
    protected static File workDir;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        try {
            workDir = new File(ModManager.class.getProtectionDomain().getCodeSource().getLocation().toURI()
                    .getPath().replace("McLauncher.jar", ""));
            System.out.println("Running McLauncher from " + workDir);
        } catch (URISyntaxException e1) {
            workDir = new File("");
            e1.printStackTrace();
        }
        //      try {
        //           // Get a file channel for the file
        //         boolean FileNotLocked=false;
        //
        //           
        //           try {
        //            FileWriter file2 = new FileWriter(System.getProperty("java.io.tmpdir") + "/McLauncher.lock");
        //            file2.write("McLauncher lock");
        //            file2.close();
        //         } catch (IOException e) {
        //            FileNotLocked=true;
        //         }
        //           
        //           File file = new File(System.getProperty("java.io.tmpdir") + "/McLauncher.lock");
        //
        //           FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
        //           if(file.renameTo(file)) FileNotLocked=true;
        //           if(FileNotLocked){
        //              JOptionPane.showMessageDialog(null, "Already running McLauncher, Only one instance allowed at a time\n"
        //                  + "Is this wrong? Delete McLauncher.lock in your temp folder");
        //            System.out.println("Already running McLauncher, Only one instance allowed at a time");
        //            System.exit(0);
        //           }
        //           lock = channel.lock();
        //
        //           try {
        //               lock = channel.tryLock();
        //           } catch (OverlappingFileLockException e) {
        //           }
        //           channel.close();
        //       } catch (Exception e) {
        //       }

        //Not added because i do not want to lock people out from using McLauncher if somehow this fails.
        //      if(new File(System.getProperty("java.io.tmpdir") + "/McLauncher.lock").exists()){
        //         System.exit(0);//close this instance if McLauncher is already running.
        //      }
        //      try {
        //         FileWriter file = new FileWriter(System.getProperty("java.io.tmpdir") + "/McLauncher.lock");
        //         file.write("McLauncher one instance lock");
        //         file.close();
        //      } catch (IOException e) {
        //         System.out.println("Severe, failed to create temp lock file");
        //      }
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    con = new Console();
                    con.setVisible(false);
                    Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
                    int x = (int) ((dimension.getWidth() - con.getWidth()) / 2);
                    int y = (int) ((dimension.getHeight() - con.getHeight()) / 2);
                    con.setLocation(x, y);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                        if ("Nimbus".equals(info.getName())) {
                            UIManager.setLookAndFeel(info.getClassName());
                            break;
                        }
                    }
                    ModManager frame = new ModManager();
                    frame.setResizable(false);
                    frame.setVisible(true);
                    Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
                    int x = (int) ((dimension.getWidth() - frame.getWidth()) / 2);
                    int y = (int) ((dimension.getHeight() - frame.getHeight()) / 2);
                    frame.setLocation(x, y);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */

    @SuppressWarnings("serial")
    public ModManager() throws MalformedURLException {
        setResizable(false);
        setTitle("McLauncher " + McVersion);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 700, 400);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
        tabbedPane.setBounds(0, 0, 694, 372);
        contentPane.add(tabbedPane);

        profileListMdl = new DefaultListModel<String>();
        ModListModel = new DefaultListModel<String>();
        listModel = new DefaultListModel<String>();
        getCurrentMods();

        panelLauncher = new JPanel();
        tabbedPane.addTab("Launcher", null, panelLauncher, null);
        panelLauncher.setLayout(null);
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(556, 36, 132, 248);
        panelLauncher.add(scrollPane);
        profileList = new JList<String>(profileListMdl);
        profileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        scrollPane.setViewportView(profileList);

        btnNewProfile = new JButton("New");
        btnNewProfile.setFont(new Font("SansSerif", Font.PLAIN, 12));
        btnNewProfile.setBounds(479, 4, 76, 20);
        panelLauncher.add(btnNewProfile);
        btnNewProfile.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                newProfile(txtProfile.getText());
            }
        });
        btnNewProfile.setToolTipText("Click to create a new profile.");

        JButton btnRenameProfile = new JButton("Rename");
        btnRenameProfile.setBounds(479, 25, 76, 20);
        panelLauncher.add(btnRenameProfile);
        btnRenameProfile.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                renameProfile();
            }
        });
        btnRenameProfile.setToolTipText("Click to rename the selected profile");

        JButton btnDelProfile = new JButton("Delete");
        btnDelProfile.setBounds(479, 50, 76, 20);
        panelLauncher.add(btnDelProfile);
        btnDelProfile.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                deleteProfile();
            }
        });
        btnDelProfile.setToolTipText("Click to delete the selected profile.");

        JButton btnLaunch = new JButton("Launch");
        btnLaunch.setBounds(605, 319, 89, 23);
        panelLauncher.add(btnLaunch);
        btnLaunch.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (selProfile != null) {
                    LaunchFactorioWithSelectedMods(false); //dont ignore
                }
            }
        });
        btnLaunch.setToolTipText("Click to launch factorio with the selected mod profile.");

        lblAvailableMods = new JLabel("Available Mods");
        lblAvailableMods.setBounds(4, 155, 144, 14);
        panelLauncher.add(lblAvailableMods);
        lblAvailableMods.setFont(new Font("SansSerif", Font.PLAIN, 10));

        lblAvailableMods.setText("Available Mods: " + -1);

        txtGamePath = new JTextField();
        txtGamePath.setBounds(4, 5, 211, 23);
        panelLauncher.add(txtGamePath);
        txtGamePath.setToolTipText("Select tha path to your game!");
        txtGamePath.setFont(new Font("Tahoma", Font.PLAIN, 8));
        txtGamePath.setText("Game Path");
        txtGamePath.setColumns(10);

        JButton btnFind = new JButton("find");
        btnFind.setBounds(227, 3, 32, 23);
        panelLauncher.add(btnFind);

        txtProfile = new JTextField();
        txtProfile.setBounds(556, 2, 132, 22);
        panelLauncher.add(txtProfile);
        txtProfile.setToolTipText("The name of NEW or RENAME profiles");
        txtProfile.setText("Profile1");
        txtProfile.setColumns(10);

        lblModsEnabled = new JLabel("Mods Enabled: -1");
        lblModsEnabled.setBounds(335, 155, 95, 16);
        panelLauncher.add(lblModsEnabled);
        lblModsEnabled.setFont(new Font("SansSerif", Font.PLAIN, 10));

        JScrollPane scrollPane_1 = new JScrollPane();
        scrollPane_1.setBounds(0, 167, 211, 165);
        panelLauncher.add(scrollPane_1);
        modsList = new JList<String>(listModel);
        modsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        modsList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                String modName = util.getModVersion(modsList.getSelectedValue());
                lblModVersion.setText("Mod Version: " + modName);
                checkDependency(modsList);
                if (modName.contains(".zip")) {
                    new File(System.getProperty("java.io.tmpdir") + modName.replace(".zip", "")).delete();
                }
                if (System.currentTimeMillis() - lastClickTime <= 300) { //Double click
                    addMod();
                }
                lastClickTime = System.currentTimeMillis();
            }
        });
        scrollPane_1.setViewportView(modsList);
        modsList.setFont(new Font("Tahoma", Font.PLAIN, 9));

        JScrollPane scrollPane_2 = new JScrollPane();
        scrollPane_2.setBounds(333, 167, 211, 165);
        panelLauncher.add(scrollPane_2);
        enabledModsList = new JList<String>(ModListModel);
        enabledModsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        enabledModsList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                lblModVersion.setText("Mod Version: " + util.getModVersion(enabledModsList.getSelectedValue()));
                checkDependency(enabledModsList);
                if (System.currentTimeMillis() - lastClickTime <= 300) { //Double click
                    removeMod();
                }
                lastClickTime = System.currentTimeMillis();
            }
        });
        enabledModsList.setFont(new Font("SansSerif", Font.PLAIN, 10));
        scrollPane_2.setViewportView(enabledModsList);

        JButton btnEnable = new JButton("Enable");
        btnEnable.setBounds(223, 200, 90, 28);
        panelLauncher.add(btnEnable);
        btnEnable.setToolTipText("Add mod -->");

        JButton btnDisable = new JButton("Disable");
        btnDisable.setBounds(223, 240, 90, 28);
        panelLauncher.add(btnDisable);
        btnDisable.setToolTipText("Disable mod ");

        JLabel lblModsAvailable = new JLabel("Mods available");
        lblModsAvailable.setBounds(4, 329, 89, 14);
        panelLauncher.add(lblModsAvailable);
        lblModsAvailable.setFont(new Font("SansSerif", Font.PLAIN, 10));

        JLabel lblEnabledMods = new JLabel("Enabled Mods");
        lblEnabledMods.setBounds(337, 329, 89, 16);
        panelLauncher.add(lblEnabledMods);
        lblEnabledMods.setFont(new Font("SansSerif", Font.PLAIN, 10));

        lblModVersion = new JLabel("Mod Version: (select a mod first)");
        lblModVersion.setBounds(4, 117, 183, 14);
        panelLauncher.add(lblModVersion);
        lblModVersion.setFont(new Font("SansSerif", Font.PLAIN, 10));

        lblRequiredMods = new JLabel("Required Mods: " + reqModsStr);
        lblRequiredMods.setBounds(6, 143, 538, 14);
        panelLauncher.add(lblRequiredMods);
        lblRequiredMods.setHorizontalAlignment(SwingConstants.RIGHT);
        lblRequiredMods.setFont(new Font("SansSerif", Font.PLAIN, 10));

        JButton btnNewButton = new JButton("TEST");
        btnNewButton.setVisible(testBtnEnabled);
        btnNewButton.setEnabled(testBtnEnabled);
        btnNewButton.setBounds(338, 61, 90, 28);
        panelLauncher.add(btnNewButton);
        btnUpdate.setBounds(218, 322, 103, 20);
        panelLauncher.add(btnUpdate);

        btnUpdate.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                util.updateLauncher();
            }
        });
        btnUpdate.setVisible(false);
        btnUpdate.setFont(new Font("SansSerif", Font.PLAIN, 9));

        lblModRequires = new JLabel("Mod Requires: (Select a mod first)");
        lblModRequires.setBounds(4, 127, 317, 16);
        panelLauncher.add(lblModRequires);

        btnLaunchIgnore = new JButton("Launch + ignore");
        btnLaunchIgnore.setToolTipText("Ignore any errors that McLauncher may not correctly account for.");
        btnLaunchIgnore.setFont(new Font("SansSerif", Font.PLAIN, 11));
        btnLaunchIgnore.setBounds(556, 284, 133, 23);
        panelLauncher.add(btnLaunchIgnore);

        JButton btnConsole = new JButton("Console");
        btnConsole.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boolean changeto = !con.isVisible();
                con.setVisible(changeto);
                con.updateConsole();
            }
        });
        btnConsole.setBounds(335, 0, 90, 28);
        panelLauncher.add(btnConsole);

        JPanel panelDownloadMods = new JPanel();
        tabbedPane.addTab("Download Mods", null, panelDownloadMods, null);
        panelDownloadMods.setLayout(null);

        scrollPane_3 = new JScrollPane();
        scrollPane_3.setBounds(0, 0, 397, 303);
        panelDownloadMods.add(scrollPane_3);

        dlModel = new DefaultTableModel(new Object[][] {},
                new String[] { "Mod Name", "Author", "Version", "Tags" }) {
            Class[] columnTypes = new Class[] { String.class, String.class, String.class, Object.class };

            public Class getColumnClass(int columnIndex) {
                return columnTypes[columnIndex];
            }

            boolean[] columnEditables = new boolean[] { false, false, false, false };

            public boolean isCellEditable(int row, int column) {
                return columnEditables[column];
            };
        };

        tSorter = new TableRowSorter<DefaultTableModel>(dlModel);
        tableDownloads = new JTable();
        tableDownloads.setRowSorter(tSorter);
        tableDownloads.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                trow = tableDownloads.getSelectedRow();
                trow = tableDownloads.getRowSorter().convertRowIndexToModel(trow);
                getDlModData();
                canDownloadMod = true;
            }
        });
        tableDownloads.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                trow = tableDownloads.getSelectedRow();
                trow = tableDownloads.getRowSorter().convertRowIndexToModel(trow);
                getDlModData();
                canDownloadMod = true;
            }
        });
        tableDownloads.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        tableDownloads.setShowVerticalLines(true);
        tableDownloads.setShowHorizontalLines(true);
        tableDownloads.setModel(dlModel);
        tableDownloads.getColumnModel().getColumn(0).setPreferredWidth(218);
        tableDownloads.getColumnModel().getColumn(1).setPreferredWidth(97);
        tableDownloads.getColumnModel().getColumn(2).setPreferredWidth(77);
        scrollPane_3.setViewportView(tableDownloads);

        btnDownload = new JButton("Download");
        btnDownload.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (canDownloadMod && !CurrentlyDownloading) {
                    String dlUrl = getModDownloadUrl();
                    try {
                        if (dlUrl.equals("") || dlUrl.equals(" ") || dlUrl == null) {
                            con.log("Log", "No download link for mod, got... '" + dlUrl + "'");
                        } else {
                            CurrentlyDownloading = true;
                            CurrentDownload = new Download(new URL(dlUrl), McLauncher);
                        }
                    } catch (MalformedURLException e1) {
                        con.log("Log", "Failed to download mod... No download URL?");
                    }
                }
            }
        });
        btnDownload.setBounds(307, 308, 90, 28);
        panelDownloadMods.add(btnDownload);

        btnGotoMod = new JButton("Mod Page");
        btnGotoMod.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                util.openWebpage(modPageUrl);
            }
        });
        btnGotoMod.setEnabled(false);
        btnGotoMod.setBounds(134, 308, 90, 28);
        panelDownloadMods.add(btnGotoMod);

        pBarDownloadMod = new JProgressBar();
        pBarDownloadMod.setBounds(538, 308, 150, 10);
        panelDownloadMods.add(pBarDownloadMod);

        pBarExtractMod = new JProgressBar();
        pBarExtractMod.setBounds(538, 314, 150, 10);
        panelDownloadMods.add(pBarExtractMod);

        lblDownloadModInfo = new JLabel("Download progress");
        lblDownloadModInfo.setBounds(489, 326, 199, 16);
        panelDownloadMods.add(lblDownloadModInfo);
        lblDownloadModInfo.setHorizontalAlignment(SwingConstants.TRAILING);

        panelModImg = new JPanel();
        panelModImg.setBounds(566, 0, 128, 128);
        panelDownloadMods.add(panelModImg);

        txtFilterText = new JTextField();
        txtFilterText.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                if (txtFilterText.getText().equals("Filter Text")) {
                    txtFilterText.setText("");
                    newFilter();
                }
            }
        });
        txtFilterText.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if (!txtFilterText.getText().equals("Filter Text")) {
                    newFilter();
                }
            }
        });
        txtFilterText.setText("Filter Text");
        txtFilterText.setBounds(0, 308, 122, 28);
        panelDownloadMods.add(txtFilterText);
        txtFilterText.setColumns(10);

        comboBox = new JComboBox();
        comboBox.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                newFilter();
            }
        });
        comboBox.setModel(new DefaultComboBoxModel(new String[] { "No tag filter", "Vanilla", "Machine", "Mechanic",
                "New Ore", "Module", "Big Mod", "Power", "GUI", "Map-Gen", "Must-Have", "Equipment" }));
        comboBox.setBounds(403, 44, 150, 26);
        panelDownloadMods.add(comboBox);

        lblModDlCounter = new JLabel("Mod database: ");
        lblModDlCounter.setFont(new Font("SansSerif", Font.PLAIN, 10));
        lblModDlCounter.setBounds(403, 5, 162, 16);
        panelDownloadMods.add(lblModDlCounter);

        txtrDMModDescription = new JTextArea();
        txtrDMModDescription.setBackground(Color.LIGHT_GRAY);
        txtrDMModDescription.setBorder(new LineBorder(new Color(0, 0, 0)));
        txtrDMModDescription.setFocusable(false);
        txtrDMModDescription.setEditable(false);
        txtrDMModDescription.setLineWrap(true);
        txtrDMModDescription.setWrapStyleWord(true);
        txtrDMModDescription.setText("Mod Description: ");
        txtrDMModDescription.setBounds(403, 132, 285, 75);
        panelDownloadMods.add(txtrDMModDescription);

        lblDMModTags = new JTextArea();
        lblDMModTags.setFocusable(false);
        lblDMModTags.setEditable(false);
        lblDMModTags.setBorder(new LineBorder(new Color(0, 0, 0)));
        lblDMModTags.setWrapStyleWord(true);
        lblDMModTags.setLineWrap(true);
        lblDMModTags.setBackground(Color.LIGHT_GRAY);
        lblDMModTags.setText("Mod Tags: ");
        lblDMModTags.setBounds(403, 71, 160, 60);
        panelDownloadMods.add(lblDMModTags);

        lblDMRequiredMods = new JTextArea();
        lblDMRequiredMods.setFocusable(false);
        lblDMRequiredMods.setEditable(false);
        lblDMRequiredMods.setText("Required Mods: ");
        lblDMRequiredMods.setWrapStyleWord(true);
        lblDMRequiredMods.setLineWrap(true);
        lblDMRequiredMods.setBorder(new LineBorder(new Color(0, 0, 0)));
        lblDMRequiredMods.setBackground(Color.LIGHT_GRAY);
        lblDMRequiredMods.setBounds(403, 208, 285, 57);
        panelDownloadMods.add(lblDMRequiredMods);

        lblDLModLicense = new JLabel("");
        lblDLModLicense.setHorizontalAlignment(SwingConstants.RIGHT);
        lblDLModLicense.setBounds(403, 294, 285, 16);
        panelDownloadMods.add(lblDLModLicense);

        lblWipmod = new JLabel("");
        lblWipmod.setBounds(395, 314, 64, 16);
        panelDownloadMods.add(lblWipmod);

        JButton btnCancel = new JButton("Cancel");
        btnCancel.setToolTipText("Stop downloading");
        btnCancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                CurrentDownload.cancel();
            }
        });
        btnCancel.setBounds(230, 308, 72, 28);
        panelDownloadMods.add(btnCancel);

        panelOptions = new JPanel();
        tabbedPane.addTab("Options", null, panelOptions, null);
        panelOptions.setLayout(null);
        scrollPane_4.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane_4.setBounds(0, 0, 694, 342);
        panelOptions.add(scrollPane_4);

        panel = new JPanel();
        scrollPane_4.setViewportView(panel);
        panel.setLayout(null);

        lblCloseMclauncherAfter = new JLabel("Close McLauncher after launching Factorio?");
        lblCloseMclauncherAfter.setBounds(6, 6, 274, 16);
        panel.add(lblCloseMclauncherAfter);

        lblCloseMclauncherAfter_1 = new JLabel("Close McLauncher after updating?");
        lblCloseMclauncherAfter_1.setBounds(6, 34, 274, 16);
        panel.add(lblCloseMclauncherAfter_1);

        lblSortNewestDownloadable = new JLabel("Sort newest downloadable mods first?");
        lblSortNewestDownloadable.setBounds(6, 62, 274, 16);
        panel.add(lblSortNewestDownloadable);

        tglbtnNewModsFirst = new JToggleButton("Toggle");
        tglbtnNewModsFirst.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                lblNeedrestart.setText("McLauncher needs to restart for that to work");
                writeData();
            }
        });
        tglbtnNewModsFirst.setSelected(true);
        tglbtnNewModsFirst.setBounds(281, 56, 66, 28);
        panel.add(tglbtnNewModsFirst);

        tglbtnCloseAfterUpdate = new JToggleButton("Toggle");
        tglbtnCloseAfterUpdate.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                writeData();
            }
        });
        tglbtnCloseAfterUpdate.setBounds(281, 28, 66, 28);
        panel.add(tglbtnCloseAfterUpdate);

        tglbtnCloseAfterLaunch = new JToggleButton("Toggle");
        tglbtnCloseAfterLaunch.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                writeData();
            }
        });
        tglbtnCloseAfterLaunch.setBounds(281, 0, 66, 28);
        panel.add(tglbtnCloseAfterLaunch);

        tglbtnDisplayon = new JToggleButton("On");
        tglbtnDisplayon.setFont(new Font("SansSerif", Font.PLAIN, 10));
        tglbtnDisplayon.setSelected(true);
        tglbtnDisplayon.setBounds(588, 308, 44, 28);
        panel.add(tglbtnDisplayon);

        tglbtnDisplayoff = new JToggleButton("Off");
        tglbtnDisplayoff.setFont(new Font("SansSerif", Font.PLAIN, 10));
        tglbtnDisplayoff.setBounds(644, 308, 44, 28);
        panel.add(tglbtnDisplayoff);

        lblInfo = new JLabel("What enabled and disabled look like");
        lblInfo.setFont(new Font("SansSerif", Font.PLAIN, 10));
        lblInfo.setHorizontalAlignment(SwingConstants.TRAILING);
        lblInfo.setBounds(359, 314, 231, 16);
        panel.add(lblInfo);

        JSeparator separator = new JSeparator();
        separator.setBounds(6, 55, 676, 24);
        panel.add(separator);

        JSeparator separator_1 = new JSeparator();
        separator_1.setBounds(6, 27, 676, 18);
        panel.add(separator_1);

        JSeparator separator_2 = new JSeparator();
        separator_2.setBounds(6, 84, 676, 24);
        panel.add(separator_2);

        JSeparator separator_3 = new JSeparator();
        separator_3.setOrientation(SwingConstants.VERTICAL);
        separator_3.setBounds(346, 0, 16, 336);
        panel.add(separator_3);

        lblNeedrestart = new JLabel("");
        lblNeedrestart.setBounds(6, 314, 341, 16);
        panel.add(lblNeedrestart);

        tglbtnSendAnonData = new JToggleButton("Toggle");
        tglbtnSendAnonData.setToolTipText("Information regarding the activity of McLauncher.");
        tglbtnSendAnonData.setSelected(true); //set enabled by default.
        tglbtnSendAnonData.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                writeData();
            }
        });
        tglbtnSendAnonData.setBounds(622, 0, 66, 28);
        panel.add(tglbtnSendAnonData);

        lblSendAnonymousUse = new JLabel("Send anonymous use data?");
        lblSendAnonymousUse.setBounds(359, 6, 251, 16);
        panel.add(lblSendAnonymousUse);

        lblDeleteOldMod = new JLabel("Delete old mod before updating?");
        lblDeleteOldMod.setBounds(359, 34, 251, 16);
        panel.add(lblDeleteOldMod);

        tglbtnDeleteBeforeUpdate = new JToggleButton("Toggle");
        tglbtnDeleteBeforeUpdate.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                writeData();
            }
        });
        tglbtnDeleteBeforeUpdate.setBounds(622, 28, 66, 28);
        panel.add(tglbtnDeleteBeforeUpdate);

        tglbtnAlertOnModUpdateAvailable = new JToggleButton("Toggle");
        tglbtnAlertOnModUpdateAvailable.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                writeData();
            }
        });
        tglbtnAlertOnModUpdateAvailable.setSelected(true);
        tglbtnAlertOnModUpdateAvailable.setBounds(281, 86, 66, 28);
        panel.add(tglbtnAlertOnModUpdateAvailable);

        separator_4 = new JSeparator();
        separator_4.setBounds(0, 112, 676, 24);
        panel.add(separator_4);

        JLabel lblAlertModHas = new JLabel("Alert mod has update on launch?");
        lblAlertModHas.setBounds(6, 92, 231, 16);
        panel.add(lblAlertModHas);

        panelChangelog = new JPanel();
        tabbedPane.addTab("Changelog", null, panelChangelog, null);
        panelChangelog.setLayout(new BoxLayout(panelChangelog, BoxLayout.X_AXIS));

        scrollPane_6 = new JScrollPane();
        panelChangelog.add(scrollPane_6);

        textChangelog = new JTextArea();
        scrollPane_6.setViewportView(textChangelog);
        textChangelog.setEditable(false);
        textChangelog.setText(
                "v0.4.6\r\n\r\n+Fix problem where config file would not save in the correct location. (Thanks Arano-kai)\r\n+McLauncher will now save when you select a path, or profile. (Thanks Arano-kai)\r\n+Fixed an issue where McLauncher could not get the version from a .zip mod. (Thanks Arano-kai)\r\n+Added a Cancel button to stop downloading the current mod. (Suggested by  Arano-kai)\r\n\r\n\r\n\r\nv0.4.5\r\n\r\n+McLauncher should now correctly warn you on failed write/read access.\r\n+McLauncher should now work when a user has both zip and installer versions of factorio. (Thanks Jeroon)\r\n+Attempt to fix an error with dependency and .zip files, With versions. (Thanks Arano-kai)\r\n+Fix only allow single selection of mods. (Thanks Arano-kai)\r\n+Fix for the Launch+Ignore button problem on linux being cut off. (Thanks Arano-kai)\r\n+Display download progress.\r\n+Fixed an error that was thrown when clicking on some mods that had a single dependency mod.\r\n+Double clicking on a mod will now enable or disable the mod. (Thanks Arano-kai)");
        btnLaunchIgnore.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (selProfile != null) {
                    LaunchFactorioWithSelectedMods(true); //ignore errors, launch.
                }
            }
        });
        btnLaunchIgnore.setVisible(false);
        //This is my test button. I use this to test things then implement them into the swing.
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                testButtonCode(e);
            }
        });
        //Disable mods button. (from profile)
        btnDisable.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                removeMod();
            }
        });
        //Enable mods button. (to profile)
        btnEnable.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                addMod();
            }
        });
        //Game path button
        btnFind.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                findPath();
            }
        });
        //mouseClick event lister for when you click on a new profile
        profileList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                selectedProfile();
            }
        });

        readData(); //Load settings

        init(); //some extra init
        getMods(); //Get the mods the user has installed
    }

    protected void testButtonCode(ActionEvent e) {
        boolean changeto = !con.isVisible();
        con.setVisible(changeto);
        //con.txtConsole.setText("Wall Of Text \n Test2?");

    }

    protected String getModDownloadUrl() {
        String[] modData = dlModList[trow].split("~"); //trow = Download Mods Table Row
        return modData[7]; //Return mod data
    }

    protected void findModInDownloads(String string) {
        boolean foundit = false;
        for (int i = 0; i < dlModList.length; i++) {
            String[] modData = dlModList[i].split("~");
            if (modData[0].toLowerCase().equals(string)) {
                foundit = true;
                dlModListSelected = dlModList[i];
                break;
            }
        }
    }

    //0modname, 1author, 2version, 3mod_tags, 4description, 5required_mods, 
    //6updates, 7download_url, 8update_url, 9icon_url, 10downloads, 11mod_page, 12copyright

    protected void getDlModData() { //Display the data from the mod selected in Download Mods
        String[] modData = dlModList[trow].split("~");
        lblDMModTags.setText("Mod Tags: " + modData[3]);
        //Set description text size smaller if too large
        if (util.getStringWidth("Mod Description: " + modData[4]) > 800) {
            txtrDMModDescription.setFont(new Font("SansSerif", Font.PLAIN, 10));
        } else
            txtrDMModDescription.setFont(new Font("SansSerif", Font.PLAIN, 12));
        con.log("Log", "Selected downloadable mod " + modData[0]);

        txtrDMModDescription.setText("Mod Description: " + modData[4]);
        lblDMRequiredMods.setText("Required Mods: " + modData[5]);

        if (modData[9].equals("")) {
            setModImg("http://www.mclama.com/Downloads/ModIcon.png");
            //If we found no icon, use the default icon provided.
        } else
            setModImg(modData[9]);
        if (!modData[11].equals("")) {
            try { //If mod has a mod page, enable the button and set the URL
                modPageUrl = new URL(modData[11]);
                btnGotoMod.setEnabled(true);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        } else
            btnGotoMod.setEnabled(false); //If we don't have a mod page, disable the button.
        if (!modData[12].equals(" ")) { //Show a license for the mod.
            String str = modData[0] + " is under the " + modData[12] + " license.";
            if (util.getStringWidth(str) > 280) {
                lblDLModLicense.setFont(new Font("SansSerif", Font.PLAIN, 10));
            } else
                lblDLModLicense.setFont(new Font("SansSerif", Font.PLAIN, 12));
            lblDLModLicense.setText(str);
        } else
            lblDLModLicense.setText(""); //If no license, Then don't show.
        //Show the user that this is a WIP mod before downloading
        if (modData[3].contains("WIP"))
            lblWipmod.setText("WIP Mod!");
        else
            lblWipmod.setText("");

        //if user already has mod, Consider this mod an update
        if (util.IsModInstalled(modData[0])) {
            btnDownload.setText("Update");
        } else
            btnDownload.setText("Download"); //if not, its a new download.

    }

    private void newFilter() { //Filter the mods in Download Mods based off mod tags and search filter
        String regex = "";
        String comboText = "";
        if (!txtFilterText.getText().equals("Filter Text")) {
            regex = "(?i)" + Pattern.quote(txtFilterText.getText().toLowerCase());
        }
        if (!comboBox.getSelectedItem().toString().equals("No tag filter")) {
            comboText = "(?i)" + Pattern.quote(comboBox.getSelectedItem().toString());
        }
        List<RowFilter<Object, Object>> rfs = new ArrayList<RowFilter<Object, Object>>(2);
        rfs.add(RowFilter.regexFilter(regex, 0, 1, 2));
        rfs.add(RowFilter.regexFilter(comboText, 3));

        RowFilter<DefaultTableModel, Object> rf = null;
        //If current expression doesn't parse, don't update.
        try {
            //rf = RowFilter.regexFilter(regex,0,1,2,3);
            rf = RowFilter.andFilter(rfs);
        } catch (java.util.regex.PatternSyntaxException e) {
            return;
        }
        tSorter.setRowFilter(rf);
    }

    private void init() {
        util = new Utility(this);
        try {
            String out = new Scanner(new URL(McCheckVersionPath).openStream(), "UTF-8").useDelimiter("\\A").next();
            con.log("Log", "McLauncher version: " + McVersion + " Checked version: " + out);
            //Compare versions
            if (!util.testSameVersion(out, McVersion) && util.newerVersion(out, McVersion)) {
                btnUpdate.setVisible(true);
                //Disabled for now
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //Statistics so i can see usage
        util.noteInfo("Launch");
        //Set default mod icon to show
        setModImg("http://www.mclama.com/Downloads/ModIcon.png");
        //Hide the tags from the jTable so we can sort through it later
        int tindex = tableDownloads.getColumnModel().getColumnIndex("Tags");
        TableColumn col = tableDownloads.getColumnModel().getColumn(tindex);
        tableDownloads.getColumnModel().removeColumn(col);

        //0modname, 1author, 2version, 3mod_tags, 4description, 5required_mods, 
        //6updates, 7download_url, 8update_url, 9icon_url, 10downloads, 11mod_page, 12copyright

        setModDownloads();

        /*
         * Options
         */
        if (tglbtnNewModsFirst.isSelected())
            Collections.reverse(Arrays.asList(dlModList)); //reverse our list of mods data.
    }

    private void setModDownloads() {
        String downloadModList = util.getModDownloads();
        dlModList = downloadModList.split(";");
        con.log("Log", "Downloadable mods found... " + dlModList.length);
        lblModDlCounter.setText("Downloadable mod Count: " + dlModList.length);

        if (tglbtnNewModsFirst.isSelected()) {
            for (int i = dlModList.length - 1; i > -1; i--) {
                addModDownloadToModel(i);
            }
        } else {
            for (int i = 0; i < dlModList.length; i++) {
                addModDownloadToModel(i);
            }
        }
    }

    private void addModDownloadToModel(int i) {
        String[] modData = dlModList[i].split("~");
        dlModel.addRow(new Object[] { modData[0].toLowerCase(), modData[1], modData[2], modData[3] });
    }

    private void setModImg(String modImgPath) {
        try {
            modImg = util.scanImage(modImgPath);
            panelModImg.removeAll();
            panelModImg.add(new JLabel(new ImageIcon(modImg)));
        } catch (IOException e) {
            con.log("Error", "Failed to set mod image.");
            e.printStackTrace();
        }
    }

    protected void checkDependency(JList<String> list) {
        checkDependency(util.getModDependency(list.getSelectedValue()).replace("[", "").replace("]", "")
                .replace("\"", "").split(","));
    }

    protected String checkDependency(String[] depen) {
        String requiredMods = "";
        if (depen[0] != "") { //if it does not equal nothing.
            //con.log("Log","Dependency.. " + depen[0] + "-------" + depen[1]);
            for (int i = 0; i < depen.length; i++) {
                boolean optmod = false;
                boolean reqmv = false; //required mod version
                String[] optstr = null;
                String[] reqstr = null;
                byte optbyte = 0;
                if (depen[i].contains("?")) {
                    //Optional mod
                    optmod = true;
                    optstr = depen[i].split("\\? ");//0 will always be empty
                    //con.log("Log",optstr[0]); //0 will always be empty
                    //con.log("Log",optstr[1]);
                }
                if (depen[i].contains(">=")) {
                    //requires mod over version
                    reqmv = true;
                    if (optmod) {
                        reqstr = optstr[1].split(" >= ");
                    } else
                        reqstr = depen[i].split(" >= ");
                    //con.log("Log",reqstr[0]);
                    //con.log("Log",reqstr[1]);
                } else {
                    //just requires mod
                }
                String depenMod = "";
                if (optmod) { //optional mod
                    if (reqmv) { //required mod version
                        depenMod = reqstr[0];
                    } else
                        depenMod = optstr[1];
                }
                if (reqmv) {
                    String depenVers = reqstr[1];
                    if (!optmod)
                        depenMod = reqstr[0];
                    String requireStr = ("Requires mod " + depenMod + " with version " + depenVers + ".")
                            .replace("mod base", "Factorio");
                    if (optmod) {
                        requireStr.replace("Requires ", "Optional mod requires ");
                    }
                    con.log("Debug", requireStr);
                    if (!modIsEnabled(depenMod)) {//if mod is not already enabled
                        if (!optmod) { //if its not a optional mod
                            requiredMods += (depenMod + "_" + depenVers + ", ");
                        }
                    } else { //else if mod IS enabled
                        if (optmod) { //if mod is optional
                            if (util.newerVersion(util.getModVersion(depenMod), depenVers)
                                    || util.testSameVersion(util.getModVersion(depenMod), depenVers)) {
                                //If installed version is newer or same version than required
                                //then do nothing
                            } else {//else we require the mod
                                requiredMods += (depenMod + "_" + depenVers + ", ");
                            }
                        } else if (util.newerVersion(util.getModVersion(depenMod), depenVers)
                                || util.testSameVersion(util.getModVersion(depenMod), depenVers)) {
                            //If installed version is newer or same version than required
                            //then do nothing
                        } else {//else we require the mod
                            requiredMods += (depenMod + "_" + depenVers + ", ");
                        }
                    }
                } else { //if it does not require mod version
                    //con.log("Log",depen[i]);
                    if (!modIsEnabled(depen[i])) {
                        requiredMods += depen[i] + ", ";
                    }
                }
            } //end for loop
            if (requiredMods.equals(", ") || requiredMods.equals(""))
                requiredMods = "None or Already enabled";
            lblModRequires.setText("Mod Requires: " + requiredMods);
            return requiredMods;
        } else
            con.log("Debug", "no mod dependency");
        lblModRequires.setText("Mod Requires: None or Already enabled");
        return "None or Already enabled";
    }

    private boolean modIsEnabled(String depenMod) {
        for (int i = selProfile.mods.size() - 1; i >= 0; i--) {
            String p = selProfile.mods.get(i);
            if (util.remVer(p).toLowerCase().equals(util.remVer(depenMod).toLowerCase())) { //if the mod is enabled
                return true;
            }
        }
        return false;
    }

    protected void deleteProfile() {
        String str = (String) profileList.getSelectedValue();
        profileListMdl.removeAllElements();

        for (int i = 0; i <= profiles.size() - 1; i++) {
            Profile p = profiles.get(i);
            if (p.name.equals(str)) {
                selProfile = null;
                profiles.remove(i); //remove profile from list
                con.log("Log", "Deleted profile... " + str);
                writeData();
            } else {
                profileListMdl.addElement(p.name);
            }
        }
    }

    protected void renameProfile() {
        boolean doit = true;
        for (int i = profiles.size() - 1; i >= 0; i--) {
            Profile p = profiles.get(i);
            if (p.name.equals(txtProfile.getText())) {
                doit = false; //if we find a duplicate name, don't do it!
            }
        }
        if (doit) {
            selProfile.name = txtProfile.getText();
            profileListMdl.removeAllElements();

            for (int i = 0; i <= profiles.size() - 1; i++) {
                Profile p = profiles.get(i);
                profileListMdl.addElement(p.name);
                if (p.name.equals(txtProfile.getText())) {
                    profileList.setSelectedValue(profileListMdl.lastElement(), true);
                    selectedProfile();
                    con.log("Log", "Renamed profile");
                    writeData();
                }
            }
        }
    }

    private void selectedProfile() {
        try {
            String name = profileList.getSelectedValue().toString();
            con.log("Log", "Selected profile " + name);
            for (int i = profiles.size() - 1; i >= 0; i--) {
                Profile p = profiles.get(i);
                if (name.equals(p.name)) {
                    selProfile = p; //selected profile
                    i = 0;
                    lblModsEnabled.setText("Enabled Mods: " + selProfile.mods.size());
                    updateEnabledMods();
                }
            }
            writeData();
        } catch (Exception e) {
            con.log("Warning", "Failed to select the previously selected profile on launch");
        }
    }

    protected void removeMod() {
        if (enabledModsList.getSelectedValue() == null) {
            con.log("Log", "please select a mod to remove first");
        } else { //No problems, lets go
            String str = enabledModsList.getSelectedValue().toString();
            selProfile.removeMod(str);
            updateEnabledMods();
        }
        writeData();
    }

    protected void addMod() {
        if (modsList.getSelectedValue() == null) {
            con.log("Log", "please select a mod to add first");
        } else if (profileList.getSelectedValue() == null) {
            con.log("Log", "please select a profile before adding a mod");
        } else { //No problems, lets go
            String str = modsList.getSelectedValue().toString();

            if (selProfile.findMod(str)) {
                selProfile.mods.add(str);
                updateEnabledMods();
            }
        }
        writeData();
    }

    protected void updateEnabledMods() {
        ModListModel.removeAllElements();
        for (int i = selProfile.mods.size() - 1; i >= 0; i--) {
            String p = selProfile.mods.get(i);
            ModListModel.addElement(p);
        }
        lblModsEnabled.setText("Enabled Mods: " + selProfile.mods.size());
    }

    protected void newProfile(String text) {
        boolean doit = true;
        for (int i = profiles.size() - 1; i >= 0; i--) {
            Profile p = profiles.get(i);
            if (p.name.equals(text)) {
                doit = false;
            }
        }
        if (doit && text.length() > 2) {
            profiles.add(new Profile(text, "base"));
            profileListMdl.addElement(text);
            txtProfile.setText("Profile" + (profiles.size() + 1));
        }
        writeData();
    }

    @SuppressWarnings("unchecked")
    private void readData() {
        JSONParser parser = new JSONParser();

        try {

            Object obj = parser.parse(new FileReader(workDir + "/McLauncher.json"));
            con.log("Log", "Running McLauncher from "
                    + new File(ModManager.class.getProtectionDomain().getCodeSource().getLocation().getPath()));

            JSONObject jsonObject = (JSONObject) obj;

            JSONArray msg = (JSONArray) jsonObject.get("data");
            Iterator<JSONObject> iterator = msg.iterator();
            JSONObject factObj = iterator.next();

            gamePath = (String) factObj.get("path");
            if (gamePath == null)
                gamePath = (String) parser.parse(new FileReader("."));
            txtGamePath.setText(gamePath);
            con.log("Log", "game path set to..." + gamePath);
            checkAccess();

            validatePath();
            con.log("Log", "Using mod path " + modPath);

            String lastProfile = (String) factObj.get("lastprofile");
            //Options tab
            try {
                tglbtnNewModsFirst.setSelected((boolean) factObj.get("newModsFirst"));
                tglbtnCloseAfterLaunch.setSelected((boolean) factObj.get("closeAfterLaunch"));
                tglbtnCloseAfterUpdate.setSelected((boolean) factObj.get("closeAfterUpdate"));
                tglbtnSendAnonData.setSelected((boolean) factObj.get("sendAnonData"));
                tglbtnDeleteBeforeUpdate.setSelected((boolean) factObj.get("deleteBeforeUpdate"));
                tglbtnAlertOnModUpdateAvailable.setSelected((boolean) factObj.get("AlertOnModUpdateAvailable"));
            } catch (Exception e1) {
                con.log("Warning", "Failed to read some data. Was McLauncher updated?");
            }

            msg = (JSONArray) jsonObject.get("profiles");
            iterator = msg.iterator();
            int count = 0;
            factObj = iterator.next();

            msg = (JSONArray) factObj.get("profile" + count);
            Iterator<JSONObject> iter = msg.iterator();
            while (iter.hasNext()) {
                JSONObject pObj = iter.next();

                String name = (String) pObj.get("name");
                String mods = (String) pObj.get("mods");
                con.log("Log", "Profile: " + name + " with " + mods + " loaded.");

                Profile pfile = new Profile(name, null);
                profiles.add(pfile);
                profileListMdl.addElement(name);
                //Lets add its mods now
                //con.log("Log",mods.replace("[", "").replace("]", ""));
                String[] pmods = mods.replace("[", "").replace("]", "").split(", ");
                for (int i = 0; i < pmods.length; i++) {
                    pfile.mods.add(pmods[i]);
                }

                try {
                    if (lastProfile.equals(name)) {
                        profileList.setSelectedValue(profileListMdl.lastElement(), true);
                        selectedProfile();
                    }
                } catch (Exception e) {
                    con.log("Error", " Last profile was null");
                }

                count++;
                try {
                    msg = (JSONArray) factObj.get("profile" + count);
                    iter = msg.iterator();
                } catch (NullPointerException e) {
                    con.log("Log", count + " profiles loaded.");
                    break;
                }
            }
            txtProfile.setText("Profile" + (profiles.size() + 1));

        } catch (FileNotFoundException e) {
            con.log("Log", "McLauncher.json not found, Saving first-time info.");
            newProfile("First profile");
            profileList.setSelectedValue(profileListMdl.lastElement(), true);
            selectedProfile();
            writeData();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();

        }
    }

    @SuppressWarnings("unchecked")
    private void writeData() {
        JSONObject obj = new JSONObject();
        JSONArray dlist = new JSONArray();
        JSONObject data = new JSONObject();
        //Data
        data.put("path", gamePath);
        data.put("lastprofile", profileList.getSelectedValue());
        //Options
        data.put("newModsFirst", tglbtnNewModsFirst.isSelected());
        data.put("closeAfterLaunch", tglbtnCloseAfterLaunch.isSelected());
        data.put("closeAfterUpdate", tglbtnCloseAfterUpdate.isSelected());
        data.put("sendAnonData", tglbtnSendAnonData.isSelected());
        data.put("deleteBeforeUpdate", tglbtnDeleteBeforeUpdate.isSelected());
        data.put("AlertOnModUpdateAvailable", tglbtnAlertOnModUpdateAvailable.isSelected());
        //put list into data
        dlist.add(data);

        obj.put("data", dlist);

        //Profiles
        JSONArray profileList = new JSONArray(); //profiles list
        JSONObject profileListObj = new JSONObject(); //profiles
        JSONArray profileDataList = new JSONArray(); //profile data list
        JSONObject profileData = new JSONObject(); //profile data

        //We now have to cycle every profile and create the data, then add it to the list.
        for (int i = profiles.size() - 1; i >= 0; i--) {
            Profile p = profiles.get(i);
            profileData = new JSONObject();
            profileDataList = new JSONArray();
            profileData.put("name", p.name);
            profileData.put("mods", p.mods.toString());
            //con.log("Log",p.name + "..." + p.mods.toString());
            profileDataList.add(profileData);
            //con.log("Log","list.." + profileDataList.toString());
            profileListObj.put("profile" + i, profileDataList);
        }
        profileList.add(profileListObj);
        obj.put("profiles", profileList);

        try {
            FileWriter file = new FileWriter(workDir + "/McLauncher.json");
            file.write(obj.toJSONString());
            file.flush();
            file.close();
        } catch (IOException e) {
            con.log("Severe", "FAILED to save McLauncher data");
            e.printStackTrace();
        }
        con.log("Log", "Saved McLauncher data.");
    }

    private void getModCount() {
        if (modPath == null) {
            getMods();
        } else if (modDir != null) {
            modCount = modDir.length + 1; //+1 for base
            con.log("Log", "Available Mods: " + modCount);
            lblAvailableMods.setText("Available Mods: " + modCount);
        }
    }

    protected void getMods() {
        if (modPath != null) {
            File file = new File(modPath);
            modDir = file.list(new FilenameFilter() {
                @Override
                public boolean accept(File current, String name) {
                    File mfile = new File(current, name);
                    if (name.endsWith(".zip") || mfile.isDirectory())
                        return true;
                    else
                        return false;
                }
            });
            //con.log("Log",Arrays.toString(modDir));
            getModCount();
            getCurrentMods();
        }
    }

    public void findPath() {
        chooser = new JFileChooser();
        if (gamePath != null)
            chooser.setCurrentDirectory(new java.io.File(gamePath));
        else
            chooser.setCurrentDirectory(new java.io.File("."));
        chooser.setDialogTitle(choosertitle);
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        chooser.setAcceptAllFileFilterUsed(false);

        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            con.log("Log", "getCurrentDirectory(): " + chooser.getCurrentDirectory());

            con.log("Log", "getSelectedFile() : " + chooser.getSelectedFile());

            gamePath = "" + chooser.getSelectedFile();
            if (gamePath.endsWith("/mods")) {
                gamePath = gamePath.replace("/mods", "");
            }
            con.log("Log", gamePath);
            txtGamePath.setText(gamePath);

            validatePath();
            checkAccess();
            getMods();
            writeData();
        } else {
            con.log("Log", "No Selection ");
        }
    }

    private void getCurrentMods() {
        listModel.removeAllElements();
        String updateableMods = "";
        if (modDir != null) {
            listModel.addElement("base");
            for (int i = 0; i < modDir.length; i++) {
                listModel.addElement(modDir[i]);
                String modname = modDir[i];
                if (tglbtnAlertOnModUpdateAvailable.isSelected()) {
                    updateableMods += util.checkModForUpdate(modname);
                } //Check if the mod has an update
            }
            if (tglbtnAlertOnModUpdateAvailable.isSelected()) {
                if (!updateableMods.equals(""))
                    JOptionPane.showMessageDialog(null, "The following mods can be updated.\n" + updateableMods);
            }
        } else {
            listModel.addElement("Please select a Game Path.");
            listModel.addElement("An example would be:");
            listModel.addElement("G:/Games/Factorio_0.9.8.9400");
        }

    }

    private void checkAccess() {
        boolean tested = true;
        if (!(gamePath.equals("") | gamePath == null)) {
            File f = new File(gamePath); //Check if we have read and write access
            if (!f.canWrite() & !f.canRead()) {
                tested = false; //if no access
                con.log("Severe", "Failed to write and read");
            }
        }

        try {
            FileWriter file = new FileWriter(gamePath + "/testingmclauncherwriteacess.txt");
            file.write("Testing Mclauncher");
            file.close();
        } catch (IOException e) {
            tested = false; //failed to write
            con.log("Severe", "Failed write access.");
        }

        if (!new File(gamePath + "/testingmclauncherwriteacess.txt").exists()) {
            tested = false; //failed to read
            con.log("Severe", "Failed read access.");

        } else
            new File(gamePath + "/testingmclauncherwriteacess.txt").delete();

        if (tested) {
            con.log("Log", "Access to read and write allowed.");
        } else {
            JOptionPane.showMessageDialog(null,
                    "McLauncher does not have access to read or write files.\nMclauncher will not work without access.");
            //System.exit(0);
        }
    }

    public void validatePath() {
        String testPath = gamePath;
        if (testPath.equals("")) {
            JOptionPane.showMessageDialog(null, "Invalid game path.");
        } else { //If there is atleast some form of path here
            if (new File(testPath + "/data").isDirectory()) {
                //Correct factorio path
                if (new File(testPath + "/config.lnk").exists()) {
                    //Then we are using the installer.
                    modPath = System.getenv("APPDATA") + "/factorio/mods/";
                    con.log("Log", "Using installer factorio.");
                } else
                    modPath = testPath + "/mods/";
                con.log("Log", "Using zip factorio.");
            } else
                JOptionPane.showMessageDialog(null, "Invalid game path.");
        }
    }

    @SuppressWarnings("unchecked")
    protected void LaunchFactorioWithSelectedMods(boolean ignore) {
        if (enabledModsDependencyCheck(ignore) || ignore) {
            if (selProfile.mods.size() != 0) {
                JSONObject obj = new JSONObject();
                JSONArray dlist = new JSONArray();
                JSONObject data = new JSONObject();
                //Data
                for (int i = selProfile.mods.size() - 1; i >= 0; i--) {
                    String str = selProfile.mods.get(i);
                    data = new JSONObject();
                    data.put("name", util.remVer(str.replace(".zip", "")));
                    data.put("enabled", "true");
                    dlist.add(data);
                }

                obj.put("mods", dlist);

                try {
                    FileWriter file = new FileWriter(modPath + "mod-list.json");
                    file.write(obj.toJSONString());
                    file.flush();
                    file.close();
                    con.log("Log", "Saved mod-list.json    " + modPath + "mod-list.json");
                } catch (IOException e) {
                    con.log("Severe", "Failed to save mod-list.json, Maybe no write access?");
                }
            }

            File fWindows = new File(gamePath + "/bin/win32/Factorio.exe");
            File fWindows64 = new File(gamePath + "/bin/x64/Factorio.exe");
            File fLinux = new File(gamePath + "/bin/i386/factorio");
            File fLinux64 = new File(gamePath + "/bin/x64/factorio");
            File fMac = new File(gamePath);

            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                if (fWindows.exists()) {
                    runGame(fWindows.toString());
                } else if (fWindows64.exists()) {
                    runGame(fWindows64.toString());
                }
            } else if (System.getProperty("os.name").toLowerCase().contains("linux")) {
                if (fLinux.exists()) {
                    runGame(fLinux.toString());
                } else if (fLinux64.exists()) {
                    runGame(fLinux64.toString());
                }
            } else if (System.getProperty("os.name").toLowerCase().contains("mac")) {
                if (fMac.exists()) {
                    runGame(fMac.toString());
                }
            }

            con.log("Log", "Launching game with profile..." + selProfile.name);
            //Statistics
            util.noteInfo("GameLaunch");
            if (tglbtnCloseAfterLaunch.isSelected()) { //Option 
                System.exit(0); //close launcher if enabled
            }
        }
    }

    private boolean enabledModsDependencyCheck(boolean ignore) {
        String checkStr = "";
        boolean canLaunch = true;
        for (int i = selProfile.mods.size() - 1; i >= 0; i--) {
            String p = selProfile.mods.get(i);
            //String str = checkDependency(p);
            String str = checkDependency(
                    util.getModDependency(p).replace("[", "").replace("]", "").replace("\"", "").split(","));
            if (!str.equals("") && !str.equals("None or Already enabled")) {
                String[] str2 = str.split("_");
                if (!checkStr.contains(str2[0])) {
                    checkStr += str2[0] + "\n";
                }
            }
        }
        if (!checkStr.equals("") && !checkStr.equals("None or Already enabled")) {
            canLaunch = false;
            con.log("Severe", "Launch failure with.. " + checkStr);
            if (!ignore) {
                JOptionPane.showMessageDialog(null,
                        "You must enable the following mods before this profile can be launched\n" + checkStr);
            }
            lblRequiredMods.setText("Required Mods: " + checkStr.replace("\\n", ", "));
            btnLaunchIgnore.setVisible(true);
        }
        return canLaunch;
    }

    @SuppressWarnings("unused")
    private void runGame(String string) {
        try {
            con.log("Log", "Launched game at... " + string);
            Process pFactorio = Runtime.getRuntime().exec(string);
        } catch (IOException e) {
            con.log("Severe", "Failed to launch game at " + string);
        }
    }
}