888 lines
24 KiB
C++
888 lines
24 KiB
C++
// ==========================================================================
|
|
// Class Implementation : COXConvertedFile
|
|
// ==========================================================================
|
|
|
|
// Source file : convfile.cpp
|
|
|
|
// Version: 9.3
|
|
|
|
// This software along with its related components, documentation and files ("The Libraries")
|
|
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
|
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
|
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
|
// to obtain this file, or directly from our office. For a copy of the license governing
|
|
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
|
|
|
// //////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h" // standard MFC include
|
|
#include "convfile.h" // class specification
|
|
#include <limits.h> // for INT_MAX definition
|
|
#include "UTBStrop.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char BASED_CODE THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
IMPLEMENT_DYNAMIC(COXConvertedFile, CFile)
|
|
|
|
#ifdef _DEBUG
|
|
// Helper macro for TRACE output
|
|
#define FILE_FROM_TEXT(nFrom) (nFrom == CFile::begin ? TEXT("begin") : \
|
|
(nFrom == CFile::end ? TEXT("end") : TEXT("current")) )
|
|
#endif
|
|
|
|
#define new DEBUG_NEW
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Definition of static members
|
|
|
|
|
|
// Data members -------------------------------------------------------------
|
|
// protected:
|
|
// BOOL m_bEnabled;
|
|
// --- Whether conversion has been enabled or not
|
|
// This only enables or disables the calls to the conversion finctions
|
|
|
|
// UINT m_nBufferLength;
|
|
// --- The length of the buffer (defined at construction time)
|
|
|
|
// LPBYTE m_pOriginalBuffer;
|
|
// --- The original (not converted) buffer itself (or NULL when not allocated)
|
|
|
|
// COXWatchBuffer m_convertedBuffer;
|
|
// --- The converted buffer
|
|
|
|
// int m_nBufferPos;
|
|
// --- Current position in the converted buffer
|
|
|
|
// DWORD m_nFilePos;
|
|
// --- The file position that corresponds with the beginning of buffer
|
|
|
|
// BOOL m_bRead;
|
|
// --- Whether the present contents of the buffer has been read from file
|
|
|
|
// BOOL m_bOpenedForRead;
|
|
// --- Whether the file has been opened for Read
|
|
|
|
// BOOL m_bOpenedForWrite;
|
|
// --- Whether the file has been opened for Write
|
|
|
|
// CFile* m_pDelegateToFile
|
|
// --- The next transformation in the transformation chain
|
|
|
|
// private:
|
|
|
|
// Member functions ---------------------------------------------------------
|
|
// public:
|
|
|
|
COXConvertedFile::COXConvertedFile(UINT wBufferLength, BOOL bConvertEnabled /* = TRUE */)
|
|
:
|
|
m_bEnabled(bConvertEnabled),
|
|
m_nBufferLength(wBufferLength),
|
|
m_pOriginalBuffer(NULL),
|
|
m_convertedBuffer(),
|
|
m_nBufferPos(0),
|
|
m_nFilePos(0),
|
|
m_bRead(FALSE),
|
|
m_bOpenedForRead(FALSE),
|
|
m_bOpenedForWrite(FALSE),
|
|
m_pDelegateToFile(NULL)
|
|
{
|
|
// ... Must have strict positive buffer length
|
|
ASSERT(0 < m_nBufferLength);
|
|
m_pOriginalBuffer = new BYTE[m_nBufferLength];
|
|
m_convertedBuffer.Create(m_nBufferLength);
|
|
ASSERT_VALID(this);
|
|
}
|
|
|
|
UINT COXConvertedFile::GetBufferLength() const
|
|
{
|
|
return m_nBufferLength;
|
|
}
|
|
|
|
BOOL COXConvertedFile::IsConvertEnabled() const
|
|
{
|
|
ASSERT_VALID(this);
|
|
return m_bEnabled;
|
|
}
|
|
|
|
BOOL COXConvertedFile::EnableConvert(BOOL bEnable /* = TRUE */)
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
|
|
// Before changing the state, first flush everything
|
|
if (m_bEnabled != bEnable)
|
|
{
|
|
// Check whether we have an empty or completely filled buffer,
|
|
// because changing the conversion state is otherwise not possible
|
|
if ((m_nBufferPos != 0) && ((UINT)m_nBufferPos != m_nBufferLength))
|
|
{
|
|
TRACE(TEXT("COXConvertedFile::EnableConvert : Changing the conversion state with half filled buffer is not allowed\n"));
|
|
return FALSE;
|
|
}
|
|
ASSERT(GetPosition() % GetBufferLength() == 0);
|
|
Flush();
|
|
|
|
m_bEnabled = bEnable;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
WORD COXConvertedFile::ForceEnableConvert(BOOL bEnable /* = TRUE */)
|
|
{
|
|
// Even when m_bEnabled == bEnable bytes are skipped until the next
|
|
// buffer boundary, this is for compatibility reasons
|
|
|
|
WORD wBytesSkipped = 0;
|
|
|
|
// Check whether we have an empty or completely filled buffer,
|
|
if (IsOpen() && (m_nBufferPos != 0) && ((UINT)m_nBufferPos != m_nBufferLength))
|
|
{
|
|
// Not completely filled buffer, skip bytes
|
|
wBytesSkipped = WORD(m_nBufferLength - m_nBufferPos);
|
|
LPBYTE pSkippedBytes = new BYTE[wBytesSkipped];
|
|
|
|
TRY
|
|
{
|
|
// ... File is open, so must be reading or writing
|
|
ASSERT(m_bOpenedForRead || m_bOpenedForWrite);
|
|
if (m_bOpenedForWrite)
|
|
{
|
|
/// .... Write '\0'-bytes
|
|
memset(pSkippedBytes, 0, wBytesSkipped);
|
|
Write(pSkippedBytes, wBytesSkipped);
|
|
}
|
|
else
|
|
{
|
|
// ... Must be able to read to the end of the buffer
|
|
VERIFY(wBytesSkipped == Read(pSkippedBytes, wBytesSkipped));
|
|
#ifdef _DEBUG
|
|
// Check whether the skipped bytes are all '\0'
|
|
WORD nByteIndex = 0;
|
|
while ((nByteIndex < wBytesSkipped) && (pSkippedBytes[nByteIndex] == '\0'))
|
|
nByteIndex++;
|
|
if (nByteIndex < wBytesSkipped)
|
|
TRACE(TEXT("COXConvertedFile::ForceEnableConvert : Skipping non-zero bytes\n"));
|
|
#endif
|
|
}
|
|
}
|
|
CATCH_ALL(px)
|
|
{
|
|
delete[] pSkippedBytes;
|
|
THROW_LAST();
|
|
}
|
|
END_CATCH_ALL
|
|
delete[] pSkippedBytes;
|
|
}
|
|
|
|
// ... Should be able to change conversion state now
|
|
VERIFY(EnableConvert(bEnable));
|
|
|
|
return wBytesSkipped;
|
|
}
|
|
|
|
BOOL COXConvertedFile::IsOpen() const
|
|
{
|
|
ASSERT_VALID(this);
|
|
if (m_pDelegateToFile != NULL)
|
|
return (m_pDelegateToFile->m_hFile != CFile::hFileNull);
|
|
else
|
|
return (m_hFile != CFile::hFileNull);
|
|
}
|
|
|
|
CFile* COXConvertedFile::Duplicate() const
|
|
{
|
|
ASSERT_VALID(this);
|
|
if (m_pDelegateToFile != NULL)
|
|
return m_pDelegateToFile->Duplicate();
|
|
else
|
|
return CFile::Duplicate();
|
|
}
|
|
|
|
BOOL COXConvertedFile::Open(LPCTSTR pszFileName, UINT nOpenFlags,
|
|
CFileException* pException /* = NULL */)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_pDelegateToFile == NULL);
|
|
|
|
// ... Store open flag
|
|
m_bOpenedForWrite = (nOpenFlags & CFile::modeWrite) || (nOpenFlags & CFile::modeReadWrite);
|
|
m_bOpenedForRead = TRUE; // Read always permitted CFile::modeRead == 0x0000,
|
|
|
|
// ... Beginning of buffer and file
|
|
m_nBufferPos = 0;
|
|
m_nFilePos = 0;
|
|
|
|
// ... Nothing actually read
|
|
m_bRead = FALSE;
|
|
|
|
// Call base member implementation
|
|
BOOL bResult = CFile::Open(pszFileName, nOpenFlags, pException);
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
BOOL COXConvertedFile::DelegateOpen(CFile* pDelegateToFile, UINT nOpenFlags)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(pDelegateToFile != NULL);
|
|
|
|
// ... Store open flag
|
|
m_bOpenedForWrite = (nOpenFlags & CFile::modeWrite) || (nOpenFlags & CFile::modeReadWrite);
|
|
m_bOpenedForRead = TRUE; // Read always permitted CFile::modeRead == 0x0000,
|
|
|
|
// ... Beginning of buffer and file
|
|
m_nBufferPos = 0;
|
|
m_nFilePos = 0;
|
|
|
|
// ... Nothing actually read
|
|
m_bRead = FALSE;
|
|
|
|
// Call base member implementation
|
|
m_pDelegateToFile = pDelegateToFile;
|
|
|
|
ASSERT_VALID(this);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL COXConvertedFile::UnDelegateOpen()
|
|
{
|
|
m_bEnabled = FALSE;
|
|
|
|
m_pDelegateToFile = NULL;
|
|
|
|
m_convertedBuffer.Empty();
|
|
m_nBufferPos = 0;
|
|
m_nFilePos = 0;
|
|
|
|
m_bRead = FALSE;
|
|
m_bOpenedForRead = FALSE;
|
|
m_bOpenedForWrite = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UINT COXConvertedFile::Read(void FAR* lpBuf, UINT nCount)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(lpBuf != NULL);
|
|
ASSERT(AfxIsValidAddress(lpBuf, nCount));
|
|
|
|
// Immediately return for empty requests
|
|
if (nCount <= 0)
|
|
return 0;
|
|
|
|
if (!m_bOpenedForRead)
|
|
{
|
|
TRACE(TEXT("COXConvertedFile::Read : Trying to read while not permitted, throwing exception\n"));
|
|
AfxThrowFileException(CFileException::accessDenied);
|
|
}
|
|
|
|
UINT nCopy = 0;
|
|
UINT nRead = 0;
|
|
BOOL bEOF = FALSE;
|
|
|
|
// If buffer does not yet contain information from file, read now
|
|
if (!m_bRead)
|
|
bEOF = ReadBuffer();
|
|
|
|
while ((nRead < nCount) && !bEOF)
|
|
{
|
|
// First give caller the bytes that are in the buffer
|
|
nCopy = __min(nCount - nRead, (UINT)(m_nBufferLength - m_nBufferPos));
|
|
if (nCopy != 0)
|
|
memcpy(lpBuf, m_convertedBuffer.Get(m_nBufferPos), nCopy);
|
|
lpBuf = LPBYTE(lpBuf) + nCopy;
|
|
m_nBufferPos += nCopy;
|
|
nRead += nCopy;
|
|
|
|
// If still bytes needed : read a new buffer
|
|
if (nRead < nCount)
|
|
bEOF = ReadBuffer();
|
|
}
|
|
// ... Should not have read more bytes than asked for
|
|
ASSERT(nRead <= nCount);
|
|
return nRead;
|
|
}
|
|
|
|
void COXConvertedFile::Write(const void FAR* lpBuf, UINT nCount)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(lpBuf != NULL);
|
|
ASSERT(AfxIsValidAddress(lpBuf, nCount, FALSE));
|
|
|
|
// Immediately return for empty requests
|
|
if (nCount == 0)
|
|
return;
|
|
|
|
if (!m_bOpenedForWrite)
|
|
{
|
|
TRACE(TEXT("COXConvertedFile::Write : Trying to write while not permitted, throwing exception\n"));
|
|
AfxThrowFileException(CFileException::accessDenied);
|
|
}
|
|
|
|
UINT nCopy = 0;
|
|
UINT nWrite = 0;
|
|
|
|
while (nWrite < nCount)
|
|
{
|
|
// First write bytes in the remaining part of the buffer
|
|
nCopy = __min(nCount - nWrite, UINT(m_nBufferLength - m_nBufferPos));
|
|
m_convertedBuffer.Set(m_nBufferPos, (LPBYTE)lpBuf, nCopy);
|
|
lpBuf = LPBYTE(lpBuf) + nCopy;
|
|
m_nBufferPos += nCopy;
|
|
nWrite += nCopy;
|
|
|
|
// If still bytes to write : write the old buffer
|
|
if (nWrite < nCount)
|
|
{
|
|
// ... Must be at end of buffer
|
|
ASSERT((UINT)m_nBufferPos == m_nBufferLength);
|
|
// ... Flush old buffer
|
|
FlushBuffer();
|
|
// ... Set new bufferpos after the present one
|
|
CheckBufferPosition();
|
|
}
|
|
}
|
|
// ... Should have written the ewact amount of bytes that was asked for
|
|
ASSERT(nWrite == nCount);
|
|
ASSERT_VALID(this);
|
|
}
|
|
|
|
LPSTR COXConvertedFile::ReadString(LPSTR psz, UINT nMax)
|
|
{
|
|
// Remark : Carriage Return CR = '\r', Line Feed LF = '\n',
|
|
// End-Of-File character = '\x1A'
|
|
|
|
// Must have non-trival buffer
|
|
ASSERT(psz != NULL);
|
|
ASSERT(1 <= nMax);
|
|
// ... Given buffer must be valid (for data AND zero-terminator)
|
|
ASSERT(AfxIsValidAddress(psz, nMax));
|
|
|
|
BOOL bEOL = FALSE;
|
|
BOOL bEOF = FALSE;
|
|
// ... Position to receive the next charater read
|
|
char* pszOut = psz;
|
|
// ... Last position in buffer (can only be used for zero-terminator)
|
|
char* pszLast = &psz[nMax - 1];
|
|
|
|
// Loop while buffer not full, not End-Of-Line and not End-Of-File
|
|
while ((pszOut != pszLast) &&!bEOL && !bEOF)
|
|
{
|
|
// EOF has been reached when nothing more can be read, or EOF-character was read
|
|
bEOF = (Read(pszOut, 1) == 0);
|
|
if (!bEOF && (*pszOut == '\x1A'))
|
|
{
|
|
bEOF = TRUE,
|
|
// ... Unread EOF character, so that subsequent Reads will read it again
|
|
Seek(-1, CFile::current);
|
|
}
|
|
if (!bEOF)
|
|
{
|
|
// When LF encountered, we reached EOL
|
|
if (*pszOut == '\n')
|
|
{
|
|
bEOL = TRUE;
|
|
// ... Check whether previous read character was CR, if so remove it
|
|
if ((psz <= pszOut - 1) && (*(pszOut - 1) == '\r'))
|
|
*(pszOut - 1) = '\n';
|
|
else
|
|
pszOut++;
|
|
}
|
|
else
|
|
pszOut++;
|
|
}
|
|
|
|
}
|
|
|
|
// If last character read was CR, perhaps a LF will follow
|
|
if (!bEOF && (psz <= pszOut - 1) && (*(pszOut - 1) == '\r'))
|
|
{
|
|
char cNext;
|
|
UINT uCount;
|
|
if (m_pDelegateToFile != NULL)
|
|
uCount = m_pDelegateToFile->Read(&cNext, 1);
|
|
else
|
|
uCount = Read(&cNext, 1);
|
|
|
|
if (uCount == 1)
|
|
{
|
|
if (cNext == '\n')
|
|
// ... Replace CR by LF
|
|
*(pszOut - 1) = '\n';
|
|
else
|
|
{
|
|
// ... Unread character
|
|
Seek(-1, CFile::current);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ... Add zero-terminator
|
|
*pszOut = '\0';
|
|
return (psz < pszOut ? psz : NULL);
|
|
}
|
|
|
|
void COXConvertedFile::WriteString(LPCSTR psz)
|
|
{
|
|
// Remark : Carriage Return CR = '\r' and Line Feed LF = '\n'
|
|
|
|
// Must have valid buffer
|
|
ASSERT(psz != NULL);
|
|
|
|
LPCSTR pszOut = psz;
|
|
LPCSTR pszLF;
|
|
|
|
while (*pszOut != '\0')
|
|
{
|
|
// ... Search for first LF character
|
|
pszLF = strchr(pszOut, '\n');
|
|
if (pszLF != NULL)
|
|
{
|
|
// ... Write data, followed by CR-LF pair
|
|
Write(pszOut, (UINT)(pszLF - pszOut));
|
|
Write(TEXT("\r\n"), 2);
|
|
// ... Position have LF character
|
|
pszOut = pszLF + 1;
|
|
}
|
|
else
|
|
{
|
|
// ... Write the remaining data
|
|
Write(pszOut, (UINT)strlen(pszOut));
|
|
// ... Position on zero-terminator
|
|
pszOut += strlen(pszOut);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
// UNICODE conversion
|
|
LPSTR COXConvertedFile::ReadString(LPWSTR psz, UINT nMax)
|
|
{
|
|
// Convert UNICODE string to ANSI
|
|
LPSTR pszAnsi = new char[nMax + 1];
|
|
size_t c;
|
|
UTBStr::wcstombs(&c, pszAnsi, nMax, psz, nMax);
|
|
|
|
LPSTR pszResult = ReadString(pszAnsi, nMax);
|
|
|
|
delete [] pszAnsi;
|
|
|
|
return pszResult;
|
|
}
|
|
|
|
void COXConvertedFile::WriteString(LPCWSTR psz)
|
|
{
|
|
const size_t nMaxAnsiLength = wcslen(psz) * 2 + 1;
|
|
size_t nRequiredSize;
|
|
UTBStr::wcstombs(&nRequiredSize, NULL, nMaxAnsiLength, psz, nMaxAnsiLength);
|
|
LPSTR pszAnsi = new char[nRequiredSize + 1];
|
|
UTBStr::wcstombs(&nRequiredSize, pszAnsi, nRequiredSize, psz, nRequiredSize + 1);
|
|
|
|
WriteString(pszAnsi);
|
|
|
|
delete [] pszAnsi;
|
|
}
|
|
|
|
#endif // WIN32
|
|
|
|
LONG COXConvertedFile::Seek(LONG lOff, UINT nFrom)
|
|
{
|
|
// Remark :
|
|
// This function calls CFile::GetLength()
|
|
// which calls CFile::SeekToEnd()
|
|
// which calls the virtual Seek()
|
|
// and thus arrives in this COXConvertedFile::Seek() again
|
|
// That's why this function uses a private version of CFile::GetLength()
|
|
ASSERT_VALID(this);
|
|
DWORD lAbsoluteFilePos;
|
|
DWORD nNewFilePos;
|
|
switch (nFrom)
|
|
{
|
|
case CFile::begin:
|
|
lAbsoluteFilePos = lOff;
|
|
break;
|
|
case CFile::current:
|
|
lAbsoluteFilePos = (DWORD) GetPosition() + lOff;
|
|
break;
|
|
case CFile::end:
|
|
lAbsoluteFilePos = GetLengthPrivate() + lOff;
|
|
break;
|
|
default:
|
|
TRACE(TEXT("COXConvertedFile::Seek : Unexpected case in switch nFrom = %u\n"), nFrom);
|
|
ASSERT(FALSE);
|
|
lAbsoluteFilePos = 0;
|
|
break;
|
|
}
|
|
// ... Align on buffer boundaries
|
|
nNewFilePos = (lAbsoluteFilePos / m_nBufferLength) * m_nBufferLength;
|
|
if (nNewFilePos != m_nFilePos)
|
|
// New position outside current buffer
|
|
{
|
|
// ... Flush the old buffer
|
|
Flush();
|
|
// ... Init new buffer to correct position and mark as unread
|
|
m_nFilePos = nNewFilePos;
|
|
m_bRead = FALSE;
|
|
}
|
|
|
|
// ... Compute new position in buffer
|
|
ASSERT(lAbsoluteFilePos - m_nFilePos < INT_MAX);;
|
|
m_nBufferPos = int(lAbsoluteFilePos - m_nFilePos);
|
|
ASSERT((0 <= m_nBufferPos) && ((UINT)m_nBufferPos <= m_nBufferLength));
|
|
|
|
|
|
#ifdef _DEBUG
|
|
// TRACE(TEXT("COXConvertedFile::Seek : Converting from (%s, %li) to (%s, %li)\n"),
|
|
// FILE_FROM_TEXT(nFrom), lOff, FILE_FROM_TEXT(CFile::begin), m_nFilePos);
|
|
#endif
|
|
|
|
ASSERT_VALID(this);
|
|
if (m_pDelegateToFile != NULL)
|
|
return (LONG) m_pDelegateToFile->Seek(m_nFilePos, CFile::begin) + m_nBufferPos;
|
|
else
|
|
return (LONG) CFile::Seek(m_nFilePos, CFile::begin) + m_nBufferPos;
|
|
}
|
|
|
|
#if _MFC_VER >= 0x0700
|
|
ULONGLONG COXConvertedFile::GetPosition() const
|
|
#else
|
|
DWORD COXConvertedFile::GetPosition() const
|
|
#endif
|
|
{
|
|
ASSERT_VALID(this);
|
|
return m_nFilePos + m_nBufferPos;
|
|
}
|
|
|
|
void COXConvertedFile::Flush()
|
|
{
|
|
// Flush unwritten bytes to file
|
|
ASSERT_VALID(this);
|
|
|
|
// If writing, write the buffer now
|
|
if (m_bOpenedForWrite)
|
|
{
|
|
FlushBuffer();
|
|
// ... To be compatible with WIN32 CFile::Flush() will only be called
|
|
// when yje file is opened for writing
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->Flush();
|
|
else
|
|
CFile::Flush();
|
|
}
|
|
|
|
// If reading mark it as unread
|
|
m_bRead = FALSE;
|
|
}
|
|
|
|
void COXConvertedFile::Close()
|
|
{
|
|
ASSERT_VALID(this);
|
|
Flush();
|
|
if (m_pDelegateToFile == NULL)
|
|
CFile::Close();
|
|
|
|
m_bOpenedForRead = FALSE;
|
|
m_bOpenedForWrite = FALSE;
|
|
m_nBufferPos = 0;
|
|
m_nFilePos = 0;
|
|
m_bRead = FALSE;
|
|
}
|
|
|
|
void COXConvertedFile::Abort()
|
|
{
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->Abort();
|
|
else
|
|
CFile::Abort();
|
|
|
|
m_bOpenedForRead = FALSE;
|
|
m_bOpenedForWrite = FALSE;
|
|
m_nBufferPos = 0;
|
|
m_nFilePos = 0;
|
|
m_bRead = FALSE;
|
|
}
|
|
|
|
void COXConvertedFile::LockRange(DWORD dwPos, DWORD dwCount)
|
|
{
|
|
ASSERT_VALID(this);
|
|
// ... Round down starting position
|
|
DWORD dwNewPos = (dwPos / m_nBufferLength) * m_nBufferLength;
|
|
// ... Round up count
|
|
DWORD dwNewCount = ((dwCount + m_nBufferLength - 1) / m_nBufferLength) * m_nBufferLength;
|
|
|
|
TRACE(TEXT("COXConvertedFile::LockRange : Converting from %lu %lu to %lu %lu\n"),
|
|
dwPos, dwCount, dwNewPos, dwNewCount);
|
|
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->LockRange(dwNewPos, dwNewCount);
|
|
else
|
|
CFile::LockRange(dwNewPos, dwNewCount);
|
|
}
|
|
|
|
void COXConvertedFile::UnlockRange(DWORD dwPos, DWORD dwCount)
|
|
{
|
|
ASSERT_VALID(this);
|
|
// ... Round down starting position
|
|
DWORD dwNewPos = (dwPos / m_nBufferLength) * m_nBufferLength;
|
|
// ... Round up count
|
|
DWORD dwNewCount = ((dwCount + m_nBufferLength - 1) / m_nBufferLength) * m_nBufferLength;
|
|
|
|
TRACE(TEXT("COXConvertedFile::UnlockRange : Converting from %lu %lu to %lu %lu\n"),
|
|
dwPos, dwCount, dwNewPos, dwNewCount);
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->UnlockRange(dwNewPos, dwNewCount);
|
|
else
|
|
CFile::UnlockRange(dwNewPos, dwNewCount);
|
|
}
|
|
|
|
void COXConvertedFile::SetLength(DWORD dwNewLen)
|
|
{
|
|
ASSERT_VALID(this);
|
|
// ... Round up length
|
|
DWORD dwNewNewLen = ((dwNewLen + m_nBufferLength - 1) / m_nBufferLength) * m_nBufferLength;
|
|
TRACE(TEXT("COXConvertedFile::SetLength : Converting from %lu to %lu\n"),
|
|
dwNewLen, dwNewNewLen);
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->SetLength(dwNewNewLen);
|
|
else
|
|
CFile::SetLength(dwNewNewLen);
|
|
}
|
|
|
|
#if _MFC_VER >= 0x0700
|
|
ULONGLONG COXConvertedFile::GetLength() const
|
|
#else
|
|
DWORD COXConvertedFile::GetLength() const
|
|
#endif
|
|
{
|
|
ASSERT_VALID(this);
|
|
// TRACE(TEXT("COXConvertedFile::GetLength : returning %lu, which might rounded\n"), CFile::GetLength());
|
|
|
|
// CFile::GetLength will internally call COXConvertedFile::Seek() to determine the length
|
|
// this will return the adjusted langth
|
|
if (m_pDelegateToFile != NULL)
|
|
return m_pDelegateToFile->GetLength();
|
|
else
|
|
return CFile::GetLength();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
|
|
#ifdef _DEBUG
|
|
void COXConvertedFile::Dump(CDumpContext& dc) const
|
|
{
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->Dump(dc);
|
|
else
|
|
CFile::Dump(dc);
|
|
|
|
dc << TEXT("\nm_bEnabled : ") << (WORD)m_bEnabled;
|
|
dc << TEXT("\nm_nBufferLength : ") << (WORD)m_nBufferLength;
|
|
dc << TEXT("\nm_pOriginalBuffer : ") << (char *)m_pOriginalBuffer;
|
|
dc << TEXT("\nm_convertedBuffer ") << m_convertedBuffer;
|
|
dc << TEXT("\nm_nBufferPos ; ") << (WORD)m_nBufferPos;
|
|
dc << TEXT("\nm_nFilePos : ") << m_nFilePos;
|
|
dc << TEXT("\nm_bRead : ") << (WORD)m_bRead;
|
|
dc << TEXT("\nm_bOpenedForRead : ") << (WORD)m_bOpenedForRead;
|
|
dc << TEXT("\nm_bOpenedForWrite : ") << (WORD)m_bOpenedForWrite;
|
|
}
|
|
|
|
void COXConvertedFile::AssertValid() const
|
|
{
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->AssertValid();
|
|
else
|
|
CFile::AssertValid();
|
|
ASSERT(0 <= m_nBufferPos);
|
|
ASSERT((UINT)m_nBufferPos <= m_nBufferLength);
|
|
ASSERT(AfxIsValidAddress(m_pOriginalBuffer, m_nBufferLength));
|
|
ASSERT(AfxIsValidAddress(m_convertedBuffer.Get(), m_nBufferLength));
|
|
}
|
|
#endif
|
|
|
|
COXConvertedFile::~COXConvertedFile()
|
|
{
|
|
delete[] m_pOriginalBuffer;
|
|
m_convertedBuffer.Destroy();
|
|
}
|
|
|
|
// protected:
|
|
DWORD COXConvertedFile::GetLengthPrivate() const
|
|
{
|
|
DWORD dwLen, dwCur;
|
|
|
|
// Seek is a non const operation
|
|
if (m_pDelegateToFile != NULL)
|
|
{
|
|
dwCur = (DWORD) m_pDelegateToFile->Seek(0L, CFile::current);
|
|
// ... SeekToEnd() is replaced by Seek(0, CFile::end)
|
|
dwLen = (DWORD) m_pDelegateToFile->Seek(0L, CFile::end);
|
|
VERIFY(dwCur == (DWORD)(m_pDelegateToFile->Seek(dwCur, CFile::begin)));
|
|
}
|
|
else
|
|
{
|
|
dwCur = (DWORD) ((CFile*)this)->CFile::Seek(0L, CFile::current);
|
|
// ... SeekToEnd() is replaced by Seek(0, CFile::end)
|
|
dwLen = (DWORD) ((CFile*)this)->CFile::Seek(0, CFile::end);
|
|
VERIFY(dwCur == (DWORD)(((CFile*)this)->CFile::Seek(dwCur, CFile::begin)));
|
|
}
|
|
|
|
return dwLen;
|
|
}
|
|
|
|
BOOL COXConvertedFile::ReadBuffer()
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns : TRUE if no data was read because EOF has been reached
|
|
// If EOF has been reached but some data could still be returned
|
|
// FALSE will be returned
|
|
// --- Effect : Reads a new contents for the internal buffer
|
|
{
|
|
UINT nRead;
|
|
|
|
// ... Flush buffer when it contains modified info
|
|
FlushBuffer();
|
|
|
|
// ... If already at the end of this buffer, move file position to the next
|
|
CheckBufferPosition();
|
|
|
|
// ... Set actual file position to the correct position
|
|
// (must be on buffer boundary)
|
|
ASSERT(m_nFilePos % m_nBufferLength == 0);
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->Seek(m_nFilePos, CFile::begin);
|
|
else
|
|
CFile::Seek(m_nFilePos, CFile::begin);
|
|
|
|
// ... Read buffer from file
|
|
memset(m_pOriginalBuffer, 0, m_nBufferLength);
|
|
if (m_pDelegateToFile != NULL)
|
|
nRead = m_pDelegateToFile->Read(m_pOriginalBuffer, m_nBufferLength);
|
|
else
|
|
nRead = CFile::Read(m_pOriginalBuffer, m_nBufferLength);
|
|
if (IsConvertEnabled())
|
|
VERIFY(ConvertRead(m_pOriginalBuffer, m_convertedBuffer.Get()));
|
|
else
|
|
memcpy(m_convertedBuffer.Get(), m_pOriginalBuffer, m_nBufferLength);
|
|
|
|
// ... If not EOF, mark as actually read from file
|
|
m_bRead = (nRead != 0);
|
|
|
|
return !m_bRead;
|
|
}
|
|
|
|
void COXConvertedFile::FlushBuffer()
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns :
|
|
// --- Effect : Writes the contents of the internal buffer to file
|
|
// when it was modified and not yet written
|
|
// It does not change any internal buffer positions
|
|
{
|
|
if (!m_convertedBuffer.IsModified())
|
|
return;
|
|
|
|
// ... Must have write access
|
|
ASSERT(m_bOpenedForWrite);
|
|
// If the contents of the buffer was originally read from file
|
|
// or all the buffer was overwritten : OK
|
|
if (!m_bRead && m_convertedBuffer.GetUnmodified() != -1)
|
|
{
|
|
// There exist some bytes in the buffer that do not originate from file
|
|
// and are never overwritten
|
|
// Now get the original contents for the unmodified bytes
|
|
|
|
// ... Must be able to read as well
|
|
ASSERT(m_bOpenedForRead);
|
|
int nRead;
|
|
int nUnmodPos;
|
|
LPBYTE pTempOrgBuffer = new BYTE[m_nBufferLength];
|
|
LPBYTE pTempConvBuffer = new BYTE[m_nBufferLength];
|
|
memset(pTempOrgBuffer, 0, m_nBufferLength);
|
|
|
|
// ... Go back to the original beginning of the buffer
|
|
if (m_pDelegateToFile != NULL)
|
|
{
|
|
m_pDelegateToFile->Seek(m_nFilePos, CFile::begin);
|
|
nRead = m_pDelegateToFile->Read(pTempOrgBuffer, m_nBufferLength);
|
|
}
|
|
else
|
|
{
|
|
CFile::Seek(m_nFilePos, CFile::begin);
|
|
nRead = CFile::Read(pTempOrgBuffer, m_nBufferLength);
|
|
}
|
|
if (IsConvertEnabled())
|
|
VERIFY(ConvertRead(pTempOrgBuffer, pTempConvBuffer));
|
|
else
|
|
memcpy(pTempConvBuffer, pTempOrgBuffer, m_nBufferLength);
|
|
|
|
// ... While there exist still bytes from file that are not in the buffer
|
|
// copy them
|
|
while ( ((nUnmodPos = m_convertedBuffer.GetUnmodified()) != -1) &&
|
|
(nUnmodPos <= nRead) )
|
|
{
|
|
// ... This will change the state from unmodified to modified
|
|
ASSERT(!m_convertedBuffer.IsModified(nUnmodPos));
|
|
m_convertedBuffer.Set(nUnmodPos, &pTempConvBuffer[nUnmodPos]);
|
|
ASSERT(m_convertedBuffer.IsModified(nUnmodPos));
|
|
}
|
|
// ... Now no unmodified bytes exist anymore or
|
|
// their corresponding bytes in the file do not exist
|
|
ASSERT((m_convertedBuffer.GetUnmodified() == -1) ||
|
|
(nRead < m_convertedBuffer.GetUnmodified()));
|
|
m_convertedBuffer.SetModified(TRUE);
|
|
|
|
delete[] pTempOrgBuffer;
|
|
delete[] pTempConvBuffer;
|
|
}
|
|
|
|
// ... Set actual file position to the correct position
|
|
// (must be on buffer boundary)
|
|
ASSERT(m_nFilePos % m_nBufferLength == 0);
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->Seek(m_nFilePos, CFile::begin);
|
|
else
|
|
CFile::Seek(m_nFilePos, CFile::begin);
|
|
|
|
// Actually write to file
|
|
if (IsConvertEnabled())
|
|
VERIFY(ConvertWrite(m_convertedBuffer.Get(), m_pOriginalBuffer));
|
|
else
|
|
memcpy(m_pOriginalBuffer, m_convertedBuffer.Get(), m_nBufferLength);
|
|
|
|
if (m_pDelegateToFile != NULL)
|
|
m_pDelegateToFile->Write(m_pOriginalBuffer, m_nBufferLength);
|
|
else
|
|
CFile::Write(m_pOriginalBuffer, m_nBufferLength);
|
|
|
|
// ... Mark as unmodified and read from file (because we have just written it)
|
|
m_convertedBuffer.SetModified(FALSE);
|
|
m_bRead = TRUE;
|
|
}
|
|
|
|
void COXConvertedFile::CheckBufferPosition()
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns :
|
|
// --- Effect : When the buffer position is after the end of the buffer
|
|
// put it on 0 and move the file position
|
|
{
|
|
if ((UINT)m_nBufferPos == m_nBufferLength)
|
|
{
|
|
m_nFilePos += m_nBufferLength;
|
|
m_nBufferPos = 0;
|
|
m_convertedBuffer.Empty();
|
|
m_bRead = FALSE;
|
|
}
|
|
}
|
|
// private:
|
|
|
|
// Message handlers ---------------------------------------------------------
|
|
|
|
// ==========================================================================
|