home *** CD-ROM | disk | FTP | other *** search
- #include "async.h"
-
- LibCall LONG
- SeekAsync( _REG( a0 ) AsyncFile *file, _REG( d0 ) LONG position, _REG( d1 ) SeekModes mode )
- {
- #ifdef ASIO_NOEXTERNALS
- struct DosLibrary *DOSBase;
- #endif
- LONG current, target;
- LONG minBuf, maxBuf;
- LONG bytesArrived;
- LONG diff;
- LONG filePos;
- LONG roundTarget;
- D_S( struct FileInfoBlock, fib );
-
- #ifdef ASIO_NOEXTERNALS
- DOSBase = file->af_DOSBase;
- #endif
-
- bytesArrived = AS_WaitPacket( file );
-
- if( bytesArrived < 0 )
- {
- return( -1 );
- }
-
- if( file->af_ReadMode )
- {
- /* figure out what the actual file position is */
- filePos = Seek( file->af_File, 0, OFFSET_CURRENT );
-
- if( filePos < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- /* figure out what the caller's file position is */
- current = filePos - ( file->af_BytesLeft + bytesArrived ) + file->af_SeekOffset;
- file->af_SeekOffset = 0;
-
- /* figure out the absolute offset within the file where we must seek to */
- if( mode == MODE_CURRENT )
- {
- target = current + position;
- }
- else if( mode == MODE_START )
- {
- target = position;
- }
- else /* if( mode == MODE_END ) */
- {
- if( !ExamineFH( file->af_File, fib ) )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- target = fib->fib_Size + position;
- }
-
- /* figure out what range of the file is currently in our buffers */
- minBuf = current - ( LONG ) ( file->af_Offset - file->af_Buffers[ file->af_CurrentBuf ] );
- maxBuf = current + file->af_BytesLeft + bytesArrived; /* WARNING: this is one too big */
-
- if( file->af_Received > 1 )
- {
- /* MH: Bugfix. Prevents some seeks from being slow (some buffers
- * would be read twice, since the code would think the data
- * wasn't in our buffers when it really was).
- */
- minBuf -= file->af_BufferSize;
- }
-
- diff = target - current;
-
- if( ( target < minBuf ) || ( target >= maxBuf ) )
- {
- /* the target seek location isn't currently in our buffers, so
- * move the actual file pointer to the desired location, and then
- * restart the async read thing...
- */
-
- /* this is to keep our file reading block-aligned on the device.
- * block-aligned reads are generally quite a bit faster, so it is
- * worth the trouble to keep things aligned
- */
- roundTarget = ( target / file->af_BlockSize ) * file->af_BlockSize;
-
- if( Seek( file->af_File, roundTarget - filePos, OFFSET_CURRENT ) < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- AS_SendPacket( file, file->af_Buffers[ 0 ] );
-
- file->af_SeekOffset = target - roundTarget;
- file->af_BytesLeft = 0;
- file->af_CurrentBuf = 0;
- /* MH: Note: af_Offset need not be set here. Since af_BytesLeft == 0,
- * ReadAsync will handle everything correctly.
- */
- /* MH: Both buffers are dropped, and we must keep track of that. */
- file->af_Received = 0;
- }
- else if( ( target < current ) || ( diff <= file->af_BytesLeft ) )
- {
- /* one of the two following things is true:
- *
- * 1. The target seek location is within the current read buffer,
- * but before the current location within the buffer. Move back
- * within the buffer and pretend we never got the pending packet,
- * just to make life easier, and faster, in the read routine.
- *
- * 2. The target seek location is ahead within the current
- * read buffer. Advance to that location. As above, pretend to
- * have never received the pending packet.
- */
-
- AS_RequeuePacket( file );
-
- file->af_BytesLeft -= diff;
- file->af_Offset += diff;
- }
- else
- {
- /* at this point, we know the target seek location is within
- * the buffer filled in by the packet that we just received
- * at the start of this function. Throw away all the bytes in the
- * current buffer, send a packet out to get the async thing going
- * again, readjust buffer pointers to the seek location, and return
- * with a grin on your face... :-)
- */
-
- AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] );
-
- diff -= file->af_BytesLeft;
- file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + diff;
- file->af_BytesLeft = bytesArrived - diff;
- /* MH: This "buffer switching" is important to do. It wasn't done!
- * This explains strange read "errors" one could encounter now
- * and then.
- */
- file->af_CurrentBuf = 1 - file->af_CurrentBuf;
- }
- }
- else
- {
- /* flush the buffers */
- if( file->af_BufferSize > file->af_BytesLeft )
- {
- if( Write(
- file->af_File,
- file->af_Buffers[ file->af_CurrentBuf ],
- file->af_BufferSize - file->af_BytesLeft ) < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
- }
-
- /* this will unfortunately generally result in non block-aligned file
- * access. We could be sneaky and try to resync our file pos at a
- * later time, but we won't bother. Seeking in write-only files is
- * relatively rare (except when writing IFF files with unknown chunk
- * sizes, where the chunk size has to be written after the chunk data)
- */
-
- /* MH: Ideas on how to improve the above (not tested, since I don't need
- * the SeekAsync for writing in any of my programs at the moment! ;):
- *
- * Add a new field to the AsyncFile struct. af_WriteOffset or something
- * like that (maybe af_SeekOffset can be used). Like in the read case, we
- * calculate a roundTarget, but we don't seek to that (but rather to the
- * "absolute" position), and save the difference in the struct. af_BytesLeft
- * and af_Offset are adjusted to point into the "middle" of the buffer,
- * where the write will occur. WriteAsync then needs some minor changes.
- * Instead of simply writing the buffer from the start, we add the offset
- * (saved above) to the buffer base, and write the partial buffer. The
- * offset is then cleared. Voila: The file still block-aligned, at the price
- * of some non-optimal buffer usage.
- *
- * Problem: As it is now, Arg3 in the packet is always set to the buffer size. With
- * the above fix, this would have to be updated for each SendPacket.
- */
-
- current = Seek( file->af_File, position, mode );
-
- if( current < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- file->af_BytesLeft = file->af_BufferSize;
- file->af_CurrentBuf = 0;
- file->af_Offset = file->af_Buffers[ 0 ];
- }
-
- return( current );
- }
-