/*
Copyright (C) 2006 - 2009 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 "DumpState.h"
#include "Debugger.h"
#include "State.h"
#include "DebuggerCoreInterface.h"
#include "Util.h"
#include "Instruction.h"

#include <QMenu>
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>

namespace {
	//------------------------------------------------------------------------------
	// Name: outputHexString(T value)
	// Desc: 
	//------------------------------------------------------------------------------
	template <class T>
	std::string outputHexString(T value) {
		std::stringstream ss;
		ss << std::hex << std::setw(sizeof(T) * 2) << std::setfill('0') <<  static_cast<edb::reg_t>(value);
		return ss.str();
	}
}

//------------------------------------------------------------------------------
// Name: DumpState()
// Desc: 
//------------------------------------------------------------------------------
DumpState::DumpState() : m_Menu(0) {
}

//------------------------------------------------------------------------------
// Name: ~DumpState()
// Desc: 
//------------------------------------------------------------------------------
DumpState::~DumpState() {
}

//------------------------------------------------------------------------------
// Name: menu(QWidget *parent)
// Desc: 
//------------------------------------------------------------------------------
QMenu *DumpState::menu(QWidget *parent) {

	if(m_Menu == 0) {
		m_Menu = new QMenu(tr("DumpState"), parent);
		m_Menu->addAction (tr("&Dump Current State"), this, SLOT(doMenu()), QKeySequence(tr("Ctrl+D")));
	}
	
	return m_Menu;
}

//------------------------------------------------------------------------------
// Name: dumpCode(const State &state)
// Desc: 
//------------------------------------------------------------------------------
void DumpState::dumpCode(const State &state) {
	edb::address_t address = state.instructionPointer();

	for(int i = 0; i < 6; ++i) {
		quint8 buf[edb::Instruction::MAX_SIZE];
		int size = sizeof(buf);
		
		if(edb::v1::getInstructionBytes(address, buf, size)) {
			edb::Instruction insn(buf, size, address, std::nothrow);
			if(insn.valid()) {
				std::cout << outputHexString(address) << ": " << insn.format() << "\n";
			} else {
				break;
			}
			address += insn.size();
		} else {
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: dumpRegisters(const State &state)
// Desc: 
//------------------------------------------------------------------------------
void DumpState::dumpRegisters(const State &state) {
#if defined(EDB_X86)
	std::cout << "     eax:" << outputHexString(state.eax);
	std::cout << " ebx:" << outputHexString(state.ebx);
	std::cout << "  ecx:" << outputHexString(state.ecx);
	std::cout << "  edx:" << outputHexString(state.edx);
	std::cout << "     eflags:" << outputHexString(state.eflags);
	std::cout << "\n";
	std::cout << "     esi:" << outputHexString(state.esi);
	std::cout << " edi:" << outputHexString(state.edi);
	std::cout << "  esp:" << outputHexString(state.esp);
	std::cout << "  ebp:" << outputHexString(state.ebp);
	std::cout << "     eip:" << outputHexString(state.eip);
	std::cout << "\n";
	std::cout << "     cs:" << outputHexString(state.cs);
	std::cout << "  ds:" << outputHexString(state.ds);
	std::cout << "  es:" << outputHexString(state.es);
	std::cout << "  fs:" << outputHexString(state.fs);
	std::cout << "  gs:" << outputHexString(state.gs);
	std::cout << "  ss:" << outputHexString(state.ss);
	std::cout << "    ";
	std::cout << ((state.eflags & (1 << 11)) != 0 ? 'O' : 'o') << ' ';
	std::cout << ((state.eflags & (1 << 10)) != 0 ? 'D' : 'd') << ' ';
	std::cout << ((state.eflags & (1 <<  9)) != 0 ? 'I' : 'i') << ' ';
	std::cout << ((state.eflags & (1 <<  8)) != 0 ? 'T' : 't') << ' ';
	std::cout << ((state.eflags & (1 <<  7)) != 0 ? 'S' : 's') << ' ';
	std::cout << ((state.eflags & (1 <<  6)) != 0 ? 'Z' : 'z') << ' ';
	std::cout << ((state.eflags & (1 <<  4)) != 0 ? 'A' : 'a') << ' ';
	std::cout << ((state.eflags & (1 <<  2)) != 0 ? 'P' : 'p') << ' ';
	std::cout << ((state.eflags & (1 <<  0)) != 0 ? 'C' : 'c');
	std::cout << "\n";
#elif defined(EDB_X86_64)
	std::cout << "     rax:" << outputHexString(state.rax);
	std::cout << " rbx:" << outputHexString(state.rbx);
	std::cout << "  rcx:" << outputHexString(state.rcx);
	std::cout << "  rdx:" << outputHexString(state.rdx);
	std::cout << "     rflags:" << outputHexString(state.rflags);
	std::cout << "\n";
	std::cout << "     rsi:" << outputHexString(state.rsi);
	std::cout << " rdi:" << outputHexString(state.rdi);
	std::cout << "  rsp:" << outputHexString(state.rsp);
	std::cout << "  rbp:" << outputHexString(state.rbp);
	std::cout << "        rip:" << outputHexString(state.rip);
	std::cout << "\n";
	std::cout << "      r8:" << outputHexString(state.r8);
	std::cout << "  r9:" << outputHexString(state.r9);
	std::cout << "  r10:" << outputHexString(state.r10);
	std::cout << "  r11:" << outputHexString(state.r11);
	std::cout << "           ";
	std::cout << ((state.rflags & (1 << 11)) != 0 ? 'O' : 'o') << ' ';
	std::cout << ((state.rflags & (1 << 10)) != 0 ? 'D' : 'd') << ' ';
	std::cout << ((state.rflags & (1 <<  9)) != 0 ? 'I' : 'i') << ' ';
	std::cout << ((state.rflags & (1 <<  8)) != 0 ? 'T' : 't') << ' ';
	std::cout << ((state.rflags & (1 <<  7)) != 0 ? 'S' : 's') << ' ';
	std::cout << ((state.rflags & (1 <<  6)) != 0 ? 'Z' : 'z') << ' ';
	std::cout << ((state.rflags & (1 <<  4)) != 0 ? 'A' : 'a') << ' ';
	std::cout << ((state.rflags & (1 <<  2)) != 0 ? 'P' : 'p') << ' ';
	std::cout << ((state.rflags & (1 <<  0)) != 0 ? 'C' : 'c');
	std::cout << "\n";
	std::cout << "     r12:" << outputHexString(state.r12);
	std::cout << " r13:" << outputHexString(state.r13);
	std::cout << "  r14:" << outputHexString(state.r14);
	std::cout << "  r15:" << outputHexString(state.r15);
	std::cout << "\n";
	std::cout << "      cs:" << outputHexString(state.cs);
	std::cout << "  ds:" << outputHexString(state.ds);
	std::cout << "   es:" << outputHexString(state.es);
	std::cout << "   fs:" << outputHexString(state.fs);
	std::cout << "\n";
	std::cout << "      gs:" << outputHexString(state.gs);
	std::cout << "  ss:" << outputHexString(state.ss);
	std::cout << "\n";
#endif
}

//------------------------------------------------------------------------------
// Name: dumpLines(edb::address_t address, int lines)
// Desc: 
//------------------------------------------------------------------------------
void DumpState::dumpLines(edb::address_t address, int lines) {
	for(int i = 0; i < lines; ++i) {
		quint8 buf[16];
		if(edb::v1::debuggerBase->readBytes(address, buf, sizeof(buf))) {
			std::cout << outputHexString(address) << " : ";
			
			for(int j = 0x00; j < 0x04; ++j) std::cout << outputHexString(buf[j]) << " ";
			std::cout << " ";
			for(int j = 0x04; j < 0x08; ++j) std::cout << outputHexString(buf[j]) << " ";
			std::cout << "- ";
			for(int j = 0x08; j < 0x0c; ++j) std::cout << outputHexString(buf[j]) << " ";
			std::cout << " ";
			for(int j = 0x0c; j < 0x10; ++j) std::cout << outputHexString(buf[j]) << " ";

			for(int j = 0; j < 16; ++j) {
				const quint8 ch = buf[j];
				std::cout << (((util::safe_ctype<std::isprint>(ch) || util::safe_ctype<std::isspace>(ch)) && ch < 0x80) ? static_cast<char>(ch) : '.');
			}
					
			std::cout << "\n";
		} else {
			break;
		}
		address += 16;
	}
}

//------------------------------------------------------------------------------
// Name: dumpStack(const State &state)
// Desc: 
//------------------------------------------------------------------------------
void DumpState::dumpStack(const State &state) {
	dumpLines(state.stackPointer(), 4);
}

//------------------------------------------------------------------------------
// Name: dumpData(const State &state)
// Desc: 
//------------------------------------------------------------------------------
void DumpState::dumpData(const State &state) {
#if defined(EDB_X86)
	dumpLines(state.esi, 2);
#elif defined(EDB_X86_64)
	dumpLines(state.rsi, 2);
#endif
}

//------------------------------------------------------------------------------
// Name: doMenu()
// Desc: 
//------------------------------------------------------------------------------
void DumpState::doMenu() {

	const State &state = edb::v1::currentState();

	std::cout << "------------------------------------------------------------------------------\n";
	dumpRegisters(state);
	std::cout << "[" << outputHexString(state.ss) << ":" << outputHexString(state.stackPointer()) << "]---------------------------------------------------------[stack]\n";
	dumpStack(state);
#if defined(EDB_X86)
	std::cout << "[" << outputHexString(state.ds) << ":" << outputHexString(state.esi) << "]---------------------------------------------------------[ data]\n";
#elif defined(EDB_X86_64)
	std::cout << "[" << outputHexString(state.ds) << ":" << outputHexString(state.rsi) << "]---------------------------------------------------------[ data]\n";
#endif
	dumpData(state);
	std::cout << "[" << outputHexString(state.cs) << ":" << outputHexString(state.instructionPointer()) << "]---------------------------------------------------------[ code]\n";
	dumpCode(state);
	std::cout << "------------------------------------------------------------------------------\n";
}

Q_EXPORT_PLUGIN2(DumpState, DumpState)
