/*
 * ************************************************************************************************************
 * Author: Marcin Załęczny
 * Copyright (C)2011 Marcin Załęczny <mzaleczny@hotmail.com>
 * License: The GNU Lesser General Public License, version 3.0 (LGPLv3)
 * Library Name: mzstddebug
 * Version: 1.02
 * First release date:   2011-02-03
 * Current release date: 2011-03-04
 *
 *
 * This file is part of mzstddebug library
 * 
 * mzstddebug library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * mzstddebug library 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * ************************************************************************************************************
 */

#include "mzDebugger.h"
#include <dbghelp.h>
#include "mzstd.h"


VOID CDebugger::_commonInit() {
    this->setRealClassName(L"CDebugger");
    this->_loadedSymbols = NULL;
}

CDebugger::CDebugger() {
    this->_pHandle = NULL;
    this->_commonInit();
}

CDebugger::~CDebugger() {
    if (this->_pHandle) {
        this->close();
    }
}


BOOL CDebugger::open() {
    if (this->_pHandle) {
        return FALSE;
    }
    
    BOOL retval;
    DWORD options = SymGetOptions();
    options |= SYMOPT_DEBUG;
	SymSetOptions(options);
    this->_pHandle = GetCurrentProcess();
    retval = SymInitialize(this->_pHandle, NULL, FALSE);
    if (!retval) {
        this->_pHandle = NULL;
        LOG(GetDetailedLastSystemErrorMessage().toWCHAR());
    } else {
        this->_loadedSymbols = new CArrayList();
    }

    return retval;
}

BOOL CDebugger::close() {
    BOOL result = FALSE;

    if (this->_loadedSymbols) {
        result = this->unloadSymbols();
        this->_cleanSymbols();
    }
    
    if (this->_pHandle) {
        result = SymCleanup(this->_pHandle);
        this->_pHandle = NULL;
    }

    return result;
}

DWORD64 CDebugger::loadSymbols(CString imageName) {
    CHAR buf[512];
    imageName.toAnsi(buf, 512);
    DWORD64 loadAddress = SymLoadModule64(this->_pHandle, NULL, buf, NULL, 0, 0);
    if (!loadAddress) {
        LOG(GetDetailedLastSystemErrorMessage().toWCHAR());
    } else {
        DWORD64* pointer = new DWORD64;
        if (pointer) {
            this->_loadedSymbols->append((LONG)pointer);
        } else {
            SymUnloadModule64(this->_pHandle, loadAddress); 
            loadAddress = 0;
        }
    }
    
    return loadAddress;
}

BOOL CDebugger::unloadSymbols() {
    BOOL result = TRUE;
    
    if (this->_loadedSymbols) {
        DWORD64* loadAddr;
        for (ULONG i = 0; i < this->_loadedSymbols->size(); i++) {
            loadAddr = (DWORD64*)this->_loadedSymbols->getAt(i);
            if (loadAddr) {
                if (SymUnloadModule64(this->_pHandle, *loadAddr)) {
                    delete loadAddr;
                    this->_loadedSymbols->setAt(i, 0);
                } else {
                    result = FALSE;
                }
            }
        }
        this->_loadedSymbols->del(0);
    }
    
    return result;
}

VOID CDebugger::_cleanSymbols() {
    if (this->_loadedSymbols) {
        DWORD64* p;
        for (ULONG i = 0; i < this->_loadedSymbols->size(); i++) {
            p = (DWORD64*)this->_loadedSymbols->getAt(i);
            if (p) {
                delete p;
            }
        }
        delete this->_loadedSymbols;
        this->_loadedSymbols = NULL;
    }
}
