/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "DebuggerMain.h"
#include "DebugEventServer.h"
#include "Debugger.h"
#include "Configuration.h"
#include "ByteStreamRegion.h"
#include "SymbolManager.h"
#include "DebuggerPluginInterface.h"
#include "DebuggerCoreInterface.h"
#include "DialogAttach.h"
#include "DialogMemoryRegions.h"
#include "Util.h"
#include "DisassemblyLib.h"
#include "version.h"

#include <QSettings>
#include <QToolButton>
#include <QMessageBox>
#include <QFileDialog>
#include <QInputDialog>
#include <QTableWidgetItem>
#include <QFileInfo>

#include <fstream>
#include <cassert>

// TODO: move elf parsing functionality to another class
#include <elf.h>

//------------------------------------------------------------------------------
// Name: DebuggerMain(QWidget *parent)
// Desc: constructor
//------------------------------------------------------------------------------
DebuggerMain::DebuggerMain(QWidget *parent) : QMainWindow(parent), 
		m_DebugEventServer(0), m_UpdateGUI(true), m_ReenableBreakPointAddress(0), 
		m_LastEvent(0), m_StepWasRun(false), m_LastOpenDirectory("") {

	ui.setupUi(this);
	
	// setup the global pointers as early as possible
	Debugger::debuggerGUI		= this;
	Debugger::debugEventHandler	= this;

	// make the stack view all nice..
	// TODO: remember what the user set it as, and restore it!
	ui.stackView->setRowWidth(1);
	ui.stackView->setWordWidth(4);
	ui.stackView->setShowAsciiDump(false);
	
	// setup tabs
	setupTabButtons();
	
	// set the listbox to about 4 lines
	QFontMetrics fm(ui.listWidget->font());
	ui.listWidget->setFixedHeight(fm.height() * 5);
	
	// register our DebugEvent type so it can be used in 
	// inter-thread signals
	qRegisterMetaType<DebugEvent>("DebugEvent");
	
	// make sure our widgets use custom context menus
	ui.registerList->setContextMenuPolicy(Qt::CustomContextMenu);
	ui.cpuView->setContextMenuPolicy(Qt::CustomContextMenu);
	ui.stackView->setContextMenuPolicy(Qt::CustomContextMenu);
			
	// add toggles for the dock windows
	ui.menu_View->addAction(ui.registersDock->toggleViewAction());
	ui.menu_View->addAction(ui.dataDock->toggleViewAction());
	ui.menu_View->addAction(ui.stackDock->toggleViewAction());

	// remove the one in the designer and put in our built one
	// it's a bit ugly to do it this way, but the designer wont let me 
	// make a tabless entry..and the ones i create look slightly diff 
	// (probably lack of layout manager in mine...
	ui.tabWidget->removeTab(0);
	createNewDataTab();
	
	m_StackViewInfo.view = ui.stackView;
	
	QFont stackFont;
	stackFont.fromString(Debugger::config()->StackViewFont);
	ui.stackView->setFont(stackFont);
	
	// setup the register view
	QTreeWidgetItem *gpr = ui.registerList->addCatagory("General Purpose");
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // eax
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // ebx
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // ecx
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // edx
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // ebp
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // esp
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // esi
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // edi
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // eip
	m_RegisterViewItems.push_back(ui.registerList->addItem(gpr, "")); // eflags
	
	QTreeWidgetItem *segs = ui.registerList->addCatagory("Segments");
	m_RegisterViewItems.push_back(ui.registerList->addItem(segs, "")); // cs
	m_RegisterViewItems.push_back(ui.registerList->addItem(segs, "")); // ds
	m_RegisterViewItems.push_back(ui.registerList->addItem(segs, "")); // es
	m_RegisterViewItems.push_back(ui.registerList->addItem(segs, "")); // fs
	m_RegisterViewItems.push_back(ui.registerList->addItem(segs, "")); // gs
	m_RegisterViewItems.push_back(ui.registerList->addItem(segs, "")); // ss
	
	updateRegisterView("");
	
	m_WorkingDirectory = QDir().absolutePath();
}

//------------------------------------------------------------------------------
// Name: ~DebuggerMain()
// Desc: destructor
//------------------------------------------------------------------------------
DebuggerMain::~DebuggerMain() {

	switch(Debugger::config()->closeBehaviour) {
	case Configuration::Terminate:
		on_action_Kill_triggered();
		break;
	case Configuration::Detach:
		on_action_Detach_triggered();
		break;
	}
	
	// remove the last tab
	removeDataTab();
	
	// clean up the stream for the stack view too
	safeDelete(m_StackViewInfo.stream);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::loadRecentFileList() {
	QSettings settings;
	settings.beginGroup("Recent Files");
	
	for(int i = 0; i < MaxRecentFiles; ++i) {
		const QString s = settings.value(QString().sprintf("File%d", i), "").toString();
		if(s != "") {
			m_RecentFileList.push_back(s);
		}
	}
	settings.endGroup();
	updateRecentFileList();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::saveRecentFileList() {
	QSettings settings;
	settings.beginGroup("Recent Files");
	
	for(int i = 0; i < MaxRecentFiles; ++i) {
		QString s;
		if(i < m_RecentFileList.size()) {
			s = m_RecentFileList[i];
		}
		settings.setValue(QString().sprintf("File%d", i), s);
	}
	settings.endGroup();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::openRecentMenuItem() {
	QAction *const action = qobject_cast<QAction *>(sender());
	if(action != 0) {
		const QString s = action->data().toString();
		doOpenFile(s);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateRecentFileList() {

	QMenu *menu = new QMenu(this);
	for(QVector<QString>::iterator it = m_RecentFileList.begin(); it != m_RecentFileList.end(); ++it) {
		QAction *action = menu->addAction(*it, this, SLOT(openRecentMenuItem()));
		if(action != 0) {
			action->setData(*it);
		}
	}
	menu->addSeparator();
	menu->addAction("Clear &Menu", this, SLOT(clearRecentMenu()));
	ui.action_Recent_Files->setMenu(menu);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::clearRecentMenu() {
	m_RecentFileList.clear();
	updateRecentFileList();
	
}

//------------------------------------------------------------------------------
// Name: finishPluginSetup(QVector<DebuggerPluginInterface *> &plugins)
// Desc: finalizes plugin setup by adding each to the menu, we can do this now 
//       that we have a GUI widget to attach it to
//------------------------------------------------------------------------------
void DebuggerMain::finishPluginSetup(QVector<DebuggerPluginInterface *> &plugins) {
	// setup the menu for all plugins that which to do so
	foreach (DebuggerPluginInterface *plugin, plugins) {
		QMenu *const menu = plugin->menu(this);
		if(menu != 0) {
			ui.menu_Plugins->addMenu(menu);
		}
	}
}

//------------------------------------------------------------------------------
// Name: closeEvent(QCloseEvent *)
// Desc: triggered on main window close, saves window state
//------------------------------------------------------------------------------
void DebuggerMain::closeEvent(QCloseEvent *) {
	saveWindowState();
	saveRecentFileList();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::loadWindowState() {
	QSettings settings;
	settings.beginGroup("Window");
	const QByteArray state = settings.value("window_state").toByteArray();
	
	const int width = settings.value("window_width", -1).toInt();
	const int height = settings.value("window_height", -1).toInt();
	if(width != -1) {
		resize(width, size().height());
	}
	
	if(height != -1) {
		resize(size().width(), height);
	}
	
	settings.endGroup();
	restoreState(state);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::saveWindowState() {
	QSettings settings;
	const QByteArray state = saveState();
	settings.beginGroup("Window");
	settings.setValue("window_state", state);
	settings.setValue("window_width", width());
	settings.setValue("window_height", height());
	settings.endGroup();
}

//------------------------------------------------------------------------------
// Name: showEvent(QShowEvent *)
// Desc: triggered on show, restores window state
//------------------------------------------------------------------------------
void DebuggerMain::showEvent(QShowEvent *) {
	loadWindowState();
	loadRecentFileList();
}

//------------------------------------------------------------------------------
// Name: setupTabButtons()
// Desc: creates the add/remove tab buttons in the data view
//------------------------------------------------------------------------------
void DebuggerMain::setupTabButtons() {
	// add the corner widgets to the data view
	m_AddTab = new QToolButton(ui.tabWidget);
	m_DelTab = new QToolButton(ui.tabWidget);
	
	m_AddTab->setToolButtonStyle(Qt::ToolButtonIconOnly);
	m_DelTab->setToolButtonStyle(Qt::ToolButtonIconOnly);
	
	m_AddTab->setIcon(QIcon(":/debugger/pics/addtab.png"));
	m_DelTab->setIcon(QIcon(":/debugger/pics/closetab.png"));
	m_AddTab->setAutoRaise(true);
	m_DelTab->setAutoRaise(true);
	m_DelTab->setEnabled(false);
	
	ui.tabWidget->setCornerWidget(m_AddTab, Qt::TopLeftCorner);
	ui.tabWidget->setCornerWidget(m_DelTab, Qt::TopRightCorner);
	
	connect(m_AddTab, SIGNAL(clicked()), this, SLOT(createNewDataTab()));
	connect(m_DelTab, SIGNAL(clicked()), this, SLOT(removeDataTab()));
}

//------------------------------------------------------------------------------
// Name: on_registerList_customContextMenuRequested(const QPoint &pos)
// Desc: context menu handler for register view
//------------------------------------------------------------------------------
void DebuggerMain::on_registerList_customContextMenuRequested(const QPoint &pos) {\

	// TODO: only display menu if they right clicked on a valid item
	QMenu *menu = new QMenu(this);
	menu->addAction("&Follow In Dump", this, SLOT(followInDump()));
	menu->addAction("&Follow In Dump (New Tab)", this, SLOT(followInDumpNewTab()));
	menu->addAction("&Follow In Stack", this, SLOT(followInStack()));
	menu->exec(ui.registerList->mapToGlobal(pos));
	safeDelete(menu);
}

//------------------------------------------------------------------------------
// Name: on_cpuView_breakPointToggled(BaseTypes::address_t address)
// Desc: handler for toggling the breakpoints
//------------------------------------------------------------------------------
void DebuggerMain::on_cpuView_breakPointToggled(BaseTypes::address_t address) {
	Debugger::toggleBreakPoint(address);
}

//------------------------------------------------------------------------------
// Name: valueFromName(const QString &s, unsigned int mode)
// Desc: gets the value of a register based on name, takes into account a "mode"
//       mask, which lets us select catagories of registers to consider valid
// Note: portability issue, implementation uses x86 specific names
//------------------------------------------------------------------------------
BaseTypes::reg_t *DebuggerMain::valueFromName(const QString &s, unsigned int mode) {
	
	if(mode & State::EFLAGS_FLAG && s.startsWith("EFLAGS:"))	return &m_CurrentState.eflags;
	if(mode & State::EAX_FLAG && s.startsWith("EAX:")) 			return &m_CurrentState.eax;
	if(mode & State::EBX_FLAG && s.startsWith("EBX:")) 			return &m_CurrentState.ebx;
	if(mode & State::ECX_FLAG && s.startsWith("ECX:")) 			return &m_CurrentState.ecx;
	if(mode & State::EDX_FLAG && s.startsWith("EDX:")) 			return &m_CurrentState.edx;
	if(mode & State::EBP_FLAG && s.startsWith("EBP:")) 			return &m_CurrentState.ebp;
	if(mode & State::ESP_FLAG && s.startsWith("ESP:")) 			return &m_CurrentState.esp;
	if(mode & State::EDI_FLAG && s.startsWith("EDI:")) 			return &m_CurrentState.edi;;
	if(mode & State::ESI_FLAG && s.startsWith("ESI:")) 			return &m_CurrentState.esi;
	if(mode & State::EIP_FLAG && s.startsWith("EIP:")) 			return &m_CurrentState.eip;
	if(mode & State::CS_FLAG && s.startsWith("CS:")) 			return &m_CurrentState._cs;
	if(mode & State::DS_FLAG && s.startsWith("DS:")) 			return &m_CurrentState._ds;
	if(mode & State::ES_FLAG && s.startsWith("ES:")) 			return &m_CurrentState._es;
	if(mode & State::FS_FLAG && s.startsWith("FS:")) 			return &m_CurrentState._fs;
	if(mode & State::GS_FLAG && s.startsWith("GS:")) 			return &m_CurrentState._gs;
	if(mode & State::SS_FLAG && s.startsWith("SS:")) 			return &m_CurrentState._ss;
	
	return 0;
}

//------------------------------------------------------------------------------
// Name: followInStack()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::followInStack() {
	const QTreeWidgetItem *const i = ui.registerList->currentItem();
	if(i != 0) {
		const QString s = i->text(0);
		const unsigned int *value = valueFromName(s, State::GPR_FLAGS);

		if(value != 0) {
			if(!Debugger::dumpStack(*value)) {
				QMessageBox::information(this, "EDB", "There appears to be no memory at that location");
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: followRegisterInDump(bool tabbed)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::followRegisterInDump(bool tabbed) {
	const QTreeWidgetItem *const i = ui.registerList->currentItem();
	if(i != 0) {
		const QString s = i->text(0);
		const unsigned int *value = valueFromName(s, State::GPR_FLAGS);

		if(value != 0) {
			if(!Debugger::dumpData(*value, tabbed)) {
				QMessageBox::information(this, "EDB", "There appears to be no memory at that location");
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_registerList_itemDoubleClicked(QTreeWidgetItem *item)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_registerList_itemDoubleClicked(QTreeWidgetItem *item) {

	const QString s = item->text(0);
	unsigned int *value = valueFromName(s, State::ANY_FLAGS);
	
	if(value != 0) {
		if(Debugger::getValueFromUser(*value, "Register Value")) {			
			Debugger::debuggerBase->setState(m_CurrentState);
			updateGUI();
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_action_About_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_About_triggered() {

	QMessageBox::about(this, "About EDB", QString().sprintf(
		"<p>EDB (Evan's Debugger) is designed to be an easy to use, modular, and cross platform debugger.</p>"
		"<p>More information and updates can be found at <a href=\"http://www.codef00.com\">http://www.codef00.com</a></p>"
		"<p>Written by Evan Teran.</p>"
		"<p>version: %s</p>", version_string));
}

//------------------------------------------------------------------------------
// Name: on_actionAbout_QT_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionAbout_QT_triggered() {
	QMessageBox::aboutQt(this, "About QT");
}

//------------------------------------------------------------------------------
// Name: mnuCPUJumpToAddress()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUJumpToAddress() {
    bool ok;
    QString text = QInputDialog::getText(this, "Goto Address", "Address:", QLineEdit::Normal, "", &ok);
    if (ok && !text.isEmpty()) {
		const BaseTypes::address_t address = text.toUInt(&ok, 16);
		if(ok) {
			if(!Debugger::jumpToAddress(address)) {
				QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location.");
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: mnuCPUJumpToEIP()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUJumpToEIP() {
	if(!Debugger::jumpToAddress(m_CurrentState.eip)) {
		QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location.");
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Configure_Debugger_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Configure_Debugger_triggered() {
	
	if(Debugger::dialogOptions()->exec() == QDialog::Accepted) {	
		// reload symbols incase they changed

		safeDelete(Debugger::symbolManager);
		Debugger::symbolManager = new SymbolManager(Debugger::config()->symbolPath);

		if(Debugger::memoryRegions != 0) {
			Debugger::memoryRegions->sync();
		}
		
		QFont stackFont;
		stackFont.fromString(Debugger::config()->StackViewFont);
		ui.stackView->setFont(stackFont);

		updateGUI();
	}
}

//------------------------------------------------------------------------------
// Name: on_cpuView_customContextMenuRequested(const QPoint &pos)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_cpuView_customContextMenuRequested(const QPoint &pos) {
	QMenu *menu = new QMenu(this);
	// TODO: add comment
	//menu->addAction("Add &Comment", this, SLOT(""));
	//menu->addSeparator();
	menu->addAction("&Goto Address", this, SLOT(mnuCPUJumpToAddress()));
	menu->addAction("&Goto EIP", this, SLOT(mnuCPUJumpToEIP()));
	
	menu->exec(ui.cpuView->viewport()->mapToGlobal(pos));
	safeDelete(menu);
}

//------------------------------------------------------------------------------
// Name: mnuGotoAddress()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuGotoAddress() {

    bool ok;
    QString text = QInputDialog::getText(this, "Goto Address", "Address:", QLineEdit::Normal, "", &ok);
    if (ok && !text.isEmpty()) {
		const BaseTypes::address_t address = text.toUInt(&ok, 16);
		if(ok) {		
			if(!Debugger::dumpData(address)) {
				QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location.");
			}
		}
	}
}


//------------------------------------------------------------------------------
// Name: doFollowInDump(QHexView *hv)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doFollowInDump(QHexView *hv) {
	if(hv != 0) {
		if(hv->hasSelectedText()) {
			const QByteArray data = hv->selectedBytes();

			if(data.size() == sizeof(BaseTypes::address_t)) {
				union {
					quint8	b[sizeof(BaseTypes::address_t)];
					quint32	d;
				} dword;

				for(unsigned int i = 0; i < sizeof(BaseTypes::address_t); ++i) {
					dword.b[i] = data[i];
				}

				if(!Debugger::dumpData(dword.d)) {
					QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location");
				}
			} else {
				QMessageBox::information(this, "Nothing Selected", QString().sprintf("Please select a %d bytes to use this function.", sizeof(BaseTypes::address_t)));
			}
		} else {
			QMessageBox::information(this, "Nothing Selected", QString().sprintf("Please select a %d bytes to use this function.", sizeof(BaseTypes::address_t)));
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuFollowInDump() {
	doFollowInDump(qobject_cast<QHexView *>(ui.tabWidget->currentWidget()));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackFollowInDump() {
	doFollowInDump(ui.stackView);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionApplication_Arguments_triggered() {
    bool ok;
    const QString text = QInputDialog::getText(this, "Application Arguments", "Arguments:", QLineEdit::Normal, m_Arguments, &ok);
	if(ok) {
		m_Arguments = text;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionApplication_Working_Directory_triggered() {

    bool ok;
    const QString text = QInputDialog::getText(this, "Application Working Directory", "Working Directory:", QLineEdit::Normal, m_WorkingDirectory, &ok);
	if(ok) {
		m_WorkingDirectory = text;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
// Note: portability issue, assumes data is 4 bytes wide
//------------------------------------------------------------------------------
void DebuggerMain::pushDWORD() {

	uint32_t value;
	
	
	Debugger::debuggerBase->readBytes(m_CurrentState.esp - 4, &value, sizeof(value));

	if(Debugger::getValueFromUser(value, "Enter value to push")) {

		m_CurrentState.esp -= 4;
		Debugger::debuggerBase->writeBytes(m_CurrentState.esp, &value, sizeof(value));

		Debugger::debuggerBase->setState(m_CurrentState);
		updateGUI();
	}
	
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
// Note: portability issue, assumes data is 4 bytes wide
//------------------------------------------------------------------------------
void DebuggerMain::popDWORD() {
	m_CurrentState.esp += 4;
	updateGUI();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_stackView_customContextMenuRequested(const QPoint &pos) {
	QHexView *const s = ui.stackView;
	if(s != 0) {
		QMenu *menu = s->createStandardContextMenu();
		menu->addSeparator();
		menu->addAction("Follow Address In &Dump", this, SLOT(mnuStackFollowInDump()));
		menu->addSeparator();
		menu->addAction("&Edit Bytes", this, SLOT(modifyBytesStack()));
		menu->addSeparator();
		menu->addAction("&Push DWORD", this, SLOT(pushDWORD()));
		menu->addAction("P&op DWORD", this, SLOT(popDWORD()));
		menu->exec(s->mapToGlobal(pos));
		safeDelete(menu);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::dumpDataToFile() {
	QHexView *const s = qobject_cast<QHexView *>(ui.tabWidget->currentWidget());
	if(s != 0) {
		

		const QString filename = QFileDialog::getSaveFileName(this, "Save File", m_LastOpenDirectory, "");
		if(filename != "") {
			
			QFile file(filename);
			file.open(QIODevice::WriteOnly);
			if(file.isOpen()) {
				const QByteArray bytes = s->allBytes();
				file.write(bytes);
			}			
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::modifyBytesDump() {
	QHexView *const s = qobject_cast<QHexView *>(ui.tabWidget->currentWidget());
	if(s != 0) {
		const BaseTypes::address_t address = s->selectedBytesAddress();
		const unsigned int size = s->selectedBytesSize();
		QByteArray bytes = s->selectedBytes();
		
		
		if(Debugger::getBinaryStringFromUser(bytes, "Edit Binary String", size)) {
		
			while(bytes.size() < static_cast<int>(size)) {
				bytes.push_back('\x00');
			}
		
			Debugger::debuggerBase->writeBytes(address, bytes.data(), size);
			updateGUI();
		}	
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::modifyBytesStack() {
	QHexView *const s = ui.stackView;
	if(s != 0) {
		const BaseTypes::address_t address = s->selectedBytesAddress();
		const unsigned int size = s->selectedBytesSize();
		QByteArray bytes = s->selectedBytes();
		
		
		if(Debugger::getBinaryStringFromUser(bytes, "Edit Binary String", size)) {
		
			while(bytes.size() < static_cast<int>(size)) {
				bytes.push_back('\x00');
			}
		
			Debugger::debuggerBase->writeBytes(address, bytes.data(), size);
			updateGUI();
		}	
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::dumpContextMenu(const QPoint &pos) {
	QHexView *const s = qobject_cast<QHexView *>(sender());
	if(s != 0) {
		QMenu *menu = s->createStandardContextMenu();
		menu->addSeparator();
		menu->addAction("Follow Address In &Dump", this, SLOT(mnuFollowInDump()));
		menu->addAction("&Goto Address", this, SLOT(mnuGotoAddress()));
		menu->addSeparator();
		menu->addAction("&Edit Bytes", this, SLOT(modifyBytesDump()));
		menu->addSeparator();
		menu->addAction("&Dump To File", this, SLOT(dumpDataToFile()));
		menu->exec(s->mapToGlobal(pos));

		safeDelete(menu);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateMenuState(GUI_STATE guiState) {

	switch(guiState) {
	case PAUSED:
	#if 0
		ui.actionRun_Until_Return->setEnabled(true);
	#endif
		ui.action_Run->setEnabled(true);
		ui.action_Pause->setEnabled(false);
		ui.action_Step_Into->setEnabled(true);
		ui.action_Step_Over->setEnabled(true);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(true);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(true);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(true);
		break;
	case RUNNING:
	#if 0
		ui.actionRun_Until_Return->setEnabled(false);
	#endif
		ui.action_Run->setEnabled(false);
		ui.action_Pause->setEnabled(true);
		ui.action_Step_Into->setEnabled(false);
		ui.action_Step_Over->setEnabled(false);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(false);
		break;
	case TERMINATED:
	#if 0
		ui.actionRun_Until_Return->setEnabled(false);
	#endif
		ui.action_Run->setEnabled(false);
		ui.action_Pause->setEnabled(false);
		ui.action_Step_Into->setEnabled(false);
		ui.action_Step_Over->setEnabled(false);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(false);
		break;
	}
	m_GUIState = guiState;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventSignaled() {
	switch(m_LastEvent.exitSignal()) {
	case DebugEvent::sigkill:
		QMessageBox::information(this, "Application Killed", "The debugged application was killed.");
		break;
	default:
		Debugger::log(QString().sprintf("Unknown Termination Signal Recieved! %d", m_LastEvent.exitSignal()));
		break;
	}

	on_action_Detach_triggered();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventStopped() {


	// ok we just came in from a signal, we need to test some things, 
	// generally, we will want to check if it was a step, if it was, was it because
	// we just hit a break point or because we wanted to run, but had to step first
	// in case were were on a breakpoint already...

	switch(m_LastEvent.stopSignal()) {
	case DebugEvent::sigsegv:
		QMessageBox::information(this, "Illegal Access Fault", QString().sprintf(
			"The debugged application encountered a segmentation fault!\n"
			"The address %#08x could not be accessed.\n\n"
			"If you would like to pass this exception to the application press Shift+[F7/F8/F9]", 
			Debugger::debuggerBase->getFaultAddress())
			);
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigill:
		QMessageBox::information(this, "Illegal Instruction Fault", 
			"The debugged application attempted to execute an illegal instruction.\n\n"
			"If you would like to pass this exception to the application press Shift+[F7/F8/F9]"
			);
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigfpe:
		QMessageBox::information(this, "Floating Point Exception", 
			"The debugged application encountered a floating-point exception!\n"
			"If you would like to pass this exception to the application press Shift+[F7/F8/F9]"
			);
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigstop:
		// user asked to pause the debugged process
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigtrap:
		
		// we just got a SIGTRAP, there are a few possible causes
		// #1: we just did an execve, in which case we want to stop cause we just attached
		// #2: we hit a 0xCC breakpoint, if so, then we want to stop
		// #3: we did a step
		
		do {
			// prevEIP will have the address of the instruction which brought us here!
			const BaseTypes::address_t prevEIP = m_CurrentState.eip - Debugger::debuggerBase->executionBreakPointSize();
			
			// look it up in our breakpoint list, make sure it is one of OUR int3s!
			// if it is, we need to backup EIP and pause ourselves
			const BreakPoint *const bp = Debugger::debuggerBase->findBreakPoint(prevEIP);
			if(bp != 0 && bp->enabled()) {
						
				// if it's a one time breakpoint then we should remove it upon
				// triggering, this is mainly used for "run-until" situations
				// like step over

				if(bp->oneTime()) {
					Debugger::debuggerBase->removeBreakpoint(prevEIP);
				} else {
					// TODO: make this work
					//++bp->passcount;					
				}

				// back up eip, since we executed a breakpoint instead of 
				// the real code that belongs there
				m_CurrentState.eip = prevEIP;
				Debugger::debuggerBase->setState(m_CurrentState);

				m_StepWasRun = false;
			} else {
				// it wasn't one of our int3s, so what do we do?

				// this code attempts to cancel a run which will
				// cause overrun a breakpoint potentially				
				uint8_t breakpointData[Debugger::debuggerBase->executionBreakPointSize()];
				if(Debugger::debuggerBase->readBytes(prevEIP, breakpointData, sizeof(breakpointData))) {
					if(std::memcmp(
						breakpointData, 
						Debugger::debuggerBase->executionBreakPointData().data(), 
						Debugger::debuggerBase->executionBreakPointSize()) == 0) {
						m_StepWasRun = false;			
					}
				}
			}
		} while(0);
		break;

	case DebugEvent::sigchld:
	case DebugEvent::sigcont:
		// this one is probably not very interesting, so we'll just resume
		Debugger::debuggerBase->resume(m_LastEvent.stopSignal());
		// we do an early return so it's like it never happened
		return;
	default:
		Debugger::log(QString().sprintf("Unknown Stop Signal Recieved! %d", m_LastEvent.stopSignal()));
		Debugger::debuggerBase->resume(m_LastEvent.stopSignal());
		return;
	}

	// re-enable any breakpoints we disabled
	if(m_ReenableBreakPointAddress != 0) {
		Debugger::debuggerBase->enableBreakpoint(m_ReenableBreakPointAddress);
		m_ReenableBreakPointAddress = 0;
	}

	// if we stepped with the intention of running, well we should run then!
	if(m_StepWasRun) {
		m_StepWasRun = false;
		Debugger::debuggerBase->resume(0);
	} else {
		updateGUI();
		updateMenuState(PAUSED);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventExited() {
	QMessageBox::information(this, "Application Exited", QString().sprintf("The debugged application exited normally with exit code %d.",  m_LastEvent.exitStatus()));
	on_action_Detach_triggered();
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEvent(DebugEvent event) {
	if(Debugger::debuggerBase != 0) {
		Debugger::debuggerBase->getState(m_CurrentState);
	}
	
	// it is possible but rare that that we get an event when no memory regions
	// are setup (on detach)
	if(Debugger::memoryRegions != 0) {
		Debugger::memoryRegions->sync();
	}
	
	m_LastEvent = event;
	
	switch(m_LastEvent.reason()) {
	case DebugEvent::EVENT_STOPPED:
		handleEventStopped();
		break;
		
	case DebugEvent::EVENT_SIGNALED:
		handleEventSignaled();
		break;
		
	case DebugEvent::EVENT_EXITED:
		handleEventExited();
		break;
		
	case DebugEvent::EVENT_UNKNOWN:
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::debugEventHandler(DebugEvent event) {

	if(Debugger::debugEventHandler != 0) {
		Debugger::debugEventHandler->handleEvent(event);
	} else {
		assert(0);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::DataViewInfo::update() {

	safeDelete(stream);
	stream = new ByteStreamRegion(region);
	
	if(view != 0) {
		view->setAddressOffset(region.start);
		view->setData(stream);
	}
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateData(DebuggerMain::DataViewInfo *v) {
	if(v != 0) {
		v->update();	
		ui.tabWidget->setTabText(ui.tabWidget->indexOf(v->view), QString().sprintf("%08x-%08x", v->region.start, v->region.end));
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::clearData(DataViewInfo *v) {
	if(v != 0) {
		v->view->clear();
		v->view->scrollTo(0);
		ui.tabWidget->setTabText(ui.tabWidget->indexOf(v->view), QString().sprintf("%08x-%08x", v->region.start, v->region.end));
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateInstructionInfo(BaseTypes::address_t address) {

	ui.listWidget->clear();
	
	if(Debugger::debuggerBase != 0) {
		uint8_t buffer[MAX_INSN_SIZE];
		Instruction insn;
		
		// TODO: make sure we could read here!
		Debugger::debuggerBase->readBytes(address, buffer, sizeof(buffer));
		const int size = insn.disassemble(buffer, sizeof(buffer), address, 0);
		if(size != 0) {
		
			// fetch the 3 possible operands
			const Operand *const operands[3] = {
				insn.operand_1st(),
				insn.operand_2nd(),
				insn.operand_3rd()
			};

			// figure out the instruction type and display some information about it
			switch(insn.type()) {
			case insn_return:
				do {
					BaseTypes::address_t returnAddress;
					Debugger::debuggerBase->readBytes(m_CurrentState.esp, &returnAddress, sizeof(returnAddress));
					
					const QString symname = findFunctionSymbol(returnAddress, "");
					if(symname != "") {
						ui.listWidget->addItem(QString().sprintf("Return to %08x <%s>", returnAddress, qPrintable(symname)));
					} else {
						ui.listWidget->addItem(QString().sprintf("Return to %08x", returnAddress));
					}
				} while(0);
				break;
			case insn_jcc:
				do {
					bool taken = true;
					const bool cf = (m_CurrentState.eflags & 0x0001) != 0;
					const bool zf = (m_CurrentState.eflags & 0x0040) != 0;
					const bool of = (m_CurrentState.eflags & 0x0800) != 0;
					const bool df = (m_CurrentState.eflags & 0x0400) != 0;
					const bool sf = (m_CurrentState.eflags & 0x0080) != 0;
					const bool pf = (m_CurrentState.eflags & 0x0004) != 0;

					if((insn.flagsTested() & insn_carry_set)				&& !cf) taken = false;
					if((insn.flagsTested() & insn_zero_set)					&& !zf) taken = false;
					if((insn.flagsTested() & insn_oflow_set)				&& !of) taken = false;
					if((insn.flagsTested() & insn_dir_set)					&& !df) taken = false;
					if((insn.flagsTested() & insn_sign_set)					&& !sf) taken = false;
					if((insn.flagsTested() & insn_parity_set)				&& !pf) taken = false;
					if((insn.flagsTested() & insn_carry_clear)				&& cf) taken = false;
					if((insn.flagsTested() & insn_zero_clear)				&& zf) taken = false;
					if((insn.flagsTested() & insn_oflow_clear)				&& of) taken = false;
					if((insn.flagsTested() & insn_dir_clear)				&& df) taken = false;
					if((insn.flagsTested() & insn_sign_clear)				&& sf) taken = false;
					if((insn.flagsTested() & insn_parity_clear)				&& pf) taken = false;

					if((insn.flagsTested() & insn_zero_set_or_sign_ne_oflow)	&& (!zf && !(sf != of)))	taken = false;
					if((insn.flagsTested() & insn_carry_or_zero_set)			&& !(cf && zf))	taken = false;
					if((insn.flagsTested() & insn_sign_eq_oflow)				&& !(sf == of))	taken = false;
					if((insn.flagsTested() & insn_sign_ne_oflow)				&& (sf == of))	taken = false;

					if(taken) {
						ui.listWidget->addItem("jump taken");
					} else {
						ui.listWidget->addItem("jump NOT taken");
					}
				} while(0);
				// FALL THROUGH!
			case insn_jmp:
			case insn_call:
			case insn_callcc:
				if(operands[0] != 0) {
					switch(operands[0]->type()) {
					case op_relative_far:
					case op_relative_near:
					case op_register:
						do {
							const BaseTypes::address_t effective_address = operands[0]->effectiveAddress(m_CurrentState);

							char tmp[100];
							operands[0]->format(tmp, sizeof(tmp), Debugger::config()->syntax);

							const QString symname = findFunctionSymbol(effective_address, "");
							if(symname != "") {
								ui.listWidget->addItem(QString().sprintf("%s = %08x <%s>", tmp, effective_address, qPrintable(symname)));
							} else {
								ui.listWidget->addItem(QString().sprintf("%s = %08x", tmp, effective_address));
							}
						} while(0);
						break;

					case op_expression:
					default:
						do {
						
							const BaseTypes::address_t effective_address = operands[0]->effectiveAddress(m_CurrentState);
							uint32_t target;

							char tmp[100];
							operands[0]->format(tmp, sizeof(tmp), Debugger::config()->syntax);

							const bool ok = Debugger::debuggerBase->readBytes(effective_address, &target, sizeof(target));
							
							if(ok) {
								const QString symname = findFunctionSymbol(target, "");
								if(symname != "") {
									ui.listWidget->addItem(QString().sprintf("%s = [%08x] = %08x <%s>", tmp, effective_address, target, qPrintable(symname)));
								} else {
									ui.listWidget->addItem(QString().sprintf("%s = [%08x] = %08x", tmp, effective_address, target));
								}
							} else {
								ui.listWidget->addItem(QString().sprintf("%s = [%08x] = ?", tmp, effective_address));
							}
						} while(0);
						break;
					}
				}
				break;
			default:
				for(int j = 0; j < 3; ++j) {

					if(operands[j] != 0) {
						switch(operands[j]->type()) {
						case op_relative_far:
						case op_relative_near:
						case op_register:
							do {
								const BaseTypes::address_t effective_address = operands[j]->effectiveAddress(m_CurrentState);

								char tmp[100];
								operands[j]->format(tmp, sizeof(tmp), Debugger::config()->syntax);

								ui.listWidget->addItem(QString().sprintf("%s = %08x", tmp, effective_address));
							} while(0);
							break;
						case op_expression:
							do {
								const BaseTypes::address_t effective_address = operands[j]->effectiveAddress(m_CurrentState);
								uint32_t target;

								const bool ok = Debugger::debuggerBase->readBytes(effective_address, &target, sizeof(target));

								char tmp[100];
								operands[j]->format(tmp, sizeof(tmp), Debugger::config()->syntax);

								if(ok) {
									ui.listWidget->addItem(QString().sprintf("%s = [%08x] = %08x", tmp, effective_address, target));
								} else {
									ui.listWidget->addItem(QString().sprintf("%s = [%08x] = ?", tmp, effective_address));
								}
							} while(0);
							break;
						default:
							break;
						}
					}
				}
				break;

			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateDisassembly(BaseTypes::address_t address, const MemoryRegions::Region &r) {

	ui.cpuView->setCurrentAddress(m_CurrentState.eip);
	ui.cpuView->setAddressOffset(r.start);
	ui.cpuView->setRegion(r);
	
	if(!ui.cpuView->addressShown(m_CurrentState.eip)) {
		ui.cpuView->scrollTo(address);
	}
	
	updateInstructionInfo(address);
}

//------------------------------------------------------------------------------
// Name: findFunctionSymbol(BaseTypes::address_t address, const QString &defaultValue)
// Desc: 
//------------------------------------------------------------------------------
QString DebuggerMain::findFunctionSymbol(BaseTypes::address_t address, const QString &defaultValue) {
	QString symname(defaultValue);

	if(Debugger::symbolManager != 0) {
		const SymbolManager::Symbol *const s = Debugger::symbolManager->findNearSymbol(address);
		if(s != 0) {
			symname = QString().sprintf("%s+%x", qPrintable(s->name), address - s->address);
		}
	}
	
	return symname;
}

//------------------------------------------------------------------------------
// Name: doUpdateRegister(QTreeWidgetItem *item, const QString &name, BaseTypes::reg_t value)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doUpdateRegister(QTreeWidgetItem *item, const QString &name, BaseTypes::reg_t value) {
	QString regString;
	// TODO: uses elided text for ascii string?
	if(Debugger::memoryRegions != 0 && Debugger::getASCIIStringAtAddress(value, regString, 4, 128)) {
		item->setText(0, QString().sprintf("%s: %08x ASCII \"%s\"", qPrintable(name), value, qPrintable(regString)));
	} else {
		item->setText(0, QString().sprintf("%s: %08x", qPrintable(name), value));
	}
}

//------------------------------------------------------------------------------
// Name: updateRegisterView(const QString &defaultRegionName)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateRegisterView(const QString &defaultRegionName) {
	
	if(Debugger::memoryRegions != 0) {
		doUpdateRegister(m_RegisterViewItems[0], "EAX", m_CurrentState.eax);
		doUpdateRegister(m_RegisterViewItems[1], "EBX", m_CurrentState.ebx);
		doUpdateRegister(m_RegisterViewItems[2], "ECX", m_CurrentState.ecx);
		doUpdateRegister(m_RegisterViewItems[3], "EDX", m_CurrentState.edx);
		doUpdateRegister(m_RegisterViewItems[4], "EBP", m_CurrentState.ebp);
		doUpdateRegister(m_RegisterViewItems[5], "ESP", m_CurrentState.esp);
		doUpdateRegister(m_RegisterViewItems[6], "ESI", m_CurrentState.esi);
		doUpdateRegister(m_RegisterViewItems[7], "EDI", m_CurrentState.edi);

		const QString symname = findFunctionSymbol(m_CurrentState.eip, defaultRegionName);
		m_RegisterViewItems[8]->setText(0, QString().sprintf("EIP: %08x <%s>", m_CurrentState.eip, qPrintable(symname)));
		m_RegisterViewItems[9]->setText(0, QString().sprintf("EFLAGS: %08x", m_CurrentState.eflags));

		m_RegisterViewItems[10]->setText(0, QString().sprintf("CS: %04x", m_CurrentState.cs & 0xffff));
		m_RegisterViewItems[11]->setText(0, QString().sprintf("DS: %04x", m_CurrentState.ds & 0xffff));
		m_RegisterViewItems[12]->setText(0, QString().sprintf("ES: %04x", m_CurrentState.es & 0xffff));
		m_RegisterViewItems[13]->setText(0, QString().sprintf("FS: %04x", m_CurrentState.fs & 0xffff));
		m_RegisterViewItems[14]->setText(0, QString().sprintf("GS: %04x", m_CurrentState.gs & 0xffff));
		m_RegisterViewItems[15]->setText(0, QString().sprintf("SS: %04x", m_CurrentState.ss & 0xffff));
	} else {
		m_RegisterViewItems[0]->setText(0, "EAX: 00000000");
		m_RegisterViewItems[1]->setText(0, "EBX: 00000000");
		m_RegisterViewItems[2]->setText(0, "ECX: 00000000");
		m_RegisterViewItems[3]->setText(0, "EDX: 00000000");
		m_RegisterViewItems[4]->setText(0, "EBP: 00000000");
		m_RegisterViewItems[5]->setText(0, "ESP: 00000000");
		m_RegisterViewItems[6]->setText(0, "ESI: 00000000");
		m_RegisterViewItems[7]->setText(0, "EDI: 00000000");
		m_RegisterViewItems[8]->setText(0, "EIP: 00000000");
		m_RegisterViewItems[9]->setText(0, "EFLAGS: 00000000");
		m_RegisterViewItems[10]->setText(0, "CS: 0000");
		m_RegisterViewItems[11]->setText(0, "DS: 0000");
		m_RegisterViewItems[12]->setText(0, "ES: 0000");
		m_RegisterViewItems[13]->setText(0, "FS: 0000");
		m_RegisterViewItems[14]->setText(0, "GS: 0000");
		m_RegisterViewItems[15]->setText(0, "SS: 0000");
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateStackView() {
	if(Debugger::memoryRegions == 0 || !Debugger::dumpStack(m_CurrentState.esp)) {
		ui.stackView->clear();
		ui.stackView->scrollTo(0);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateCPUView(MemoryRegions::Region &region) {
	if(Debugger::memoryRegions == 0 || !Debugger::memoryRegions->findRegion(m_CurrentState.eip, region)) {
		ui.cpuView->clear();
		ui.cpuView->scrollTo(0);
		ui.listWidget->clear();
	} else {
		updateDisassembly(m_CurrentState.eip, region);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateDataViews() {
	if(Debugger::memoryRegions != 0) {
		// update all data views with the current region data
		foreach(DataViewInfo *info, m_DataRegions) {

			// make sure the regions are still valid..
			if(Debugger::memoryRegions->findRegion(info->region.start)) {
				updateData(info);
			} else {
				clearData(info);
			}
		}
	} else {
		// clear all data views
		foreach(DataViewInfo *info, m_DataRegions) {
			clearData(info);
		}
	}
}

//------------------------------------------------------------------------------
// Name: updateGUI()
// Desc: updates all the different displays
//------------------------------------------------------------------------------
void DebuggerMain::updateGUI() {

	if(m_UpdateGUI) {
	
		MemoryRegions::Region region;

		updateCPUView(region);
		updateDataViews();
		updateStackView();
		updateRegisterView(region.name);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doResume(bool passExceptionToApp) {
	
	// are we currently sitting on a breakpoint
	const bool atBreakPoint = Debugger::debuggerBase->findBreakPoint(m_CurrentState.eip) != 0;
	
	// if we are on a breakpoint, disable it, and set the reenable 
	// address to the current eip
	if(atBreakPoint) {
		Debugger::debuggerBase->disableBreakpoint(m_CurrentState.eip);
		m_ReenableBreakPointAddress = m_CurrentState.eip;
	}

	// set the state to "running"
	updateMenuState(RUNNING);
	
	// if neccessary pass the signal to the application, otherwise just step
	if(passExceptionToApp && m_LastEvent.stopped() && m_LastEvent.stopSignal() != SIGTRAP && m_LastEvent.stopSignal() != SIGSTOP) {
		Debugger::debuggerBase->step(m_LastEvent.stopSignal());
	} else {
		Debugger::debuggerBase->step(0);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Run_Pass_Signal_To_Application_triggered() {
	m_StepWasRun = true;
	doResume(true);	
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Into_Pass_Signal_To_Application_triggered() {
	doResume(true);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Over_Pass_Signal_To_Application_triggered() {
	uint8_t buffer[MAX_INSN_SIZE];
	
	Instruction insn;
	Debugger::debuggerBase->readBytes(m_CurrentState.eip, buffer, sizeof(buffer));
		
	const int size = insn.disassemble(buffer, sizeof(buffer), 0, 0);
	
	if(size != 0 && (insn.type() == insn_call || insn.type() == insn_callcc || insn.prefix() & (insn_rep_zero | insn_rep_notzero))) {
		// add a temporary breakpoint at the instruction just after the call
		Debugger::debuggerBase->addBreakpoint(m_CurrentState.eip + size, true);
		on_action_Run_Pass_Signal_To_Application_triggered();
	} else {
		on_action_Step_Into_Pass_Signal_To_Application_triggered();
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Run_triggered() {
	m_StepWasRun = true;
	doResume(false);	
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Into_triggered() {
	doResume(false);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Over_triggered() {

	uint8_t buffer[MAX_INSN_SIZE];
	Instruction insn;

	Debugger::debuggerBase->readBytes(m_CurrentState.eip, buffer, sizeof(buffer));
		
	const int size = insn.disassemble(buffer, sizeof(buffer), 0, 0);
	
	if(size != 0 && (insn.type() == insn_call || insn.type() == insn_callcc || insn.prefix() & (insn_rep_zero | insn_rep_notzero))) {
		// add a temporary breakpoint at the instruction just after the call
		Debugger::debuggerBase->addBreakpoint(m_CurrentState.eip + size, true);
		on_action_Run_triggered();
	} else {
		on_action_Step_Into_triggered();
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionRun_Until_Return_triggered() {
	// TODO: implement this
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Pause_triggered() {
	Debugger::debuggerBase->pause();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Restart_triggered() {
	// TODO: implement this
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::cleanupEventThread() {

	if(m_DebugEventServer != 0) {
		
		// make sure we don't get any spurious events!
		disconnect(m_DebugEventServer, SIGNAL(debugEvent(DebugEvent)), this, SLOT(debugEventHandler(DebugEvent)));
		
		// make sure it thinks it shoudl exit
		m_DebugEventServer->m_ShouldExit = true;

		// this is ugly, but it seems to work pretty well
		// basically we have marked the thread as "shoudl exit"
		// but, we may be blocked (likley) waiting for the next event
		// so we intentionally cause one last event giving it a change to
		// check the loop invariant one more time, which will allow
		// the thread to exit cleanly
		if(m_GUIState == RUNNING) {
			Debugger::debuggerBase->pause();
			updateMenuState(PAUSED);
		} else {
			Debugger::debuggerBase->step(0);
			updateMenuState(RUNNING);
		}
		m_DebugEventServer->wait();

		// TODO: find a way to wake up the waiting event thread
		m_DebugEventServer = 0;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::cleanupDebugger() {
	
	cleanupEventThread();

	safeDelete(Debugger::memoryRegions);
	safeDelete(Debugger::symbolManager);

	// clear up the data view
	while(ui.tabWidget->count() > 1) {
		removeDataTab();
	}
	
	setWindowTitle("EDB");
	
	updateGUI();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doDetachFromProcess(bool kill) {

	cleanupDebugger();
	
	m_StepWasRun = false;
	
	if(Debugger::debuggerBase != 0) {
		if(kill) {
			Debugger::debuggerBase->kill();
		} else {
			Debugger::debuggerBase->detach();
		}
	}
	
	std::memset(&m_CurrentState, 0, sizeof(m_CurrentState));
	
	updateMenuState(TERMINATED);
}

//------------------------------------------------------------------------------
// Name: on_action_Detach_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Detach_triggered() {
	doDetachFromProcess(false);
}

//------------------------------------------------------------------------------
// Name: on_action_Kill_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Kill_triggered() {
	doDetachFromProcess(true);
}

//------------------------------------------------------------------------------
// Name: getELFEntryPoint(const QString &s)
// Desc: 
//------------------------------------------------------------------------------
BaseTypes::address_t DebuggerMain::getELFEntryPoint(const QString &s) {

	union ElfHeader {
		Elf32_Ehdr elf32;
		Elf64_Ehdr elf64;
	};

	std::ifstream file(qPrintable(s), std::ios::binary);
	if(file) {
		ElfHeader header;
		file.seekg(0);
		if(file.read(reinterpret_cast<char *>(&header), sizeof(header))) {
			if(std::memcmp(header.elf32.e_ident, ELFMAG, SELFMAG) == 0) {
				switch(header.elf32.e_ident[EI_CLASS]) {
				case ELFCLASS32:
					return header.elf32.e_entry;
					break;
				case ELFCLASS64:
					// NOT YET SUPPORTED
					assert(0);
					return header.elf64.e_entry;
					break;
				}
			}
		}
	}
	return 0;
}



//------------------------------------------------------------------------------
// Name: setInitialDebuggerState()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::setInitialDebuggerState() {

	updateMenuState(PAUSED);
		
	m_DebugEventServer = new DebugEventServer(this);
	connect(m_DebugEventServer, SIGNAL(debugEvent(DebugEvent)), this, SLOT(debugEventHandler(DebugEvent)));
	m_DebugEventServer->start();
	
	Debugger::symbolManager = new SymbolManager(Debugger::config()->symbolPath);
	Debugger::memoryRegions = new MemoryRegions(Debugger::debuggerBase->pid());
	Debugger::memoryRegions->sync();
	
	const QVector<MemoryRegions::Region> &l = Debugger::memoryRegions->regions();

	assert(m_DataRegions.size() > 0);

	if(l.size() >= 2) {
		m_DataRegions[0]->region = l[1];
	} else {
		m_DataRegions[0]->region = MemoryRegions::Region();
	}
	
	std::memset(&m_CurrentState, 0, sizeof(m_CurrentState));
	
	m_StepWasRun = false;
}

//------------------------------------------------------------------------------
// Name: setInitialBreakpoint(const QString &s)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::setInitialBreakpoint(const QString &s) {
	BaseTypes::address_t entryPoint = 0;
	if(Debugger::config()->initialBreakPoint == Configuration::MainSymbol) {

		const QFileInfo fileInfo(s);
		QString mainSymbol = fileInfo.baseName();
		if(fileInfo.completeSuffix() != "") {
			mainSymbol += ".";
			mainSymbol += fileInfo.completeSuffix();
		}
		
		mainSymbol += "::main";

		const SymbolManager::Symbol *const sym = Debugger::symbolManager->findSymbol(mainSymbol);

		if(sym != 0) {
			entryPoint = sym->address;
		} else {
			entryPoint = getELFEntryPoint(s);
		}
	} else if(Debugger::config()->initialBreakPoint == Configuration::EntryPoint) {
		entryPoint = getELFEntryPoint(s);
	}

	if(entryPoint != 0) {
		Debugger::debuggerBase->addBreakpoint(entryPoint);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doOpenFile(const QString &s) {
	if(s != "") {
		m_LastOpenDirectory = QFileInfo(s).canonicalFilePath();
	
		on_action_Detach_triggered();
	
		if(Debugger::debuggerBase->open(s, m_WorkingDirectory, m_Arguments)) {
			setInitialDebuggerState();
			setInitialBreakpoint(s);
			setWindowTitle(QString().sprintf("EDB - %s", qPrintable(s)));
			
			// update recent file list
			if(!m_RecentFileList.contains(s)) {
				m_RecentFileList.push_front(s);
				
				// make sure we don't add more than the max
				while(m_RecentFileList.size() > MaxRecentFiles) {
					m_RecentFileList.pop_back();
				}
				
				updateRecentFileList();
			}
			
			
		} else {
			QMessageBox::information(this, "Could Not Attach", "Failed to open and attach to process, please check priviledges and try again.");
		}
		
		Debugger::debuggerBase->getState(m_CurrentState);
		updateGUI();
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Open_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Open_triggered() {
	doOpenFile(QFileDialog::getOpenFileName(this, "Choose a file", m_LastOpenDirectory, ""));
}

//------------------------------------------------------------------------------
// Name: doAttach(BaseTypes::pid_t pid)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doAttach(BaseTypes::pid_t pid) {
	if(pid != Debugger::debuggerBase->pid()) {

		on_action_Detach_triggered();

		if(Debugger::debuggerBase->attach(pid)) {
			setInitialDebuggerState();
			setWindowTitle(QString().sprintf("EDB - [%d]", pid));
		} else {
			QMessageBox::information(this, "EDB", "Failed to attach to process, please check priviledges and try again.");				
		}

		Debugger::debuggerBase->getState(m_CurrentState);
		updateGUI();
	} else {
		QMessageBox::information(this, "EDB", "You are already debugging that process!");
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Attach_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Attach_triggered() {
	static DialogAttach *dlg = 0;
	if(dlg == 0) {
		dlg = new DialogAttach(this);
	} 
	
	const int ret = dlg->exec();
	
	if(ret == QDialog::Accepted) {
		QList<QTableWidgetItem *> sel = dlg->ui.tableWidget->selectedItems();
		if(sel.size() != 0) {
			BaseTypes::pid_t pid = sel.begin()[0]->text().toUInt();
			doAttach(pid);
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Memory_Regions_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Memory_Regions_triggered() {
	static DialogMemoryRegions *dlg = 0;
	if(dlg == 0) {
		dlg = new DialogMemoryRegions(this);
	}
	
	const int ret = dlg->exec();
	
	if(ret == QDialog::Accepted) {
		QList<QTableWidgetItem *> sel = dlg->ui.tableWidget->selectedItems();
		if(sel.size() != 0) {
			const BaseTypes::address_t region_start	= sel.begin()[0]->text().toUInt(0, 16);
			Debugger::dumpData(region_start);
		}
	}
}

//------------------------------------------------------------------------------
// Name: createNewDataTab()
// Desc: duplicates the current tab creating a new one
//------------------------------------------------------------------------------
void DebuggerMain::createNewDataTab() {
	
	const int currentIndex = ui.tabWidget->currentIndex();
	
	DataViewInfo *newDataView = new DataViewInfo;
	
	if(currentIndex != -1) {
		// duplicate the current region
		newDataView->region = m_DataRegions[currentIndex]->region;
	}
	
	newDataView->stream	= new ByteStreamRegion(newDataView->region);
	newDataView->view	= new QHexView;
		
	// setup the context menu
	newDataView->view->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(newDataView->view, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(dumpContextMenu(const QPoint &)));
	
	newDataView->view->setAddressOffset(newDataView->region.start);
	newDataView->view->setData(newDataView->stream);
	
	m_DataRegions.push_back(newDataView);

	// create the tab!
	ui.tabWidget->addTab(newDataView->view, QString().sprintf("%08x-%08x", newDataView->region.start, newDataView->region.end));
	ui.tabWidget->setCurrentIndex(ui.tabWidget->count() - 1);
	
	if(ui.tabWidget->count() > 1) {
		m_DelTab->setEnabled(true);
	}
}

//------------------------------------------------------------------------------
// Name: removeDataTab() 
// Desc: handles removing of a memory view tab
//------------------------------------------------------------------------------
void DebuggerMain::removeDataTab() {
	const int currentIndex = ui.tabWidget->currentIndex();
	
	DataViewInfo *info = m_DataRegions[currentIndex];
	m_DataRegions.remove(currentIndex);
	
	ui.tabWidget->removeTab(currentIndex);
	if(ui.tabWidget->count() <= 1) {
		m_DelTab->setEnabled(false);
	}

	if(info != 0) {
		safeDelete(info->stream);
		safeDelete(info->view);
	}
	
	safeDelete(info);
}
