/*++

Copyright (C) 1997-2001 Microsoft Corporation

Module Name:

    COUT.CPP

Abstract:

    Declares the COut class.

History:

    a-davj  06-April-97   Created.

--*/

#include "precomp.h"
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <arrtempl.h>

#include "cout.h"
#include "trace.h"
#include "strings.h"
#include "mrciclass.h"
#include "bmof.h"

//***************************************************************************
//
//  COut::COut
//
//  DESCRIPTION:
//
//  Constructor.  Allocates initial buffer.
//
//***************************************************************************

COut::COut(PDBG pDbg)
{
    m_pDbg = pDbg;
    m_dwSize = INIT_SIZE;
    m_pMem = (BYTE *)malloc(m_dwSize);
    m_dwCurr = 0;
    m_bPadString = TRUE;
}

//***************************************************************************
//
//  COut::~COut
//
//  DESCRIPTION:
//
//  Destructor.  Frees the buffer.
//
//***************************************************************************

COut::~COut()
{
    if(m_pMem)
        free(m_pMem);
}

//***************************************************************************
//
//  void COut::WriteToFile
//
//  DESCRIPTION:
//
//  Creates a file and writes the buffer to it.  Like other compilers, it
//  overwrites any existing files.
//
//  PARAMETERS:
//
//  pFile               File name to write to.
//
//***************************************************************************

BOOL COut::WriteToFile(
                        IN LPSTR pFile)
{
    BOOL bRet = FALSE;
    BYTE * pCompressed = NULL;
    DWORD one = 1;
    DWORD dwSize;
    int iRet;
    DWORD dwCompressedSize;
    DWORD dwSignature = BMOF_SIG;
    CMRCICompression * pCompress = new CMRCICompression;
    if(pCompress == NULL)
        return FALSE;
    CDeleteMe<CMRCICompression> dm(pCompress);

    // Test if there are flavors

    if(m_Flavors.Size() > 0)
    {
        // write out the flavor information

        void * lOffSet;
        void * lFlavor; 
        AppendBytes((BYTE *)"BMOFQUALFLAVOR11", 16);
        long lNum = m_Flavors.Size();
        AppendBytes( (BYTE *)&lNum, 4);

        for(long lCnt = 0; lCnt < lNum; lCnt++)
        {
            lOffSet = m_Offsets.GetAt(lCnt);
            lFlavor = m_Flavors.GetAt(lCnt);
            AppendBytes( (BYTE *)&lOffSet, 4);
            AppendBytes( (BYTE *)&lFlavor, 4);
        }


    }


    int fh = NULL;
    if(pFile == NULL)
        return FALSE;

    fh = _open(pFile, _O_BINARY | O_RDWR | _O_TRUNC | _O_CREAT, _S_IREAD |_S_IWRITE);
    if(fh == -1)
    {
        Trace(true, m_pDbg, FILE_CREATE_FAILED, pFile, errno);
        goto Cleanup;
    }


    // Create a compressed version of the blob

    dwSize = (m_dwCurr > 0x7000) ? m_dwCurr : 0x7000;

    pCompressed = new BYTE[dwSize];
    if(pCompressed == NULL)
        return FALSE;

    dwCompressedSize = pCompress->Mrci1MaxCompress( m_pMem, m_dwCurr, pCompressed, dwSize);

    if(dwCompressedSize == 0xffffffff || dwCompressedSize == 0)
    {
        Trace(true, m_pDbg, COMPRESSION_FAILED);
        goto Cleanup;
    }

    // write the decomression signature, the decompressed size, and the compressed size

    iRet = _write(fh, (BYTE *)&dwSignature, sizeof(DWORD));
    if(iRet != sizeof(DWORD))
    {
        Trace(true, m_pDbg, FILE_WRITE_FAILED, pFile, errno);
        goto Cleanup;
    }

    iRet = _write(fh, (BYTE *)&one, sizeof(DWORD));
    iRet = _write(fh, (BYTE *)&dwCompressedSize, sizeof(DWORD));
    iRet = _write(fh, (BYTE *)&m_dwCurr, sizeof(DWORD));

    // write the compressed data and then free the buffer

    iRet = _write(fh, pCompressed, dwCompressedSize);
    
    if((DWORD)iRet != dwCompressedSize)
        Trace(true, m_pDbg, FILE_WRITE_FAILED, pFile, errno);
    else
        bRet = TRUE;

Cleanup:

    if(fh != NULL)
        _close(fh);
    if(pCompressed)
        delete pCompressed;
    return bRet;
    
}

//***************************************************************************
//
//  DWORD COut::AppendBytes
//
//  DESCRIPTION:
//
//  Adds bytes to the end of the buffer.
//
//  PARAMETERS:
//
//  pSrc                Pointer to data source.
//  dwSize              Number of bytes to add.
//
//  RETURN VALUE:
//
//  Number of bytes added.
//
//***************************************************************************

DWORD COut::AppendBytes(
                        IN BYTE * pSrc, 
                        IN DWORD dwSize)
{
    char * pZero = "\0\0\0\0\0\0\0";
    DWORD dwRet = WriteBytes(m_dwCurr, pSrc, dwSize);
    m_dwCurr += dwRet;
    DWORD dwLeftOver = dwSize & 3;
    if(dwLeftOver && m_bPadString)
    {
        dwRet = WriteBytes(m_dwCurr, (BYTE *)pZero, dwLeftOver);
        m_dwCurr += dwLeftOver;
    }
    return dwRet;
}

//***************************************************************************
//
//  DWORD COut::WriteBSTR
//
//  DESCRIPTION:
//
//  Adds a bstr to the buffer.  Quite simple for now, by might be enhanced
//  later on to compress.
//
//  PARAMETERS:
//
//  bstr                bstr to add.
//
//  RETURN VALUE:
//
//  Number of bytes added.
//
//***************************************************************************

DWORD COut::WriteBSTR(
                        IN BSTR bstr)
{
    return AppendBytes((BYTE *)bstr, 2*(wcslen(bstr) + 1));
}


//***************************************************************************
//
//  DWORD COut::WriteBytes
//
//  DESCRIPTION:
//
//  writes some bytes to the buffer, or possibly adds to the end.
//
//  PARAMETERS:
//
//  dwOffset            Offset in buffer where bytes should go
//  pSrc                points to source data
//  dwSize              number of bytes to copy
//
//  RETURN VALUE:
//
//  Number of bytes copied
//
//***************************************************************************

DWORD COut::WriteBytes(
                        IN DWORD dwOffset, 
                        IN BYTE * pSrc, 
                        IN DWORD dwSize)
{
    if(m_pMem == NULL)
        return 0;

    // check if reallocation is needed!

    if(dwOffset + dwSize > m_dwSize)
    {
        DWORD dwAddSize = ADDITIONAL_SIZE;
        if(dwSize > dwAddSize)
            dwAddSize = dwSize;
        BYTE * pNew = (BYTE *)realloc(m_pMem, m_dwSize + dwAddSize);
        if(pNew == NULL)
        {
            free(m_pMem);
            m_pMem = NULL;
            return 0;
        }
        else
        {
            m_pMem = pNew;
            m_dwSize += dwAddSize;
        }
    }

    memcpy(m_pMem+dwOffset, pSrc, dwSize);
    return dwSize;
}

//***************************************************************************
//
//  DWORD COut::AddFlavor
//
//  DESCRIPTION:
//
//  Save the flavor value for the current offset.
//
//  PARAMETERS:
//
//  long                flavor to be saved
//
//  RETURN VALUE:
//
//  TRUE if OK;
//
//***************************************************************************

BOOL COut::AddFlavor(IN long lFlavor)
{
#ifdef _WIN64
    m_Offsets.Add((void *)IntToPtr(m_dwCurr));
    m_Flavors.Add((void *)IntToPtr(lFlavor));
#else
    m_Offsets.Add((void *)m_dwCurr);
    m_Flavors.Add((void *)lFlavor);
#endif	
    return TRUE;
}