//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (c) 1998-1998 Microsoft Corporation
//
//  File:       fileio.cpp
//
//--------------------------------------------------------------------------

// FileIO.cpp
//

#include "aariff.h"

STDAPI AllocFileStream(LPCSTR szFileName, DWORD dwDesiredAccess, IStream **ppstream)
{
    HANDLE          hfile;          // handle to open file
    CFileStream *   pstream;        // IStream implementation

    // in case of error...
    *ppstream = NULL;

    // open the file
    if( dwDesiredAccess == GENERIC_READ )
    {
        hfile = CreateFile( szFileName, dwDesiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
    }
    else if( dwDesiredAccess == GENERIC_WRITE )
    {
        hfile = CreateFile( szFileName, dwDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
    }
    else
    {
        return E_INVALIDARG;
    }
    if( hfile == INVALID_HANDLE_VALUE )
    {
        //DisplayDebug( 5, "File open error: Can't open \"%s\", error code %d", szFileName, GetLastError() );
        return E_FAIL; // to do: real error code
    }

    // create the Windows object
    if ((pstream = new CFileStream(hfile)) == NULL)
        return E_OUTOFMEMORY;

    // return an IStream pointer
    *ppstream = (IStream *) pstream;
    return S_OK;
}

STDAPI AllocRIFFStream( IStream* pStream, IAARIFFStream** ppRiff )
{
    if( ( *ppRiff = (IAARIFFStream*) new CRIFFStream( pStream ) ) == NULL )
    {
        return E_OUTOFMEMORY;
    }
    return S_OK;
}


/* MyRead, MyWrite, MySeek
 *
 * These are functionally identical to mmioRead, mmioWrite, and mmioSeek,
 * except for the absence of the HMMIO parameter.
 */

long CRIFFStream::MyRead(void *pv, long cb)
{
    ULONG cbRead;
    if (FAILED(m_pStream->Read(pv, cb, &cbRead)))
        return -1;
    return cbRead;
}

long CRIFFStream::MyWrite(const void *pv, long cb)
{
    ULONG cbWritten;
    if (FAILED(m_pStream->Write(pv, cb, &cbWritten)))
        return -1;
    return cbWritten;
}

long CRIFFStream::MySeek(long lOffset, int iOrigin)
{
    LARGE_INTEGER   dlibSeekTo;
    ULARGE_INTEGER  dlibNewPos;

    dlibSeekTo.HighPart = 0;
    dlibSeekTo.LowPart = lOffset;
    if (FAILED(m_pStream->Seek(dlibSeekTo, iOrigin, &dlibNewPos)))
        return -1;

    return dlibNewPos.LowPart;
}


UINT CRIFFStream::Descend(LPMMCKINFO lpck, LPMMCKINFO lpckParent, UINT wFlags)
{
    FOURCC          ckidFind;       // chunk ID to find (or NULL)
    FOURCC          fccTypeFind;    // form/list type to find (or NULL)

    /* figure out what chunk id and form/list type to search for */
    if (wFlags & MMIO_FINDCHUNK)
        ckidFind = lpck->ckid, fccTypeFind = NULL;
    else
    if (wFlags & MMIO_FINDRIFF)
        ckidFind = FOURCC_RIFF, fccTypeFind = lpck->fccType;
    else
    if (wFlags & MMIO_FINDLIST)
        ckidFind = FOURCC_LIST, fccTypeFind = lpck->fccType;
    else
        ckidFind = fccTypeFind = NULL;

    lpck->dwFlags = 0L;

    for(;;)
    {
        UINT        w;

        /* read the chunk header */
        if (MyRead(lpck, 2 * sizeof(DWORD)) !=
            2 * sizeof(DWORD))
        return MMIOERR_CHUNKNOTFOUND;
        FixBytes( FBT_LONG, &lpck->cksize );

        /* store the offset of the data part of the chunk */
        if ((lpck->dwDataOffset = MySeek(0L, SEEK_CUR)) == -1)
            return MMIOERR_CANNOTSEEK;

        /* see if the chunk is within the parent chunk (if given) */
        if ((lpckParent != NULL) &&
            (lpck->dwDataOffset - 8L >=
             lpckParent->dwDataOffset + lpckParent->cksize))
            return MMIOERR_CHUNKNOTFOUND;

        /* if the chunk if a 'RIFF' or 'LIST' chunk, read the
         * form type or list type
         */
        if ((lpck->ckid == FOURCC_RIFF) || (lpck->ckid == FOURCC_LIST))
        {
            if (MyRead(&lpck->fccType,
                     sizeof(DWORD)) != sizeof(DWORD))
                return MMIOERR_CHUNKNOTFOUND;
        }
        else
            lpck->fccType = NULL;

        /* if this is the chunk we're looking for, stop looking */
        if ( ((ckidFind == NULL) || (ckidFind == lpck->ckid)) &&
             ((fccTypeFind == NULL) || (fccTypeFind == lpck->fccType)) )
            break;

        /* ascend out of the chunk and try again */
        if ((w = Ascend(lpck, 0)) != 0)
            return w;
    }

    return 0;
}


UINT CRIFFStream::Ascend(LPMMCKINFO lpck, UINT /*wFlags*/)
{
    if (lpck->dwFlags & MMIO_DIRTY)
    {
        /* <lpck> refers to a chunk created by CreateChunk();
         * check that the chunk size that was written when
         * CreateChunk() was called is the real chunk size;
         * if not, fix it
         */
        LONG            lOffset;        // current offset in file
        LONG            lActualSize;    // actual size of chunk data

        if ((lOffset = MySeek(0L, SEEK_CUR)) == -1)
            return MMIOERR_CANNOTSEEK;
        if ((lActualSize = lOffset - lpck->dwDataOffset) < 0)
            return MMIOERR_CANNOTWRITE;

        if (LOWORD(lActualSize) & 1)
        {
            /* chunk size is odd -- write a null pad byte */
            if (MyWrite("\0", 1) != 1)
                return MMIOERR_CANNOTWRITE;

        }

        if (lpck->cksize == (DWORD)lActualSize)
            return 0;

        /* fix the chunk header */
        lpck->cksize = lActualSize;
        if (MySeek(lpck->dwDataOffset - sizeof(DWORD), SEEK_SET) == -1)
            return MMIOERR_CANNOTSEEK;
        FixBytes( FBT_LONG, &lpck->cksize );
        if (MyWrite(&lpck->cksize, sizeof(DWORD)) != sizeof(DWORD))  {
        	FixBytes( FBT_LONG, &lpck->cksize );
            return MMIOERR_CANNOTWRITE;
        }
        FixBytes( FBT_LONG, &lpck->cksize );
    }

    /* seek to the end of the chunk, past the null pad byte
     * (which is only there if chunk size is odd)
     */
    if (MySeek(lpck->dwDataOffset + lpck->cksize + (lpck->cksize & 1L),
            SEEK_SET) == -1)
        return MMIOERR_CANNOTSEEK;

    return 0;
}


UINT CRIFFStream::CreateChunk(LPMMCKINFO lpck, UINT wFlags)
{
    int             iBytes;         // bytes to write
    LONG            lOffset;        // current offset in file

    /* store the offset of the data part of the chunk */
    if ((lOffset = MySeek(0L, SEEK_CUR)) == -1)
        return MMIOERR_CANNOTSEEK;
    lpck->dwDataOffset = lOffset + 2 * sizeof(DWORD);

    /* figure out if a form/list type needs to be written */
    if (wFlags & MMIO_CREATERIFF)
        lpck->ckid = FOURCC_RIFF, iBytes = 3 * sizeof(DWORD);
    else
    if (wFlags & MMIO_CREATELIST)
        lpck->ckid = FOURCC_LIST, iBytes = 3 * sizeof(DWORD);
    else
        iBytes = 2 * sizeof(DWORD);

    /* write the chunk header */
	FixBytes( FBT_MMCKINFO, lpck );
    if (MyWrite(lpck, (LONG) iBytes) != (LONG) iBytes)  {
    	FixBytes( FBT_MMCKINFO, lpck );
        return MMIOERR_CANNOTWRITE;
    }
    FixBytes( FBT_MMCKINFO, lpck );

    lpck->dwFlags = MMIO_DIRTY;

    return 0;
}