5 GetFileSize( AsyncFile
*file
, LONG
*size
)
7 #ifdef ASIO_NOEXTERNALS
8 struct DosLibrary
*DOSBase
= file
->af_DOSBase
;
10 D_S( struct FileInfoBlock
, fib
);
12 if( !ExamineFH( file
->af_File
, fib
) )
14 AS_RecordSyncFailure( file
);
18 *size
= fib
->fib_Size
;
24 SeekAsync( _REG( a0
) AsyncFile
*file
, _REG( d0
) LONG position
, _REG( d1
) SeekModes mode
)
26 #ifdef ASIO_NOEXTERNALS
27 struct DosLibrary
*DOSBase
= file
->af_DOSBase
;
29 LONG current
, target
, roundTarget
, filePos
;
30 LONG minBuf
, maxBuf
, bytesArrived
, diff
;
33 bytesArrived
= AS_WaitPacket( file
);
35 /* MH: No packets can be pending here! */
37 if( bytesArrived
< 0 )
39 /* MH: Experimental: Try to allow "resume" of seeks past EOF. */
41 if( file
->af_SeekPastEOF
)
43 /* MH: Restore saved values, to make resume possible */
44 bytesArrived
= file
->af_LastRes1
;
45 file
->af_BytesLeft
= file
->af_LastBytesLeft
;
53 if( file
->af_ReadMode
)
55 /* figure out what the actual file position is */
56 filePos
= Seek( file
->af_File
, 0, OFFSET_CURRENT
);
60 AS_RecordSyncFailure( file
);
64 /* figure out what the caller's file position is */
65 current
= filePos
- ( file
->af_BytesLeft
+ bytesArrived
) + file
->af_SeekOffset
;
67 /* MH: We can't clear af_SeekOffset here. If another seek is done
68 * directly after this one, it would mean that we will both return
69 * the wrong position, and start reading from the wrong position.
71 /* file->af_SeekOffset = 0; */
73 /* figure out the absolute offset within the file where we must seek to */
74 if( mode
== MODE_CURRENT
)
76 target
= current
+ position
;
78 else if( mode
== MODE_START
)
82 else /* if( mode == MODE_END ) */
84 if( !GetFileSize( file
, &fileSize
) )
89 target
= fileSize
+ position
;
92 /* MH: Here we must be able to handle two different situations:
93 * 1) A seek directly after having dropped both buffers, and started
94 * refilling (typical case: File open).
95 * 2) Other seeks (typical case: A seek after some initial reading).
97 * We need to subtract with "af_Buffers[ 1 - file->af_CurrentBuf ]",
98 * as af_CurrentBuf refers to the *arrived* buffer, not the one we're
99 * currently reading from (and af_Offset points into the buffer we're
102 * In case 1, there will be only one packet received. af_CurrentBuf
103 * will be zero, and refers to the newly arrived buffer (as it
104 * should). For proper behaviour in the minBuf calculation, we have
105 * set af_Offset to point to af_Buffers[ 1 ], when starting reading
106 * to empty buffers. That way wee need no special case code here.
107 * ReadAsync() can handle this, as af_BytesLeft == 0 in that case.
110 /* figure out what range of the file is currently in our buffers */
111 minBuf
= current
- ( LONG
) ( file
->af_Offset
- file
->af_Buffers
[ 1 - file
->af_CurrentBuf
] );
112 maxBuf
= current
+ file
->af_BytesLeft
+ bytesArrived
; /* WARNING: this is one too big */
114 diff
= target
- current
;
117 Printf( "Target: %ld, minBuf: %ld, maxBuf: %ld, current: %ld, diff: %ld, bytesLeft: %ld\n",
118 target
, minBuf
, maxBuf
, current
, diff
, file
->af_BytesLeft
);
121 if( ( target
< minBuf
) || ( target
>= maxBuf
) )
123 /* the target seek location isn't currently in our buffers, so
124 * move the actual file pointer to the desired location, and then
125 * restart the async read thing...
128 if( target
>= maxBuf
)
130 /* MH: There's a fair chance that we really tried to seek
131 * past EOF. In order to tell for sure, we need to compare
132 * the seek target with the file size. The roundTarget may
133 * be before real EOF, so the "real" Seek() call might
134 * not notice any problems.
136 if( !GetFileSize( file
, &fileSize
) )
141 if( target
> fileSize
)
143 /* MH: Experimental: Try to allow "resume" of
146 file
->af_SeekPastEOF
= TRUE
;
148 SetIoErr( ERROR_SEEK_ERROR
);
149 AS_RecordSyncFailure( file
);
154 /* this is to keep our file reading block-aligned on the device.
155 * block-aligned reads are generally quite a bit faster, so it is
156 * worth the trouble to keep things aligned
158 roundTarget
= ( target
/ file
->af_BlockSize
) * file
->af_BlockSize
;
160 if( Seek( file
->af_File
, roundTarget
- filePos
, OFFSET_CURRENT
) < 0 )
162 AS_RecordSyncFailure( file
);
166 AS_SendPacket( file
, file
->af_Buffers
[ 0 ] );
168 file
->af_SeekOffset
= target
- roundTarget
;
169 file
->af_BytesLeft
= 0;
170 file
->af_CurrentBuf
= 0;
172 /* MH: We set af_Offset to the buffer not being filled, to be able to
173 * handle a new seek directly after this one (see above; minBuf
174 * calculation). If we start reading after this seek, ReadAsync()
175 * will handle everything correctly, as af_BytesLeft == 0.
177 file
->af_Offset
= file
->af_Buffers
[ 1 ];
179 else if( ( target
< current
) || ( diff
<= file
->af_BytesLeft
) )
181 /* one of the two following things is true:
183 * 1. The target seek location is within the current read buffer,
184 * but before the current location within the buffer. Move back
185 * within the buffer and pretend we never got the pending packet,
186 * just to make life easier, and faster, in the read routine.
188 * 2. The target seek location is ahead within the current
189 * read buffer. Advance to that location. As above, pretend to
190 * have never received the pending packet.
193 AS_RequeuePacket( file
);
195 file
->af_BytesLeft
-= diff
;
196 file
->af_Offset
+= diff
;
198 /* MH: We don't need to clear the seek offset here, since
199 * if we get here, we must have read some data from the current
200 * buffer, and af_SeekOffset will be zero then (done by
204 /* MH: If we're recovering from seek past EOF, restore some
207 if( file
->af_SeekPastEOF
)
209 file
->af_Packet
.sp_Pkt
.dp_Res1
= file
->af_LastRes1
;
214 /* at this point, we know the target seek location is within
215 * the buffer filled in by the packet that we just received
216 * at the start of this function. Throw away all the bytes in the
217 * current buffer, send a packet out to get the async thing going
218 * again, readjust buffer pointers to the seek location, and return
219 * with a grin on your face... :-)
222 /* MH: Don't refill the buffer we just got, but the other one! */
223 AS_SendPacket( file
, file
->af_Buffers
[ 1 - file
->af_CurrentBuf
] );
225 /* MH: Account for bytes left in buffer we drop *and* the af_SeekOffset.
227 diff
-= file
->af_BytesLeft
- file
->af_SeekOffset
;
229 /* MH: Set the offset into the current (newly arrived) buffer */
230 file
->af_Offset
= file
->af_Buffers
[ file
->af_CurrentBuf
] + diff
;
231 file
->af_BytesLeft
= bytesArrived
- diff
;
233 /* MH: We need to clear the seek offset here, since we can't do it above.
235 file
->af_SeekOffset
= 0;
237 /* MH: This "buffer switching" is important to do. It wasn't done!
238 * This explains the errors one could encounter now and then.
239 * The AS_SendPacket() call above is not the cause, and *is* correct.
241 file
->af_CurrentBuf
= 1 - file
->af_CurrentBuf
;
246 /* flush the buffers */
247 if( file
->af_BufferSize
> file
->af_BytesLeft
)
251 file
->af_Buffers
[ file
->af_CurrentBuf
],
252 file
->af_BufferSize
- file
->af_BytesLeft
) < 0 )
254 AS_RecordSyncFailure( file
);
259 /* this will unfortunately generally result in non block-aligned file
260 * access. We could be sneaky and try to resync our file pos at a
261 * later time, but we won't bother. Seeking in write-only files is
262 * relatively rare (except when writing IFF files with unknown chunk
263 * sizes, where the chunk size has to be written after the chunk data)
266 /* MH: Ideas on how to improve the above (not tested, since I don't need
267 * the SeekAsync for writing in any of my programs at the moment! ;):
269 * Add a new field to the AsyncFile struct. af_WriteOffset or something like
270 * that (af_SeekOffset can probably be used). Like in the read case, we
271 * calculate a roundTarget, but we don't seek to that (but rather to the
272 * "absolute" position), and save the difference in the struct. af_BytesLeft
273 * and af_Offset are adjusted to point into the "middle" of the buffer,
274 * where the write will occur. Writes then needs some minor changes:
275 * Instead of simply writing the buffer from the start, we add the offset
276 * (saved above) to the buffer base, and write the partial buffer. The
277 * offset is then cleared. Voila: The file is still block-aligned, at the
278 * price of some non-optimal buffer usage.
280 * Problem: As it is now, Arg3 in the packet is always set to the buffer size.
281 * With the above fix, this would have to be updated for each SendPacket (i.e.
282 * a new argument would be needed).
285 current
= Seek( file
->af_File
, position
, mode
);
289 AS_RecordSyncFailure( file
);
293 file
->af_BytesLeft
= file
->af_BufferSize
;
294 file
->af_CurrentBuf
= 0;
295 file
->af_Offset
= file
->af_Buffers
[ 0 ];
298 if( file
->af_SeekPastEOF
)
300 /* MH: Clear up any error flags, and restore last Res1. */
301 file
->af_SeekPastEOF
= FALSE
;