/**************************************************************************** * * DIRECTIO.CPP * * routines for reading Standard AVI files * * Copyright (c) 1992 - 1995 Microsoft Corporation. All Rights Reserved. * * * implementation of a disk i/o class designed to optimise * sequential reading and writing to disk by using overlapped i/o (for read * ahead and write behind) and using large buffers written with no buffering. * ***************************************************************************/ #include #include #include "debug.h" #include "directio.h" #ifdef USE_DIRECTIO // // implementation of a disk i/o class designed to optimise // sequential reading and writing to disk by using overlapped i/o (for read // ahead and write behind) and using large buffers written with no buffering. // -- CFileStream class methods --------------------------------------- // initialise to known (invalid) state CFileStream::CFileStream() { m_State = Invalid; m_Position = 0; m_hFile = INVALID_HANDLE_VALUE; #ifdef CHICAGO ZeroMemory(&m_qio, sizeof(m_qio)); #endif } BOOL CFileStream::Open(LPTSTR file, BOOL bWrite, BOOL bTruncate) { if (m_State != Invalid) { return FALSE; } // remember this for default streaming mode m_bWrite = bWrite; DWORD dwAccess = GENERIC_READ; if (bWrite) { dwAccess |= GENERIC_WRITE; } // open the file. Always get read access. exclusive open if we // are writing the file, otherwise deny other write opens. // never truncate the file, since the file may be de-fragmented. #ifdef CHICAGO DWORD dwFlags = FILE_FLAG_NO_BUFFERING; #else DWORD dwFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING; #endif m_hFile = CreateFile(file, dwAccess, (bWrite ? 0 : FILE_SHARE_READ), NULL, OPEN_ALWAYS, dwFlags, 0); if (m_hFile == INVALID_HANDLE_VALUE) { return FALSE; } #ifdef CHICAGO if ( ! QioInitialize(&m_qio, m_hFile, THREAD_PRIORITY_HIGHEST)) { CloseHandle (m_hFile); return FALSE; } #endif // find the bytes per sector that we have to round to for this file // -requires finding the 'root path' for this file. TCHAR ch[MAX_PATH]; LPTSTR ptmp; //required arg GetFullPathName(file, sizeof(ch)/sizeof(ch[0]), ch, &ptmp); // truncate this to the name of the root directory if ((ch[0] == TEXT('\\')) && (ch[1] == TEXT('\\'))) { // path begins with \\server\share\path so skip the first // three backslashes ptmp = &ch[2]; while (*ptmp && (*ptmp != TEXT('\\'))) { ptmp++; } if (*ptmp) { // advance past the third backslash ptmp++; } } else { // path must be drv:\path ptmp = ch; } // find next backslash and put a null after it while (*ptmp && (*ptmp != TEXT('\\'))) { ptmp++; } // found a backslash ? if (*ptmp) { // skip it and insert null ptmp++; *ptmp = TEXT('\0'); } DWORD dwtmp1, dwtmp2, dwtmp3; if (!GetDiskFreeSpace(ch, &dwtmp1, &m_SectorSize, &dwtmp2, &dwtmp3)) m_SectorSize = 2048; // sigh. now init the first buffer // sets the right buffer count and size for current mode m_State = Stopped; if (!EnsureBuffersValid()) { return FALSE; } m_Current = 0; m_Position = 0; // if asked to truncate the file, we will not actually do so, since this // could throw away a de-fragged file. We will however, note that the file // size is 0 and use this to affect reading and writing past 'eof' - eg // if you write 8 bytes to the beginning of a truncated file, we do not // need to read in the first sector beforehand. if (bTruncate) { m_Size = 0; } else { // get the current file size m_Size = GetFileSize(m_hFile, NULL); } // all set return TRUE; } BOOL CFileStream::Seek(DWORD pos) { // we just record this and go away //if (pos < m_Position) { // DPF("seek back by 0x%x to 0x%x\n", m_Position - pos, pos); //} m_Position = pos; return TRUE; } DWORD CFileStream::GetCurrentPosition() { return m_Position; } BOOL CFileStream::Write(LPBYTE pData, DWORD count, DWORD * pbyteswritten) { *pbyteswritten = 0; // error if file not opened if (m_State == Invalid) { return FALSE; } DWORD nBytes; while (count > 0) { // is our current buffer ready to write this data ? // (we need to tell it eof pos as well since if eof is // in middle of buffer but beyond valid data, ok to write.) if ((m_Current < 0) || (!m_Buffers[m_Current].QueryPosition(m_Position, m_Size))) { // commit this buffer if we have changed position beyond it if (m_Current >= 0) { if (!m_Buffers[m_Current].Commit()) { // file error - abort return FALSE; } // if we are streaming, then advance to next buffer while // current one is writing. if (m_State != Stopped) { m_Current = NextBuffer(m_Current); } } else { m_Current = 0; } // make sure that previous operations on this buffer have completed if (!m_Buffers[m_Current].WaitComplete()) { // i/o error return FALSE; } } // we either have a buffer that has already pre-read the sector // we start writing to, or we have an idle buffer that // will do the pre-read for us if (!m_Buffers[m_Current].Write(m_Position, pData, count, m_Size, &nBytes)) { return FALSE; } count -= nBytes; pData += nBytes; m_Position += nBytes; *pbyteswritten += nBytes; } if (m_Position > m_Size) { m_Size = m_Position; } return TRUE; } BOOL CFileStream::Read(LPBYTE pData, DWORD count, DWORD * pbytesread) { *pbytesread = 0; // error if file not opened if (m_State == Invalid) { return FALSE; } // force the read to be within the file size limits if (m_Position >= m_Size) { // all done - nothing read return TRUE; } else { count = min(count, (m_Size - m_Position)); } BOOL bDoReadAhead = FALSE; DWORD nBytes; while (count > 0) { // is data within current buffer if ((m_Current < 0) || (!m_Buffers[m_Current].QueryPosition(m_Position, m_Size))) { if (m_Current >= 0) { // commit this buffer if we have changed position beyond it if (!m_Buffers[m_Current].Commit()) { // file error - abort return FALSE; } // advance to next buffer (if streaming) if (m_State == Writing) { m_Current = NextBuffer(m_Current); } else if (m_State == Reading) { // smart read-ahead strategy: try to find in existing // buffers, and only issue a read-ahead if we take the // highest buffer int n = NextBuffer(m_Current); m_Current = -1; for (int i = 0; i < m_NrValid; i++) { if (m_Buffers[n].QueryPosition(m_Position, m_Size)) { m_Current = n; break; } n = NextBuffer(n); } if (m_Current < 0) { // read-ahead is messed up because we have made too big // a seek for the current buffer size // Best thing is to use the lowest buffer (should be the // one after the highest, and to restart readaheads with // this position). m_Current = NextBuffer(m_HighestBuffer); m_HighestBuffer = m_Current; DPF("using idle %d\n", m_Current); } if (m_Current == m_HighestBuffer) { bDoReadAhead = TRUE; } } } else { m_Current = 0; if (m_Current == m_HighestBuffer) { bDoReadAhead = TRUE; } } // make sure that previous operations on this buffer have completed if (!m_Buffers[m_Current].WaitComplete()) { // i/o error return FALSE; } } // now we have a buffer that either contains the data we want, or // is idle and ready to fetch it. if (!m_Buffers[m_Current].Read(m_Position, pData, count, m_Size, &nBytes)) { return FALSE; } count -= nBytes; pData += nBytes; m_Position += nBytes; *pbytesread += nBytes; // do read ahead now if necessary (the Read() call may have required // a seek and read if the data was not in the buffer, so delay the // read-ahead until after it has completed). if (bDoReadAhead) { // remember that this new buffer contains the highest position // -- we should issue another readahead when we start using this // buffer. m_HighestBuffer = NextBuffer(m_Current); DWORD p = m_Buffers[m_Current].GetNextPosition(); m_Buffers[m_HighestBuffer].ReadAhead(p, m_Size); bDoReadAhead = FALSE; } } return TRUE; } // set the right buffer size and count for current mode BOOL CFileStream::EnsureBuffersValid() { if (m_State == Invalid) { // file not opened return FALSE; } #ifdef CHICAGO if (m_State == Writing) { m_NrValid = 4; // total 256k } else if (m_State == Reading) { m_NrValid = 4; // total 256k } else { m_NrValid = 1; // total 64k } int size = (64 * 1024); #else if (m_State == Writing) { m_NrValid = 2; // total 512k } else if (m_State == Reading) { m_NrValid = 4; // total 256k } else { m_NrValid = 1; // total 64k } int size = (64 * 1024); if (m_State == Writing) size = (256 * 1024); #endif int i =0; Assert(m_NrValid <= NR_OF_BUFFERS); // init valid buffers for (; i < m_NrValid; i++) { #ifdef CHICAGO if (!m_Buffers[i].Init(m_SectorSize, size, &m_qio)) { #else if (!m_Buffers[i].Init(m_SectorSize, size, m_hFile)) { #endif return FALSE; } } // discard others for (; i < NR_OF_BUFFERS; i++) { m_Buffers[i].FreeMemory(); } return TRUE; } BOOL CFileStream::StartStreaming() { if (m_bWrite) { return StartWriteStreaming(); } else { return StartReadStreaming(); } } BOOL CFileStream::StartWriteStreaming() { m_State = Writing; if (!EnsureBuffersValid()) { return FALSE; } return TRUE; } BOOL CFileStream::StartReadStreaming() { // commit the current buffer if (!m_Buffers[m_Current].Commit()) { return FALSE; } m_State = Reading; if (!EnsureBuffersValid()) { return FALSE; } // start read-ahead on buffer 0 - read from current position // (tell buffer the eof point so it won't bother reading beyond it) // remember that this is the highest current buffer - when we start using // this buffer it is time to issue the next readahead (this allows for // seeks backwards and forwards within the valid buffers without upsetting // the read-aheads). m_HighestBuffer = 0; m_Buffers[0].ReadAhead(m_Position, m_Size); // set m_Current invalid: this ensures that we will wait for read-ahead // to complete before getting data, and that when we start using it, we // will issue the next read-ahead. m_Current = -1; return TRUE; } BOOL CFileStream::StopStreaming() { // complete all i/o if (!CommitAndWait()) { return FALSE; } m_Current = 0; m_State = Stopped; // recalc buffer size/count for new mode if (!EnsureBuffersValid()) { return FALSE; } return TRUE; } // wait for all transfers to complete. BOOL CFileStream::CommitAndWait() { // write current buffer // if (!m_Buffers[m_Current].Commit()) return FALSE; #ifdef CHICAGO // flush all buffers that have been queued // //QioCommit (&m_qio); #endif // wait for all buffers to complete for (int i = 0; i < m_NrValid; i++) { if (!m_Buffers[i].WaitComplete()) { return FALSE; } } // no need to reset m_Current return TRUE; } // destructor will call Commit() CFileStream::~CFileStream() { if (m_hFile != INVALID_HANDLE_VALUE) { CommitAndWait(); #ifdef CHICAGO QioShutdown (&m_qio); #endif CloseHandle(m_hFile); } } // --- CFileBuffer methods ----------------------------------------- // initiate to an invalid (no buffer ready) state CFileBuffer::CFileBuffer() { m_pBuffer = NULL; m_pAllocedMem = NULL; m_State = Invalid; #ifdef CHICAGO m_pqio = NULL; #endif } // allocate memory and become idle. BOOL #ifdef CHICAGO CFileBuffer::Init(DWORD nBytesPerSector, DWORD buffersize, LPQIO pqio) #else CFileBuffer::Init(DWORD nBytesPerSector, DWORD buffersize, HANDLE hfile) #endif { if (m_State != Invalid) { if ((nBytesPerSector == m_BytesPerSector) && (buffersize == RoundSizeToSector(m_TotalSize))) { // we're there already return TRUE; } // discard what we have FreeMemory(); } Assert(m_State == Invalid); // round up RAWIO_SIZE to a multiple of sector size m_BytesPerSector = nBytesPerSector; m_TotalSize = (DWORD) RoundSizeToSector(buffersize); m_DataLength = 0; m_State = Idle; m_bDirty = FALSE; #ifdef CHICAGO m_pqio = pqio; m_pAllocedMem = (unsigned char *)VirtualAlloc (NULL, m_TotalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (m_pAllocedMem == NULL) return FALSE; #else m_hFile = hfile; m_pAllocedMem = new BYTE[m_TotalSize]; if (m_pAllocedMem == NULL) return FALSE; m_Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!m_Overlapped.hEvent) { delete[] m_pAllocedMem; return FALSE; } #endif // this is where my naming scheme falls down. RoundPos rounds down, and // RoundSize rounds up. to correctly align the buffer and stay within it, // we need to round the start address up and the size down. // round start address up to sector size m_pBuffer = (LPBYTE) RoundSizeToSector((LONG_PTR) m_pAllocedMem); // remove rounding from size - and round it again! m_TotalSize = (DWORD) RoundPosToSector(m_TotalSize - (m_pBuffer - m_pAllocedMem)); return TRUE; } // revert to invalid state (eg when streaming stops) void CFileBuffer::FreeMemory() { if (m_State == Idle) { Commit(); } if (m_State == Busy) { WaitComplete(); } if (m_State != Invalid) { #ifdef CHICAGO VirtualFree (m_pAllocedMem, 0, MEM_RELEASE); m_pBuffer = NULL; m_pAllocedMem = NULL; #else CloseHandle(m_Overlapped.hEvent); delete[] m_pAllocedMem; #endif m_State = Invalid; } } // calls commit if dirty before freeing everything. CFileBuffer::~CFileBuffer() { FreeMemory(); } // does this position occur anywhere within the current buffer ? // needs to know current eof for some cases (writing beyond eof // if eof is within this buffer is ok to this buffer). // // we can use this buffer if: // 1. if the buffer is empty and the write is past eof (where eof is rounded // to a sector boundary). // // 2. if the start position is within the current m_DataLength // // 3. if eof is within the buffer and the write is past eof // // all reads are limited by the caller to be within the file limits, so // the reading case is covered by 2 above (1 and 3 will not occur). // // all other cases will require the read of data that is not in the buffer. // or the (early) committing of data in the buffer // BOOL CFileBuffer::QueryPosition(DWORD pos, DWORD filesize) { if (m_State == Invalid) { return FALSE; } // round filesize to sector boundary filesize = (DWORD) RoundSizeToSector(filesize); if (pos >= filesize) { // write is past eof. ok if buffer empty or if buffer contains // eof (and has space in it) if ((m_DataLength == 0) || ((m_Position + m_DataLength == filesize) && (m_DataLength < m_TotalSize))) { return TRUE; } // we have data that needs to be flushed before we can do this return FALSE; } else { if ((pos >= m_Position) && (pos < m_Position + m_DataLength)) { // we have this byte return TRUE; } // we don't have this byte of valid data. we have some other. // // you might think that if the write begins on a sector boundary, and // this buffer's data is not dirty you could permit this without a // pre-read - but we don't know yet where the write will end, and if // it ends mid-sector and not past current eof, we will need to // read that sector in. return FALSE; } } // write some data to buffer (must be committed separately) // filesize parameter is the file size before this write, and is used to // control what we do with the partial sector at beginning and end // -if not past current eof, we need to read the current sector before // writing to it. BOOL CFileBuffer::Write( DWORD pos, LPBYTE pData, DWORD count, DWORD filesize, DWORD * pbytesWritten) { // remember for later (during commit) m_FileLength = filesize; *pbytesWritten = 0; if (m_State != Idle) { if (!WaitComplete()) { return FALSE; } } if (m_State == Invalid) { // naughty boy! return FALSE; } // do we need to commit the current contents or read anything ? // if there is data, and the start position is not within the valid data // range, then flush this lot. note that we count the region from // end of valid data to end of actual buffer as valid data if the eof // is within this buffer. if ((m_DataLength > 0) && ((pos < m_Position) || (pos >= m_Position + m_TotalSize) || ((pos >= m_Position + m_DataLength) && ((m_Position + m_DataLength) < filesize)))) { // we're not ok - need to flush current contents if (!Commit() || !WaitComplete()) { return FALSE; } m_DataLength = 0; } // if empty (or we just flushed it), we can start at the beginning if (m_DataLength == 0) { m_Position = (DWORD) RoundPosToSector(pos); // do we need to read the partial sector? if ((pos < RoundSizeToSector(filesize)) && (pos % m_BytesPerSector != 0)) { // yes - write starts partway through a valid sector m_DataLength = m_BytesPerSector; if (!ReadIntoBuffer(0, m_Position, m_BytesPerSector) || !WaitComplete()) { return FALSE; } } } // we can start the data. now what about the end? // if it all fits within the buffer, and it ends mid-sector and the // final sector is within the file length but not currently in the // buffer, we will need to pre-read the final buffer if ((pos + count) < (m_Position + m_TotalSize)) { if ((pos + count) % m_BytesPerSector) { // we have to write a partial sector - is it past eof or within // valid region ? if ((pos+count > m_Position + m_DataLength) && (pos+count < filesize)) { // yes need to read partial sector DWORD sec = (DWORD) RoundPosToSector(pos+count); // need to temporarily set m_DataLength // to the amount read so that WaitComplete can check // its ok m_DataLength = m_BytesPerSector; if (!ReadIntoBuffer( sec - m_Position, // index in buffer sec, // position in file m_BytesPerSector) || !WaitComplete()) { return FALSE; } // set size correctly again m_DataLength = (sec - m_Position) + m_BytesPerSector; } } } // now we can stuff the data in int index = pos - m_Position; *pbytesWritten = min(count, m_TotalSize - index); CopyMemory( &m_pBuffer[index], pData, *pbytesWritten); // adjust data length if ((index + *pbytesWritten) > m_DataLength) { m_DataLength = (DWORD) RoundSizeToSector(index + *pbytesWritten); } m_bDirty = TRUE; return TRUE; } // read data from buffer (will seek and read if necessary first) BOOL CFileBuffer::Read( DWORD pos, LPBYTE pData, DWORD count, DWORD filelength, DWORD * pBytesRead) { Assert(m_State == Idle); // remember this for read completion checking m_FileLength = filelength; *pBytesRead = 0; if ((pos < m_Position) || (pos >= m_Position + m_DataLength)) { // not in current buffer - flush current contents if dirty if (!Commit() || !WaitComplete()) { return FALSE; } m_Position = (DWORD) RoundPosToSector(pos); // remember if we round the start down, we also need to increase // the length (as well as rounding it up at the other end) // force a minimum read size to avoid lots of single sectors m_DataLength = count + (pos - m_Position); m_DataLength = max(MIN_READ_SIZE, m_DataLength); m_DataLength = (DWORD) RoundSizeToSector(m_DataLength); m_DataLength = min(m_DataLength, m_TotalSize); if (!ReadIntoBuffer(0, m_Position, m_DataLength) || !WaitComplete()) { return FALSE; } } // we have (at least the start part of) the data in the buffer int offset = pos - m_Position; count = min(count, m_DataLength - offset); CopyMemory(pData, &m_pBuffer[offset], count); *pBytesRead = count; return TRUE; } // what is the first file position after this buffer's valid data // ---return this even if still busy reading it DWORD CFileBuffer::GetNextPosition() { if ((m_State == Invalid) || (m_DataLength == 0)) { return 0; } else { return m_Position + m_DataLength; } } // initiate a read-ahead void CFileBuffer::ReadAhead(DWORD start, DWORD filelength) { if (m_State != Idle) { if (!CheckComplete()) { return; } } // we may already hold this position if (QueryPosition(start, filelength)) { return; } m_FileLength = filelength; if (m_bDirty) { // current data needs to be flushed to disk. // we should initiate this, but we can't wait for // it to complete, so we won't do the read-ahead Commit(); return; } m_Position = (DWORD) RoundPosToSector(start); m_DataLength = min((DWORD) RoundSizeToSector(filelength - m_Position), m_TotalSize); ReadIntoBuffer(0, m_Position, m_DataLength); // no wait - this is an async readahead. } // initiate the i/o from the buffer BOOL CFileBuffer::Commit() { if ((m_State != Idle) || (!m_bDirty)) { return TRUE; } #ifndef CHICAGO DWORD nrWritten; #endif #ifdef CHICAGO m_State = Busy; m_qiobuf.dwOffset = m_Position; m_qiobuf.lpv = m_pBuffer; m_qiobuf.cb = m_DataLength; m_qiobuf.cbDone = 0; m_qiobuf.bWrite = TRUE; m_qiobuf.dwError = ERROR_IO_PENDING; QioAdd (m_pqio, &m_qiobuf); #else ResetEvent(m_Overlapped.hEvent); m_State = Busy; //start from m_Position m_Overlapped.Offset = m_Position; m_Overlapped.OffsetHigh = 0; if (WriteFile(m_hFile, m_pBuffer, m_DataLength, &nrWritten, &m_Overlapped)) { DPF(("instant completion")); // if it completed already, then sort out the new position if (nrWritten != m_DataLength) { DPF("commit- bad length %d not %d", nrWritten, m_DataLength); return FALSE; } m_bDirty = FALSE; m_State = Idle; } else { // should be pending if (GetLastError() != ERROR_IO_PENDING) { // no longer busy m_State = Idle; DPF("commit error %d", GetLastError()); return FALSE; } } #endif // we must do this here, since WaitComplete could complete a // partial read that would leave the buffer dirty. // we are safe since the buffer will remain Busy until this is // actually TRUE. (if we fail to write to the disk then the // file state is guaranteed messed up). m_bDirty = FALSE; return TRUE; } // wait for any pending commit or read to complete and check for errors. BOOL CFileBuffer::WaitComplete() { if (m_State == ErrorOccurred) { // the i/o has completed in error but we haven't been able to // report the fact yet m_State = Idle; return FALSE; } if (m_State == Busy) { DWORD actual; // no longer busy m_State = Idle; #ifdef CHICAGO if ( ! QioWait (m_pqio, &m_qiobuf, TRUE)) return FALSE; actual = m_qiobuf.cbDone; #else if (!GetOverlappedResult(m_hFile, &m_Overlapped, &actual, TRUE)) { DPF("WC: GetOverlapped failed %d", GetLastError()); return FALSE; } #endif if (actual != m_DataLength) { // rounding to sector size may have taken us past eof if (m_Position + actual != m_FileLength) { DPF("WC: actual wrong (%d not %d)", actual, m_DataLength); return FALSE; } } } return TRUE; } // non-blocking check to see if async io is complete BOOL CFileBuffer::CheckComplete() { if (m_State == Idle) { return TRUE; } if (m_State != Busy) { return FALSE; // invalid or error } #ifdef CHICAGO if (QioWait(m_pqio, &m_qiobuf, FALSE)) return FALSE; else if (m_qiobuf.dwError == 0) { m_State = Idle; return TRUE; } m_State = ErrorOccurred; return FALSE; #else DWORD actual; if (GetOverlappedResult(m_hFile, &m_Overlapped, &actual, FALSE)) { if ((actual == m_DataLength) || (actual + m_Position == m_FileLength)) { m_State = Idle; return TRUE; } } else if (GetLastError() == ERROR_IO_INCOMPLETE) { // still busy return FALSE; } // some error state occurred - this must be reported by WaitComplete() m_State = ErrorOccurred; DPF("CheckComplete error %d", GetLastError()); return FALSE; #endif } // initiates an async read request into the buffer (can be an insertion into // middle of buffer rather than a complete buffer fill - and so will not // adjust m_Position or m_DataLength). reads count bytes // offset bytes from the start of the buffer, pos bytes from the start of the // file. Assumes necessary rounding of length and position has already happened. BOOL CFileBuffer::ReadIntoBuffer(int offset, DWORD pos, DWORD count) { Assert(m_State == Idle); #ifndef CHICAGO DWORD nrRead; #endif #ifdef CHICAGO m_State = Busy; m_qiobuf.dwOffset = pos; m_qiobuf.lpv = (LPVOID)(m_pBuffer + offset); m_qiobuf.cb = count; m_qiobuf.cbDone = 0; m_qiobuf.bWrite = FALSE; m_qiobuf.dwError = ERROR_IO_PENDING; // if this read is not sector aligned, we cannot do it // in async in chicago, so do it right now! // if ((count & 511) || (pos & 511) || (offset & 511)) { DWORD dwOff; m_qiobuf.bPending = FALSE; DPF("%s %X bytes (non-aligned) at %08X into %08X\r\n", m_qiobuf.bWrite ? "Writing" : "Reading", m_qiobuf.cb, m_qiobuf.dwOffset, m_qiobuf.lpv); dwOff = SetFilePointer (m_pqio->hFile, m_qiobuf.dwOffset, NULL, FILE_BEGIN); if (dwOff != m_qiobuf.dwOffset) { m_qiobuf.dwError = GetLastError(); DPF("avifile32 non-aligned seek error %d", m_qiobuf.dwError); return FALSE; } else if ( ! ReadFile (m_pqio->hFile, m_qiobuf.lpv, m_qiobuf.cb, &m_qiobuf.cbDone, NULL) || (m_qiobuf.cbDone != m_qiobuf.cb)) { m_qiobuf.dwError = GetLastError (); DPF("avifile32 non-aligned read error %d", m_qiobuf.dwError); return FALSE; } m_State = Idle; } else return QioAdd (m_pqio, &m_qiobuf); #else ResetEvent(m_Overlapped.hEvent); m_State = Busy; //start from pos m_Overlapped.Offset = pos; m_Overlapped.OffsetHigh = 0; if (ReadFile(m_hFile, &m_pBuffer[offset], count, &nrRead, &m_Overlapped)) { m_State = Idle; DPF(("instant completion")); // if it completed already, then sort out the new position if (nrRead != count) { // rounding to sector size may have taken us past eof - // in this case we must still ask for the full sector, but // we will be told about the actual size if (m_Position + nrRead != m_FileLength) { DPF("ReadInto: actual wrong"); return FALSE; } } } else { // should be pending if (GetLastError() != ERROR_IO_PENDING) { DPF("read failed %d\n", GetLastError()); // no longer busy m_State = Idle; DPF("ReadInto failed %d", GetLastError()); return FALSE; } } #endif return TRUE; } #endif // USE_DIRECTIO