[mupen64plus-qt] 01/03: Imported Upstream version 1.8

Andreas Tille tille at debian.org
Mon Jan 11 21:26:21 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository mupen64plus-qt.

commit 8b188b4f3602644d36b50cc8d0f18d4430946370
Author: Andreas Tille <tille at debian.org>
Date:   Mon Jan 11 22:21:53 2016 +0100

    Imported Upstream version 1.8
---
 .gitignore                       |    1 +
 LICENSE                          |   27 +
 README.md                        |  123 ++++
 macosx/mupen64plus.icns          |  Bin 0 -> 53030 bytes
 mupen64plus-qt.pro               |   71 +++
 resources/images/mupen64plus.png |  Bin 0 -> 11626 bytes
 resources/images/not-found.png   |  Bin 0 -> 2222 bytes
 resources/mupen64plus-qt.1       |   41 ++
 resources/mupen64plus-qt.desktop |   11 +
 resources/mupen64plusqt.qrc      |    7 +
 resources/other/LICENSE          |   10 +
 src/aboutdialog.cpp              |   78 +++
 src/aboutdialog.h                |   61 ++
 src/clickablewidget.cpp          |   51 ++
 src/clickablewidget.h            |   54 ++
 src/common.cpp                   |  323 ++++++++++
 src/common.h                     |  108 ++++
 src/configeditor.cpp             |  139 +++++
 src/configeditor.h               |   91 +++
 src/downloaddialog.cpp           |   86 +++
 src/downloaddialog.h             |   68 +++
 src/emulatorhandler.cpp          |  259 ++++++++
 src/emulatorhandler.h            |   73 +++
 src/global.h                     |   46 ++
 src/logdialog.cpp                |   68 +++
 src/logdialog.h                  |   53 ++
 src/main.cpp                     |   65 ++
 src/mainwindow.cpp               | 1248 +++++++++++++++++++++++++++++++++++++
 src/mainwindow.h                 |  160 +++++
 src/romcollection.cpp            |  442 ++++++++++++++
 src/romcollection.h              |   82 +++
 src/settingsdialog.cpp           |  759 +++++++++++++++++++++++
 src/settingsdialog.h             |   95 +++
 src/settingsdialog.ui            | 1255 ++++++++++++++++++++++++++++++++++++++
 src/thegamesdbscrapper.cpp       |  288 +++++++++
 src/thegamesdbscrapper.h         |   58 ++
 src/treewidgetitem.cpp           |   55 ++
 src/treewidgetitem.h             |   46 ++
 windows/icon.rc                  |    1 +
 windows/mupen64plus.ico          |  Bin 0 -> 66610 bytes
 40 files changed, 6403 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a222802
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.user*
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4068104
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, Dan Hasting
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the organization nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f87e00d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,123 @@
+# Mupen64Plus-Qt
+
+A basic cross-platform launcher. This was adapted from CEN64-Qt to work with Mupen64Plus.
+
+A discussion thread as well as links to binary downloads can be found [here](http://www.emutalk.net/threads/54976-Mupen64Plus-Qt).
+
+## Building
+
+### Linux
+
+You'll need to make sure you have qmake, g++, the Qt development libraries and the QuaZIP development files installed. On Debian/Ubuntu, this can be accomplished by:
+
+```
+# apt-get install qt5-qmake g++ qtbase5-dev libquazip-qt5-dev libqt5sql5-sqlite
+```
+
+Once the needed packages are installed, create the Makefile with qmake and then build with make:
+
+```
+$ qmake
+$ make
+```
+
+If qmake returns "qmake: could not find a Qt installation of ''", you can try running `QT_SELECT=qt5 qmake` or `/usr/lib/x86_64-linux-gnu/qt5/bin/qmake`. Some distributions also contain a `qmake-qt5` symlink.
+
+#### Building with Qt4
+
+Install the Qt4 dependencies instead. On Debian/Ubuntu:
+
+```
+# apt-get install qt4-qmake g++ libqt4-dev libquazip-dev libqt4-sql-sqlite
+```
+
+Then create the Makefile with qmake and build with make:
+
+```
+$ qmake-qt4
+$ make
+```
+
+
+## Usage
+
+Mupen64Plus-Qt utilizes the console UI to launch games. It contains support for most of the command line parameters. These can be viewed by running Mupen64Plus from a terminal with the --help parameter or [here](https://code.google.com/p/mupen64plus/wiki/UIConsoleUsage).
+
+### --noosd/--osd
+
+One or the other is sent to Mupen64Plus based on whether the "On Screen Display" checkbox is checked under the graphics tab.
+
+### --fullscreen/--windowed
+
+Similarly, one or the other is sent based on the "Fullscreen" checkbox underneath the graphics tab.
+
+### --resolution
+
+This can be configured with the "Resolution" combo box underneath the graphics tab. The list is generated based on common resolutions smaller than your current desktop resolution. If you require a resolution not in the list, just set the "default" option which will not send the --resolution parameter to Mupen64Plus (Mupen64Plus will read from whatever setting is within it's configuration file in this case).
+
+### --configdir
+
+This is configured under the paths tab. This is entirely optional, but allows you to specific a different directory to load the mupen64plus.cfg file from (useful if you want to say, load different input settings at different times).
+
+### --datadir
+
+This is set underneath the paths tab. Mupen64Plus-Qt will also make use of the data directory setting to load the GoodName text and other information from the mupen64plus.ini file, so make sure this directory contains that file. It's usually /usr/share/mupen64plus on Linux or the same directory as the executable on Windows. 
+
+### --plugindir
+
+The plugin directory is usually /usr/lib/mupen64plus or within the same directory as the executable on Windows. Mupen64Plus-Qt also makes use of this directory setting to populate the options on the plugins tab (you must click Ok first before they will populate).
+
+### --gfx/--audio/--input/--rsp
+
+These are configured under the plugins tab once the plugin directory has been set.
+
+### ---emumode
+
+This is configured under the emulation tab.
+
+### --saveoptions/--nosaveoptions
+
+One or the other is sent to Mupen64Plus based on the "Save Options" checkbox underneath the other tab. If --saveoptions is set, then Mupen64Plus will save whatever settings are passed to it into its configuration file (mupen64plus.cfg)
+
+
+## Game Information
+
+Mupen64Plus-Qt supports downloading game information and cover images from [thegamesdb.net](http://thegamesdb.net/). This can be enabled under the Other tab. It's recommended you have the data directory set (under Paths) before using this. Once enabled, you'll need to refresh your ROMs list to download the information. Afterwards, images and other information about the game can be added to the layouts.
+
+### Updating Game Information
+
+If a game is not found or is incorrect, Mupen64Plus-Qt supports refreshing information for a single ROM. Just select the rom and go to File->Download/Update Info. From here you can enter a different search or the specific ID of the game (from the URL of the game on thegamesdb.net).
+
+#### Manually Updating Information
+
+If desired, you can also manually update the information from TheGamesDB. Note that if any information is incorrect, it's best to create an account on TheGamesDB and fix it there so all users can benefit.
+
+The information cache can be found here:
+
+Linux: ~/.local/share/mupen64plus-qt/cache/\<MD5 of game\>/  
+Windows: cache folder in same directory as executable -> MD5 of game  
+OSX: ~/Library/Application Support/mupen64plus-qt/cache/\<MD5 of game\>/
+
+You can find the MD5 of a game by using the table or list view and adding "MD5" to the current information.
+
+Edit data.xml with a text editor and replace any information you want to change. You can also replace boxart-front.{jpg,png} with an image of your choosing.
+
+### Deleting Game Information
+
+You can delete the game information fetched from TheGamesDB by using File->Delete Current Info. Note that if you are deleting a game's information because the game doesn't exist on TheGamesDB and Mupen64Plus-Qt pulled the information for different game, it's better to create an account on TheGamesDB and add the game so other users can benefit as well.
+
+This will cause Mupen64Plus-Qt to not update the information for this game until you force it with "Download/Update Info..."
+
+
+## Mupen64Plus Config Editor
+
+Mupen64Plus-Qt contains an editor with syntax highlighting for mupen64plus.cfg. To use it, make sure you have your config directory set under Settings->Configure->Paths. Mupen64Plus-Qt should auto-detect this setting for you. If it doesn't, the default location is:
+
+Linux: /home/\<user\>/.config/mupen64plus/  
+Windows: C:/Users/\<user\>/AppData/Roaming/Mupen64Plus/  
+OSX: /Users/\<user\>/.config/mupen64plus/
+
+
+## Linux: GLideN64 Workaround
+
+Mupen64Plus-Qt contains support for [this](https://github.com/gonetz/GLideN64/issues/454#issuecomment-126853972) workaround for GLideN64 on Linux. There is no graphical option for this as it's more of a hack and should be fixed on GLideN64's end. Edit your configuration file at ~/.config/mupen64plus/mupen64plus-qt.conf and add "forcegl33=true" under [Other].
diff --git a/macosx/mupen64plus.icns b/macosx/mupen64plus.icns
new file mode 100644
index 0000000..b8f3b1e
Binary files /dev/null and b/macosx/mupen64plus.icns differ
diff --git a/mupen64plus-qt.pro b/mupen64plus-qt.pro
new file mode 100644
index 0000000..5ab4f0a
--- /dev/null
+++ b/mupen64plus-qt.pro
@@ -0,0 +1,71 @@
+QT       += core network xml sql
+
+lessThan(QT_MAJOR_VERSION, 5) {
+    QT   += gui
+} else {
+    QT   += widgets
+}
+
+macx {
+    TARGET = Mupen64Plus-Qt
+} else {
+    TARGET = mupen64plus-qt
+}
+
+TEMPLATE = app
+macx:ICON = macosx/mupen64plus.icns
+win32:RC_FILE = windows/icon.rc
+
+
+SOURCES += src/main.cpp \
+    src/common.cpp \
+    src/mainwindow.cpp \
+    src/aboutdialog.cpp \
+    src/settingsdialog.cpp \
+    src/treewidgetitem.cpp \
+    src/clickablewidget.cpp \
+    src/configeditor.cpp \
+    src/downloaddialog.cpp \
+    src/logdialog.cpp \
+    src/emulatorhandler.cpp \
+    src/romcollection.cpp \
+    src/thegamesdbscrapper.cpp
+
+HEADERS += src/global.h \
+    src/common.h \
+    src/mainwindow.h \
+    src/aboutdialog.h \
+    src/settingsdialog.h \
+    src/treewidgetitem.h \
+    src/clickablewidget.h \
+    src/configeditor.h \
+    src/downloaddialog.h \
+    src/logdialog.h \
+    src/emulatorhandler.h \
+    src/romcollection.h \
+    src/thegamesdbscrapper.h
+
+RESOURCES += resources/mupen64plusqt.qrc
+
+FORMS += src/settingsdialog.ui
+
+win32|macx {
+    CONFIG += staticlib
+    DEFINES += QUAZIP_STATIC
+
+    #Download quazip source and copy the quazip directory to project
+    SOURCES += quazip/*.cpp
+    SOURCES += quazip/*.c
+    HEADERS += quazip/*.h
+} else {
+    lessThan(QT_MAJOR_VERSION, 5) {
+        LIBS += -lquazip
+    } else {
+        # Debian distributions use a different library name for Qt5 quazip
+        system("uname -a | grep -E 'Debian|Ubuntu' > /dev/null") {
+            LIBS += -lquazip-qt5
+        } else {
+            LIBS += -lquazip5
+        }
+    }
+}
diff --git a/resources/images/mupen64plus.png b/resources/images/mupen64plus.png
new file mode 100644
index 0000000..ba2f7a9
Binary files /dev/null and b/resources/images/mupen64plus.png differ
diff --git a/resources/images/not-found.png b/resources/images/not-found.png
new file mode 100644
index 0000000..5a76141
Binary files /dev/null and b/resources/images/not-found.png differ
diff --git a/resources/mupen64plus-qt.1 b/resources/mupen64plus-qt.1
new file mode 100644
index 0000000..fdadff2
--- /dev/null
+++ b/resources/mupen64plus-qt.1
@@ -0,0 +1,41 @@
+.\" Copyright (c) 2013, Dan Hasting
+.\" All rights reserved.
+.\"
+.\" %%%LICENSE_START(BSD_3_CLAUSE)
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" * Redistributions of source code must retain the above copyright
+.\"   notice, this list of conditions and the following disclaimer.
+.\" * Redistributions in binary form must reproduce the above copyright
+.\"   notice, this list of conditions and the following disclaimer in
+.\"   the documentation and/or other materials provided with the
+.\"   distribution.
+.\" * Neither the name of the organization nor the names of its
+.\"   contributors may be used to endorse or promote products derived 
+.\"   from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE THE COPYRIGHT HOLDER OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" %%%LICENSE_END
+.TH "Mupen64Plus-Qt" "1" "May 2015" "Mupen64Plus-Qt" "User Commands"
+.SH "NAME"
+Mupen64Plus-Qt \- a basic launcher for Mupen64Plus
+.SH "SYNOPSIS"
+\fBmupen64plus-qt\fR
+.SH "DESCRIPTION"
+\fBMupen64Plus-Qt\fR is a basic launcher for the mupen64plus-ui-console frontend. It was adapted from CEN64-Qt to work with Mupen64Plus.
+.TP
+Mupen64Plus-Qt supports downloading game information and cover images from thegamesdb.net. This can be enabled under the Other tab. It's recommended you have the data directory set (under Paths) before using this. Once enabled, you'll need to refresh your ROMs list to download the information. Afterwards, images and other information about the game can be added to the layouts.
+.TP
+If a game is not found, Mupen64Plus-Qt supports refreshing information for a single ROM. Just select the rom and go to File->Download/Update Info. From here you can enter a different search or the specific ID of the game (from the URL of the game on thegamesdb.net).
diff --git a/resources/mupen64plus-qt.desktop b/resources/mupen64plus-qt.desktop
new file mode 100755
index 0000000..516effa
--- /dev/null
+++ b/resources/mupen64plus-qt.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Mupen64Plus-Qt
+GenericName=Mupen64Plus Launcher
+Comment=A basic launcher for Mupen64plus
+Exec=mupen64plus-qt
+Icon=mupen64plus-qt
+Terminal=false
+Categories=Game;Emulator;
+Keywords=emulator;mupen;mupen64plus;Nintendo;64;
diff --git a/resources/mupen64plusqt.qrc b/resources/mupen64plusqt.qrc
new file mode 100644
index 0000000..87d7901
--- /dev/null
+++ b/resources/mupen64plusqt.qrc
@@ -0,0 +1,7 @@
+<RCC>
+    <qresource prefix="/">
+        <file>images/mupen64plus.png</file>
+        <file>other/LICENSE</file>
+        <file>images/not-found.png</file>
+    </qresource>
+</RCC>
diff --git a/resources/other/LICENSE b/resources/other/LICENSE
new file mode 100644
index 0000000..4f7ab37
--- /dev/null
+++ b/resources/other/LICENSE
@@ -0,0 +1,10 @@
+Copyright (c) 2013, Dan Hasting
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROF [...]
\ No newline at end of file
diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp
new file mode 100644
index 0000000..ba62b39
--- /dev/null
+++ b/src/aboutdialog.cpp
@@ -0,0 +1,78 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "aboutdialog.h"
+
+
+AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent)
+{
+    setWindowTitle(tr("About Mupen64Plus-Qt"));
+    setMinimumSize(600, 300);
+
+    aboutLayout = new QGridLayout(this);
+
+    icon = new QLabel(this);
+    icon->setPixmap(QPixmap(":/images/mupen64plus.png"));
+
+    QFile licenseFile(":/other/LICENSE");
+    licenseFile.open(QIODevice::ReadOnly);
+    license = new QPlainTextEdit(licenseFile.readAll(), this);
+    license->setReadOnly(true);
+    licenseFile.close();
+
+    QString description = "<b>Mupen64Plus-Qt</b><br />Version " + Version + "<br /><br />";
+    description += tr("A basic launcher for Mupen64Plus using Qt.");
+    QString mupen64 = "<a href=\"http://www.mupen64plus.org/\">Mupen64Plus website</a>";
+    QString github = "<a href=\"https://github.com/dh4/mupen64plus-qt\">Github repository</a>";
+
+    descriptionLabel = new QLabel(description, this);
+    mupen64Link = new QLabel(mupen64, this);
+    githubLink = new QLabel(github, this);
+
+    mupen64Link->setOpenExternalLinks(true);
+    githubLink->setOpenExternalLinks(true);
+
+    buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, this);
+
+    aboutLayout->addWidget(icon, 0, 0, 4, 1);
+    aboutLayout->addWidget(descriptionLabel, 0, 1);
+    aboutLayout->addWidget(license, 1, 1);
+    aboutLayout->addWidget(mupen64Link, 3, 1);
+    aboutLayout->addWidget(githubLink, 4, 1);
+    aboutLayout->addWidget(buttonBox, 5, 1);
+    aboutLayout->setColumnStretch(1, 1);
+    aboutLayout->setRowStretch(1, 1);
+    aboutLayout->setColumnMinimumWidth(0, 150);
+
+    connect(buttonBox, SIGNAL(accepted()), this, SLOT(close()));
+
+    setLayout(aboutLayout);
+}
diff --git a/src/aboutdialog.h b/src/aboutdialog.h
new file mode 100644
index 0000000..5030199
--- /dev/null
+++ b/src/aboutdialog.h
@@ -0,0 +1,61 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef ABOUTDIALOG_H
+#define ABOUTDIALOG_H
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QPlainTextEdit>
+
+#include "global.h"
+
+
+class AboutDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit AboutDialog(QWidget *parent = 0);
+
+private:
+    QDialogButtonBox *buttonBox;
+    QGridLayout *aboutLayout;
+    QLabel *descriptionLabel;
+    QLabel *githubLink;
+    QLabel *mupen64Link;
+    QLabel *icon;
+    QPlainTextEdit *license;
+};
+
+#endif // ABOUTDIALOG_H
diff --git a/src/clickablewidget.cpp b/src/clickablewidget.cpp
new file mode 100644
index 0000000..069cee8
--- /dev/null
+++ b/src/clickablewidget.cpp
@@ -0,0 +1,51 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "clickablewidget.h"
+
+
+ClickableWidget::ClickableWidget(QWidget *parent) : QWidget(parent)
+{
+}
+
+
+void ClickableWidget::mousePressEvent(QMouseEvent *event)
+{
+    if (event->button() == Qt::LeftButton)
+        emit singleClicked(this);
+}
+
+
+void ClickableWidget::mouseDoubleClickEvent(QMouseEvent *event)
+{
+    if (event->button() == Qt::LeftButton)
+        emit doubleClicked(this);
+}
diff --git a/src/clickablewidget.h b/src/clickablewidget.h
new file mode 100644
index 0000000..2b05f2d
--- /dev/null
+++ b/src/clickablewidget.h
@@ -0,0 +1,54 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef CLICKABLEWIDGET_H
+#define CLICKABLEWIDGET_H
+
+#include <QMouseEvent>
+#include <QWidget>
+
+
+class ClickableWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit ClickableWidget(QWidget *parent = 0);
+
+protected:
+    void mousePressEvent(QMouseEvent *event);
+    void mouseDoubleClickEvent(QMouseEvent *event);
+
+signals:
+    void singleClicked(QWidget *current);
+    void doubleClicked(QWidget *current);
+};
+
+#endif // CLICKABLEWIDGET_H
diff --git a/src/common.cpp b/src/common.cpp
new file mode 100644
index 0000000..ff2a838
--- /dev/null
+++ b/src/common.cpp
@@ -0,0 +1,323 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "common.h"
+
+
+QByteArray byteswap(QByteArray romData)
+{
+    QByteArray flipped;
+
+    if (romData.left(4).toHex() == "37804012") {
+        for (int i = 0; i < romData.length(); i += 2)
+        {
+            flipped.append(romData[i + 1]);
+            flipped.append(romData[i]);
+        }
+        return flipped;
+    } else {
+        return romData;
+    }
+}
+
+
+QString getDataLocation()
+{
+    QString dataDir;
+
+#ifdef Q_OS_WIN
+    dataDir = QCoreApplication::applicationDirPath();
+#else
+
+#if QT_VERSION >= 0x050000
+    dataDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+                    .replace("Mupen64Plus/Mupen64Plus-Qt","mupen64plus-qt");
+#else
+    dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation)
+                    .remove("data/").replace("Mupen64Plus/Mupen64Plus-Qt","mupen64plus-qt");
+#endif
+
+#endif
+
+     QDir data(dataDir);
+     if (!data.exists())
+         data.mkpath(dataDir);
+
+     return dataDir;
+}
+
+
+QColor getColor(QString color, int transparency)
+{
+    if (transparency <= 255) {
+        if (color == "Black")           return QColor(0,   0,   0,   transparency);
+        else if (color == "White")      return QColor(255, 255, 255, transparency);
+        else if (color == "Light Gray") return QColor(200, 200, 200, transparency);
+        else if (color == "Dark Gray")  return QColor(50,  50,  59,  transparency);
+        else if (color == "Green")      return QColor(0,   255, 0,   transparency);
+        else if (color == "Cyan")       return QColor(30,  175, 255, transparency);
+        else if (color == "Blue")       return QColor(0,   0,   255, transparency);
+        else if (color == "Purple")     return QColor(128, 0,   128, transparency);
+        else if (color == "Red")        return QColor(255, 0,   0,   transparency);
+        else if (color == "Pink")       return QColor(246, 96,  171, transparency);
+        else if (color == "Orange")     return QColor(255, 165, 0,   transparency);
+        else if (color == "Yellow")     return QColor(255, 255, 0,   transparency);
+        else if (color == "Brown")      return QColor(127, 70,  44,  transparency);
+    }
+
+    return QColor(0, 0, 0, 255);
+}
+
+
+int getDefaultWidth(QString id, int imageWidth)
+{
+    if (id == "Overview")
+        return 400;
+    else if (id == "GoodName" || id.left(8) == "Filename" || id == "Game Title")
+        return 300;
+    else if (id == "MD5")
+        return 250;
+    else if (id == "Internal Name" || id == "Publisher" || id == "Developer")
+        return 200;
+    else if (id == "ESRB" || id == "Genre")
+        return 150;
+    else if (id == "Save Type" || id == "Release Date")
+        return 100;
+    else if (id == "CRC1" || id == "CRC2")
+        return 90;
+    else if (id == "Size" || id == "Rumble" || id == "Players" || id == "Rating")
+        return 75;
+    else if (id == "Game Cover")
+        return imageWidth;
+    else
+        return 100;
+}
+
+
+int getGridSize(QString which)
+{
+    QString size = SETTINGS.value("Grid/imagesize","Medium").toString();
+
+    if (which == "height") {
+        if (SETTINGS.value("Grid/label", "true").toString() == "true") {
+            if (size == "Extra Small") return 65;
+            if (size == "Small")       return 90;
+            if (size == "Medium")      return 145;
+            if (size == "Large")       return 190;
+            if (size == "Extra Large") return 250;
+        } else {
+            if (size == "Extra Small") return 47;
+            if (size == "Small")       return 71;
+            if (size == "Medium")      return 122;
+            if (size == "Large")       return 172;
+            if (size == "Extra Large") return 224;
+        }
+    } else if (which == "width") {
+        if (size == "Extra Small") return 60;
+        if (size == "Small")       return 90;
+        if (size == "Medium")      return 160;
+        if (size == "Large")       return 225;
+        if (size == "Extra Large") return 300;
+    } else if (which == "font") {
+        if (size == "Extra Small") return 5;
+        if (size == "Small")       return 7;
+        if (size == "Medium")      return 10;
+        if (size == "Large")       return 12;
+        if (size == "Extra Large") return 13;
+    }
+    return 0;
+}
+
+
+QSize getImageSize(QString view)
+{
+    QString size = SETTINGS.value(view+"/imagesize","Medium").toString();
+
+    if (view == "Table") {
+        if (size == "Extra Small") return QSize(33, 24);
+        if (size == "Small")       return QSize(48, 35);
+        if (size == "Medium")      return QSize(69, 50);
+        if (size == "Large")       return QSize(103, 75);
+        if (size == "Extra Large") return QSize(138, 100);
+    } else if (view == "Grid" || view == "List") {
+        if (size == "Extra Small") return QSize(48, 35);
+        if (size == "Small")       return QSize(69, 50);
+        if (size == "Medium")      return QSize(138, 100);
+        if (size == "Large")       return QSize(203, 150);
+        if (size == "Extra Large") return QSize(276, 200);
+    }
+
+    return QSize();
+}
+
+
+QString getRomInfo(QString identifier, const Rom *rom, bool removeWarn, bool sort)
+{
+    QString text = "";
+
+    if (identifier == "GoodName")
+        text = rom->goodName;
+    else if (identifier == "Filename")
+        text = rom->baseName;
+    else if (identifier == "Filename (extension)")
+        text = rom->fileName;
+    else if (identifier == "Zip File")
+        text = rom->zipFile;
+    else if (identifier == "Internal Name")
+        text = rom->internalName;
+    else if (identifier == "Size")
+        text = rom->size;
+    else if (identifier == "MD5")
+        text = rom->romMD5.toLower();
+    else if (identifier == "CRC1")
+        text = rom->CRC1.toLower();
+    else if (identifier == "CRC2")
+        text = rom->CRC2.toLower();
+    else if (identifier == "Players")
+        text = rom->players;
+    else if (identifier == "Rumble")
+        text = rom->rumble;
+    else if (identifier == "Save Type")
+        text = rom->saveType;
+    else if (identifier == "Game Title")
+        text = rom->gameTitle;
+    else if (identifier == "Release Date")
+        text = rom->releaseDate;
+    else if (identifier == "Overview")
+        text = rom->overview;
+    else if (identifier == "ESRB")
+        text = rom->esrb;
+    else if (identifier == "Genre")
+        text = rom->genre;
+    else if (identifier == "Publisher")
+        text = rom->publisher;
+    else if (identifier == "Developer")
+        text = rom->developer;
+    else if (identifier == "Rating")
+        text = rom->rating;
+
+    if (!removeWarn)
+        return text;
+    else if (text == "Unknown ROM" || text == "Requires catalog file" || text == "Not found") {
+        if (sort)
+            return "ZZZ"; //Sort warnings at the end
+        else
+            return "";
+    } else
+        return text;
+}
+
+
+QGraphicsDropShadowEffect *getShadow(bool active)
+{
+    QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;
+
+    if (active) {
+        shadow->setBlurRadius(25.0);
+        shadow->setColor(getColor(SETTINGS.value("Grid/activecolor","Cyan").toString(), 255));
+        shadow->setOffset(0);
+    } else {
+        shadow->setBlurRadius(10.0);
+        shadow->setColor(getColor(SETTINGS.value("Grid/inactivecolor","Black").toString(), 200));
+        shadow->setOffset(0);
+    }
+
+    return shadow;
+}
+
+
+QStringList getZippedFiles(QString completeFileName)
+{
+    QuaZip zipFile(completeFileName);
+    zipFile.open(QuaZip::mdUnzip);
+    QStringList files = zipFile.getFileNameList();
+    zipFile.close();
+
+    return files;
+}
+
+
+QByteArray *getZippedRom(QString romFileName, QString zipFile)
+{
+    QuaZipFile zippedFile(zipFile, romFileName);
+
+    zippedFile.open(QIODevice::ReadOnly);
+    QByteArray *romData = new QByteArray();
+    romData->append(zippedFile.readAll());
+    zippedFile.close();
+
+    return romData;
+}
+
+
+bool romSorter(const Rom &firstRom, const Rom &lastRom)
+{
+    QString sort, direction;
+
+    QString layout = SETTINGS.value("View/layout", "None").toString();
+    if (layout == "Grid View") {
+        sort = SETTINGS.value("Grid/sort", "Filename").toString();
+        direction = SETTINGS.value("Grid/sortdirection", "ascending").toString();
+    } else if (layout == "List View") {
+        sort = SETTINGS.value("List/sort", "Filename").toString();
+        direction = SETTINGS.value("List/sortdirection", "ascending").toString();
+    } else //just return sort by filename
+        return firstRom.fileName < lastRom.fileName;
+
+
+    QString sortFirst = "", sortLast = "";
+
+    if (sort == "Size") {
+        int firstSize = firstRom.sortSize;
+        int lastSize = lastRom.sortSize;
+
+        if (direction == "descending")
+            return firstSize > lastSize;
+        else
+            return firstSize < lastSize;
+    } else if (sort == "Release Date") {
+        sortFirst = firstRom.sortDate;
+        sortLast = lastRom.sortDate;
+    } else {
+        sortFirst = getRomInfo(sort, &firstRom, true, true);
+        sortLast = getRomInfo(sort, &lastRom, true, true);
+    }
+
+    if (sortFirst == sortLast) { //Equal so sort on filename
+        sortFirst = firstRom.fileName;
+        sortLast = lastRom.fileName;
+    }
+
+    if (direction == "descending")
+        return sortFirst > sortLast;
+    else
+        return sortFirst < sortLast;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..76266a1
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,108 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <QColor>
+#include <QCoreApplication>
+#include <QDesktopServices>
+#include <QDir>
+#include <QEventLoop>
+#include <QFile>
+#include <QGraphicsDropShadowEffect>
+#include <QMessageBox>
+#include <QObject>
+#include <QSize>
+#include <QString>
+#include <QTextStream>
+#include <QTimer>
+#include <QUrl>
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkRequest>
+#include <QtNetwork/QNetworkReply>
+#include <QtXml/QDomDocument>
+
+#include <quazip/quazip.h>
+#include <quazip/quazipfile.h>
+
+#include "global.h"
+
+
+typedef struct {
+    QString fileName;
+    QString directory;
+    QString romMD5;
+    QString internalName;
+    QString zipFile;
+
+    QString baseName;
+    QString size;
+    int sortSize;
+
+    QString goodName;
+    QString CRC1;
+    QString CRC2;
+    QString players;
+    QString saveType;
+    QString rumble;
+
+    QString gameTitle;
+    QString releaseDate;
+    QString sortDate;
+    QString overview;
+    QString esrb;
+    QString genre;
+    QString publisher;
+    QString developer;
+    QString rating;
+
+    QPixmap image;
+
+    int count;
+    bool imageExists;
+} Rom;
+
+bool romSorter(const Rom &firstRom, const Rom &lastRom);
+int getDefaultWidth(QString id, int imageWidth);
+int getGridSize(QString which);
+
+QByteArray byteswap(QByteArray romData);
+QStringList getZippedFiles(QString completeFileName);
+QByteArray *getZippedRom(QString romFileName, QString zipFile);
+QColor getColor(QString color, int transparency = 255);
+QGraphicsDropShadowEffect *getShadow(bool active);
+QSize getImageSize(QString view);
+QString getDataLocation();
+QString getRomInfo(QString identifier, const Rom *rom, bool removeWarn = false, bool sort = false);
+
+#endif // COMMON_H
diff --git a/src/configeditor.cpp b/src/configeditor.cpp
new file mode 100644
index 0000000..416e850
--- /dev/null
+++ b/src/configeditor.cpp
@@ -0,0 +1,139 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "configeditor.h"
+#include "global.h"
+
+
+ConfigEditor::ConfigEditor(QString configFile, QWidget *parent) : QDialog(parent)
+{
+    config.setFileName(configFile);
+
+    setWindowTitle(tr("Mupen64Plus Config Editor"));
+    setMinimumSize(600, 400);
+
+    editorLayout = new QGridLayout(this);
+    editorLayout->setContentsMargins(5, 10, 5, 10);
+
+    editorArea = new QTextEdit(this);
+    editorArea->setWordWrapMode(QTextOption::NoWrap);
+
+    QFont font;
+#ifdef Q_OS_LINUX
+    font.setFamily("Monospace");
+    font.setPointSize(9);
+#else
+    font.setFamily("Courier");
+    font.setPointSize(10);
+#endif
+    font.setFixedPitch(true);
+    editorArea->setFont(font);
+
+    highlighter = new ConfigHighlighter(editorArea->document());
+
+    config.open(QIODevice::ReadOnly);
+    editorArea->setPlainText(config.readAll());
+    config.close();
+
+    editorButtonBox = new QDialogButtonBox(Qt::Horizontal, this);
+    editorButtonBox->addButton(tr("Save"), QDialogButtonBox::AcceptRole);
+    editorButtonBox->addButton(QDialogButtonBox::Cancel);
+
+    editorLayout->addWidget(editorArea, 0, 0);
+    editorLayout->addWidget(editorButtonBox, 1, 0);
+
+    connect(editorButtonBox, SIGNAL(accepted()), this, SLOT(saveConfig()));
+    connect(editorButtonBox, SIGNAL(rejected()), this, SLOT(close()));
+
+    setLayout(editorLayout);
+}
+
+
+void ConfigEditor::saveConfig()
+{
+    config.open(QIODevice::WriteOnly);
+    config.write(editorArea->toPlainText().toUtf8().data());
+    config.close();
+
+    close();
+}
+
+
+ConfigHighlighter::ConfigHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)
+{
+    Rule rule;
+
+    headerFormat.setFontWeight(QFont::Bold);
+    headerFormat.setForeground(Qt::darkMagenta);
+    rule.pattern = QRegExp("\\[[A-Za-z0-9\\(\\)-\\W]*\\]");
+    rule.format = headerFormat;
+    rules.append(rule);
+
+    variableFormat.setForeground(Qt::black);
+    rule.pattern = QRegExp("[A-Za-z0-9_- ]*=");
+    rule.format = variableFormat;
+    rules.append(rule);
+
+    valueFormat.setForeground(Qt::blue);
+    rule.pattern = QRegExp("=[^\\n]+");
+    rule.format = valueFormat;
+    rules.append(rule);
+
+    separatorFormat.setFontWeight(QFont::Bold);
+    separatorFormat.setForeground(Qt::black);
+    rule.pattern = QRegExp("=");
+    rule.format = separatorFormat;
+    rules.append(rule);
+
+    quotationFormat.setForeground(Qt::darkGreen);
+    rule.pattern = QRegExp("\".*\"");
+    rule.format = quotationFormat;
+    rules.append(rule);
+
+    commentFormat.setForeground(Qt::gray);
+    rule.pattern = QRegExp("#[^\\n]*");
+    rule.format = commentFormat;
+    rules.append(rule);
+}
+
+
+void ConfigHighlighter::highlightBlock(const QString &text)
+{
+    foreach (const Rule &rule, rules) {
+        QRegExp expression(rule.pattern);
+        int index = expression.indexIn(text);
+        while (index >= 0) {
+            int length = expression.matchedLength();
+            setFormat(index, length, rule.format);
+            index = expression.indexIn(text, index + length);
+        }
+    }
+}
diff --git a/src/configeditor.h b/src/configeditor.h
new file mode 100644
index 0000000..a4ef1af
--- /dev/null
+++ b/src/configeditor.h
@@ -0,0 +1,91 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef CONFIGEDITOR_H
+#define CONFIGEDITOR_H
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QDir>
+#include <QGridLayout>
+#include <QMessageBox>
+#include <QSettings>
+#include <QSyntaxHighlighter>
+#include <QTextEdit>
+
+
+class ConfigHighlighter : public QSyntaxHighlighter
+{
+    Q_OBJECT
+
+public:
+    ConfigHighlighter(QTextDocument *parent = 0);
+
+protected:
+    void highlightBlock(const QString &text);
+
+private:
+    struct Rule {
+        QRegExp pattern;
+        QTextCharFormat format;
+    };
+
+    QVector<Rule> rules;
+
+    QTextCharFormat headerFormat;
+    QTextCharFormat variableFormat;
+    QTextCharFormat valueFormat;
+    QTextCharFormat separatorFormat;
+    QTextCharFormat quotationFormat;
+    QTextCharFormat commentFormat;
+};
+
+
+class ConfigEditor : public QDialog
+{
+    Q_OBJECT
+public:
+    explicit ConfigEditor(QString configFile, QWidget *parent = 0);
+
+private:
+    QDialogButtonBox *editorButtonBox;
+    QFile config;
+    QGridLayout *editorLayout;
+    QTextEdit *editorArea;
+
+    ConfigHighlighter *highlighter;
+
+private slots:
+    void saveConfig();
+
+};
+
+#endif // CONFIGEDITOR_H
diff --git a/src/downloaddialog.cpp b/src/downloaddialog.cpp
new file mode 100644
index 0000000..ead16a9
--- /dev/null
+++ b/src/downloaddialog.cpp
@@ -0,0 +1,86 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "downloaddialog.h"
+
+
+DownloadDialog::DownloadDialog(QString fileText, QString defaultText, QString romMD5, QWidget *parent)
+    : QDialog(parent)
+{
+    this->romMD5 = romMD5;
+    this->parent = parent;
+
+    setWindowTitle(tr("Search Game Information"));
+
+    downloadLayout = new QGridLayout(this);
+
+    fileLabel = new QLabel(tr("<b>File:</b> ") + fileText, this);
+
+    gameNameLabel = new QLabel(tr("Name of Game:"), this);
+    gameIDLabel = new QLabel(tr("or Game ID:"), this);
+
+    defaultText.remove(QRegExp("\\W*(\\(|\\[).+(\\)|\\])\\W*"));
+    gameNameField = new QLineEdit(defaultText, this);
+    gameIDField = new QLineEdit(this);
+
+    gameIDField->setToolTip(tr("From thegamesdb.net URL of game"));
+
+    downloadButtonBox = new QDialogButtonBox(Qt::Horizontal, this);
+    downloadButtonBox->addButton(tr("Search"), QDialogButtonBox::AcceptRole);
+    downloadButtonBox->addButton(QDialogButtonBox::Cancel);
+
+    downloadLayout->addWidget(fileLabel, 0, 0, 1, 2);
+    downloadLayout->addWidget(gameNameLabel, 1, 0);
+    downloadLayout->addWidget(gameIDLabel, 2, 0);
+    downloadLayout->addWidget(gameNameField, 1, 1);
+    downloadLayout->addWidget(gameIDField, 2, 1);
+    downloadLayout->addWidget(downloadButtonBox, 4, 0, 1, 3);
+    downloadLayout->setRowStretch(3,1);
+    downloadLayout->setColumnStretch(1,1);
+
+    downloadLayout->setColumnMinimumWidth(1, 300);
+    downloadLayout->setRowMinimumHeight(0, 20);
+    downloadLayout->setRowMinimumHeight(3, 20);
+
+    connect(downloadButtonBox, SIGNAL(accepted()), this, SLOT(runDownloader()));
+    connect(downloadButtonBox, SIGNAL(rejected()), this, SLOT(close()));
+
+    setLayout(downloadLayout);
+}
+
+
+void DownloadDialog::runDownloader()
+{
+    close();
+
+    scrapper = new TheGamesDBScrapper(parent, true);
+    scrapper->downloadGameInfo(romMD5, gameNameField->text(), gameIDField->text());
+}
diff --git a/src/downloaddialog.h b/src/downloaddialog.h
new file mode 100644
index 0000000..49c4528
--- /dev/null
+++ b/src/downloaddialog.h
@@ -0,0 +1,68 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef DOWNLOADDIALOG_H
+#define DOWNLOADDIALOG_H
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+
+#include "common.h"
+#include "thegamesdbscrapper.h"
+
+
+class DownloadDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    explicit DownloadDialog(QString fileText, QString defaultText, QString romMD5, QWidget *parent = 0);
+
+private:
+    QDialogButtonBox *downloadButtonBox;
+    QGridLayout *downloadLayout;
+    QLabel *fileLabel;
+    QLabel *gameNameLabel;
+    QLabel *gameIDLabel;
+    QLineEdit *gameNameField;
+    QLineEdit *gameIDField;
+    QString romMD5;
+    QWidget *parent;
+
+    TheGamesDBScrapper *scrapper;
+
+private slots:
+    void runDownloader();
+};
+
+#endif // DOWNLOADDIALOG_H
diff --git a/src/emulatorhandler.cpp b/src/emulatorhandler.cpp
new file mode 100644
index 0000000..dd1b781
--- /dev/null
+++ b/src/emulatorhandler.cpp
@@ -0,0 +1,259 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "emulatorhandler.h"
+
+
+EmulatorHandler::EmulatorHandler(QWidget *parent) : QObject(parent)
+{
+    this->parent = parent;
+
+    lastOutput = "";
+}
+
+void EmulatorHandler::checkStatus(int status)
+{
+    if (status > 0) {
+        QMessageBox exitDialog(parent);
+        exitDialog.setWindowTitle(tr("Warning"));
+        exitDialog.setText(tr("Mupen64Plus quit unexpectedly. Check the log for more information."));
+        exitDialog.setIcon(QMessageBox::Warning);
+        exitDialog.addButton(QMessageBox::Ok);
+        exitDialog.addButton("View Log...", QMessageBox::HelpRole);
+
+        int ret = exitDialog.exec();
+        if (ret == 0) emit showLog();
+    }
+}
+
+
+void EmulatorHandler::cleanTemp()
+{
+    QFile::remove(QDir::tempPath() + "/mupen64plus-qt/temp.z64");
+}
+
+
+void EmulatorHandler::emitFinished()
+{
+    emit finished();
+}
+
+
+QStringList EmulatorHandler::parseArgString(QString argString)
+{
+    QStringList result;
+    QString arg;
+    bool inQuote = false;
+    bool inApos = false;
+
+    for (int i = 0; i < argString.size(); i++)
+    {
+        // Check if inside of a quote
+        if (argString.at(i) == QLatin1Char('"')) {
+            inQuote = !inQuote;
+
+            // Only continue if outside of both quotes and apostrophes
+            if (arg.isEmpty() || (!inQuote && !inApos)) continue;
+        }
+
+        // Same check for apostrophes
+        if (argString.at(i) == QLatin1Char('\'')) {
+            inApos = !inApos;
+            if (arg.isEmpty() || (!inQuote && !inApos)) continue;
+        }
+
+        if (!inQuote && !inApos && argString.at(i).isSpace()) {
+            if (!arg.isEmpty()) {
+                result += arg;
+                arg.clear();
+            }
+        } else
+            arg += argString.at(i);
+    }
+
+    if (!arg.isEmpty())
+        result += arg;
+
+    return result;
+}
+
+
+void EmulatorHandler::readOutput()
+{
+    lastOutput = emulatorProc->readAllStandardOutput();
+}
+
+
+void EmulatorHandler::startEmulator(QDir romDir, QString romFileName, QString zipFileName)
+{
+    QString completeRomPath;
+    bool zip = false;
+
+    if (zipFileName != "") { //If zipped file, extract and write to temp location for loading
+        zip = true;
+
+        QString zipFile = romDir.absoluteFilePath(zipFileName);
+        QuaZipFile zippedFile(zipFile, romFileName);
+
+        zippedFile.open(QIODevice::ReadOnly);
+        QByteArray romData;
+        romData.append(zippedFile.readAll());
+        zippedFile.close();
+
+        QString tempDir = QDir::tempPath() + "/mupen64plus-qt";
+        QDir().mkdir(tempDir);
+        completeRomPath = tempDir + "/temp.n64";
+
+        QFile tempRom(completeRomPath);
+        tempRom.open(QIODevice::WriteOnly);
+        tempRom.write(romData);
+        tempRom.close();
+    } else
+        completeRomPath = romDir.absoluteFilePath(romFileName);
+
+    QString mupen64Path = SETTINGS.value("Paths/mupen64plus", "").toString();
+    QString dataPath = SETTINGS.value("Paths/data", "").toString();
+    QString configPath = SETTINGS.value("Paths/config", "").toString();
+    QString pluginPath = SETTINGS.value("Paths/plugins", "").toString();
+    QDir dataDir(dataPath);
+    QDir configDir(configPath);
+    QDir pluginDir(pluginPath);
+
+    QString emuMode = SETTINGS.value("Emulation/mode", "").toString();
+
+    QString resolution = SETTINGS.value("Graphics/resolution", "").toString();
+
+    QString videoPlugin = SETTINGS.value("Plugins/video", "").toString();
+    QString audioPlugin = SETTINGS.value("Plugins/audio", "").toString();
+    QString inputPlugin = SETTINGS.value("Plugins/input", "").toString();
+    QString rspPlugin = SETTINGS.value("Plugins/rsp", "").toString();
+
+    QFile mupen64File(mupen64Path);
+    QFile romFile(completeRomPath);
+
+
+    //Sanity checks
+    if(!mupen64File.exists() || QFileInfo(mupen64File).isDir() || !QFileInfo(mupen64File).isExecutable()) {
+        QMessageBox::warning(parent, tr("Warning"), tr("Mupen64Plus executable not found."));
+        if (zip) cleanTemp();
+        return;
+    }
+
+    if(!romFile.exists() || QFileInfo(romFile).isDir()) {
+        QMessageBox::warning(parent, tr("Warning"), tr("ROM file not found."));
+        if (zip) cleanTemp();
+        return;
+    }
+
+    romFile.open(QIODevice::ReadOnly);
+    QByteArray romCheck = romFile.read(4);
+    romFile.close();
+
+    if (romCheck.toHex() != "80371240" && romCheck.toHex() != "37804012") {
+        QMessageBox::warning(parent, tr("Warning"), tr("Not a valid ROM File."));
+        if (zip) cleanTemp();
+        return;
+    }
+
+
+    QStringList args;
+
+    if (SETTINGS.value("saveoptions", "").toString() == "true")
+        args << "--saveoptions";
+    else
+        args << "--nosaveoptions";
+
+    if (dataPath != "" && dataDir.exists())
+        args << "--datadir" << dataPath;
+    if (configPath != "" && configDir.exists())
+        args << "--configdir" << configPath;
+    if (pluginPath != "" && pluginDir.exists())
+        args << "--plugindir" << pluginPath;
+
+    if (emuMode != "")
+        args << "--emumode" << emuMode;
+
+    if (SETTINGS.value("Graphics/osd", "").toString() == "true")
+        args << "--osd";
+    else
+        args << "--noosd";
+
+    if (SETTINGS.value("Graphics/fullscreen", "").toString() == "true")
+        args << "--fullscreen";
+    else
+        args << "--windowed";
+
+    if (resolution != "")
+        args << "--resolution" << resolution;
+
+    if (videoPlugin != "")
+        args << "--gfx" << videoPlugin;
+    if (audioPlugin != "")
+        args << "--audio" << audioPlugin;
+    if (inputPlugin != "")
+        args << "--input" << inputPlugin;
+    if (rspPlugin != "")
+        args << "--rsp" << rspPlugin;
+
+    QString otherParameters = SETTINGS.value("Other/parameters", "").toString();
+    if (otherParameters != "")
+        args.append(parseArgString(otherParameters));
+
+    args << completeRomPath;
+
+    emulatorProc = new QProcess(this);
+    connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(readOutput()));
+    connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(emitFinished()));
+    connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(checkStatus(int)));
+
+    if (zip)
+        connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(cleanTemp()));
+
+    // GLideN64 workaround. Can be removed if workaround is no longer needed
+    // See: https://github.com/gonetz/GLideN64/issues/454#issuecomment-126853972
+    if (SETTINGS.value("Other/forcegl33", "").toString() == "true") {
+        QProcessEnvironment emulatorEnv = QProcessEnvironment::systemEnvironment();
+        emulatorEnv.insert("MESA_GL_VERSION_OVERRIDE", "3.3COMPAT");
+        emulatorProc->setProcessEnvironment(emulatorEnv);
+    }
+
+    emulatorProc->setWorkingDirectory(QFileInfo(mupen64File).dir().canonicalPath());
+    emulatorProc->setProcessChannelMode(QProcess::MergedChannels);
+    emulatorProc->start(mupen64Path, args);
+
+    emit started();
+}
+
+
+void EmulatorHandler::stopEmulator()
+{
+    emulatorProc->terminate();
+}
diff --git a/src/emulatorhandler.h b/src/emulatorhandler.h
new file mode 100644
index 0000000..c2484db
--- /dev/null
+++ b/src/emulatorhandler.h
@@ -0,0 +1,73 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef EMULATORHANDLER_H
+#define EMULATORHANDLER_H
+
+#include <QCryptographicHash>
+#include <QFile>
+#include <QDir>
+#include <QMessageBox>
+#include <QObject>
+#include <QProcess>
+
+#include "common.h"
+
+
+class EmulatorHandler : public QObject
+{
+    Q_OBJECT
+public:
+    explicit EmulatorHandler(QWidget *parent = 0);
+    void startEmulator(QDir romDir, QString romFileName, QString zipFileName = "");
+    void stopEmulator();
+
+    QString lastOutput;
+
+signals:
+    void finished();
+    void showLog();
+    void started();
+
+private:
+    QStringList parseArgString(QString argString);
+
+    QProcess *emulatorProc;
+    QWidget *parent;
+
+private slots:
+    void checkStatus(int status);
+    void cleanTemp();
+    void emitFinished();
+    void readOutput();
+};
+
+#endif // EMULATORHANDLER_H
diff --git a/src/global.h b/src/global.h
new file mode 100644
index 0000000..063397b
--- /dev/null
+++ b/src/global.h
@@ -0,0 +1,46 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#include <QSettings>
+
+
+const QString Version = "1.8";
+
+#ifdef Q_OS_WIN
+    #define SETTINGS QSettings("mupen64plus-qt.ini", QSettings::IniFormat)
+#else
+    #define SETTINGS QSettings("mupen64plus", "mupen64plus-qt")
+#endif
+
+#endif // GLOBAL_H
diff --git a/src/logdialog.cpp b/src/logdialog.cpp
new file mode 100644
index 0000000..2197157
--- /dev/null
+++ b/src/logdialog.cpp
@@ -0,0 +1,68 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "logdialog.h"
+
+
+LogDialog::LogDialog(QString lastOutput, QWidget *parent) : QDialog(parent)
+{
+    setWindowTitle(tr("Mupen64Plus Log"));
+    setMinimumSize(600, 400);
+
+    logLayout = new QGridLayout(this);
+    logLayout->setContentsMargins(5, 10, 5, 10);
+
+    logArea = new QTextEdit(this);
+    logArea->setWordWrapMode(QTextOption::NoWrap);
+
+    QFont font;
+#ifdef Q_OS_LINUX
+    font.setFamily("Monospace");
+    font.setPointSize(9);
+#else
+    font.setFamily("Courier");
+    font.setPointSize(10);
+#endif
+    font.setFixedPitch(true);
+    logArea->setFont(font);
+
+    logArea->setPlainText(lastOutput);
+
+    logButtonBox = new QDialogButtonBox(Qt::Horizontal, this);
+    logButtonBox->addButton(tr("Close"), QDialogButtonBox::AcceptRole);
+
+    logLayout->addWidget(logArea, 0, 0);
+    logLayout->addWidget(logButtonBox, 1, 0);
+
+    connect(logButtonBox, SIGNAL(accepted()), this, SLOT(close()));
+
+    setLayout(logLayout);
+}
diff --git a/src/logdialog.h b/src/logdialog.h
new file mode 100644
index 0000000..051f460
--- /dev/null
+++ b/src/logdialog.h
@@ -0,0 +1,53 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef LOGDIALOG_H
+#define LOGDIALOG_H
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QTextEdit>
+
+
+class LogDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    explicit LogDialog(QString lastOutput, QWidget *parent = 0);
+
+private:
+    QDialogButtonBox *logButtonBox;
+    QGridLayout *logLayout;
+    QTextEdit *logArea;
+};
+
+#endif // LOGDIALOG_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..f6c4751
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,65 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QSettings>
+
+#include "global.h"
+#include "mainwindow.h"
+
+
+int main(int argc, char *argv[])
+{
+    QApplication application(argc, argv);
+
+    QCoreApplication::setOrganizationName("Mupen64Plus");
+    QCoreApplication::setApplicationName("Mupen64Plus-Qt");
+
+    MainWindow window;
+
+
+    QString maximized = SETTINGS.value("Geometry/maximized", "").toString();
+    QString windowx = SETTINGS.value("Geometry/windowx", "").toString();
+    QString windowy = SETTINGS.value("Geometry/windowy", "").toString();
+
+    if (maximized == "true") {
+        window.showMaximized();
+    } else {
+        window.show();
+    }
+
+    if (windowx == "" && windowy == "") {
+        window.move(QApplication::desktop()->screen()->rect().center() - window.rect().center());
+    }
+
+    return application.exec();
+}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
new file mode 100644
index 0000000..b0154b2
--- /dev/null
+++ b/src/mainwindow.cpp
@@ -0,0 +1,1248 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "mainwindow.h"
+
+
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
+{
+    setWindowTitle(tr("Mupen64Plus-Qt"));
+    setWindowIcon(QIcon(":/images/mupen64plus.png"));
+
+    autoloadSettings();
+
+    emulation = new EmulatorHandler(this);
+
+    connect(emulation, SIGNAL(started()), this, SLOT(disableButtons()));
+    connect(emulation, SIGNAL(finished()), this, SLOT(enableButtons()));
+    connect(emulation, SIGNAL(showLog()), this, SLOT(openLog()));
+
+
+    romCollection = new RomCollection(QStringList() << "*.z64" << "*.v64" << "*.n64" << "*.zip",
+                                      QStringList() << SETTINGS.value("Paths/roms","").toString().split("|"),
+                                      this);
+
+    connect(romCollection, SIGNAL(updateStarted(bool)), this, SLOT(disableViews(bool)));
+    connect(romCollection, SIGNAL(romAdded(Rom*, int)), this, SLOT(addToView(Rom*, int)));
+    connect(romCollection, SIGNAL(updateEnded(int, bool)), this, SLOT(enableViews(int, bool)));
+
+
+    mainWidget = new QWidget(this);
+    setCentralWidget(mainWidget);
+    setGeometry(QRect(SETTINGS.value("Geometry/windowx", 0).toInt(),
+                      SETTINGS.value("Geometry/windowy", 0).toInt(),
+                      SETTINGS.value("Geometry/width", 900).toInt(),
+                      SETTINGS.value("Geometry/height", 600).toInt()));
+
+    createMenu();
+    createRomView();
+
+    mainLayout = new QVBoxLayout(mainWidget);
+    mainLayout->setMenuBar(menuBar);
+
+    mainLayout->addWidget(emptyView);
+    mainLayout->addWidget(tableView);
+    mainLayout->addWidget(gridView);
+    mainLayout->addWidget(listView);
+
+    mainLayout->setMargin(0);
+
+    mainWidget->setLayout(mainLayout);
+    mainWidget->setMinimumSize(300, 200);
+}
+
+
+void MainWindow::addToView(Rom *currentRom, int count)
+{
+    if (SETTINGS.value("View/layout", "None") == "Table View")
+        addToTableView(currentRom);
+    else if (SETTINGS.value("View/layout", "None") == "Grid View")
+        addToGridView(currentRom, count);
+    else if (SETTINGS.value("View/layout", "None") == "List View")
+        addToListView(currentRom, count);
+}
+
+
+void MainWindow::addToGridView(Rom *currentRom, int count)
+{
+    ClickableWidget *gameGridItem = new ClickableWidget(gridWidget);
+    gameGridItem->setMinimumWidth(getGridSize("width"));
+    gameGridItem->setMaximumWidth(getGridSize("width"));
+    gameGridItem->setGraphicsEffect(getShadow(false));
+
+    //Assign ROM data to widget for use in click events
+    gameGridItem->setProperty("fileName", currentRom->fileName);
+    gameGridItem->setProperty("directory", currentRom->directory);
+    if (currentRom->goodName == "Unknown ROM" || currentRom->goodName == "Requires catalog file")
+        gameGridItem->setProperty("search", currentRom->internalName);
+    else
+        gameGridItem->setProperty("search", currentRom->goodName);
+    gameGridItem->setProperty("romMD5", currentRom->romMD5);
+    gameGridItem->setProperty("zipFile", currentRom->zipFile);
+
+    QGridLayout *gameGridLayout = new QGridLayout(gameGridItem);
+    gameGridLayout->setColumnStretch(0, 1);
+    gameGridLayout->setColumnStretch(3, 1);
+    gameGridLayout->setRowMinimumHeight(1, getImageSize("Grid").height());
+
+    QLabel *gridImageLabel = new QLabel(gameGridItem);
+    gridImageLabel->setMinimumHeight(getImageSize("Grid").height());
+    gridImageLabel->setMinimumWidth(getImageSize("Grid").width());
+    QPixmap image;
+
+    if (currentRom->imageExists) {
+        //Use uniform aspect ratio to account for fluctuations in TheGamesDB box art
+        Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio;
+
+        //Don't warp aspect ratio though if image is too far away from standard size (JP box art)
+        float aspectRatio = float(currentRom->image.width()) / currentRom->image.height();
+
+        if (aspectRatio < 1.1 || aspectRatio > 1.8)
+            aspectRatioMode = Qt::KeepAspectRatio;
+
+        image = currentRom->image.scaled(getImageSize("Grid"), aspectRatioMode, Qt::SmoothTransformation);
+    } else
+        image = QPixmap(":/images/not-found.png").scaled(getImageSize("Grid"), Qt::IgnoreAspectRatio,
+                                                         Qt::SmoothTransformation);
+
+    gridImageLabel->setPixmap(image);
+    gridImageLabel->setAlignment(Qt::AlignCenter);
+    gameGridLayout->addWidget(gridImageLabel, 1, 1);
+
+    if (SETTINGS.value("Grid/label","true") == "true") {
+        QLabel *gridTextLabel = new QLabel(gameGridItem);
+
+        //Don't allow label to be wider than image
+        gridTextLabel->setMaximumWidth(getImageSize("Grid").width());
+
+        QString text = "";
+        QString labelText = SETTINGS.value("Grid/labeltext","Filename").toString();
+
+        text = getRomInfo(labelText, currentRom);
+
+        gridTextLabel->setText(text);
+
+        QString textHex = getColor(SETTINGS.value("Grid/labelcolor","White").toString()).name();
+        int fontSize = getGridSize("font");
+
+        gridTextLabel->setStyleSheet("QLabel { font-weight: bold; color: " + textHex + "; font-size: "
+                                     + QString::number(fontSize) + "px; }");
+        gridTextLabel->setWordWrap(true);
+        gridTextLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
+
+        gameGridLayout->addWidget(gridTextLabel, 2, 1);
+    }
+
+    gameGridItem->setLayout(gameGridLayout);
+
+    gameGridItem->setMinimumHeight(gameGridItem->sizeHint().height());
+
+    int columnCount = SETTINGS.value("Grid/columncount", "4").toInt();
+    gridLayout->addWidget(gameGridItem, count / columnCount + 1, count % columnCount + 1);
+    gridWidget->adjustSize();
+
+    connect(gameGridItem, SIGNAL(singleClicked(QWidget*)), this, SLOT(highlightGridWidget(QWidget*)));
+    connect(gameGridItem, SIGNAL(doubleClicked(QWidget*)), this, SLOT(launchRomFromWidget(QWidget*)));
+}
+
+
+void MainWindow::addToListView(Rom *currentRom, int count)
+{
+    QStringList visible = SETTINGS.value("List/columns", "Filename|Internal Name|Size").toString().split("|");
+
+    if (visible.join("") == "" && SETTINGS.value("List/displaycover", "") != "true")
+        //Otherwise no columns, so don't bother populating
+        return;
+
+    ClickableWidget *gameListItem = new ClickableWidget(listWidget);
+    gameListItem->setContentsMargins(0, 0, 20, 0);
+
+    //Assign ROM data to widget for use in click events
+    gameListItem->setProperty("fileName", currentRom->fileName);
+    gameListItem->setProperty("directory", currentRom->directory);
+    if (currentRom->goodName == "Unknown ROM" || currentRom->goodName == "Requires catalog file")
+        gameListItem->setProperty("search", currentRom->internalName);
+    else
+        gameListItem->setProperty("search", currentRom->goodName);
+    gameListItem->setProperty("romMD5", currentRom->romMD5);
+    gameListItem->setProperty("zipFile", currentRom->zipFile);
+
+    QGridLayout *gameListLayout = new QGridLayout(gameListItem);
+    gameListLayout->setColumnStretch(3, 1);
+
+    //Add image
+    if (SETTINGS.value("List/displaycover", "") == "true") {
+        QLabel *listImageLabel = new QLabel(gameListItem);
+        listImageLabel->setMinimumHeight(getImageSize("List").height());
+        listImageLabel->setMinimumWidth(getImageSize("List").width());
+
+        QPixmap image;
+
+        if (currentRom->imageExists)
+            image = currentRom->image.scaled(getImageSize("List"), Qt::KeepAspectRatio,
+                                            Qt::SmoothTransformation);
+        else
+            image = QPixmap(":/images/not-found.png").scaled(getImageSize("List"), Qt::KeepAspectRatio,
+                                                             Qt::SmoothTransformation);
+
+        listImageLabel->setPixmap(image);
+        listImageLabel->setAlignment(Qt::AlignCenter);
+        gameListLayout->addWidget(listImageLabel, 0, 1);
+    }
+
+    //Create text label
+    QLabel *listTextLabel = new QLabel("", gameListItem);
+    QString listText = "";
+
+    int i = 0;
+
+    foreach (QString current, visible)
+    {
+        QString addition = "<style>h2 { margin: 0; }</style>";
+
+        if (i == 0 && SETTINGS.value("List/firstitemheader","true") == "true")
+            addition += "<h2>";
+        else
+            addition += "<b>" + current + ":</b> ";
+
+        addition += getRomInfo(current, currentRom, true) + "<br />";
+
+        if (i == 0 && SETTINGS.value("List/firstitemheader","true") == "true")
+            addition += "</h2>";
+
+        if (addition != "<style>h2 { margin: 0; }</style><b>" + current + ":</b> <br />")
+            listText += addition;
+
+        i++;
+    }
+
+    //Remove last break tag
+    listText.remove(QRegExp("<br />$"));
+
+    listTextLabel->setText(listText);
+    listTextLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    listTextLabel->setWordWrap(true);
+    gameListLayout->addWidget(listTextLabel, 0, 3);
+
+    gameListLayout->setColumnMinimumWidth(0, 20);
+    gameListLayout->setColumnMinimumWidth(2, 10);
+    gameListItem->setLayout(gameListLayout);
+
+    if (count != 0) {
+        QFrame *separator = new QFrame();
+        separator->setFrameShape(QFrame::HLine);
+        separator->setStyleSheet("margin:0;padding:0;");
+        listLayout->addWidget(separator);
+    }
+
+    listLayout->addWidget(gameListItem);
+
+    connect(gameListItem, SIGNAL(singleClicked(QWidget*)), this, SLOT(highlightListWidget(QWidget*)));
+    connect(gameListItem, SIGNAL(doubleClicked(QWidget*)), this, SLOT(launchRomFromWidget(QWidget*)));
+}
+
+
+void MainWindow::addToTableView(Rom *currentRom)
+{
+    QStringList visible = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|");
+
+    if (visible.join("") == "") //Otherwise no columns, so don't bother populating
+        return;
+
+    fileItem = new TreeWidgetItem(tableView);
+
+    //Filename for launching ROM
+    fileItem->setText(0, currentRom->fileName);
+
+    //Directory ROM is located in
+    fileItem->setText(1, currentRom->directory);
+
+    //GoodName or Internal Name for searching
+    if (currentRom->goodName == "Unknown ROM" || currentRom->goodName == "Requires catalog file")
+        fileItem->setText(2, currentRom->internalName);
+    else
+        fileItem->setText(2, currentRom->goodName);
+
+    //MD5 for cache info
+    fileItem->setText(3, currentRom->romMD5.toLower());
+
+    //Zip file
+    fileItem->setText(4, currentRom->zipFile);
+
+    int i = 5, c = 0;
+    bool addImage = false;
+
+    foreach (QString current, visible)
+    {
+        QString text = getRomInfo(current, currentRom);
+        fileItem->setText(i, text);
+
+        if (current == "GoodName" || current == "Game Title") {
+            if (text == "Unknown ROM" || text == "Requires catalog file" || text == "Not found") {
+                fileItem->setForeground(i, QBrush(Qt::gray));
+                fileItem->setData(i, Qt::UserRole, "ZZZ"); //end of sorting
+            } else
+                fileItem->setData(i, Qt::UserRole, text);
+        }
+
+        if (current == "Size")
+            fileItem->setData(i, Qt::UserRole, currentRom->sortSize);
+
+        if (current == "Release Date")
+            fileItem->setData(i, Qt::UserRole, currentRom->sortDate);
+
+        if (current == "Game Cover") {
+            c = i;
+            addImage = true;
+        }
+
+        QStringList center, right;
+
+        center << "MD5" << "CRC1" << "CRC2" << "Rumble" << "ESRB" << "Genre" << "Publisher" << "Developer";
+        right << "Size" << "Players" << "Save Type" << "Release Date" << "Rating";
+
+        if (center.contains(current))
+            fileItem->setTextAlignment(i, Qt::AlignHCenter | Qt::AlignVCenter);
+        else if (right.contains(current))
+            fileItem->setTextAlignment(i, Qt::AlignRight | Qt::AlignVCenter);
+
+        i++;
+    }
+
+    tableView->addTopLevelItem(fileItem);
+
+
+    if (currentRom->imageExists && addImage) {
+        QPixmap image(currentRom->image.scaled(getImageSize("Table"), Qt::KeepAspectRatio,
+                                              Qt::SmoothTransformation));
+
+        QWidget *imageContainer = new QWidget(tableView);
+        QGridLayout *imageGrid = new QGridLayout(imageContainer);
+        QLabel *imageLabel = new QLabel(imageContainer);
+
+        imageLabel->setPixmap(image);
+        imageGrid->addWidget(imageLabel, 1, 1);
+        imageGrid->setColumnStretch(0, 1);
+        imageGrid->setColumnStretch(2, 1);
+        imageGrid->setRowStretch(0, 1);
+        imageGrid->setRowStretch(2, 1);
+        imageGrid->setContentsMargins(0,0,0,0);
+
+        imageContainer->setLayout(imageGrid);
+
+        tableView->setItemWidget(fileItem, c, imageContainer);
+    }
+}
+
+
+void MainWindow::autoloadSettings()
+{
+    QString mupen64Path = SETTINGS.value("Paths/mupen64plus", "").toString();
+    QString dataPath = SETTINGS.value("Paths/data", "").toString();
+    QString pluginPath = SETTINGS.value("Paths/plugins", "").toString();
+
+    if (mupen64Path == "" && dataPath == "" && pluginPath == "") {
+#ifdef Q_OS_LINUX
+        //If user has not entered any settings, check common locations for them
+        QStringList mupen64Check, dataCheck, pluginCheck;
+
+        mupen64Check << "/usr/games/mupen64plus"
+                     << "/usr/bin/mupen64plus";
+
+        pluginCheck  << "/usr/lib/mupen64plus"
+                     << "/usr/lib/x86_64-linux-gnu/mupen64plus"
+                     << "/usr/lib/i386-linux-gnu/mupen64plus"
+                     << "/usr/lib/mupen64plus/mupen64plus";
+
+        dataCheck    << "/usr/share/mupen64plus"
+                     << "/usr/share/games/mupen64plus";
+
+
+        foreach (QString check, mupen64Check)
+            if (QFileInfo(check).exists())
+                SETTINGS.setValue("Paths/mupen64plus", check);
+
+        foreach (QString check, pluginCheck)
+            if (QFileInfo(check+"/mupen64plus-video-rice.so").exists())
+                SETTINGS.setValue("Paths/plugins", check);
+
+        foreach (QString check, dataCheck)
+            if (QFileInfo(check+"/mupen64plus.ini").exists())
+                SETTINGS.setValue("Paths/data", check);
+#endif
+
+#ifdef Q_OS_WIN
+        //Check for Mupen64Plus within the same directory
+        QString currentDir = QCoreApplication::applicationDirPath();
+
+        if (QFileInfo(currentDir+"/mupen64plus-ui-console.exe").exists())
+            SETTINGS.setValue("Paths/mupen64plus", currentDir+"/mupen64plus-ui-console.exe");
+        else if (QFileInfo(currentDir+"/mupen64plus.exe").exists())
+            SETTINGS.setValue("Paths/mupen64plus", currentDir+"/mupen64plus.exe");
+
+        if (QFileInfo(currentDir+"/mupen64plus-video-rice.dll").exists())
+            SETTINGS.setValue("Paths/plugins", currentDir);
+
+        if (QFileInfo(currentDir+"/mupen64plus.ini").exists())
+            SETTINGS.setValue("Paths/data", currentDir);
+#endif
+
+#ifdef Q_OS_OSX
+        //Check for Mupen64Plus App within the same directory
+        QString currentDir = QCoreApplication::applicationDirPath();
+
+        QString mupen64App = currentDir+"/mupen64plus.app/Contents";
+        if (QFileInfo(mupen64App+"/MacOS/mupen64plus").exists()) {
+            SETTINGS.setValue("Paths/mupen64plus", mupen64App+"/MacOS/mupen64plus");
+            SETTINGS.setValue("Paths/plugins", mupen64App+"/MacOS");
+            SETTINGS.setValue("Paths/data", mupen64App+"/Resources");
+        }
+#endif
+    }
+
+    //Check default location for mupen64plus.cfg in case user wants to use editor
+    QString configPath = SETTINGS.value("Paths/config", "").toString();
+
+    if (configPath == "") {
+#if QT_VERSION >= 0x050000
+        QString homeDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first();
+#else
+        QString homeDir = QDesktopServices::storageLocation(QDesktopServices::HomeLocation);
+#endif
+
+#ifdef Q_OS_WIN
+        QString configCheck = homeDir + "/AppData/Roaming/Mupen64Plus/";
+#else
+        QString configCheck = homeDir + "/.config/mupen64plus";
+#endif
+
+        if (QFileInfo(configCheck+"/mupen64plus.cfg").exists())
+            SETTINGS.setValue("Paths/config", configCheck);
+    }
+}
+
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+    SETTINGS.setValue("Geometry/windowx", geometry().x());
+    SETTINGS.setValue("Geometry/windowy", geometry().y());
+    SETTINGS.setValue("Geometry/width", geometry().width());
+    SETTINGS.setValue("Geometry/height", geometry().height());
+    if (isMaximized())
+        SETTINGS.setValue("Geometry/maximized", true);
+    else
+        SETTINGS.setValue("Geometry/maximized", "");
+
+    saveColumnWidths();
+
+    event->accept();
+}
+
+
+void MainWindow::createMenu()
+{
+    menuBar = new QMenuBar(this);
+
+
+    fileMenu = new QMenu(tr("&File"), this);
+    openAction = fileMenu->addAction(tr("&Open ROM..."));
+    fileMenu->addSeparator();
+    refreshAction = fileMenu->addAction(tr("&Refresh List"));
+    downloadAction = fileMenu->addAction(tr("&Download/Update Info..."));
+    deleteAction = fileMenu->addAction(tr("D&elete Current Info..."));
+#ifndef Q_OS_OSX //OSX does not show the quit action so the separator is unneeded
+    fileMenu->addSeparator();
+#endif
+    quitAction = fileMenu->addAction(tr("&Quit"));
+
+    openAction->setIcon(QIcon::fromTheme("document-open"));
+    refreshAction->setIcon(QIcon::fromTheme("view-refresh"));
+    quitAction->setIcon(QIcon::fromTheme("application-exit"));
+
+    downloadAction->setEnabled(false);
+    deleteAction->setEnabled(false);
+
+    menuBar->addMenu(fileMenu);
+
+
+    emulationMenu = new QMenu(tr("&Emulation"), this);
+    startAction = emulationMenu->addAction(tr("&Start"));
+    stopAction = emulationMenu->addAction(tr("St&op"));
+    emulationMenu->addSeparator();
+    logAction = emulationMenu->addAction(tr("View Log..."));
+
+    startAction->setIcon(QIcon::fromTheme("media-playback-start"));
+    stopAction->setIcon(QIcon::fromTheme("media-playback-stop"));
+
+    startAction->setEnabled(false);
+    stopAction->setEnabled(false);
+
+    menuBar->addMenu(emulationMenu);
+
+
+    settingsMenu = new QMenu(tr("&Settings"), this);
+    layoutMenu = settingsMenu->addMenu(tr("&Layout"));
+    layoutGroup = new QActionGroup(this);
+
+    QStringList layouts;
+    layouts << "None" << "Table View" << "Grid View" << "List View";
+
+    QString layoutValue = SETTINGS.value("View/layout", "None").toString();
+
+    foreach (QString layoutName, layouts)
+    {
+        QAction *layoutItem = layoutMenu->addAction(layoutName);
+        layoutItem->setData(layoutName);
+        layoutItem->setCheckable(true);
+        layoutGroup->addAction(layoutItem);
+
+        //Only enable layout changes when Mupen64Plus is not running
+        menuEnable << layoutItem;
+
+        if(layoutValue == layoutName)
+            layoutItem->setChecked(true);
+    }
+
+    editorAction = settingsMenu->addAction(tr("Edit mupen64plus.cfg..."));
+#ifndef Q_OS_OSX //OSX does not show the configure action so the separator is unneeded
+    settingsMenu->addSeparator();
+#endif
+    configureAction = settingsMenu->addAction(tr("&Configure..."));
+    configureAction->setIcon(QIcon::fromTheme("preferences-other"));
+
+    menuBar->addMenu(settingsMenu);
+
+
+    helpMenu = new QMenu(tr("&Help"), this);
+    aboutAction = helpMenu->addAction(tr("&About"));
+    aboutAction->setIcon(QIcon::fromTheme("help-about"));
+    menuBar->addMenu(helpMenu);
+
+
+    //Create list of actions that are enabled only when Mupen64Plus is not running
+    menuEnable << startAction
+               << logAction
+               << openAction
+               << refreshAction
+               << downloadAction
+               << deleteAction
+               << configureAction
+               << editorAction
+               << quitAction;
+
+    //Create list of actions that are disabled when Mupen64Plus is not running
+    menuDisable << stopAction;
+
+    connect(openAction, SIGNAL(triggered()), this, SLOT(openRom()));
+    connect(refreshAction, SIGNAL(triggered()), romCollection, SLOT(addRoms()));
+    connect(downloadAction, SIGNAL(triggered()), this, SLOT(openDownloader()));
+    connect(deleteAction, SIGNAL(triggered()), this, SLOT(openDeleteDialog()));
+    connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
+    connect(startAction, SIGNAL(triggered()), this, SLOT(launchRomFromMenu()));
+    connect(stopAction, SIGNAL(triggered()), this, SLOT(stopEmulator()));
+    connect(logAction, SIGNAL(triggered()), this, SLOT(openLog()));
+    connect(configureAction, SIGNAL(triggered()), this, SLOT(openSettings()));
+    connect(editorAction, SIGNAL(triggered()), this, SLOT(openEditor()));
+    connect(aboutAction, SIGNAL(triggered()), this, SLOT(openAbout()));
+    connect(layoutGroup, SIGNAL(triggered(QAction*)), this, SLOT(updateLayoutSetting()));
+}
+
+
+void MainWindow::createRomView()
+{
+    //Create empty view
+    emptyView = new QScrollArea(this);
+    emptyView->setStyleSheet("QScrollArea { border: none; }");
+    emptyView->setBackgroundRole(QPalette::Base);
+    emptyView->setAutoFillBackground(true);
+    emptyView->setHidden(true);
+
+    emptyLayout = new QGridLayout(emptyView);
+
+    emptyIcon = new QLabel(emptyView);
+    emptyIcon->setPixmap(QPixmap(":/images/mupen64plus.png"));
+
+    emptyLayout->addWidget(emptyIcon, 1, 1);
+    emptyLayout->setColumnStretch(0, 1);
+    emptyLayout->setColumnStretch(2, 1);
+    emptyLayout->setRowStretch(0, 1);
+    emptyLayout->setRowStretch(2, 1);
+
+    emptyView->setLayout(emptyLayout);
+
+
+    //Create table view
+    tableView = new QTreeWidget(this);
+    tableView->setWordWrap(false);
+    tableView->setAllColumnsShowFocus(true);
+    tableView->setRootIsDecorated(false);
+    tableView->setSortingEnabled(true);
+    tableView->setStyleSheet("QTreeView { border: none; } QTreeView::item { height: 25px; }");
+
+    headerView = new QHeaderView(Qt::Horizontal, this);
+    tableView->setHeader(headerView);
+    tableView->setHidden(true);
+
+
+    //Create grid view
+    gridView = new QScrollArea(this);
+    gridView->setObjectName("gridView");
+    gridView->setStyleSheet("#gridView { border: none; }");
+    gridView->setBackgroundRole(QPalette::Dark);
+    gridView->setAlignment(Qt::AlignHCenter);
+    gridView->setHidden(true);
+
+    gridView->verticalScrollBar()->setObjectName("vScrollBar");
+    gridView->horizontalScrollBar()->setObjectName("hScrollBar");
+
+    setGridBackground();
+
+
+    gridWidget = new QWidget(gridView);
+    gridWidget->setObjectName("gridWidget");
+    gridView->setWidget(gridWidget);
+
+    gridLayout = new QGridLayout(gridWidget);
+    gridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
+    gridLayout->setRowMinimumHeight(0, 10);
+
+    gridWidget->setLayout(gridLayout);
+
+    gridCurrent = false;
+    currentGridRom = 0;
+
+
+    //Create list view
+    listView = new QScrollArea(this);
+    listView->setStyleSheet("QScrollArea { border: none; }");
+    listView->setBackgroundRole(QPalette::Base);
+    listView->setWidgetResizable(true);
+    listView->setHidden(true);
+
+    listWidget = new QWidget(listView);
+    listView->setWidget(listWidget);
+
+    listLayout = new QVBoxLayout(listWidget);
+    listLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
+    listWidget->setLayout(listLayout);
+
+    listCurrent = false;
+    currentListRom = 0;
+
+
+    QString visibleLayout = SETTINGS.value("View/layout", "None").toString();
+
+    if (visibleLayout == "Table View")
+        tableView->setHidden(false);
+    else if (visibleLayout == "Grid View")
+        gridView->setHidden(false);
+    else if (visibleLayout == "List View")
+        listView->setHidden(false);
+    else
+        emptyView->setHidden(false);
+
+    romCollection->cachedRoms();
+
+    connect(tableView, SIGNAL(clicked(QModelIndex)), this, SLOT(enableButtons()));
+    connect(tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(launchRomFromTable()));
+    connect(headerView, SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
+            this, SLOT(saveSortOrder(int,Qt::SortOrder)));
+}
+
+
+void MainWindow::disableButtons()
+{
+    toggleMenus(false);
+}
+
+
+void MainWindow::disableViews(bool imageUpdated)
+{
+    resetLayouts(imageUpdated);
+    tableView->clear();
+
+    tableView->setEnabled(false);
+    gridView->setEnabled(false);
+    listView->setEnabled(false);
+    downloadAction->setEnabled(false);
+    deleteAction->setEnabled(false);
+    startAction->setEnabled(false);
+    stopAction->setEnabled(false);
+
+    //Save position in current layout
+    positionx = 0;
+    positiony = 0;
+
+    if (SETTINGS.value("View/layout", "None") == "Table View") {
+        positionx = tableView->horizontalScrollBar()->value();
+        positiony = tableView->verticalScrollBar()->value();
+    } else if (SETTINGS.value("View/layout", "None") == "Grid View") {
+        positionx = gridView->horizontalScrollBar()->value();
+        positiony = gridView->verticalScrollBar()->value();
+    } else if (SETTINGS.value("View/layout", "None") == "List View") {
+        positionx = listView->horizontalScrollBar()->value();
+        positiony = listView->verticalScrollBar()->value();
+    }
+}
+
+
+void MainWindow::enableButtons()
+{
+    toggleMenus(true);
+}
+
+
+void MainWindow::enableViews(int romCount, bool cached)
+{
+    if (romCount != 0) { //else no ROMs, so leave views disabled
+        QStringList tableVisible = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|");
+
+        if (tableVisible.join("") != "")
+            tableView->setEnabled(true);
+
+        gridView->setEnabled(true);
+        listView->setEnabled(true);
+
+        if (cached) {
+            QTimer *timer = new QTimer(this);
+            timer->setSingleShot(true);
+            timer->setInterval(0);
+            timer->start();
+
+            if (SETTINGS.value("View/layout", "None") == "Table View")
+                connect(timer, SIGNAL(timeout()), this, SLOT(setTablePosition()));
+            else if (SETTINGS.value("View/layout", "None") == "Grid View")
+                connect(timer, SIGNAL(timeout()), this, SLOT(setGridPosition()));
+            else if (SETTINGS.value("View/layout", "None") == "List View")
+                connect(timer, SIGNAL(timeout()), this, SLOT(setListPosition()));
+        }
+    }
+}
+
+
+QString MainWindow::getCurrentRomInfo(int index)
+{
+    if (index < 3) {
+        const char *infoChar;
+        int table;
+
+        switch (index) {
+            case 0:  infoChar = "fileName"; table = 0; break;
+            case 1:  infoChar = "search";   table = 2; break;
+            case 2:  infoChar = "romMD5";   table = 3; break;
+            default: infoChar = "";         table = 0; break;
+        }
+
+        QString visibleLayout = SETTINGS.value("View/layout", "None").toString();
+
+        if (visibleLayout == "Table View")
+            return tableView->currentItem()->data(table, 0).toString();
+        else if (visibleLayout == "Grid View" && gridCurrent)
+            return gridLayout->itemAt(currentGridRom)->widget()->property(infoChar).toString();
+        else if (visibleLayout == "List View" && listCurrent)
+            return listLayout->itemAt(currentListRom)->widget()->property(infoChar).toString();
+    }
+
+    return "";
+}
+
+
+void MainWindow::highlightGridWidget(QWidget *current)
+{
+    //Set all to inactive shadow
+    QLayoutItem *gridItem;
+    for (int item = 0; (gridItem = gridLayout->itemAt(item)) != NULL; item++)
+    {
+        gridItem->widget()->setGraphicsEffect(getShadow(false));
+
+        if (gridItem->widget() == current)
+            currentGridRom = item;
+    }
+
+    //Set current to active shadow
+    current->setGraphicsEffect(getShadow(true));
+
+    gridCurrent = true;
+    toggleMenus(true);
+}
+
+
+void MainWindow::highlightListWidget(QWidget *current)
+{
+    //Reset all margins
+    QLayoutItem *listItem;
+    for (int item = 0; (listItem = listLayout->itemAt(item)) != NULL; item++)
+    {
+        listItem->widget()->setContentsMargins(0, 0, 20, 0);
+
+        if (listItem->widget() == current)
+            currentListRom = item;
+    }
+
+    //Give current left margin to stand out
+    current->setContentsMargins(20, 0, 0, 0);
+
+    listCurrent = true;
+    toggleMenus(true);
+}
+
+
+void MainWindow::launchRomFromMenu()
+{
+    QString visibleLayout = layoutGroup->checkedAction()->data().toString();
+
+    if (visibleLayout == "Table View")
+        launchRomFromTable();
+    else if (visibleLayout == "Grid View" && gridCurrent)
+        launchRomFromWidget(gridLayout->itemAt(currentGridRom)->widget());
+    else if (visibleLayout == "List View" && listCurrent)
+        launchRomFromWidget(listLayout->itemAt(currentListRom)->widget());
+}
+
+
+void MainWindow::launchRomFromTable()
+{
+    QString romFileName = QVariant(tableView->currentItem()->data(0, 0)).toString();
+    QString romDirName = QVariant(tableView->currentItem()->data(1, 0)).toString();
+    QString zipFileName = QVariant(tableView->currentItem()->data(4, 0)).toString();
+    emulation->startEmulator(QDir(romDirName), romFileName, zipFileName);
+}
+
+
+void MainWindow::launchRomFromWidget(QWidget *current)
+{
+    QString romFileName = current->property("fileName").toString();
+    QString romDirName = current->property("directory").toString();
+    QString zipFileName = current->property("zipFile").toString();
+    emulation->startEmulator(QDir(romDirName), romFileName, zipFileName);
+}
+
+
+void MainWindow::launchRomFromZip()
+{
+    QString fileName = zipList->currentItem()->text();
+    zipDialog->close();
+
+    emulation->startEmulator(QDir(), fileName, openPath);
+}
+
+
+void MainWindow::openAbout()
+{
+    AboutDialog aboutDialog(this);
+    aboutDialog.exec();
+}
+
+
+void MainWindow::openDeleteDialog()
+{
+    scrapper = new TheGamesDBScrapper(this);
+    scrapper->deleteGameInfo(getCurrentRomInfo(0), getCurrentRomInfo(2));
+    delete scrapper;
+
+    romCollection->cachedRoms();
+}
+
+
+void MainWindow::openDownloader()
+{
+    DownloadDialog downloadDialog(getCurrentRomInfo(0), getCurrentRomInfo(1), getCurrentRomInfo(2), this);
+    downloadDialog.exec();
+
+    romCollection->cachedRoms();
+}
+
+
+void MainWindow::openEditor()
+{
+    QString configPath = SETTINGS.value("Paths/config", "").toString();
+    QDir configDir = QDir(configPath);
+    QString configFile = configDir.absoluteFilePath("mupen64plus.cfg");
+    QFile config(configFile);
+
+    if (configPath == "" || !config.exists()) {
+        QMessageBox::information(this, "Not Found", QString("Editor requires config directory to be ")
+                                 + "set to a directory with mupen64plus.cfg.\n\n"
+                                 + "See here for the default config location:\n"
+                                 + "https://code.google.com/p/mupen64plus/wiki/FileLocations");
+    } else {
+        ConfigEditor configEditor(configFile, this);
+        configEditor.exec();
+    }
+}
+
+
+void MainWindow::openLog()
+{
+    if (emulation->lastOutput == "") {
+        QMessageBox::information(this, "No Output", QString("There is no log. Either Mupen64Plus has not ")
+                                 + "yet run or there was no output from the last run.");
+    } else {
+        LogDialog logDialog(emulation->lastOutput, this);
+        logDialog.exec();
+    }
+}
+
+
+void MainWindow::openSettings()
+{
+    QString tableImageBefore = SETTINGS.value("Table/imagesize", "Medium").toString();
+    QString columnsBefore = SETTINGS.value("Table/columns", "Filename|Size").toString();
+    QString downloadBefore = SETTINGS.value("Other/downloadinfo", "").toString();
+
+    SettingsDialog settingsDialog(this, 0);
+    settingsDialog.exec();
+
+    QString tableImageAfter = SETTINGS.value("Table/imagesize", "Medium").toString();
+    QString columnsAfter = SETTINGS.value("Table/columns", "Filename|Size").toString();
+    QString downloadAfter = SETTINGS.value("Other/downloadinfo", "").toString();
+
+    //Reset columns widths if user has selected different columns to display
+    if (columnsBefore != columnsAfter) {
+        SETTINGS.setValue("Table/width", "");
+        tableView->setColumnCount(3);
+        tableView->setHeaderLabels(QStringList(""));
+    }
+
+    QStringList romSave = SETTINGS.value("Paths/roms","").toString().split("|");
+    if (romCollection->romPaths != romSave) {
+        romCollection->updatePaths(romSave);
+        romCollection->addRoms();
+    } else if (downloadBefore == "" && downloadAfter == "true") {
+        romCollection->addRoms();
+    } else {
+        if (tableImageBefore != tableImageAfter)
+            romCollection->cachedRoms(true);
+        else
+            romCollection->cachedRoms(false);
+    }
+
+    setGridBackground();
+    toggleMenus(true);
+}
+
+
+void MainWindow::openRom()
+{
+    QString filter = "N64 ROMs (";
+    foreach (QString type, romCollection->getFileTypes(true)) filter += type + " ";
+    filter += ");;All Files (*)";
+
+    openPath = QFileDialog::getOpenFileName(this, tr("Open ROM File"), romCollection->romPaths.at(0), filter);
+    if (openPath != "") {
+        if (QFileInfo(openPath).suffix() == "zip") {
+            QStringList zippedFiles = getZippedFiles(openPath);
+
+            QString last;
+            int count = 0;
+
+            foreach (QString file, zippedFiles) {
+                QString ext = file.right(4).toLower();
+
+                if (romCollection->getFileTypes().contains("*" + ext)) {
+                    last = file;
+                    count++;
+                }
+            }
+
+            if (count == 0)
+                QMessageBox::information(this, tr("No ROMs"), tr("No ROMs found in ZIP file."));
+            else if (count == 1)
+                emulation->startEmulator(QDir(QFileInfo(openPath).dir()), last, openPath);
+            else { //More than one ROM in zip file, so let user select
+                openZipDialog(zippedFiles);
+            }
+        } else
+            emulation->startEmulator(QDir(QFileInfo(openPath).dir()), openPath);
+    }
+}
+
+
+void MainWindow::openZipDialog(QStringList zippedFiles)
+{
+    zipDialog = new QDialog(this);
+    zipDialog->setWindowTitle(tr("Select ROM"));
+    zipDialog->setMinimumSize(200, 150);
+    zipDialog->resize(300, 150);
+
+    zipLayout = new QGridLayout(zipDialog);
+    zipLayout->setContentsMargins(5, 10, 5, 10);
+
+    zipList = new QListWidget(zipDialog);
+    foreach (QString file, zippedFiles) {
+        QString ext = file.right(4);
+
+        if (romCollection->getFileTypes().contains("*" + ext))
+            zipList->addItem(file);
+    }
+    zipList->setCurrentRow(0);
+
+    zipButtonBox = new QDialogButtonBox(Qt::Horizontal, zipDialog);
+    zipButtonBox->addButton(tr("Launch"), QDialogButtonBox::AcceptRole);
+    zipButtonBox->addButton(QDialogButtonBox::Cancel);
+
+    zipLayout->addWidget(zipList, 0, 0);
+    zipLayout->addWidget(zipButtonBox, 1, 0);
+
+    connect(zipList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(launchRomFromZip()));
+    connect(zipButtonBox, SIGNAL(accepted()), this, SLOT(launchRomFromZip()));
+    connect(zipButtonBox, SIGNAL(rejected()), zipDialog, SLOT(close()));
+
+    zipDialog->setLayout(zipLayout);
+
+    zipDialog->exec();
+}
+
+
+void MainWindow::resetLayouts(bool imageUpdated)
+{
+    QStringList tableVisible = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|");
+
+    int hidden = 5;
+
+    saveColumnWidths();
+    QStringList widths = SETTINGS.value("Table/width", "").toString().split("|");
+
+    headerLabels.clear();
+    headerLabels << "" << "" << "" << "" << "" << tableVisible; //First 5 blank for hidden columns
+
+    //Remove Game Cover title for aesthetics
+    for (int i = 0; i < headerLabels.size(); i++)
+        if (headerLabels.at(i) == "Game Cover") headerLabels.replace(i, "");
+
+    tableView->setColumnCount(headerLabels.size());
+    tableView->setHeaderLabels(headerLabels);
+    headerView->setSortIndicatorShown(false);
+
+    int height = 0, width = 0;
+    if (tableVisible.contains("Game Cover")) {
+        //Get optimal height/width for cover column
+        height = getImageSize("Table").height() * 1.1;
+        width = getImageSize("Table").width() * 1.2;
+
+        tableView->setStyleSheet("QTreeView { border: none; } QTreeView::item { height: "
+                               + QString::number(height) + "px; }");
+    } else
+        tableView->setStyleSheet("QTreeView { border: none; } QTreeView::item { height: 25px; }");
+
+    QStringList sort = SETTINGS.value("Table/sort", "").toString().split("|");
+    if (sort.size() == 2) {
+        if (sort[1] == "descending")
+            headerView->setSortIndicator(tableVisible.indexOf(sort[0]) + hidden, Qt::DescendingOrder);
+        else
+            headerView->setSortIndicator(tableVisible.indexOf(sort[0]) + hidden, Qt::AscendingOrder);
+    }
+
+    tableView->setColumnHidden(0, true); //Hidden filename for launching emulator
+    tableView->setColumnHidden(1, true); //Hidden directory of ROM location
+    tableView->setColumnHidden(2, true); //Hidden goodname for searching
+    tableView->setColumnHidden(3, true); //Hidden md5 for cache info
+    tableView->setColumnHidden(4, true); //Hidden column for zip file
+
+    int i = hidden;
+    foreach (QString current, tableVisible)
+    {
+        if (i == hidden) {
+            int c = i;
+            if (current == "Game Cover") c++; //If first column is game cover, use next column
+
+            if (SETTINGS.value("Table/stretchfirstcolumn", "true") == "true")
+#if QT_VERSION >= 0x050000
+                tableView->header()->setSectionResizeMode(c, QHeaderView::Stretch);
+#else
+                tableView->header()->setResizeMode(c, QHeaderView::Stretch);
+#endif
+            else
+#if QT_VERSION >= 0x050000
+                tableView->header()->setSectionResizeMode(c, QHeaderView::Interactive);
+#else
+                tableView->header()->setResizeMode(c, QHeaderView::Interactive);
+#endif
+        }
+
+        if (widths.size() == tableVisible.size())
+            tableView->setColumnWidth(i, widths[i - hidden].toInt());
+        else
+            tableView->setColumnWidth(i, getDefaultWidth(current, width));
+
+        //Overwrite saved value if switching image sizes
+        if (imageUpdated && current == "Game Cover")
+            tableView->setColumnWidth(i, width);
+
+        i++;
+    }
+
+
+    //Reset grid view
+    QLayoutItem *gridItem;
+    while ((gridItem = gridLayout->takeAt(0)) != NULL)
+    {
+        delete gridItem->widget();
+        delete gridItem;
+    }
+
+    gridCurrent = false;
+
+
+    //Reset list view
+    QLayoutItem *listItem;
+    while ((listItem = listLayout->takeAt(0)) != NULL)
+    {
+        delete listItem->widget();
+        delete listItem;
+    }
+
+    listCurrent = false;
+}
+
+
+void MainWindow::saveColumnWidths()
+{
+    QStringList widths;
+
+    for (int i = 5; i < tableView->columnCount(); i++)
+    {
+        widths << QString::number(tableView->columnWidth(i));
+    }
+
+    if (widths.size() > 0)
+        SETTINGS.setValue("Table/width", widths.join("|"));
+}
+
+
+void MainWindow::saveSortOrder(int column, Qt::SortOrder order)
+{
+    QString columnName = headerLabels.value(column);
+
+    if (order == Qt::DescendingOrder)
+        SETTINGS.setValue("Table/sort", columnName + "|descending");
+    else
+        SETTINGS.setValue("Table/sort", columnName + "|ascending");
+}
+
+
+void MainWindow::setGridBackground()
+{
+    gridView->setStyleSheet("#gridView { border: none; }");
+
+    QString background = SETTINGS.value("Grid/background", "").toString();
+    if (background != "") {
+        QFile backgroundFile(background);
+
+        if (backgroundFile.exists() && !QFileInfo(backgroundFile).isDir())
+            gridView->setStyleSheet(QString()
+                + "#gridView { "
+                    + "border: none; "
+                    + "background: url(" + background + "); "
+                    + "background-attachment: fixed; "
+                    + "background-position: top center; "
+                + "} #gridWidget { background: transparent; } "
+            );
+    }
+}
+
+
+void MainWindow::setGridPosition()
+{
+    gridView->horizontalScrollBar()->setValue(positionx);
+    gridView->verticalScrollBar()->setValue(positiony);
+}
+
+
+void MainWindow::setListPosition()
+{
+    listView->horizontalScrollBar()->setValue(positionx);
+    listView->verticalScrollBar()->setValue(positiony);
+}
+
+
+void MainWindow::setTablePosition()
+{
+    tableView->horizontalScrollBar()->setValue(positionx);
+    tableView->verticalScrollBar()->setValue(positiony);
+}
+
+
+void MainWindow::stopEmulator()
+{
+    emulation->stopEmulator();
+}
+
+
+void MainWindow::toggleMenus(bool active)
+{
+    foreach (QAction *next, menuEnable)
+        next->setEnabled(active);
+
+    foreach (QAction *next, menuDisable)
+        next->setEnabled(!active);
+
+    tableView->setEnabled(active);
+    gridView->setEnabled(active);
+    listView->setEnabled(active);
+
+    if (tableView->currentItem() == NULL && !gridCurrent && !listCurrent) {
+        downloadAction->setEnabled(false);
+        deleteAction->setEnabled(false);
+        startAction->setEnabled(false);
+    }
+
+    if (SETTINGS.value("Other/downloadinfo", "").toString() == "") {
+        downloadAction->setEnabled(false);
+        deleteAction->setEnabled(false);
+    }
+}
+
+void MainWindow::updateLayoutSetting()
+{
+    QString visibleLayout = layoutGroup->checkedAction()->data().toString();
+    SETTINGS.setValue("View/layout", visibleLayout);
+
+    emptyView->setHidden(true);
+    tableView->setHidden(true);
+    gridView->setHidden(true);
+    listView->setHidden(true);
+
+    romCollection->cachedRoms();
+
+    if (visibleLayout == "Table View")
+        tableView->setHidden(false);
+    else if (visibleLayout == "Grid View")
+        gridView->setHidden(false);
+    else if (visibleLayout == "List View")
+        listView->setHidden(false);
+    else
+        emptyView->setHidden(false);
+
+    startAction->setEnabled(false);
+    downloadAction->setEnabled(false);
+    deleteAction->setEnabled(false);
+}
diff --git a/src/mainwindow.h b/src/mainwindow.h
new file mode 100644
index 0000000..5e40ecc
--- /dev/null
+++ b/src/mainwindow.h
@@ -0,0 +1,160 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QHeaderView>
+#include <QMainWindow>
+#include <QMenuBar>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QStatusBar>
+
+#include "aboutdialog.h"
+#include "clickablewidget.h"
+#include "common.h"
+#include "configeditor.h"
+#include "downloaddialog.h"
+#include "emulatorhandler.h"
+#include "global.h"
+#include "logdialog.h"
+#include "romcollection.h"
+#include "settingsdialog.h"
+#include "treewidgetitem.h"
+
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    MainWindow(QWidget *parent = 0);
+
+protected:
+    void closeEvent(QCloseEvent *event);
+
+private:
+    void addToGridView(Rom *currentRom, int count);
+    void addToListView(Rom *currentRom, int count);
+    void addToTableView(Rom *currentRom);
+    void autoloadSettings();
+    void createMenu();
+    void createRomView();
+    void openZipDialog(QStringList zippedFiles);
+    void resetLayouts(bool imageUpdated = false);
+    void saveColumnWidths();
+    void setGridBackground();
+    void toggleMenus(bool active);
+
+    int currentGridRom;
+    int currentListRom;
+    int positionx;
+    int positiony;
+    bool gridCurrent;
+    bool listCurrent;
+
+    QString getCurrentRomInfo(int index);
+    QString openPath;
+    QStringList headerLabels;
+
+    QAction *aboutAction;
+    QAction *configureAction;
+    QAction *deleteAction;
+    QAction *downloadAction;
+    QAction *editorAction;
+    QAction *logAction;
+    QAction *openAction;
+    QAction *quitAction;
+    QAction *refreshAction;
+    QAction *startAction;
+    QAction *stopAction;
+    QActionGroup *layoutGroup;
+    QDialog *zipDialog;
+    QDialogButtonBox *zipButtonBox;
+    QGridLayout *emptyLayout;
+    QGridLayout *gridLayout;
+    QGridLayout *zipLayout;
+    QHeaderView *headerView;
+    QLabel *emptyIcon;
+    QList<QAction*> menuEnable;
+    QList<QAction*> menuDisable;
+    QListWidget *zipList;
+    QMenu *emulationMenu;
+    QMenu *fileMenu;
+    QMenu *helpMenu;
+    QMenu *layoutMenu;
+    QMenu *settingsMenu;
+    QMenuBar *menuBar;
+    QScrollArea *emptyView;
+    QScrollArea *listView;
+    QScrollArea *gridView;
+    QTreeWidget *tableView;
+    QVBoxLayout *listLayout;
+    QVBoxLayout *mainLayout;
+    QWidget *gridWidget;
+    QWidget *listWidget;
+    QWidget *mainWidget;
+
+    EmulatorHandler *emulation;
+    RomCollection *romCollection;
+    TheGamesDBScrapper *scrapper;
+    TreeWidgetItem *fileItem;
+
+private slots:
+    void addToView(Rom *currentRom, int count);
+    void disableButtons();
+    void disableViews(bool imageUpdated);
+    void enableButtons();
+    void enableViews(int romCount, bool cached);
+    void highlightGridWidget(QWidget *current);
+    void highlightListWidget(QWidget *current);
+    void launchRomFromMenu();
+    void launchRomFromTable();
+    void launchRomFromWidget(QWidget *current);
+    void launchRomFromZip();
+    void openAbout();
+    void openDeleteDialog();
+    void openDownloader();
+    void openEditor();
+    void openLog();
+    void openSettings();
+    void openRom();
+    void saveSortOrder(int column, Qt::SortOrder order);
+    void setGridPosition();
+    void setListPosition();
+    void setTablePosition();
+    void stopEmulator();
+    void updateLayoutSetting();
+
+};
+
+#endif // MAINWINDOW_H
diff --git a/src/romcollection.cpp b/src/romcollection.cpp
new file mode 100644
index 0000000..6533044
--- /dev/null
+++ b/src/romcollection.cpp
@@ -0,0 +1,442 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "romcollection.h"
+
+
+RomCollection::RomCollection(QStringList fileTypes, QStringList romPaths, QWidget *parent) : QObject(parent)
+{
+    this->fileTypes = fileTypes;
+    this->romPaths = romPaths;
+    this->romPaths.removeAll("");
+    this->parent = parent;
+
+    setupDatabase();
+}
+
+
+Rom RomCollection::addRom(QByteArray *romData, QString fileName, QString directory, QString zipFile,
+                          QSqlQuery query)
+{
+    Rom currentRom;
+
+    currentRom.fileName = fileName;
+    currentRom.directory = directory;
+    currentRom.internalName = QString(romData->mid(32, 20)).trimmed();
+    currentRom.romMD5 = QString(QCryptographicHash::hash(*romData,
+                                QCryptographicHash::Md5).toHex());
+    currentRom.zipFile = zipFile;
+    currentRom.sortSize = romData->size();
+
+    query.bindValue(":filename",      currentRom.fileName);
+    query.bindValue(":directory",     currentRom.directory);
+    query.bindValue(":internal_name", currentRom.internalName);
+    query.bindValue(":md5",           currentRom.romMD5);
+    query.bindValue(":zip_file",      currentRom.zipFile);
+    query.bindValue(":size",          currentRom.sortSize);
+    query.exec();
+
+    initializeRom(&currentRom, false);
+
+    return currentRom;
+}
+
+
+void RomCollection::addRoms()
+{
+    emit updateStarted();
+
+    //Count files so we know how to setup the progress dialog
+    int totalCount = 0;
+
+    foreach (QString romPath, romPaths) {
+        QDir romDir(romPath);
+
+        if (romDir.exists()) {
+            QStringList files = romDir.entryList(fileTypes, QDir::Files | QDir::NoSymLinks);
+            totalCount += files.size();
+        }
+    }
+
+    QList<Rom> roms;
+
+    if (totalCount != 0) {
+        int count = 0;
+        setupProgressDialog(totalCount);
+
+        database.open();
+        QSqlQuery query("DELETE FROM rom_collection", database);
+        query.prepare(QString("INSERT INTO rom_collection ")
+                      + "(filename, directory, internal_name, md5, zip_file, size) "
+                      + "VALUES (:filename, :directory, :internal_name, :md5, :zip_file, :size)");
+
+        scrapper = new TheGamesDBScrapper(parent);
+
+        foreach (QString romPath, romPaths)
+        {
+            QDir romDir(romPath);
+            QStringList files = romDir.entryList(fileTypes, QDir::Files | QDir::NoSymLinks);
+
+            int romCount = 0;
+
+            foreach (QString fileName, files)
+            {
+                QString completeFileName = romDir.absoluteFilePath(fileName);
+                QFile file(completeFileName);
+
+                //If file is a zip file, extract info from any zipped ROMs
+                if (QFileInfo(file).suffix().toLower() == "zip") {
+                    foreach (QString zippedFile, getZippedFiles(completeFileName))
+                    {
+                        QString ext = zippedFile.right(4).toLower();
+
+                        //check for ROM files
+                        if (fileTypes.contains("*" + ext)) {
+                            QByteArray *romData = getZippedRom(zippedFile, completeFileName);
+
+                            if (fileTypes.contains("*.v64"))
+                                *romData = byteswap(*romData);
+
+                            if (romData->left(4).toHex() == "80371240") { //Else invalid
+                                roms.append(addRom(romData, zippedFile, romPath, fileName, query));
+                                romCount++;
+                            }
+
+                            delete romData;
+                        }
+                    }
+                } else { //Just a normal file
+                    file.open(QIODevice::ReadOnly);
+                    QByteArray *romData = new QByteArray(file.readAll());
+                    file.close();
+
+                    if (fileTypes.contains("*.v64"))
+                        *romData = byteswap(*romData);
+
+                    if (romData->left(4).toHex() == "80371240") { //Else invalid
+                        roms.append(addRom(romData, fileName, romPath, "", query));
+                        romCount++;
+                    }
+
+                    delete romData;
+                }
+
+                count++;
+                progress->setValue(count);
+                QCoreApplication::processEvents(QEventLoop::AllEvents);
+            }
+
+            if (romCount == 0)
+                QMessageBox::warning(parent, "Warning", "No ROMs found in " + romPath + ".");
+        }
+
+        delete scrapper;
+        database.close();
+        progress->close();
+    } else if (romPaths.size() != 0) {
+        QMessageBox::warning(parent, "Warning", "No ROMs found.");
+    }
+
+    qSort(roms.begin(), roms.end(), romSorter);
+
+    for (int i = 0; i < roms.size(); i++)
+        emit romAdded(&roms[i], i);
+
+    emit updateEnded(roms.size());
+}
+
+
+void RomCollection::cachedRoms(bool imageUpdated)
+{
+    emit updateStarted(imageUpdated);
+
+    database.open();
+    QSqlQuery query("SELECT filename, directory, md5, internal_name, zip_file, size FROM rom_collection",
+                    database);
+
+    query.last();
+    int romCount = query.at() + 1;
+    query.seek(-1);
+
+    if (romCount == -1) { //Nothing cached so try adding ROMs instead
+        addRoms();
+        return;
+    }
+
+    QList<Rom> roms;
+
+    int count = 0;
+    bool showProgress = false;
+    QTime checkPerformance;
+
+    while (query.next())
+    {
+        Rom currentRom;
+
+        currentRom.fileName = query.value(0).toString();
+        currentRom.directory = query.value(1).toString();
+        currentRom.romMD5 = query.value(2).toString();
+        currentRom.internalName = query.value(3).toString();
+        currentRom.zipFile = query.value(4).toString();
+        currentRom.sortSize = query.value(5).toInt();
+
+        //Check performance of adding first item to see if progress dialog needs to be shown
+        if (count == 0) checkPerformance.start();
+
+        initializeRom(&currentRom, true);
+        roms.append(currentRom);
+
+        if (count == 0) {
+            int runtime = checkPerformance.elapsed();
+
+            //check if operation expected to take longer than two seconds
+            if (runtime * romCount > 2000) {
+                setupProgressDialog(romCount);
+                showProgress = true;
+            }
+        }
+
+        count++;
+
+        if (showProgress) {
+            progress->setValue(count);
+            QCoreApplication::processEvents(QEventLoop::AllEvents);
+        }
+    }
+
+    database.close();
+
+    if (showProgress)
+        progress->close();
+
+    qSort(roms.begin(), roms.end(), romSorter);
+
+    for (int i = 0; i < roms.size(); i++)
+        emit romAdded(&roms[i], i);
+
+    emit updateEnded(roms.size(), true);
+}
+
+
+QStringList RomCollection::getFileTypes(bool archives)
+{
+    QStringList returnList = fileTypes;
+
+    if (!archives)
+        returnList.removeOne("*.zip");
+
+    return returnList;
+}
+
+
+void RomCollection::initializeRom(Rom *currentRom, bool cached)
+{
+    QSettings *romCatalog = new QSettings(parent);
+    QString dataPath = SETTINGS.value("Paths/data", "").toString();
+    QDir dataDir(dataPath);
+    QString catalogFile = dataDir.absoluteFilePath("mupen64plus.ini");
+
+    QDir romDir(currentRom->directory);
+
+    //Default text for GoodName to notify user
+    currentRom->goodName = "Requires catalog file";
+    currentRom->imageExists = false;
+
+    bool getGoodName = false;
+    if (QFileInfo(catalogFile).exists()) {
+        romCatalog = new QSettings(catalogFile, QSettings::IniFormat, parent);
+        getGoodName = true;
+    }
+
+    QFile file(romDir.absoluteFilePath(currentRom->fileName));
+
+    currentRom->romMD5 = currentRom->romMD5.toUpper();
+    currentRom->baseName = QFileInfo(file).completeBaseName();
+    currentRom->size = QObject::tr("%1 MB").arg((currentRom->sortSize + 1023) / 1024 / 1024);
+
+    if (getGoodName) {
+        //Join GoodName on ", ", otherwise entries with a comma won't show
+        QVariant goodNameVariant = romCatalog->value(currentRom->romMD5+"/GoodName","Unknown ROM");
+        currentRom->goodName = goodNameVariant.toStringList().join(", ");
+
+        QStringList CRC = romCatalog->value(currentRom->romMD5+"/CRC","").toString().split(" ");
+
+        if (CRC.size() == 2) {
+            currentRom->CRC1 = CRC[0];
+            currentRom->CRC2 = CRC[1];
+        }
+
+        QString newMD5 = romCatalog->value(currentRom->romMD5+"/RefMD5","").toString();
+        if (newMD5 == "")
+            newMD5 = currentRom->romMD5;
+
+        currentRom->players = romCatalog->value(newMD5+"/Players","").toString();
+        currentRom->saveType = romCatalog->value(newMD5+"/SaveType","").toString();
+        currentRom->rumble = romCatalog->value(newMD5+"/Rumble","").toString();
+    }
+
+    if (!cached && SETTINGS.value("Other/downloadinfo", "").toString() == "true") {
+        if (currentRom->goodName != "Unknown ROM" && currentRom->goodName != "Requires catalog file") {
+            scrapper->downloadGameInfo(currentRom->romMD5, currentRom->goodName);
+        } else {
+            //tweak internal name by adding spaces to get better results
+            QString search = currentRom->internalName;
+            search.replace(QRegExp("([a-z])([A-Z])"),"\\1 \\2");
+            search.replace(QRegExp("([^ \\d])(\\d)"),"\\1 \\2");
+            scrapper->downloadGameInfo(currentRom->romMD5, search);
+        }
+
+    }
+
+    if (SETTINGS.value("Other/downloadinfo", "").toString() == "true") {
+        QString cacheDir = getDataLocation() + "/cache";
+
+        QString dataFile = cacheDir + "/" + currentRom->romMD5.toLower() + "/data.xml";
+        QFile file(dataFile);
+
+        file.open(QIODevice::ReadOnly);
+        QString dom = file.readAll();
+        file.close();
+
+        QDomDocument xml;
+        xml.setContent(dom);
+        QDomNode game = xml.elementsByTagName("Game").at(0);
+
+        //Remove any non-standard characters
+        QString regex = "[^A-Za-z 0-9 \\.,\\?'""!@#\\$%\\^&\\*\\(\\)-_=\\+;:<>\\/\\\\|\\}\\{\\[\\]`~]*";
+
+        currentRom->gameTitle = game.firstChildElement("GameTitle").text().remove(QRegExp(regex));
+        if (currentRom->gameTitle == "") currentRom->gameTitle = "Not found";
+
+        currentRom->releaseDate = game.firstChildElement("ReleaseDate").text();
+
+        //Fix missing 0's in date
+        currentRom->releaseDate.replace(QRegExp("^(\\d)/(\\d{2})/(\\d{4})"), "0\\1/\\2/\\3");
+        currentRom->releaseDate.replace(QRegExp("^(\\d{2})/(\\d)/(\\d{4})"), "\\1/0\\2/\\3");
+        currentRom->releaseDate.replace(QRegExp("^(\\d)/(\\d)/(\\d{4})"), "0\\1/0\\2/\\3");
+
+        currentRom->sortDate = currentRom->releaseDate;
+        currentRom->sortDate.replace(QRegExp("(\\d{2})/(\\d{2})/(\\d{4})"), "\\3-\\1-\\2");
+
+        currentRom->overview = game.firstChildElement("Overview").text().remove(QRegExp(regex));
+        currentRom->esrb = game.firstChildElement("ESRB").text();
+
+        int count = 0;
+        QDomNode genreNode = game.firstChildElement("Genres").firstChild();
+        while(!genreNode.isNull())
+        {
+            if (count != 0)
+                currentRom->genre += "/" + genreNode.toElement().text();
+            else
+                currentRom->genre = genreNode.toElement().text();
+
+            genreNode = genreNode.nextSibling();
+            count++;
+        }
+
+        currentRom->publisher = game.firstChildElement("Publisher").text();
+        currentRom->developer = game.firstChildElement("Developer").text();
+        currentRom->rating = game.firstChildElement("Rating").text();
+
+        foreach (QString ext, QStringList() << "jpg" << "png")
+        {
+            QString imageFile = getDataLocation() + "/cache/"
+                                + currentRom->romMD5.toLower() + "/boxart-front." + ext;
+            QFile cover(imageFile);
+
+            if (cover.exists()&& currentRom->image.load(imageFile)) {
+                currentRom->imageExists = true;
+                break;
+            }
+        }
+    }
+}
+
+
+void RomCollection::setupDatabase()
+{
+    // Bump this when updating rom_collection structure
+    // Will cause clients to delete and recreate the table
+    int dbVersion = 1;
+
+    database = QSqlDatabase::addDatabase("QSQLITE");
+    database.setDatabaseName(getDataLocation() + "/mupen64plus-qt.sqlite");
+
+    if (!database.open())
+        QMessageBox::warning(parent, "Database Not Loaded",
+                             "Could not connect to Sqlite database. Application may misbehave.");
+
+    QSqlQuery version = database.exec("PRAGMA user_version");
+    version.next();
+
+    if (version.value(0).toInt() != dbVersion) { //old database version, reset rom_collection
+        version.finish();
+
+        database.exec("DROP TABLE rom_collection");
+        database.exec("PRAGMA user_version = " + QString::number(dbVersion));
+    }
+
+    database.exec(QString()
+                    + "CREATE TABLE IF NOT EXISTS rom_collection ("
+                        + "rom_id INTEGER PRIMARY KEY ASC, "
+                        + "filename TEXT NOT NULL, "
+                        + "directory TEXT NOT NULL, "
+                        + "md5 TEXT NOT NULL, "
+                        + "internal_name TEXT, "
+                        + "zip_file TEXT, "
+                        + "size INTEGER)");
+
+    database.close();
+}
+
+
+void RomCollection::setupProgressDialog(int size)
+{
+    progress = new QProgressDialog("Loading ROMs...", "Cancel", 0, size, parent);
+#if QT_VERSION >= 0x050000
+    progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowCloseButtonHint);
+    progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowMinimizeButtonHint);
+    progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowContextHelpButtonHint);
+#else
+    progress->setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
+#endif
+    progress->setCancelButton(0);
+    progress->setWindowModality(Qt::WindowModal);
+
+    progress->show();
+}
+
+
+void RomCollection::updatePaths(QStringList romPaths)
+{
+    this->romPaths = romPaths;
+    this->romPaths.removeAll("");
+}
diff --git a/src/romcollection.h b/src/romcollection.h
new file mode 100644
index 0000000..2a87d2d
--- /dev/null
+++ b/src/romcollection.h
@@ -0,0 +1,82 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef ROMCOLLECTION_H
+#define ROMCOLLECTION_H
+
+#include <QCryptographicHash>
+#include <QObject>
+#include <QProgressDialog>
+
+#include <QtSql/QSqlDatabase>
+#include <QtSql/QSqlQuery>
+
+#include "common.h"
+#include "global.h"
+#include "thegamesdbscrapper.h"
+
+
+class RomCollection : public QObject
+{
+    Q_OBJECT
+public:
+    explicit RomCollection(QStringList fileTypes, QStringList romPaths, QWidget *parent = 0);
+    void cachedRoms(bool imageUpdated = false);
+    void updatePaths(QStringList romPaths);
+
+    QStringList getFileTypes(bool archives = false);
+    QStringList romPaths;
+
+public slots:
+    void addRoms();
+
+signals:
+    void romAdded(Rom *currentRom, int count);
+    void updateEnded(int romCount, bool cached = false);
+    void updateStarted(bool imageUpdated = false);
+
+private:
+    void initializeRom(Rom *currentRom, bool cached);
+    void setupDatabase();
+    void setupProgressDialog(int size);
+
+    Rom addRom(QByteArray *romData, QString fileName, QString directory, QString zipFile, QSqlQuery query);
+
+    QStringList fileTypes;
+
+    QWidget *parent;
+    QProgressDialog *progress;
+    QSqlDatabase database;
+
+    TheGamesDBScrapper *scrapper;
+};
+
+#endif // ROMCOLLECTION_H
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
new file mode 100644
index 0000000..16b7a0c
--- /dev/null
+++ b/src/settingsdialog.cpp
@@ -0,0 +1,759 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "settingsdialog.h"
+#include "ui_settingsdialog.h"
+
+
+SettingsDialog::SettingsDialog(QWidget *parent, int activeTab) : QDialog(parent), ui(new Ui::SettingsDialog)
+{
+    ui->setupUi(this);
+    ui->tabWidget->setCurrentIndex(activeTab);
+
+
+    //Populate Paths tab
+    ui->mupen64Path->setText(SETTINGS.value("Paths/mupen64plus", "").toString());
+    ui->pluginPath->setText(SETTINGS.value("Paths/plugins", "").toString());
+    ui->dataPath->setText(SETTINGS.value("Paths/data", "").toString());
+    ui->configPath->setText(SETTINGS.value("Paths/config", "").toString());
+
+    QStringList romDirectories = SETTINGS.value("Paths/roms", "").toString().split("|");
+    romDirectories.removeAll("");
+    foreach (QString directory, romDirectories)
+        ui->romList->addItem(directory);
+
+    connect(ui->mupen64Button, SIGNAL(clicked()), this, SLOT(browseMupen64()));
+    connect(ui->pluginButton, SIGNAL(clicked()), this, SLOT(browsePlugin()));
+    connect(ui->dataButton, SIGNAL(clicked()), this, SLOT(browseData()));
+    connect(ui->configButton, SIGNAL(clicked()), this, SLOT(browseConfig()));
+    connect(ui->romAddButton, SIGNAL(clicked()), this, SLOT(addRomDirectory()));
+    connect(ui->romRemoveButton, SIGNAL(clicked()), this, SLOT(removeRomDirectory()));
+
+
+    //Populate Emulation tab
+    QString emuMode = SETTINGS.value("Emulation/mode", "").toString();
+    if (emuMode == "0")
+        ui->pureButton->setChecked(true);
+    else if (emuMode == "1")
+        ui->cachedButton->setChecked(true);
+    else
+        ui->dynamicButton->setChecked(true);
+
+
+    //Populate Graphics tab
+    if (SETTINGS.value("Graphics/osd", "").toString() == "true")
+        ui->osdOption->setChecked(true);
+    if (SETTINGS.value("Graphics/fullscreen", "").toString() == "true")
+        ui->fullscreenOption->setChecked(true);
+
+    QStringList useableModes, modes;
+    useableModes << "default"; //Allow users to use the screen resolution set in the config file
+
+    modes << "2560x1600"
+          << "2560x1440"
+          << "2048x1152"
+          << "1920x1200"
+          << "1920x1080"
+          << "1680x1050"
+          << "1600x1200"
+          << "1600x900"
+          << "1440x900"
+          << "1400x1050"
+          << "1366x768"
+          << "1360x768"
+          << "1280x1024"
+          << "1280x960"
+          << "1280x800"
+          << "1280x768"
+          << "1280x720"
+          << "1152x864"
+          << "1024x768"
+          << "1024x600"
+          << "800x600"
+          << "640x480";
+
+
+    desktop = new QDesktopWidget;
+    int screenWidth = desktop->width();
+    int screenHeight = desktop->height();
+
+    foreach (QString mode, modes)
+    {
+        QStringList values = mode.split("x");
+
+        if (values.value(0).toInt() <= screenWidth && values.value(1).toInt() <= screenHeight)
+            useableModes << mode;
+    }
+
+    ui->resolutionBox->insertItems(0, useableModes);
+    int resIndex = useableModes.indexOf(SETTINGS.value("Graphics/resolution","").toString());
+    if (resIndex >= 0) ui->resolutionBox->setCurrentIndex(resIndex);
+
+
+    //Populate Plugins tab
+    QStringList audioPlugins, inputPlugins, rspPlugins, videoPlugins;
+    pluginsDir = QDir(SETTINGS.value("Paths/plugins", "").toString());
+
+    if (pluginsDir.exists()) {
+        QStringList files = pluginsDir.entryList(QStringList() << "*", QDir::Files);
+
+        if (files.size() > 0) {
+            foreach (QString fileName, files)
+            {
+                QString baseName = QFileInfo(fileName).completeBaseName();
+
+                if (fileName.contains("-audio-"))
+                    audioPlugins << baseName;
+                else if (fileName.contains("-input-"))
+                    inputPlugins << baseName;
+                else if (fileName.contains("-rsp-"))
+                    rspPlugins << baseName;
+                else if (fileName.contains("-video-"))
+                    videoPlugins << baseName;
+            }
+        }
+    }
+
+    ui->videoBox->insertItems(0, videoPlugins);
+    ui->audioBox->insertItems(0, audioPlugins);
+    ui->inputBox->insertItems(0, inputPlugins);
+    ui->rspBox->insertItems(0, rspPlugins);
+
+    //Set Rice as default
+    QString videoDefault = "";
+    if (videoPlugins.contains("mupen64plus-video-rice"))
+        videoDefault = "mupen64plus-video-rice";
+
+    int videoIndex = videoPlugins.indexOf(SETTINGS.value("Plugins/video",videoDefault).toString());
+    int audioIndex = audioPlugins.indexOf(SETTINGS.value("Plugins/audio","").toString());
+    int inputIndex = inputPlugins.indexOf(SETTINGS.value("Plugins/input","").toString());
+    int rspIndex = rspPlugins.indexOf(SETTINGS.value("Plugins/rsp","").toString());
+
+    if (videoIndex >= 0) ui->videoBox->setCurrentIndex(videoIndex);
+    if (audioIndex >= 0) ui->audioBox->setCurrentIndex(audioIndex);
+    if (inputIndex >= 0) ui->inputBox->setCurrentIndex(inputIndex);
+    if (rspIndex >= 0) ui->rspBox->setCurrentIndex(rspIndex);
+
+
+    //Populate Table tab
+    QStringList sizes;
+    sizes << "Extra Small"
+          << "Small"
+          << "Medium"
+          << "Large"
+          << "Extra Large";
+
+    if (SETTINGS.value("Other/downloadinfo", "").toString() == "true")
+        populateTableAndListTab(true);
+    else
+        populateTableAndListTab(false);
+
+    if (SETTINGS.value("Table/stretchfirstcolumn", "true").toString() == "true")
+        ui->tableStretchOption->setChecked(true);
+
+    ui->tableSizeBox->insertItems(0, sizes);
+    int tableSizeIndex = sizes.indexOf(SETTINGS.value("Table/imagesize","Medium").toString());
+    if (tableSizeIndex >= 0) ui->tableSizeBox->setCurrentIndex(tableSizeIndex);
+
+    connect(ui->tableAddButton, SIGNAL(clicked()), this, SLOT(tableAddColumn()));
+    connect(ui->tableRemoveButton, SIGNAL(clicked()), this, SLOT(tableRemoveColumn()));
+    connect(ui->tableSortUpButton, SIGNAL(clicked()), this, SLOT(tableSortUp()));
+    connect(ui->tableSortDownButton, SIGNAL(clicked()), this, SLOT(tableSortDown()));
+
+
+    //Populate Grid tab
+    QStringList colors;
+    colors << "Black"
+           << "White"
+           << "Light Gray"
+           << "Dark Gray"
+           << "Green"
+           << "Cyan"
+           << "Blue"
+           << "Purple"
+           << "Red"
+           << "Pink"
+           << "Orange"
+           << "Yellow"
+           << "Brown";
+
+    ui->gridSizeBox->insertItems(0, sizes);
+    int gridSizeIndex = sizes.indexOf(SETTINGS.value("Grid/imagesize","Medium").toString());
+    if (gridSizeIndex >= 0) ui->gridSizeBox->setCurrentIndex(gridSizeIndex);
+
+    int gridColumnCount = SETTINGS.value("Grid/columncount","4").toInt();
+    ui->columnCountBox->setValue(gridColumnCount);
+
+    ui->shadowActiveBox->insertItems(0, colors);
+    int activeIndex = colors.indexOf(SETTINGS.value("Grid/activecolor","Cyan").toString());
+    if (activeIndex >= 0) ui->shadowActiveBox->setCurrentIndex(activeIndex);
+
+    ui->shadowInactiveBox->insertItems(0, colors);
+    int inactiveIndex = colors.indexOf(SETTINGS.value("Grid/inactivecolor","Black").toString());
+    if (inactiveIndex >= 0) ui->shadowInactiveBox->setCurrentIndex(inactiveIndex);
+
+    //Widgets to enable when label active
+    labelEnable << ui->labelTextLabel
+                << ui->labelTextBox
+                << ui->labelColorLabel
+                << ui->labelColorBox;
+
+    if (SETTINGS.value("Grid/label", "true").toString() == "true") {
+        toggleLabel(true);
+        ui->labelOption->setChecked(true);
+    } else
+        toggleLabel(false);
+
+    ui->labelColorBox->insertItems(0, colors);
+    int labelColorIndex = colors.indexOf(SETTINGS.value("Grid/labelcolor","White").toString());
+    if (labelColorIndex >= 0) ui->labelColorBox->setCurrentIndex(labelColorIndex);
+
+    ui->backgroundPath->setText(SETTINGS.value("Grid/background", "").toString());
+
+    if (SETTINGS.value("Grid/sortdirection", "ascending").toString() == "descending")
+        ui->gridDescendingOption->setChecked(true);
+
+    connect(ui->backgroundButton, SIGNAL(clicked()), this, SLOT(browseBackground()));
+    connect(ui->labelOption, SIGNAL(toggled(bool)), this, SLOT(toggleLabel(bool)));
+
+
+    //Populate List tab
+    listCoverEnable << ui->listSizeLabel
+                    << ui->listSizeBox;
+
+    if (SETTINGS.value("List/displaycover", "").toString() == "true") {
+        toggleListCover(true);
+        ui->listCoverOption->setChecked(true);
+    } else
+        toggleListCover(false);
+
+    if (SETTINGS.value("List/firstitemheader", "true").toString() == "true")
+        ui->listHeaderOption->setChecked(true);
+
+    ui->listSizeBox->insertItems(0, sizes);
+    int listSizeIndex = sizes.indexOf(SETTINGS.value("List/imagesize","Medium").toString());
+    if (listSizeIndex >= 0) ui->listSizeBox->setCurrentIndex(listSizeIndex);
+
+    if (SETTINGS.value("List/sortdirection", "ascending").toString() == "descending")
+        ui->listDescendingOption->setChecked(true);
+
+
+    connect(ui->listCoverOption, SIGNAL(toggled(bool)), this, SLOT(toggleListCover(bool)));
+    connect(ui->listAddButton, SIGNAL(clicked()), this, SLOT(listAddColumn()));
+    connect(ui->listRemoveButton, SIGNAL(clicked()), this, SLOT(listRemoveColumn()));
+    connect(ui->listSortUpButton, SIGNAL(clicked()), this, SLOT(listSortUp()));
+    connect(ui->listSortDownButton, SIGNAL(clicked()), this, SLOT(listSortDown()));
+
+
+    //Populate Other tab
+    downloadEnable << ui->tableSizeLabel
+                   << ui->tableSizeBox
+                   << ui->listCoverOption
+                   << ui->listSizeLabel
+                   << ui->listSizeBox;
+
+    if (SETTINGS.value("Other/downloadinfo", "").toString() == "true") {
+        toggleDownload(true);
+        ui->downloadOption->setChecked(true);
+    } else
+        toggleDownload(false);
+
+    if (SETTINGS.value("saveoptions", "").toString() == "true")
+        ui->saveOption->setChecked(true);
+
+    ui->parametersLine->setText(SETTINGS.value("Other/parameters", "").toString());
+
+    connect(ui->downloadOption, SIGNAL(toggled(bool)), this, SLOT(toggleDownload(bool)));
+    connect(ui->downloadOption, SIGNAL(toggled(bool)), this, SLOT(populateTableAndListTab(bool)));
+
+
+    connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(editSettings()));
+    connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+}
+
+
+SettingsDialog::~SettingsDialog()
+{
+    delete ui;
+}
+
+
+void SettingsDialog::addColumn(QListWidget *currentList, QListWidget *availableList)
+{
+    int row = availableList->currentRow();
+
+    if (row >= 0) {
+        currentList->addItem(availableList->currentItem()->text());
+        delete availableList->takeItem(row);
+    }
+}
+
+
+void SettingsDialog::addRomDirectory()
+{
+    QString path = QFileDialog::getExistingDirectory(this, tr("ROM Directory"));
+    if (path != "") {
+        //check for duplicates
+        bool found = false;
+        foreach (QListWidgetItem *item, ui->romList->findItems("*", Qt::MatchWildcard))
+            if (path == item->text())
+                found = true;
+
+        if (!found)
+            ui->romList->addItem(path);
+    }
+}
+
+
+void SettingsDialog::browseBackground()
+{
+    QString path = QFileDialog::getOpenFileName(this, tr("Background Image"));
+    if (path != "")
+        ui->backgroundPath->setText(path);
+}
+
+
+void SettingsDialog::browseMupen64()
+{
+    QString path = QFileDialog::getOpenFileName(this, tr("Mupen64Plus Executable"));
+    if (path != "")
+        ui->mupen64Path->setText(path);
+
+#ifdef Q_OS_OSX
+    //Allow OSX users to just select the .app directory and auto-populate for them
+    if (path.right(15) == "mupen64plus.app") {
+        ui->mupen64Path->setText(path+"/Contents/MacOS/mupen64plus");
+        ui->pluginPath->setText(path+"/Contents/MacOS");
+        ui->dataPath->setText(path+"/Contents/Resources");
+    }
+#endif
+}
+
+
+void SettingsDialog::browsePlugin()
+{
+    QString path = QFileDialog::getExistingDirectory(this, tr("Plugin Directory"));
+    if (path != "")
+        ui->pluginPath->setText(path);
+
+}
+
+
+void SettingsDialog::browseData()
+{
+    QString path = QFileDialog::getExistingDirectory(this, tr("Data Directory"));
+    if (path != "")
+        ui->dataPath->setText(path);
+}
+
+
+void SettingsDialog::browseConfig()
+{
+    QString path = QFileDialog::getExistingDirectory(this, tr("Config Directory"));
+    if (path != "")
+        ui->configPath->setText(path);
+}
+
+
+void SettingsDialog::editSettings()
+{
+    //Set download option first
+    if (ui->downloadOption->isChecked()) {
+        SETTINGS.setValue("Other/downloadinfo", true);
+        populateAvailable(true); //This removes thegamesdb.net options if user unselects download
+    } else {
+        SETTINGS.setValue("Other/downloadinfo", "");
+        populateAvailable(false);
+    }
+
+
+    //Paths tab
+    SETTINGS.setValue("Paths/mupen64plus", ui->mupen64Path->text());
+    SETTINGS.setValue("Paths/plugins", ui->pluginPath->text());
+    SETTINGS.setValue("Paths/data", ui->dataPath->text());
+    SETTINGS.setValue("Paths/config", ui->configPath->text());
+
+    QStringList romDirectories;
+    foreach (QListWidgetItem *item, ui->romList->findItems("*", Qt::MatchWildcard))
+        romDirectories << item->text();
+
+    SETTINGS.setValue("Paths/roms", romDirectories.join("|"));
+
+
+    //Emulation tab
+    if (ui->pureButton->isChecked())
+        SETTINGS.setValue("Emulation/mode", "0");
+    else if (ui->cachedButton->isChecked())
+        SETTINGS.setValue("Emulation/mode", "1");
+    else
+        SETTINGS.setValue("Emulation/mode", "2");
+
+
+    //Graphics tab
+    if (ui->osdOption->isChecked())
+        SETTINGS.setValue("Graphics/osd", true);
+    else
+        SETTINGS.setValue("Graphics/osd", "");
+
+    if (ui->fullscreenOption->isChecked())
+        SETTINGS.setValue("Graphics/fullscreen", true);
+    else
+        SETTINGS.setValue("Graphics/fullscreen", "");
+
+    if (ui->resolutionBox->currentText() != "default")
+        SETTINGS.setValue("Graphics/resolution", ui->resolutionBox->currentText());
+    else
+        SETTINGS.setValue("Graphics/resolution", "");
+
+
+    //Plugins tab
+    SETTINGS.setValue("Plugins/video", ui->videoBox->currentText());
+    SETTINGS.setValue("Plugins/audio", ui->audioBox->currentText());
+    SETTINGS.setValue("Plugins/input", ui->inputBox->currentText());
+    SETTINGS.setValue("Plugins/rsp", ui->rspBox->currentText());
+
+
+    //Table tab
+    QStringList tableVisibleItems;
+    foreach (QListWidgetItem *item, ui->tableCurrentList->findItems("*", Qt::MatchWildcard))
+        if (available.contains(item->text()))
+            tableVisibleItems << item->text();
+
+    SETTINGS.setValue("Table/columns", tableVisibleItems.join("|"));
+
+    if (ui->tableStretchOption->isChecked())
+        SETTINGS.setValue("Table/stretchfirstcolumn", true);
+    else
+        SETTINGS.setValue("Table/stretchfirstcolumn", "");
+
+    SETTINGS.setValue("Table/imagesize", ui->tableSizeBox->currentText());
+
+
+    //Grid tab
+    SETTINGS.setValue("Grid/imagesize", ui->gridSizeBox->currentText());
+    SETTINGS.setValue("Grid/columncount", ui->columnCountBox->value());
+    SETTINGS.setValue("Grid/inactivecolor", ui->shadowInactiveBox->currentText());
+    SETTINGS.setValue("Grid/activecolor", ui->shadowActiveBox->currentText());
+    SETTINGS.setValue("Grid/background", ui->backgroundPath->text());
+
+    if (ui->labelOption->isChecked())
+        SETTINGS.setValue("Grid/label", true);
+    else
+        SETTINGS.setValue("Grid/label", "");
+
+    SETTINGS.setValue("Grid/labeltext", ui->labelTextBox->currentText());
+    SETTINGS.setValue("Grid/labelcolor", ui->labelColorBox->currentText());
+    SETTINGS.setValue("Grid/sort", ui->gridSortBox->currentText());
+
+    if (ui->gridDescendingOption->isChecked())
+        SETTINGS.setValue("Grid/sortdirection", "descending");
+    else
+        SETTINGS.setValue("Grid/sortdirection", "ascending");
+
+
+    //List tab
+    QStringList listVisibleItems;
+    foreach (QListWidgetItem *item, ui->listCurrentList->findItems("*", Qt::MatchWildcard))
+        if (available.contains(item->text()))
+            listVisibleItems << item->text();
+
+    SETTINGS.setValue("List/columns", listVisibleItems.join("|"));
+
+    if (ui->listHeaderOption->isChecked())
+        SETTINGS.setValue("List/firstitemheader", true);
+    else
+        SETTINGS.setValue("List/firstitemheader", "");
+
+    if (ui->listCoverOption->isChecked() && ui->downloadOption->isChecked())
+        SETTINGS.setValue("List/displaycover", true);
+    else
+        SETTINGS.setValue("List/displaycover", "");
+
+    SETTINGS.setValue("List/imagesize", ui->listSizeBox->currentText());
+    SETTINGS.setValue("List/sort", ui->listSortBox->currentText());
+
+    if (ui->listDescendingOption->isChecked())
+        SETTINGS.setValue("List/sortdirection", "descending");
+    else
+        SETTINGS.setValue("List/sortdirection", "ascending");
+
+
+    //Other tab
+    if (ui->saveOption->isChecked())
+        SETTINGS.setValue("saveoptions", true);
+    else
+        SETTINGS.setValue("saveoptions", "");
+
+    SETTINGS.setValue("Other/parameters", ui->parametersLine->text());
+
+    close();
+}
+
+
+void SettingsDialog::listAddColumn()
+{
+    addColumn(ui->listCurrentList, ui->listAvailableList);
+}
+
+
+void SettingsDialog::listRemoveColumn()
+{
+    removeColumn(ui->listCurrentList, ui->listAvailableList);
+}
+
+
+void SettingsDialog::listSortDown()
+{
+    sortDown(ui->listCurrentList);
+}
+
+
+void SettingsDialog::listSortUp()
+{
+    sortUp(ui->listCurrentList);
+}
+
+
+void SettingsDialog::populateAvailable(bool downloadItems) {
+    available.clear();
+    labelOptions.clear();
+    sortOptions.clear();
+
+    available << "Filename"
+              << "Filename (extension)"
+              << "Zip File"
+              << "GoodName"
+              << "Internal Name"
+              << "Size"
+              << "MD5"
+              << "CRC1"
+              << "CRC2"
+              << "Players"
+              << "Rumble"
+              << "Save Type";
+
+    labelOptions << "Filename"
+                 << "Filename (extension)"
+                 << "GoodName"
+                 << "Internal Name";
+
+    sortOptions << "Filename"
+                << "GoodName"
+                << "Internal Name"
+                << "Size";
+
+    if (downloadItems) {
+        available << "Game Title"
+                  << "Release Date"
+                  << "Overview"
+                  << "ESRB"
+                  << "Genre"
+                  << "Publisher"
+                  << "Developer"
+                  << "Rating"
+                  << "Game Cover";
+
+        labelOptions << "Game Title"
+                     << "Release Date"
+                     << "Genre";
+
+        sortOptions << "Game Title"
+                    << "Release Date"
+                    << "ESRB"
+                    << "Genre"
+                    << "Publisher"
+                    << "Developer"
+                    << "Rating";
+    }
+
+    available.sort();
+    labelOptions.sort();
+    sortOptions.sort();
+}
+
+
+void SettingsDialog::populateTableAndListTab(bool downloadItems)
+{
+    populateAvailable(downloadItems);
+
+    QStringList tableCurrent, tableAvailable;
+    tableCurrent = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|");
+    tableAvailable = available;
+
+    foreach (QString cur, tableCurrent)
+    {
+        if (tableAvailable.contains(cur))
+            tableAvailable.removeOne(cur);
+        else //Someone added an invalid item
+            tableCurrent.removeOne(cur);
+    }
+
+    ui->tableAvailableList->clear();
+    ui->tableAvailableList->addItems(tableAvailable);
+    ui->tableAvailableList->sortItems();
+
+    ui->tableCurrentList->clear();
+    ui->tableCurrentList->addItems(tableCurrent);
+
+
+    //Grid sort field and label text
+    ui->labelTextBox->clear();
+    ui->labelTextBox->insertItems(0, labelOptions);
+    int labelTextIndex = labelOptions.indexOf(SETTINGS.value("Grid/labeltext","Filename").toString());
+    if (labelTextIndex >= 0) ui->labelTextBox->setCurrentIndex(labelTextIndex);
+
+    ui->gridSortBox->clear();
+    ui->gridSortBox->insertItems(0, sortOptions);
+    int gridSortIndex = sortOptions.indexOf(SETTINGS.value("Grid/sort","Filename").toString());
+    if (gridSortIndex >= 0) ui->gridSortBox->setCurrentIndex(gridSortIndex);
+
+
+    //List items and sort field
+    QStringList listCurrent, listAvailable;
+    listCurrent = SETTINGS.value("List/columns", "Filename|Internal Name|Size").toString().split("|");
+    listAvailable = available;
+    listAvailable.removeOne("Game Cover"); //Game Cover handled separately
+
+    foreach (QString cur, listCurrent)
+    {
+        if (listAvailable.contains(cur))
+            listAvailable.removeOne(cur);
+        else //Someone added an invalid item
+            listCurrent.removeOne(cur);
+    }
+
+    ui->listAvailableList->clear();
+    ui->listAvailableList->addItems(listAvailable);
+    ui->listAvailableList->sortItems();
+
+    ui->listCurrentList->clear();
+    ui->listCurrentList->addItems(listCurrent);
+
+    ui->listSortBox->clear();
+    ui->listSortBox->insertItems(0, sortOptions);
+    int listSortIndex = sortOptions.indexOf(SETTINGS.value("List/sort","Filename").toString());
+    if (listSortIndex >= 0) ui->listSortBox->setCurrentIndex(listSortIndex);
+}
+
+
+void SettingsDialog::removeColumn(QListWidget *currentList, QListWidget *availableList)
+{
+    int row = currentList->currentRow();
+
+    if (row >= 0) {
+        availableList->addItem(currentList->currentItem()->text());
+        delete currentList->takeItem(row);
+
+        availableList->sortItems();
+    }
+}
+
+
+void SettingsDialog::removeRomDirectory()
+{
+    int row = ui->romList->currentRow();
+
+    if (row >= 0)
+        delete ui->romList->takeItem(row);
+}
+
+
+
+void SettingsDialog::sortDown(QListWidget *currentList)
+{
+    int row = currentList->currentRow();
+
+    if (row > 0) {
+        QListWidgetItem *item = currentList->takeItem(row);
+        currentList->insertItem(row - 1, item);
+        currentList->setCurrentRow(row - 1);
+    }
+}
+
+
+void SettingsDialog::sortUp(QListWidget *currentList)
+{
+    int row = currentList->currentRow();
+
+    if (row >= 0 && row < currentList->count() - 1) {
+        QListWidgetItem *item = currentList->takeItem(row);
+        currentList->insertItem(row + 1, item);
+        currentList->setCurrentRow(row + 1);
+    }
+}
+
+
+void SettingsDialog::tableAddColumn()
+{
+    addColumn(ui->tableCurrentList, ui->tableAvailableList);
+}
+
+
+void SettingsDialog::tableRemoveColumn()
+{
+    removeColumn(ui->tableCurrentList, ui->tableAvailableList);
+}
+
+
+void SettingsDialog::tableSortDown()
+{
+    sortDown(ui->tableCurrentList);
+}
+
+
+void SettingsDialog::tableSortUp()
+{
+    sortUp(ui->tableCurrentList);
+}
+
+
+void SettingsDialog::toggleDownload(bool active)
+{
+    foreach (QWidget *next, downloadEnable)
+        next->setEnabled(active);
+
+    if (active)
+        toggleListCover(ui->listCoverOption->isChecked());
+}
+
+
+void SettingsDialog::toggleLabel(bool active)
+{
+    foreach (QWidget *next, labelEnable)
+        next->setEnabled(active);
+}
+
+
+void SettingsDialog::toggleListCover(bool active)
+{
+    foreach (QWidget *next, listCoverEnable)
+        next->setEnabled(active);
+}
diff --git a/src/settingsdialog.h b/src/settingsdialog.h
new file mode 100644
index 0000000..86d29d4
--- /dev/null
+++ b/src/settingsdialog.h
@@ -0,0 +1,95 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include <QDesktopWidget>
+#include <QFileDialog>
+#include <QListWidget>
+
+#include "global.h"
+
+
+namespace Ui {
+    class SettingsDialog;
+}
+
+class SettingsDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit SettingsDialog(QWidget *parent = 0, int activeTab = 0);
+    ~SettingsDialog();
+
+private:
+    void populateAvailable(bool downloadItems);
+
+    Ui::SettingsDialog *ui;
+
+    QDesktopWidget *desktop;
+    QDir pluginsDir;
+    QList<QWidget*> downloadEnable;
+    QList<QWidget*> labelEnable;
+    QList<QWidget*> listCoverEnable;
+    QStringList available;
+    QStringList labelOptions;
+    QStringList sortOptions;
+
+private slots:
+    void addColumn(QListWidget *currentList, QListWidget *availableList);
+    void addRomDirectory();
+    void browseBackground();
+    void browseMupen64();
+    void browsePlugin();
+    void browseData();
+    void browseConfig();
+    void editSettings();
+    void listAddColumn();
+    void listRemoveColumn();
+    void listSortDown();
+    void listSortUp();
+    void removeColumn(QListWidget *currentList, QListWidget *availableList);
+    void removeRomDirectory();
+    void populateTableAndListTab(bool downloadItems);
+    void sortDown(QListWidget *currentList);
+    void sortUp(QListWidget *currentList);
+    void tableAddColumn();
+    void tableRemoveColumn();
+    void tableSortDown();
+    void tableSortUp();
+    void toggleDownload(bool active);
+    void toggleLabel(bool active);
+    void toggleListCover(bool active);
+};
+
+#endif // SETTINGSDIALOG_H
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui
new file mode 100644
index 0000000..89aea9b
--- /dev/null
+++ b/src/settingsdialog.ui
@@ -0,0 +1,1255 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>600</width>
+    <height>550</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>550</width>
+    <height>500</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Settings</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_3">
+   <item row="3" column="0">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="tabShape">
+      <enum>QTabWidget::Rounded</enum>
+     </property>
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="pathsWidget">
+      <attribute name="title">
+       <string>Paths</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="0" column="0">
+        <widget class="QGroupBox" name="paths">
+         <property name="title">
+          <string>Mupen64Plus Files</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_4">
+          <item row="0" column="0">
+           <layout class="QGridLayout" name="pathsLayout">
+            <item row="2" column="1">
+             <widget class="QLineEdit" name="dataPath"/>
+            </item>
+            <item row="2" column="2">
+             <widget class="QPushButton" name="dataButton">
+              <property name="text">
+               <string>Browse...</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="2">
+             <widget class="QPushButton" name="configButton">
+              <property name="text">
+               <string>Browse...</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QLineEdit" name="pluginPath"/>
+            </item>
+            <item row="3" column="1">
+             <widget class="QLineEdit" name="configPath"/>
+            </item>
+            <item row="3" column="0">
+             <widget class="QLabel" name="configPathLabel">
+              <property name="text">
+               <string>Config directory:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="2">
+             <widget class="QPushButton" name="mupen64Button">
+              <property name="text">
+               <string>Browse...</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="pluginPathLabel">
+              <property name="text">
+               <string>Plugins directory:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="2">
+             <widget class="QPushButton" name="pluginButton">
+              <property name="text">
+               <string>Browse...</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QLabel" name="mupen64PathLabel">
+              <property name="text">
+               <string>Mupen64Plus executable: </string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QLineEdit" name="mupen64Path"/>
+            </item>
+            <item row="2" column="0">
+             <widget class="QLabel" name="dataPathLabel">
+              <property name="text">
+               <string>Data directory:</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <spacer name="pathsSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="0">
+        <widget class="QGroupBox" name="roms">
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>120</height>
+          </size>
+         </property>
+         <property name="title">
+          <string>ROM Directories</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_9">
+          <item row="0" column="0">
+           <layout class="QGridLayout" name="romLayout">
+            <item row="0" column="0">
+             <widget class="QListWidget" name="romList">
+              <property name="minimumSize">
+               <size>
+                <width>0</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="maximumSize">
+               <size>
+                <width>16777215</width>
+                <height>80</height>
+               </size>
+              </property>
+              <property name="baseSize">
+               <size>
+                <width>0</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="toolTip">
+               <string>Mupen64Plus-Qt will search for all .z64, .v64, .n64, and .zip files in these directories</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QWidget" name="listActionWidget" native="true">
+              <property name="maximumSize">
+               <size>
+                <width>16777215</width>
+                <height>80</height>
+               </size>
+              </property>
+              <layout class="QVBoxLayout" name="verticalLayout">
+               <item>
+                <widget class="QPushButton" name="romAddButton">
+                 <property name="text">
+                  <string>Add...</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QPushButton" name="romRemoveButton">
+                 <property name="text">
+                  <string>Remove</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="emulationWidget">
+      <attribute name="title">
+       <string>Emulation</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_6">
+       <item row="0" column="0">
+        <layout class="QGridLayout" name="emulationLayout">
+         <property name="topMargin">
+          <number>10</number>
+         </property>
+         <item row="1" column="0">
+          <widget class="QRadioButton" name="cachedButton">
+           <property name="text">
+            <string>Cached Interpreter</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="0">
+          <widget class="QRadioButton" name="pureButton">
+           <property name="text">
+            <string>Pure Interpreter</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QRadioButton" name="dynamicButton">
+           <property name="text">
+            <string>Dynamic Recompiler</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="0">
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="1">
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="graphicsWidget">
+      <attribute name="title">
+       <string>Graphics</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_10">
+       <item row="0" column="0">
+        <layout class="QGridLayout" name="graphicsLayout" columnminimumwidth="150,0">
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <item row="0" column="0">
+          <widget class="QLabel" name="osdLabel">
+           <property name="text">
+            <string>On Screen Display:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="fullscreenLabel">
+           <property name="text">
+            <string>Fullscreen:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="resolutionLabel">
+           <property name="text">
+            <string>Resolution:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QCheckBox" name="osdOption">
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QCheckBox" name="fullscreenOption">
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1">
+          <widget class="QComboBox" name="resolutionBox">
+           <property name="maximumSize">
+            <size>
+             <width>100</width>
+             <height>16777215</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="0">
+        <spacer name="graphicsVSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="1">
+        <spacer name="graphicsHSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="pluginsWidget">
+      <attribute name="title">
+       <string>Plugins</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_12">
+       <item row="0" column="0">
+        <layout class="QGridLayout" name="pluginsLayout" columnstretch="0,1" columnminimumwidth="100,0">
+         <item row="0" column="0">
+          <widget class="QLabel" name="videoLabel">
+           <property name="text">
+            <string>Video Plugin:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="audioLabel">
+           <property name="text">
+            <string>Audio Plugin:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="inputLabel">
+           <property name="text">
+            <string>Input Plugin:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="0">
+          <widget class="QLabel" name="rspLabel">
+           <property name="text">
+            <string>RSP Plugin:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QComboBox" name="videoBox"/>
+         </item>
+         <item row="1" column="1">
+          <widget class="QComboBox" name="audioBox"/>
+         </item>
+         <item row="2" column="1">
+          <widget class="QComboBox" name="inputBox"/>
+         </item>
+         <item row="3" column="1">
+          <widget class="QComboBox" name="rspBox"/>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="0">
+        <spacer name="pluginsVSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tableWidget">
+      <attribute name="title">
+       <string>Table</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_8">
+       <item row="0" column="1">
+        <layout class="QGridLayout" name="tableViewLayout">
+         <property name="leftMargin">
+          <number>20</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <item row="2" column="0">
+          <widget class="QLabel" name="tableSizeLabel">
+           <property name="text">
+            <string>Image:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1">
+          <widget class="QComboBox" name="tableSizeBox"/>
+         </item>
+         <item row="1" column="3">
+          <widget class="QListWidget" name="tableCurrentList">
+           <property name="minimumSize">
+            <size>
+             <width>160</width>
+             <height>250</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>160</width>
+             <height>300</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="4">
+          <widget class="QWidget" name="tableSortWidget" native="true">
+           <property name="maximumSize">
+            <size>
+             <width>16777215</width>
+             <height>80</height>
+            </size>
+           </property>
+           <layout class="QGridLayout" name="gridLayout_5">
+            <item row="0" column="0">
+             <layout class="QVBoxLayout" name="tableSortLayout" stretch="0,0">
+              <item>
+               <widget class="QToolButton" name="tableSortDownButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::UpArrow</enum>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QToolButton" name="tableSortUpButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::DownArrow</enum>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item row="1" column="0" colspan="2">
+          <widget class="QListWidget" name="tableAvailableList">
+           <property name="minimumSize">
+            <size>
+             <width>160</width>
+             <height>250</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>160</width>
+             <height>300</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="2">
+          <widget class="QWidget" name="tableToggleWidget" native="true">
+           <property name="maximumSize">
+            <size>
+             <width>16777215</width>
+             <height>80</height>
+            </size>
+           </property>
+           <layout class="QGridLayout" name="gridLayout_7">
+            <item row="0" column="0">
+             <layout class="QVBoxLayout" name="tableToggleLayout">
+              <item>
+               <widget class="QToolButton" name="tableAddButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::RightArrow</enum>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QToolButton" name="tableRemoveButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::LeftArrow</enum>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item row="0" column="3">
+          <widget class="QLabel" name="tableCurrentLabel">
+           <property name="text">
+            <string>Current Columns:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="3">
+          <widget class="QCheckBox" name="tableStretchOption">
+           <property name="text">
+            <string>Stretch First Column</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="0" colspan="2">
+          <widget class="QLabel" name="tableAvailableLabel">
+           <property name="text">
+            <string>Available Columns:</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="0" column="0">
+        <spacer name="tableHSpacer1">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="2">
+        <spacer name="tableHSpacer2">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="1">
+        <spacer name="tableVSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="gridWidget">
+      <attribute name="title">
+       <string>Grid</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout7">
+       <item row="1" column="0">
+        <widget class="QGroupBox" name="gridText">
+         <property name="title">
+          <string>ROM Label</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout5">
+          <item row="0" column="0">
+           <layout class="QGridLayout" name="gridTextLayout" columnstretch="0,0" columnminimumwidth="150,0">
+            <property name="sizeConstraint">
+             <enum>QLayout::SetMinAndMaxSize</enum>
+            </property>
+            <item row="0" column="0">
+             <widget class="QLabel" name="labelLabel">
+              <property name="text">
+               <string>Show Label:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QComboBox" name="labelTextBox">
+              <property name="minimumSize">
+               <size>
+                <width>150</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="maximumSize">
+               <size>
+                <width>150</width>
+                <height>16777215</height>
+               </size>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="labelTextLabel">
+              <property name="text">
+               <string>Label Text:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QCheckBox" name="labelOption">
+              <property name="text">
+               <string/>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="0">
+             <widget class="QLabel" name="labelColorLabel">
+              <property name="text">
+               <string>Label Color:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QComboBox" name="labelColorBox">
+              <property name="maximumSize">
+               <size>
+                <width>100</width>
+                <height>16777215</height>
+               </size>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item row="0" column="1">
+           <spacer name="gridHSpacer2">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QGroupBox" name="groupBox">
+         <property name="title">
+          <string>Other</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout6">
+          <item row="2" column="0">
+           <layout class="QGridLayout" name="gridOtherLayout" columnstretch="0,1,1,0" columnminimumwidth="150,0,0,0">
+            <item row="0" column="3">
+             <widget class="QPushButton" name="backgroundButton">
+              <property name="text">
+               <string>Browse...</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QLabel" name="backgroundLabel">
+              <property name="text">
+               <string>Custom Background:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="gridSortLabel">
+              <property name="text">
+               <string>Sorting:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1" colspan="2">
+             <widget class="QLineEdit" name="backgroundPath"/>
+            </item>
+            <item row="1" column="2" colspan="2">
+             <widget class="QCheckBox" name="gridDescendingOption">
+              <property name="text">
+               <string>Descending</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QComboBox" name="gridSortBox">
+              <property name="maximumSize">
+               <size>
+                <width>150</width>
+                <height>16777215</height>
+               </size>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <widget class="QGroupBox" name="gridImage">
+         <property name="title">
+          <string>ROM Image</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout4">
+          <item row="0" column="1">
+           <spacer name="gridHSpacer1">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item row="0" column="0">
+           <layout class="QGridLayout" name="gridImageLayout" rowstretch="0,0,0,0" columnstretch="0,0" columnminimumwidth="150,0">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>0</number>
+            </property>
+            <item row="2" column="0">
+             <widget class="QLabel" name="shadowInactiveLabel">
+              <property name="text">
+               <string>Shadow Inactive Color:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QLabel" name="shadowActiveLabel">
+              <property name="text">
+               <string>Shadow Active Color:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="columnCountLabel">
+              <property name="text">
+               <string>Columns:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QLabel" name="gridSizeLabel">
+              <property name="text">
+               <string>Image Size:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1">
+             <widget class="QComboBox" name="shadowActiveBox">
+              <property name="maximumSize">
+               <size>
+                <width>100</width>
+                <height>16777215</height>
+               </size>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QSpinBox" name="columnCountBox">
+              <property name="maximumSize">
+               <size>
+                <width>100</width>
+                <height>16777215</height>
+               </size>
+              </property>
+              <property name="minimum">
+               <number>1</number>
+              </property>
+              <property name="maximum">
+               <number>20</number>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QComboBox" name="gridSizeBox">
+              <property name="maximumSize">
+               <size>
+                <width>100</width>
+                <height>16777215</height>
+               </size>
+              </property>
+              <property name="editable">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QComboBox" name="shadowInactiveBox">
+              <property name="maximumSize">
+               <size>
+                <width>100</width>
+                <height>16777215</height>
+               </size>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <spacer name="gridVSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="listWidget">
+      <attribute name="title">
+       <string>List</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_30">
+       <item row="0" column="2">
+        <spacer name="listHSpacer2">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="1">
+        <layout class="QGridLayout" name="listViewLayout">
+         <property name="leftMargin">
+          <number>20</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <item row="2" column="3">
+          <widget class="QCheckBox" name="listHeaderOption">
+           <property name="text">
+            <string>First Item as Header</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="3">
+          <widget class="QLabel" name="listCurrentLabel">
+           <property name="text">
+            <string>Current Items:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="2">
+          <widget class="QWidget" name="listToggleWidget" native="true">
+           <property name="maximumSize">
+            <size>
+             <width>16777215</width>
+             <height>80</height>
+            </size>
+           </property>
+           <layout class="QGridLayout" name="gridLayout8">
+            <item row="0" column="0">
+             <layout class="QVBoxLayout" name="listToggleLayout">
+              <item>
+               <widget class="QToolButton" name="listAddButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::RightArrow</enum>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QToolButton" name="listRemoveButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::LeftArrow</enum>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item row="1" column="0" colspan="2">
+          <widget class="QListWidget" name="listAvailableList">
+           <property name="maximumSize">
+            <size>
+             <width>160</width>
+             <height>250</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="4">
+          <widget class="QWidget" name="listSortWidget" native="true">
+           <property name="maximumSize">
+            <size>
+             <width>16777215</width>
+             <height>80</height>
+            </size>
+           </property>
+           <layout class="QGridLayout" name="gridLayout9">
+            <item row="0" column="0">
+             <layout class="QVBoxLayout" name="listSortLayout" stretch="0,0">
+              <item>
+               <widget class="QToolButton" name="listSortDownButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::UpArrow</enum>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QToolButton" name="listSortUpButton">
+                <property name="text">
+                 <string>...</string>
+                </property>
+                <property name="arrowType">
+                 <enum>Qt::DownArrow</enum>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item row="1" column="3">
+          <widget class="QListWidget" name="listCurrentList">
+           <property name="maximumSize">
+            <size>
+             <width>160</width>
+             <height>250</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="0" colspan="2">
+          <widget class="QLabel" name="listAvailableLabel">
+           <property name="text">
+            <string>Available Items:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="1">
+          <widget class="QComboBox" name="listSizeBox"/>
+         </item>
+         <item row="2" column="0" colspan="2">
+          <widget class="QCheckBox" name="listCoverOption">
+           <property name="text">
+            <string>Display Cover Image</string>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="0">
+          <widget class="QLabel" name="listSizeLabel">
+           <property name="text">
+            <string>Image Size:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="4" column="3">
+          <widget class="QCheckBox" name="listDescendingOption">
+           <property name="text">
+            <string>Descending</string>
+           </property>
+          </widget>
+         </item>
+         <item row="4" column="0">
+          <widget class="QLabel" name="listSortLabel">
+           <property name="text">
+            <string>Sorting:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="4" column="1" colspan="2">
+          <widget class="QComboBox" name="listSortBox">
+           <property name="maximumSize">
+            <size>
+             <width>130</width>
+             <height>16777215</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="0" column="0">
+        <spacer name="listHSpacer1">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="1">
+        <spacer name="listVSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="otherWidget">
+      <attribute name="title">
+       <string>Other</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="0" column="0">
+        <layout class="QGridLayout" name="otherLayout" columnminimumwidth="100,0,0">
+         <property name="leftMargin">
+          <number>10</number>
+         </property>
+         <property name="topMargin">
+          <number>10</number>
+         </property>
+         <item row="0" column="2">
+          <widget class="QCheckBox" name="downloadOption">
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="2">
+          <widget class="QCheckBox" name="saveOption">
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="parametersLabel">
+           <property name="minimumSize">
+            <size>
+             <width>150</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Additional Parameters:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="0" colspan="2">
+          <widget class="QLabel" name="downloadLabel">
+           <property name="minimumSize">
+            <size>
+             <width>330</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Download Game Information (thegamesdb.net):</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" colspan="2">
+          <widget class="QLabel" name="saveLabel">
+           <property name="text">
+            <string>Save Options:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1" colspan="2">
+          <widget class="QLineEdit" name="parametersLine">
+           <property name="toolTip">
+            <string><html><head/><body><p>Use to pass additional parameters to Mupen64Plus (cheats, etc.). See <a href="https://code.google.com/p/mupen64plus/wiki/UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">https://code.google.com/p/mupen64plus/wiki/UIConsoleUsage</span></a> for more information.</p></body></html></string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="0">
+        <spacer name="otherVSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="1">
+        <spacer name="otherHSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>tabWidget</tabstop>
+  <tabstop>mupen64Path</tabstop>
+  <tabstop>pluginPath</tabstop>
+  <tabstop>dataPath</tabstop>
+  <tabstop>configPath</tabstop>
+  <tabstop>mupen64Button</tabstop>
+  <tabstop>pluginButton</tabstop>
+  <tabstop>dataButton</tabstop>
+  <tabstop>configButton</tabstop>
+  <tabstop>romList</tabstop>
+  <tabstop>romAddButton</tabstop>
+  <tabstop>romRemoveButton</tabstop>
+  <tabstop>pureButton</tabstop>
+  <tabstop>cachedButton</tabstop>
+  <tabstop>dynamicButton</tabstop>
+  <tabstop>osdOption</tabstop>
+  <tabstop>fullscreenOption</tabstop>
+  <tabstop>resolutionBox</tabstop>
+  <tabstop>videoBox</tabstop>
+  <tabstop>audioBox</tabstop>
+  <tabstop>inputBox</tabstop>
+  <tabstop>rspBox</tabstop>
+  <tabstop>tableAvailableList</tabstop>
+  <tabstop>tableAddButton</tabstop>
+  <tabstop>tableRemoveButton</tabstop>
+  <tabstop>tableCurrentList</tabstop>
+  <tabstop>tableSortDownButton</tabstop>
+  <tabstop>tableSortUpButton</tabstop>
+  <tabstop>tableSizeBox</tabstop>
+  <tabstop>tableStretchOption</tabstop>
+  <tabstop>gridSizeBox</tabstop>
+  <tabstop>columnCountBox</tabstop>
+  <tabstop>shadowInactiveBox</tabstop>
+  <tabstop>shadowActiveBox</tabstop>
+  <tabstop>labelOption</tabstop>
+  <tabstop>labelTextBox</tabstop>
+  <tabstop>labelColorBox</tabstop>
+  <tabstop>backgroundPath</tabstop>
+  <tabstop>backgroundButton</tabstop>
+  <tabstop>gridSortBox</tabstop>
+  <tabstop>gridDescendingOption</tabstop>
+  <tabstop>listAvailableList</tabstop>
+  <tabstop>listAddButton</tabstop>
+  <tabstop>listRemoveButton</tabstop>
+  <tabstop>listCurrentList</tabstop>
+  <tabstop>listSortDownButton</tabstop>
+  <tabstop>listSortUpButton</tabstop>
+  <tabstop>listCoverOption</tabstop>
+  <tabstop>listHeaderOption</tabstop>
+  <tabstop>listSizeBox</tabstop>
+  <tabstop>listSortBox</tabstop>
+  <tabstop>listDescendingOption</tabstop>
+  <tabstop>downloadOption</tabstop>
+  <tabstop>saveOption</tabstop>
+  <tabstop>parametersLine</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/thegamesdbscrapper.cpp b/src/thegamesdbscrapper.cpp
new file mode 100644
index 0000000..4830452
--- /dev/null
+++ b/src/thegamesdbscrapper.cpp
@@ -0,0 +1,288 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "thegamesdbscrapper.h"
+
+
+TheGamesDBScrapper::TheGamesDBScrapper(QWidget *parent, bool force) : QObject(parent)
+{
+    this->parent = parent;
+    this->force = force;
+    this->keepGoing = true;
+}
+
+
+void TheGamesDBScrapper::deleteGameInfo(QString fileName, QString identifier)
+{
+    QString text;
+    text = QString("<b>NOTE:</b> If you are deleting this game's information because the game doesn't exist ")
+                 + "on TheGamesDB and Mupen64Plus-Qt pulled the information for different game, it's better "
+                 + "to create an account on <a href=\"http://thegamesdb.net/\">TheGamesDB</a> and add the "
+                 + "game so other users can benefit as well."
+                 + "<br /><br />"
+                 + "This will cause Mupen64Plus-Qt to not update the information for this game until you "
+                 + "force it with \"Download/Update Info...\""
+                 + "<br /><br />"
+                 + "Delete the current information for <b>" + fileName + "</b>?";
+
+    int answer = QMessageBox::question(parent, tr("Delete Game Information"), text,
+                                       QMessageBox::Yes | QMessageBox::No);
+
+    if (answer == QMessageBox::Yes) {
+        QString gameCache = getDataLocation() + "/cache/" + identifier.toLower();
+
+        QString dataFile = gameCache + "/data.xml";
+        QFile file(dataFile);
+
+        // Remove game information
+        file.open(QIODevice::WriteOnly);
+        QTextStream stream(&file);
+        stream << "NULL";
+        file.close();
+
+        // Remove cover image
+        QString coverFile = gameCache + "/boxart-front.";
+
+        QFile coverJPG(coverFile + "jpg");
+        QFile coverPNG(coverFile + "png");
+
+        if (coverJPG.exists())
+            coverJPG.remove();
+        if (coverPNG.exists())
+            coverPNG.remove();
+
+        coverJPG.open(QIODevice::WriteOnly);
+        QTextStream streamImage(&coverJPG);
+        streamImage << "";
+        coverJPG.close();
+    }
+}
+
+
+void TheGamesDBScrapper::downloadGameInfo(QString identifier, QString searchName, QString gameID)
+{
+    if (keepGoing && identifier != "") {
+        if (force) parent->setEnabled(false);
+
+        bool updated = false;
+
+        QString gameCache = getDataLocation() + "/cache/" + identifier.toLower();
+        QDir cache(gameCache);
+
+        if (!cache.exists()) {
+            cache.mkpath(gameCache);
+        }
+
+        //Get game XML info from thegamesdb.net
+        QString dataFile = gameCache + "/data.xml";
+        QFile file(dataFile);
+
+        if (!file.exists() || file.size() == 0 || force) {
+            QUrl url;
+
+            //Remove [!], (U), etc. from GoodName for searching
+            searchName.remove(QRegExp("\\W*(\\(|\\[).+(\\)|\\])\\W*"));
+
+            //Few game specific hacks
+            //TODO: Contact thegamesdb.net and see if these can be fixed on their end
+            if (searchName == "Legend of Zelda, The - Majora's Mask")
+                searchName = "Majora's Mask";
+            else if (searchName == "Legend of Zelda, The - Ocarina of Time - Master Quest")
+                searchName = "Master Quest";
+            else if (searchName.toLower() == "f-zero x")
+                gameID = "10836";
+
+            //If user submits gameID, use that
+            if (gameID != "")
+                url.setUrl("http://thegamesdb.net/api/GetGame.php?id="
+                           + gameID + "&platform=Nintendo 64");
+            else
+                url.setUrl("http://thegamesdb.net/api/GetGame.php?name="
+                           + searchName + "&platform=Nintendo 64");
+
+            QString dom = getUrlContents(url);
+
+            QDomDocument xml;
+            xml.setContent(dom);
+            QDomNode node = xml.elementsByTagName("Data").at(0).firstChildElement("Game");
+
+            int count = 0, found = 0;
+
+            while(!node.isNull())
+            {
+                QDomElement element = node.firstChildElement("GameTitle").toElement();
+
+                if (force) { //from user dialog
+                    QDomElement date = node.firstChildElement("ReleaseDate").toElement();
+
+                    QString check = "Game: " + element.text();
+                    check.remove(QRegExp(QString("[^A-Za-z 0-9 \\.,\\?'""!@#\\$%\\^&\\*\\")
+                                         + "(\\)-_=\\+;:<>\\/\\\\|\\}\\{\\[\\]`~]*"));
+                    if (date.text() != "") check += "\nReleased on: " + date.text();
+                    check += "\n\nDoes this look correct?";
+
+                    int answer = QMessageBox::question(parent, QObject::tr("Game Information Download"),
+                                                       check, QMessageBox::Yes | QMessageBox::No);
+
+                    if (answer == QMessageBox::Yes) {
+                        found = count;
+                        updated = true;
+                        break;
+                    }
+                } else {
+                    //We only want one game, so search for a perfect match in the GameTitle element.
+                    //Otherwise this will default to 0 (the first game found)
+                    if(element.text() == searchName)
+                        found = count;
+                }
+
+                node = node.nextSibling();
+                count++;
+            }
+
+            if (!force || updated) {
+                file.open(QIODevice::WriteOnly);
+                QTextStream stream(&file);
+
+                QDomNodeList gameList = xml.elementsByTagName("Game");
+                gameList.at(found).save(stream, QDomNode::EncodingFromDocument);
+
+                file.close();
+            }
+
+            if (force && !updated) {
+                QString message;
+
+                if (count == 0)
+                    message = QObject::tr("No results found.");
+                else
+                    message = QObject::tr("No more results found.");
+
+                QMessageBox::information(parent, QObject::tr("Game Information Download"), message);
+            }
+        }
+
+
+        //Get front cover
+        QString boxartURL = "";
+        QString boxartExt = "";
+        QString coverFile = gameCache + "/boxart-front.";
+
+        QFile coverJPG(coverFile + "jpg");
+        QFile coverPNG(coverFile + "png");
+
+        if ((!coverJPG.exists() && !coverPNG.exists()) || (force && updated)) {
+            file.open(QIODevice::ReadOnly);
+            QString dom = file.readAll();
+            file.close();
+
+            QDomDocument xml;
+            xml.setContent(dom);
+            QDomNode node = xml.elementsByTagName("Game").at(0).firstChildElement("Images").firstChild();
+
+            while(!node.isNull())
+            {
+                QDomElement element = node.toElement();
+                if(element.tagName() == "boxart" && element.attribute("side") == "front")
+                    boxartURL = element.attribute("thumb");
+
+                node = node.nextSibling();
+            }
+
+            if (boxartURL != "") {
+                QUrl url("http://thegamesdb.net/banners/" + boxartURL);
+
+                //Check to save as JPG or PNG
+                boxartExt = QFileInfo(boxartURL).completeSuffix().toLower();
+                QFile cover(coverFile + boxartExt);
+
+                cover.open(QIODevice::WriteOnly);
+                cover.write(getUrlContents(url));
+                cover.close();
+            }
+        }
+
+        if (updated)
+            QMessageBox::information(parent, QObject::tr("Game Information Download"),
+                                     QObject::tr("Download Complete!"));
+
+        if (force) parent->setEnabled(true);
+    }
+}
+
+
+QByteArray TheGamesDBScrapper::getUrlContents(QUrl url)
+{
+    QNetworkAccessManager *manager = new QNetworkAccessManager;
+
+    QNetworkRequest request;
+    request.setUrl(url);
+    request.setRawHeader("User-Agent", "Mupen64Plus-Qt");
+    QNetworkReply *reply = manager->get(request);
+
+    QTimer timer;
+    timer.setSingleShot(true);
+
+    QEventLoop loop;
+    connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
+    connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
+    timer.start(10000);
+    loop.exec();
+
+    if(timer.isActive()) { //Got reply
+        timer.stop();
+
+        if(reply->error() > 0)
+            showError(reply->errorString());
+        else
+            return reply->readAll();
+
+    } else //Request timed out
+        showError("Request timed out. Check your network settings.");
+
+    return QByteArray();
+}
+
+
+void TheGamesDBScrapper::showError(QString error)
+{
+    QString question = "\n\nContinue scraping information?";
+
+    if (force)
+        QMessageBox::information(parent, tr("Network Error"), error);
+    else {
+        int answer = QMessageBox::question(parent, tr("Network Error"), error + question,
+                                           QMessageBox::Yes | QMessageBox::No);
+
+        if (answer == QMessageBox::No)
+            keepGoing = false;
+    }
+}
diff --git a/src/thegamesdbscrapper.h b/src/thegamesdbscrapper.h
new file mode 100644
index 0000000..388ec6c
--- /dev/null
+++ b/src/thegamesdbscrapper.h
@@ -0,0 +1,58 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef THEGAMESDBSCRAPPER_H
+#define THEGAMESDBSCRAPPER_H
+
+#include <QObject>
+
+#include "common.h"
+#include "global.h"
+
+
+class TheGamesDBScrapper : public QObject
+{
+    Q_OBJECT
+public:
+    explicit TheGamesDBScrapper(QWidget *parent = 0, bool force = false);
+    void deleteGameInfo(QString fileName, QString identifier);
+    void downloadGameInfo(QString identifier, QString searchName, QString gameID = "");
+
+private:
+    QByteArray getUrlContents(QUrl url);
+    void showError(QString error);
+
+    bool force;
+    bool keepGoing;
+    QWidget *parent;
+};
+
+#endif // THEGAMESDBSCRAPPER_H
diff --git a/src/treewidgetitem.cpp b/src/treewidgetitem.cpp
new file mode 100644
index 0000000..a46dcd0
--- /dev/null
+++ b/src/treewidgetitem.cpp
@@ -0,0 +1,55 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#include "treewidgetitem.h"
+
+
+//Reimplemented QTreeWidgetItem operator to sort based on both integers and text
+bool TreeWidgetItem::operator< (const QTreeWidgetItem &other) const
+{
+    int column = treeWidget()->sortColumn();
+
+    if (data(column, Qt::UserRole).toString() != "") {
+        if (data(column, Qt::UserRole).toString().contains(QRegExp("[^\\d]+"))) {
+            QString firstText = data(column, Qt::UserRole).toString();
+            QString otherText = other.data(column, Qt::UserRole).toString();
+            return firstText < otherText;
+        } else {
+            int firstNumber = data(column, Qt::UserRole).toInt();
+            int otherNumber = other.data(column, Qt::UserRole).toInt();
+            return firstNumber < otherNumber;
+        }
+    } else {
+        QString firstText = text(column);
+        QString otherText = other.text(column);
+        return firstText < otherText;
+    }
+}
diff --git a/src/treewidgetitem.h b/src/treewidgetitem.h
new file mode 100644
index 0000000..889b6c2
--- /dev/null
+++ b/src/treewidgetitem.h
@@ -0,0 +1,46 @@
+/***
+ * Copyright (c) 2013, Dan Hasting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the organization nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ***/
+
+#ifndef TREEWIDGETITEM_H
+#define TREEWIDGETITEM_H
+
+#include <QTreeWidgetItem>
+
+
+class TreeWidgetItem : public QTreeWidgetItem
+{
+public:
+    TreeWidgetItem(QTreeWidget *parent) : QTreeWidgetItem(parent) {}
+    bool operator< (const QTreeWidgetItem &other) const;
+};
+
+
+#endif // TREEWIDGETITEM_H
diff --git a/windows/icon.rc b/windows/icon.rc
new file mode 100644
index 0000000..9df71a8
--- /dev/null
+++ b/windows/icon.rc
@@ -0,0 +1 @@
+IDI_ICON1 ICON DISCARDABLE "mupen64plus.ico"
diff --git a/windows/mupen64plus.ico b/windows/mupen64plus.ico
new file mode 100644
index 0000000..3983ae1
Binary files /dev/null and b/windows/mupen64plus.ico differ

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/mupen64plus-qt.git



More information about the Pkg-games-commits mailing list