e7eabd46b9
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1545 8092fc87-c524-0410-9efc-e669fe64eaf9
198 lines
5.3 KiB
C++
198 lines
5.3 KiB
C++
/*--gio.c-----------------------------------------------------------------
|
|
* Buffered i/o.
|
|
*/
|
|
|
|
#include "main.h"
|
|
#include <io.h>
|
|
#include <stdio.h>
|
|
|
|
#define local static
|
|
|
|
/* --- Segment of optional buffer for speeding-up i/o. */
|
|
local UWORD ioBufferSeg = NULL;
|
|
local UWORD ioBufSize = 0, ioBufNext, ioBufLimit;
|
|
local BOOL ioWriting;
|
|
|
|
/* ---------- OpenIOBuffer ---------- */
|
|
void OpenIOBuffer(BOOL forWriting)
|
|
{
|
|
register WORD bufSize;
|
|
auto WORD tempSeg;
|
|
|
|
ioWriting = forWriting;
|
|
|
|
/* --- Allocate i/o buffer. */
|
|
if (ioBufferSeg == NULL) {
|
|
for (bufSize = 16 * 1024; bufSize >= 512; bufSize >>= 1) {
|
|
ioBufferSeg = DAlloc(bufSize);
|
|
if (ioBufferSeg) {
|
|
/* --- Leave at least 4 KB free for other uses. */
|
|
tempSeg = DAlloc(4096);
|
|
if (tempSeg) {
|
|
tempSeg = SFree(tempSeg);
|
|
break; /* Success; leave loop. */
|
|
} else
|
|
ioBufferSeg = SFree(ioBufferSeg);
|
|
}
|
|
}
|
|
if (ioBufferSeg != NULL)
|
|
ioBufSize = bufSize;
|
|
}
|
|
ioBufNext = ioBufLimit = 0;
|
|
/* Reading: When next==limit, buffer is empty.
|
|
* Writing: When next== 0, buffer is empty.
|
|
*/
|
|
}
|
|
|
|
/* ---------- CloseIOBuffer -----------------
|
|
* Writing: flush any remaining data to file.
|
|
* Free the i/o buffer. Do this whenever finish i/o, so RAM not tied up.
|
|
*/
|
|
void CloseIOBuffer(WORD file)
|
|
{
|
|
if (ioWriting)
|
|
GFlush(file); /* TBD: ignoring error. */
|
|
ioBufferSeg = SFree(ioBufferSeg);
|
|
ioBufSize = 0;
|
|
}
|
|
|
|
/* ---------- GRead ---------------------------------------------
|
|
* Like DOS "read" function, RETURNS # bytes read, or 0 at EOF,
|
|
* or -1 at error.
|
|
*/
|
|
WORD GRead(WORD file, UBYTE *buffer, UWORD nBytes)
|
|
{
|
|
UWORD nRemaining, nAvail, nThisTime;
|
|
|
|
if (ioBufferSeg) {
|
|
nAvail = ioBufLimit - ioBufNext;
|
|
if (!nAvail && nBytes >= ioBufSize)
|
|
goto readDirectly; /* More efficient to read buffer directly. */
|
|
|
|
/* --- Remaining # bytes that need reading. */
|
|
for (nRemaining = nBytes; nRemaining; nRemaining -= nThisTime) {
|
|
if (!nAvail) {
|
|
ioBufLimit = nAvail =
|
|
readdos(file, ioBufferSeg, 0, ioBufSize);
|
|
if (nAvail == 0xffff || nAvail == 0) {
|
|
ioBufLimit = 0;
|
|
return (nAvail);
|
|
}
|
|
ioBufNext = 0;
|
|
}
|
|
nThisTime = MIN(nRemaining, nAvail);
|
|
far_movmem(ioBufferSeg, ioBufNext,
|
|
dataseg(), (UWORD)buffer,
|
|
(UWORD)nThisTime);
|
|
buffer += nThisTime;
|
|
ioBufNext += nThisTime;
|
|
nAvail -= nThisTime;
|
|
}
|
|
return (nBytes);
|
|
} else /* No ioBuf */
|
|
readDirectly:
|
|
return (read(file, buffer, nBytes));
|
|
}
|
|
|
|
/* --------------- GReadOne --------------------------------
|
|
* Like "GRead", but read a single byte, with less overhead.
|
|
*/
|
|
WORD GReadOne(WORD file, UBYTE *buffer)
|
|
{
|
|
if (!ioBufferSeg || ioBufLimit == ioBufNext)
|
|
return GRead(file, buffer, 1);
|
|
|
|
*buffer = *BYTE_FAR_PTR(ioBufferSeg, ioBufNext);
|
|
ioBufNext++;
|
|
return (1);
|
|
}
|
|
|
|
/* ---------- GFlush ------------------------------
|
|
* Write out any bytes remaining in the buffer.
|
|
* Return what writedos does, or 0 if nothing to do.
|
|
*/
|
|
WORD GFlush(WORD file)
|
|
{
|
|
auto WORD result = 0;
|
|
|
|
if (ioBufferSeg && ioBufNext && ioWriting) {
|
|
result = writedos(file, ioBufferSeg, 0, ioBufNext);
|
|
if (result < ioBufNext)
|
|
result = -1; /* Disk must be full, turn it into an error. */
|
|
ioBufNext = 0; /* Writing: buffer is empty. */
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/* ---------- GSeek ----------------------------------------------------
|
|
* When reading, ASSUMES seeking forwards.
|
|
* When writing, flush buffer and use lseek.
|
|
* RETURNS 0 if succeeded in seeking w/i buffer. DIFFERENT THAN LSEEK.
|
|
*/
|
|
LONG GSeek(WORD file, LONG count, WORD seekType)
|
|
{
|
|
UWORD nAvail;
|
|
|
|
if (ioBufferSeg) {
|
|
if (ioWriting && ioBufNext) {
|
|
GFlush(file); /* TBD: Ignoring error code. */
|
|
goto seekDirectly;
|
|
}
|
|
nAvail = ioBufLimit - ioBufNext;
|
|
|
|
if (seekType != SEEK_CUR) { /* Check for absolute positioning */
|
|
goto emptyAndSeekDirectly;
|
|
}
|
|
if (count > nAvail) {
|
|
count -= nAvail; /* Adjust seek for bytes already read ahead. */
|
|
emptyAndSeekDirectly:
|
|
ioBufNext = ioBufLimit; /* Reading: buffer is empty. */
|
|
goto seekDirectly;
|
|
}
|
|
ioBufNext += (UWORD)count; /* Seek within the buffer. */
|
|
return (0); /* DIFFERENT THAN LSEEK */
|
|
} else
|
|
seekDirectly:
|
|
return lseek(file, count, seekType);
|
|
}
|
|
|
|
/* ---------- GWrite ---------------------------------------------
|
|
* Like DOS "write" function, RETURNS # bytes written, or -1 at error.
|
|
* DIFFERENCE: if can't write them all, returns -1 instead of # written,
|
|
* UNLESS "writeDirectly", which just uses write().
|
|
*/
|
|
WORD GWrite(WORD file, UBYTE * buffer, UWORD nBytes)
|
|
{
|
|
UWORD nRemaining, nUnused, nThisTime, nWritten;
|
|
|
|
if (ioBufferSeg) {
|
|
if (!ioBufNext && nBytes >= ioBufSize)
|
|
goto writeDirectly; /* More efficient to write buffer directly. */
|
|
|
|
nUnused = ioBufSize - ioBufNext; /* # bytes unused in buffer. */
|
|
|
|
/* Remaining # bytes that need writing. */
|
|
for (nRemaining = nBytes; nRemaining; nRemaining -= nThisTime) {
|
|
if (!nUnused) {
|
|
nWritten = writedos(file, ioBufferSeg, 0, ioBufNext);
|
|
if (nWritten != ioBufNext) {
|
|
ioBufNext = 0; /* Flush the data -- it is lost. */
|
|
return (-1); /* ERROR */
|
|
}
|
|
ioBufNext = 0;
|
|
nUnused = ioBufSize;
|
|
}
|
|
nThisTime = MIN(nRemaining, nUnused);
|
|
far_movmem(dataseg(),
|
|
(UWORD)buffer, ioBufferSeg,
|
|
ioBufNext, (UWORD)nThisTime);
|
|
buffer += nThisTime;
|
|
ioBufNext += nThisTime;
|
|
nUnused -= nThisTime;
|
|
}
|
|
return (nBytes); /* OKAY */
|
|
} else /* No ioBuf */
|
|
writeDirectly:
|
|
return (write(file, buffer, nBytes));
|
|
}
|