/*
* MzHeader.cpp - Part of the PeLib library.
*
* Copyright (c) 2004 - 2005 Sebastian Porst (webmaster@the-interweb.com)
* All rights reserved.
*
* This software is licensed under the zlib/libpng License.
* For more details see http://www.opensource.org/licenses/zlib-license.php
* or the license information file (license.htm) in the root directory 
* of PeLib.
*/

#include "MzHeader.h"
#include <iostream>

namespace PeLib
{
	/**
	* Reads data from an InputBuffer into the struct that represents the MZ header.
	* It's required that the size of the input buffer is at least as big as the
	* size of a MZ header. Otherwise we get undefined behaviour.
	* @param ibBuffer InputBuffer that holds the data.
	* @return A non-zero value is returned if a problem occured.
	**/
	void MzHeader::read(InputBuffer& ibBuffer)
	{
		ibBuffer >> m_idhHeader.e_magic;
		ibBuffer >> m_idhHeader.e_cblp;
		ibBuffer >> m_idhHeader.e_cp;
		ibBuffer >> m_idhHeader.e_crlc;
		ibBuffer >> m_idhHeader.e_cparhdr;
		ibBuffer >> m_idhHeader.e_minalloc;
		ibBuffer >> m_idhHeader.e_maxalloc;
		ibBuffer >> m_idhHeader.e_ss;
		ibBuffer >> m_idhHeader.e_sp;
		ibBuffer >> m_idhHeader.e_csum;
		ibBuffer >> m_idhHeader.e_ip;
		ibBuffer >> m_idhHeader.e_cs;
		ibBuffer >> m_idhHeader.e_lfarlc;
		ibBuffer >> m_idhHeader.e_ovno;

		for (unsigned int i=0;i<sizeof(m_idhHeader.e_res)/sizeof(m_idhHeader.e_res[0]);i++)
		{
			ibBuffer >> m_idhHeader.e_res[i];
		}

		ibBuffer >> m_idhHeader.e_oemid;
		ibBuffer >> m_idhHeader.e_oeminfo;

		for (unsigned int i=0;i<sizeof(m_idhHeader.e_res2)/sizeof(m_idhHeader.e_res2[0]);i++)
		{
			ibBuffer >> m_idhHeader.e_res2[i];
		}

		ibBuffer >> m_idhHeader.e_lfanew;
	}
	
	/**
	* Tests if the currently loaded MZ header is a valid MZ header. 
	* Note that this function does not check if the address to the PE header is valid as this is not possible.
	* Actually, the only thing this function checks is if the e_magic value is set to 0x5A4D (IMAGE_DOS_SIGNATURE).
	* Everything else is not relevant for Windows 2000 and that's the system PeLib is focusing on for now.
	* @return A boolean value that indicates if the MZ header is correct or not.
	**/
	bool MzHeader::isValid() const
	{
		// The only thing that matters on Windows 2K is the e_magic value. The entire rest is for DOS compatibility.
		return isValid(e_magic);
	}

    bool MzHeader::isValid(Field f) const
    {
        if (f == e_magic)
        {
            return m_idhHeader.e_magic == PELIB_IMAGE_DOS_SIGNATURE;
        }
        else
        {
            return true;
        }
    }

	/**
	* Corrects all erroneous values of the current MZ header. Note that this function does not correct the
	* pointer to the PE header.	
	* Actually, the only thing this function corrects is the e_magic value.
	* Everything else is not relevant for Windows 2000 and that's the system PeLib is focusing on for now.
	**/
	void MzHeader::makeValid()
	{
		// The only thing that matters on Windows is the e_magic value. The entire rest is for DOS compatibility.
		setMagicNumber(PELIB_IMAGE_DOS_SIGNATURE);
	}

    void MzHeader::makeValid(Field f)
    {
        if (f == e_magic)
        {
            setMagicNumber(PELIB_IMAGE_DOS_SIGNATURE);
        }
    }

	/**
	* Reads the MZ header from a file. Note that this function does not verify if a file is actually a MZ file.
	* For this purpose see #PeFile::MzHeader::isValid. The reason for this is simple: Otherwise it might not
	* be possible to load damaged PE files to repair them.
	* @param strFilename Name of the file which will be read.
	* @return A non-zero value is returned if a problem occured.
	**/
	int MzHeader::read(const std::string& strFilename)
	{
		std::ifstream ifFile(strFilename.c_str(), std::ios::binary);

		if (!ifFile)
		{
			return ERROR_OPENING_FILE;
		}

		if (fileSize(ifFile) < PELIB_IMAGE_DOS_HEADER::size())
		{
			return ERROR_INVALID_FILE;
		}

		ifFile.seekg(0, std::ios::beg);

		originalOffset = 0;
		
		std::vector<byte> vBuffer(PELIB_IMAGE_DOS_HEADER::size());
		ifFile.read(reinterpret_cast<char*>(&vBuffer[0]), static_cast<unsigned int>(vBuffer.size()));
		ifFile.close();

		InputBuffer ibBuffer(vBuffer);
		read(ibBuffer);
		return NO_ERROR;
	}

	/**
	* Reads the MZ header from memory. A pointer to a location in memory is passed and the data
	* at this location is treated like a MZ header structure. The MZ header does not need to be valid.
	* @param pcBuffer Pointer to a MZ header.
	* @param uiSize Length of the buffer.
	* @return A non-zero value is returned if a problem occured.
	**/
	int MzHeader::read(unsigned char* pcBuffer, unsigned int uiSize, unsigned int originalOffs)
	{
		if (uiSize < PELIB_IMAGE_DOS_HEADER::size())
		{
			return ERROR_INVALID_FILE;
		}
		
		std::vector<byte> vBuffer(pcBuffer, pcBuffer + uiSize);
		for (int i=0;i<0x40;i++) std::cout << std::hex << (int)vBuffer[i] << " ";

		originalOffset = originalOffs;
		
		InputBuffer ibBuffer(vBuffer);
		read(ibBuffer);
		return NO_ERROR;
	}

	/**
	* Rebuilds the MZ header so that it can be written to a file. It's not guaranteed that the
	* MZ header will be valid. If you want to make sure that the MZ header will be valid you
	* must call #PeLib::MzHeader::makeValid first.
	* @param vBuffer Buffer where the rebuilt MZ header will be stored.
	**/
	void MzHeader::rebuild(std::vector<byte>& vBuffer) const
	{
		OutputBuffer obBuffer(vBuffer);

		obBuffer << m_idhHeader.e_magic;
		obBuffer << m_idhHeader.e_cblp;
		obBuffer << m_idhHeader.e_cp;
		obBuffer << m_idhHeader.e_crlc;
		obBuffer << m_idhHeader.e_cparhdr;
		obBuffer << m_idhHeader.e_minalloc;
		obBuffer << m_idhHeader.e_maxalloc;
		obBuffer << m_idhHeader.e_ss;
		obBuffer << m_idhHeader.e_sp;
		obBuffer << m_idhHeader.e_csum;
		obBuffer << m_idhHeader.e_ip;
		obBuffer << m_idhHeader.e_cs;
		obBuffer << m_idhHeader.e_lfarlc;
		obBuffer << m_idhHeader.e_ovno;

		for (unsigned int i=0;i<sizeof(m_idhHeader.e_res)/sizeof(m_idhHeader.e_res[0]);i++)
		{
			obBuffer << m_idhHeader.e_res[i];
		}

		obBuffer << m_idhHeader.e_oemid;
		obBuffer << m_idhHeader.e_oeminfo;

		for (unsigned int i=0;i<sizeof(m_idhHeader.e_res2)/sizeof(m_idhHeader.e_res2[0]);i++)
		{
			obBuffer << m_idhHeader.e_res2[i];
		}

		obBuffer << m_idhHeader.e_lfanew;
	}

	/**
	* Returns the size of the MZ header. This size is actually always sizeof(IMAGE_DOS_HEADER) (== 0x40)
	* because the MZ header is a header of constant size if you disregard the dos stub. If you want to know the
	* size of the MZ header + the size of the dos stub check #PeLib::MzHeader::getAddressOfPeHeader.
	* @return Size of the MZ header.
	**/
	unsigned int MzHeader::size() const
	{
		return sizeof(m_idhHeader);
	}

	/**
	* Writes the current MZ header to a file. The file does not have to exist. If it doesn't exist
	* it will be created.
	* @param strFilename Name of the file the header will be written to.
	* @param dwOffset Offset the header will be written to (defaults to 0).
	* @return A non-zero value is returned if a problem occured.
	**/
	int MzHeader::write(const std::string& strFilename, dword dwOffset = 0) const
	{
		std::fstream ofFile(strFilename.c_str(), std::ios_base::in);
		
		if (!ofFile)
		{
			ofFile.clear();
			ofFile.open(strFilename.c_str(), std::ios_base::out | std::ios_base::binary);
		}
		else
		{
			ofFile.close();
			ofFile.open(strFilename.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
		}
		
		if (!ofFile)
		{
			return ERROR_OPENING_FILE;
		}

		ofFile.seekp(dwOffset, std::ios::beg);

		std::vector<unsigned char> vBuffer;

		rebuild(vBuffer);

		ofFile.write(reinterpret_cast<const char*>(&vBuffer[0]), static_cast<unsigned int>(vBuffer.size()));

		ofFile.close();
		
		return NO_ERROR;
	}

	/**
	* Returns the MZ header's e_magic value.
	**/
	word MzHeader::getMagicNumber() const
	{
		return m_idhHeader.e_magic;
	}

	/**
	* Returns the MZ header's e_cblp value.
	**/
	word MzHeader::getBytesOnLastPage() const
	{
		return m_idhHeader.e_cblp;
	}

	/**
	* Returns the MZ header's e_cp value.
	**/
	word MzHeader::getPagesInFile() const
	{
		return m_idhHeader.e_cp;
	}

	/**
	* Returns the MZ header's e_crlc value.
	**/
	word MzHeader::getRelocations() const
	{
		return m_idhHeader.e_crlc;
	}

	/**
	* Returns the MZ header's e_cparhdr value.
	**/
	word MzHeader::getSizeOfHeader() const
	{
		return m_idhHeader.e_cparhdr;
	}

	/**
	* Returns the MZ header's e_minalloc value.
	**/
	word MzHeader::getMinExtraParagraphs() const
	{
		return m_idhHeader.e_minalloc;
	}

	/**
	* Returns the MZ header's e_maxalloc value.
	**/
	word MzHeader::getMaxExtraParagraphs() const
	{
		return m_idhHeader.e_maxalloc;
	}

	/**
	* Returns the MZ header's e_ss value.
	**/
	word MzHeader::getSsValue() const
	{
		return m_idhHeader.e_ss;
	}

	/**
	* Returns the MZ header's e_sp value.
	**/
	word MzHeader::getSpValue() const
	{
		return m_idhHeader.e_sp;
	}

	/**
	* Returns the MZ header's e_csum value.
	**/
	word MzHeader::getChecksum() const
	{
		return m_idhHeader.e_csum;
	}

	/**
	* Returns the MZ header's e_ip value.
	**/
	word MzHeader::getIpValue() const
	{
		return m_idhHeader.e_ip;
	}

	/**
	* Returns the MZ header's e_cs value.
	**/
	word MzHeader::getCsValue() const
	{
		return m_idhHeader.e_cs;
	}

	/**
	* Returns the MZ header's e_lfarlc value.
	**/
	word MzHeader::getAddrOfRelocationTable() const
	{
		return m_idhHeader.e_lfarlc;
	}

	/**
	* Returns the MZ header's e_ovno value.
	**/
	word MzHeader::getOverlayNumber() const
	{
		return m_idhHeader.e_ovno;
	}

	/**
	* Returns the MZ header's e_oemid value.
	**/
	word MzHeader::getOemIdentifier() const
	{
		return m_idhHeader.e_oemid;
	}

	/**
	* Returns the MZ header's e_oeminfo value.
	**/
	word MzHeader::getOemInformation() const
	{
		return m_idhHeader.e_oeminfo;
	}

	/**
	* Returns the MZ header's e_lfanew value.
	**/
	dword MzHeader::getAddressOfPeHeader() const
	{
		return m_idhHeader.e_lfanew;
	}

	/**
	* Returns the MZ header's e_res[uiNr] value. If the parameter uiNr is out of range 
	* you will get undefined behaviour.
	* @param uiNr The index of the word in the e_res array (valid range: 0-3)
	**/
	word MzHeader::getReservedWords1(unsigned int uiNr) const
	{
		return m_idhHeader.e_res[uiNr];
	}

	/**
	* Returns the MZ header's e_res2[uiNr] value. If the parameter uiNr is out of range 
	* you will get undefined behaviour.
	* @param uiNr The index of the word in the e_res array (valid range: 0-9)
	**/
	word MzHeader::getReservedWords2(unsigned int uiNr) const
	{
		return m_idhHeader.e_res2[uiNr];
	}
	
	/**
	* Sets the MZ header's e_magic value.
	* @param wValue The new value of e_magic.
	**/
	void MzHeader::setMagicNumber(word wValue)
	{
		m_idhHeader.e_magic = wValue;
	}

	/**
	* Sets the MZ header's e_cblp value.
	* @param wValue The new value of e_cblp.
	**/
	void MzHeader::setBytesOnLastPage(word wValue)
	{
		m_idhHeader.e_cblp = wValue;
	}

	/**
	* Sets the MZ header's e_cp value.
	* @param wValue The new value of e_cp.
	**/
	void MzHeader::setPagesInFile(word wValue)
	{
		m_idhHeader.e_cp = wValue;
	}

	/**
	* Sets the MZ header's e_crlc value.
	* @param wValue The new value of e_crlc.
	**/  
	void MzHeader::setRelocations(word wValue)
	{
		m_idhHeader.e_crlc = wValue;
	}

	/**
	* Sets the MZ header's e_cparhdr value.
	* @param wValue The new value of e_cparhdr.
	**/  
	void MzHeader::setSizeOfHeader(word wValue)
	{
		m_idhHeader.e_cparhdr = wValue;
	}

	/**
	* Sets the MZ header's e_minalloc value.
	* @param wValue The new value of e_minalloc.
	**/  
	void MzHeader::setMinExtraParagraphs(word wValue)
	{
		m_idhHeader.e_minalloc = wValue;
	}

	/**
	* Sets the MZ header's e_maxalloc value.
	* @param wValue The new value of e_maxalloc.
	**/  
	void MzHeader::setMaxExtraParagraphs(word wValue)
	{
		m_idhHeader.e_maxalloc = wValue;
	}

	/**
	* Sets the MZ header's e_ss value.
	* @param wValue The new value of e_ss.
	**/  
	void MzHeader::setSsValue(word wValue)
	{
		m_idhHeader.e_ss = wValue;
	}

	/**
	* Sets the MZ header's e_sp value.
	* @param wValue The new value of e_sp.
	**/  
	void MzHeader::setSpValue(word wValue)
	{
		m_idhHeader.e_sp = wValue;
	}

	/**
	* Sets the MZ header's e_csum value.
	* @param wValue The new value of e_csum.
	**/  
	void MzHeader::setChecksum(word wValue)
	{
		m_idhHeader.e_csum = wValue;
	}

	/**
	* Sets the MZ header's e_ip value.
	* @param wValue The new value of e_ip.
	**/  
	void MzHeader::setIpValue(word wValue)
	{
		m_idhHeader.e_ip = wValue;
	}

	/**
	* Sets the MZ header's e_cs value.
	* @param wValue The new value of e_cs.
	**/  
	void MzHeader::setCsValue(word wValue)
	{
		m_idhHeader.e_cs = wValue;
	}

	/**
	* Sets the MZ header's e_lfarlc value.
	* @param wValue The new value of e_lfarlc.
	**/  
	void MzHeader::setAddrOfRelocationTable(word wValue)
	{
		m_idhHeader.e_lfarlc = wValue;
	}

	/**
	* Sets the MZ header's e_ovno value.
	* @param wValue The new value of e_ovno.
	**/  
	void MzHeader::setOverlayNumber(word wValue)
	{
		m_idhHeader.e_ovno = wValue;
	}

	/**
	* Sets the MZ header's e_oemid value.
	* @param wValue The new value of e_oemid.
	**/  
	void MzHeader::setOemIdentifier(word wValue)
	{
		m_idhHeader.e_oemid = wValue;
	}

	/**
	* Sets the MZ header's e_oeminfo value.
	* @param wValue The new value of e_oeminfo.
	**/  
	void MzHeader::setOemInformation(word wValue)
	{
		m_idhHeader.e_oeminfo = wValue;
	}

	/**
	* Sets the MZ header's e_lfanew value.
	* @param lValue The new value of e_lfanew.
	**/  
	void MzHeader::setAddressOfPeHeader(dword lValue)
	{
		m_idhHeader.e_lfanew = lValue;
	}
	
	/**
	* Sets the MZ header's e_res[uiNr] value. If the parameter uiNr is out of range 
	* you will get undefined behaviour.
	* @param uiNr The index of the word in the e_res array (valid range: 0-3)
	* @param wValue The new value of e_res[nr].
	**/
	void MzHeader::setReservedWords1(unsigned int uiNr, word wValue)
	{
		m_idhHeader.e_res[uiNr] = wValue;
	}

	/**
	* Sets the MZ header's e_res2[uiNr] value. If the parameter uiNr is out of range 
	* you will get undefined behaviour.
	* @param uiNr The index of the word in the e_res2 array (valid range: 0-9)
	* @param wValue The new value of e_res[nr].
	**/
	void MzHeader::setReservedWords2(unsigned int uiNr, word wValue)
	{
		m_idhHeader.e_res2[uiNr] = wValue;
	}

}
