mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-26 17:49:21 +00:00
51b5f00f5c
CiderPress and MDC now compile, and execute far enough to open their respective "about" boxes, but I doubt they'll do much more than that. * Switch from MBCS to UNICODE APIs Microsoft switched to UTF-16 (by way of UCS-2) a long time ago, and the support for MBCS seems to be getting phased out. So it's time to switch to wide strings. This is a bit awkward for CiderPress because it works with disk and file archives with 8-bit filenames, and I want NufxLib and DiskImgLib to continue to work on Linux (which has largely taken the UTF-8 approach to Unicode). The libraries will continue to work with 8-bit filenames, with CiderPress/MDC doing the conversion at the appropriate point. There were a couple of places where strings from a structure handed back by one of the libraries were used directly in the UI, or vice-versa, which is a problem because we have nowhere to store the result of the conversion. These currently have fixed place-holder "xyzzy" strings. All UI strings are now wide. Various format strings now use "%ls" and "%hs" to explicitly specify wide and narrow. This doesn't play well with gcc, so only the Windows-specific parts use those. * Various updates to vcxproj files The project-file conversion had some cruft that is now largely gone. The build now has a common output directory for the EXEs and libraries, avoiding the old post-build copy steps. * Added zlib 1.2.8 and nufxlib 2.2.2 source snapshots The old "prebuilts" directory is now gone. The libraries are now built as part of building the apps. I added a minimal set of files for zlib, and a full set for nufxlib. The Linux-specific nufxlib goodies are included for the benefit of the Linux utilities, which are currently broken (don't build). * Replace symbols used for include guards Symbols with a leading "__" are reserved.
885 lines
23 KiB
C++
885 lines
23 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* Generic file descriptor class.
|
|
*/
|
|
#include "StdAfx.h"
|
|
#include "DiskImgPriv.h"
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* GenericFD utility functions
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Copy "length" bytes from "pSrc" to "pDst". Both GenericFDs should be
|
|
* seeked to their initial positions.
|
|
*
|
|
* If "pCRC" is non-nil, this computes a CRC32 as it goes, using the zlib
|
|
* library function.
|
|
*/
|
|
/*static*/ DIError
|
|
GenericFD::CopyFile(GenericFD* pDst, GenericFD* pSrc, di_off_t length,
|
|
unsigned long* pCRC)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
const int kCopyBufSize = 32768;
|
|
unsigned char* copyBuf = nil;
|
|
int copySize;
|
|
|
|
WMSG1("+++ CopyFile: %ld bytes\n", (long) length);
|
|
|
|
if (pDst == nil || pSrc == nil || length < 0)
|
|
return kDIErrInvalidArg;
|
|
if (length == 0)
|
|
return kDIErrNone;
|
|
|
|
copyBuf = new unsigned char[kCopyBufSize];
|
|
if (copyBuf == nil)
|
|
return kDIErrMalloc;
|
|
|
|
if (pCRC != nil)
|
|
*pCRC = crc32(0L, Z_NULL, 0);
|
|
|
|
while (length != 0) {
|
|
copySize = kCopyBufSize;
|
|
if (copySize > length)
|
|
copySize = (int) length;
|
|
|
|
dierr = pSrc->Read(copyBuf, copySize);
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
if (pCRC != nil)
|
|
*pCRC = crc32(*pCRC, copyBuf, copySize);
|
|
|
|
dierr = pDst->Write(copyBuf, copySize);
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
length -= copySize;
|
|
}
|
|
|
|
bail:
|
|
delete[] copyBuf;
|
|
return dierr;
|
|
}
|
|
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* GFDFile
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* The stdio functions (fopen/fread/fwrite/fseek/ftell) are buffered and,
|
|
* therefore, faster for small operations. Unfortunately we need 64-bit
|
|
* file offsets, and it doesn't look like the Windows stdio stuff will
|
|
* support it cleanly (e.g. even the _FPOSOFF macro returns a "long").
|
|
*
|
|
* Recent versions of Linux have "fseeko", which is like fseek but takes
|
|
* an off_t, so we can continue to use the FILE* functions there. Under
|
|
* Windows "lseek" takes a long, so we have to use their specific 64-bit
|
|
* variant.
|
|
*/
|
|
#ifdef HAVE_FSEEKO
|
|
|
|
DIError
|
|
GFDFile::Open(const char* filename, bool readOnly)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
|
|
if (fFp != nil)
|
|
return kDIErrAlreadyOpen;
|
|
if (filename == nil)
|
|
return kDIErrInvalidArg;
|
|
if (filename[0] == '\0')
|
|
return kDIErrInvalidArg;
|
|
|
|
delete[] fPathName;
|
|
fPathName = new char[strlen(filename) +1];
|
|
strcpy(fPathName, filename);
|
|
|
|
fFp = fopen(filename, readOnly ? "rb" : "r+b");
|
|
if (fFp == nil) {
|
|
if (errno == EACCES)
|
|
dierr = kDIErrAccessDenied;
|
|
else
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG3(" GDFile Open failed opening '%s', ro=%d (err=%d)\n",
|
|
filename, readOnly, dierr);
|
|
return dierr;
|
|
}
|
|
fReadOnly = readOnly;
|
|
return dierr;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Read(void* buf, size_t length, size_t* pActual)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
size_t actual;
|
|
|
|
if (fFp == nil)
|
|
return kDIErrNotReady;
|
|
actual = ::fread(buf, 1, length, fFp);
|
|
if (actual == 0) {
|
|
if (feof(fFp))
|
|
return kDIErrEOF;
|
|
if (ferror(fFp)) {
|
|
dierr = ErrnoOrGeneric();
|
|
return dierr;
|
|
}
|
|
WMSG0("MYSTERY FREAD RESULT\n");
|
|
return kDIErrInternal;
|
|
}
|
|
|
|
if (pActual == nil) {
|
|
if (actual != length) {
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG3(" GDFile Read failed on %d bytes (actual=%d, err=%d)\n",
|
|
length, actual, dierr);
|
|
return dierr;
|
|
}
|
|
} else {
|
|
*pActual = actual;
|
|
}
|
|
return dierr;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Write(const void* buf, size_t length, size_t* pActual)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
|
|
if (fFp == nil)
|
|
return kDIErrNotReady;
|
|
if (fReadOnly)
|
|
return kDIErrAccessDenied;
|
|
assert(pActual == nil); // not handling this yet
|
|
if (::fwrite(buf, length, 1, fFp) != 1) {
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG2(" GDFile Write failed on %d bytes (err=%d)\n", length, dierr);
|
|
return dierr;
|
|
}
|
|
return dierr;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Seek(di_off_t offset, DIWhence whence)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
//static const long kOneGB = 1024*1024*1024;
|
|
//static const long kAlmostTwoGB = kOneGB + (kOneGB -1);
|
|
|
|
if (fFp == nil)
|
|
return kDIErrNotReady;
|
|
//assert(offset <= kAlmostTwoGB);
|
|
//if (::fseek(fFp, (long) offset, whence) != 0) {
|
|
if (::fseeko(fFp, offset, whence) != 0) {
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG1(" GDFile Seek failed (err=%d)\n", dierr);
|
|
return dierr;
|
|
}
|
|
return dierr;
|
|
}
|
|
|
|
di_off_t
|
|
GFDFile::Tell(void)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
di_off_t result;
|
|
|
|
if (fFp == nil)
|
|
return kDIErrNotReady;
|
|
//result = ::ftell(fFp);
|
|
result = ::ftello(fFp);
|
|
if (result == -1) {
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG1(" GDFile Tell failed (err=%d)\n", dierr);
|
|
return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Truncate(void)
|
|
{
|
|
#if defined(HAVE_FTRUNCATE)
|
|
int cc;
|
|
cc = ::ftruncate(fileno(fFp), (long) Tell());
|
|
if (cc != 0)
|
|
return kDIErrWriteFailed;
|
|
#elif defined(HAVE_CHSIZE)
|
|
assert(false); // not tested
|
|
int cc;
|
|
cc = ::chsize(fFd, (long) Tell());
|
|
if (cc != 0)
|
|
return kDIErrWriteFailed;
|
|
#else
|
|
# error "missing truncate"
|
|
#endif
|
|
return kDIErrNone;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Close(void)
|
|
{
|
|
if (fFp == nil)
|
|
return kDIErrNotReady;
|
|
|
|
WMSG1(" GFDFile closing '%s'\n", fPathName);
|
|
fclose(fFp);
|
|
fFp = nil;
|
|
return kDIErrNone;
|
|
}
|
|
|
|
#else /*HAVE_FSEEKO*/
|
|
|
|
DIError
|
|
GFDFile::Open(const char* filename, bool readOnly)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
|
|
if (fFd >= 0)
|
|
return kDIErrAlreadyOpen;
|
|
if (filename == nil)
|
|
return kDIErrInvalidArg;
|
|
if (filename[0] == '\0')
|
|
return kDIErrInvalidArg;
|
|
|
|
delete[] fPathName;
|
|
fPathName = new char[strlen(filename) +1];
|
|
strcpy(fPathName, filename);
|
|
|
|
fFd = open(filename, readOnly ? O_RDONLY|O_BINARY : O_RDWR|O_BINARY, 0);
|
|
if (fFd < 0) {
|
|
if (errno == EACCES)
|
|
dierr = kDIErrAccessDenied;
|
|
else
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG3(" GDFile Open failed opening '%s', ro=%d (err=%d)\n",
|
|
filename, readOnly, dierr);
|
|
return dierr;
|
|
}
|
|
fReadOnly = readOnly;
|
|
return dierr;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Read(void* buf, size_t length, size_t* pActual)
|
|
{
|
|
DIError dierr;
|
|
ssize_t actual;
|
|
|
|
if (fFd < 0)
|
|
return kDIErrNotReady;
|
|
actual = ::read(fFd, buf, length);
|
|
if (actual == 0)
|
|
return kDIErrEOF;
|
|
if (actual < 0) {
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG3(" GDFile Read failed on %d bytes (actual=%d, err=%d)\n",
|
|
length, actual, dierr);
|
|
return dierr;
|
|
}
|
|
|
|
if (pActual == nil) {
|
|
if (actual != (ssize_t) length) {
|
|
WMSG2(" GDFile Read partial (wanted=%d actual=%d)\n",
|
|
length, actual);
|
|
return kDIErrReadFailed;
|
|
}
|
|
} else {
|
|
*pActual = actual;
|
|
}
|
|
return kDIErrNone;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Write(const void* buf, size_t length, size_t* pActual)
|
|
{
|
|
DIError dierr;
|
|
ssize_t actual;
|
|
|
|
if (fFd < 0)
|
|
return kDIErrNotReady;
|
|
if (fReadOnly)
|
|
return kDIErrAccessDenied;
|
|
assert(pActual == nil); // not handling partial writes yet
|
|
actual = ::write(fFd, buf, length);
|
|
if (actual != (ssize_t) length) {
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG3(" GDFile Write failed on %d bytes (actual=%d err=%d)\n",
|
|
length, actual, dierr);
|
|
return dierr;
|
|
}
|
|
return kDIErrNone;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Seek(di_off_t offset, DIWhence whence)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
if (fFd < 0)
|
|
return kDIErrNotReady;
|
|
|
|
#ifdef WIN32
|
|
__int64 newPosn;
|
|
const __int64 kFailure = (__int64) -1;
|
|
newPosn = ::_lseeki64(fFd, (__int64) offset, whence);
|
|
#else
|
|
di_off_t newPosn;
|
|
const di_off_t kFailure = (di_off_t) -1;
|
|
newPosn = lseek(fFd, offset, whence);
|
|
#endif
|
|
|
|
if (newPosn == kFailure) {
|
|
assert((unsigned long) offset != 0xccccccccUL); // uninitialized data!
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG3(" GDFile Seek %ld-%lu failed (err=%d)\n",
|
|
(long) (offset >> 32), (unsigned long) offset, dierr);
|
|
}
|
|
return dierr;
|
|
}
|
|
|
|
di_off_t
|
|
GFDFile::Tell(void)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
di_off_t result;
|
|
|
|
if (fFd < 0)
|
|
return kDIErrNotReady;
|
|
|
|
#ifdef WIN32
|
|
result = ::_lseeki64(fFd, 0, SEEK_CUR);
|
|
#else
|
|
result = lseek(fFd, 0, SEEK_CUR);
|
|
#endif
|
|
|
|
if (result == -1) {
|
|
dierr = ErrnoOrGeneric();
|
|
WMSG1(" GDFile Tell failed (err=%d)\n", dierr);
|
|
return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Truncate(void)
|
|
{
|
|
#if defined(HAVE_FTRUNCATE)
|
|
int cc;
|
|
cc = ::ftruncate(fFd, (long) Tell());
|
|
if (cc != 0)
|
|
return kDIErrWriteFailed;
|
|
#elif defined(HAVE_CHSIZE)
|
|
int cc;
|
|
cc = ::chsize(fFd, (long) Tell());
|
|
if (cc != 0)
|
|
return kDIErrWriteFailed;
|
|
#else
|
|
# error "missing truncate"
|
|
#endif
|
|
return kDIErrNone;
|
|
}
|
|
|
|
DIError
|
|
GFDFile::Close(void)
|
|
{
|
|
if (fFd < 0)
|
|
return kDIErrNotReady;
|
|
|
|
WMSG1(" GFDFile closing '%s'\n", fPathName);
|
|
::close(fFd);
|
|
fFd = -1;
|
|
return kDIErrNone;
|
|
}
|
|
#endif /*HAVE_FSEEKO else*/
|
|
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* GFDBuffer
|
|
* ===========================================================================
|
|
*/
|
|
|
|
DIError
|
|
GFDBuffer::Open(void* buffer, di_off_t length, bool doDelete, bool doExpand,
|
|
bool readOnly)
|
|
{
|
|
if (fBuffer != nil)
|
|
return kDIErrAlreadyOpen;
|
|
if (length <= 0)
|
|
return kDIErrInvalidArg;
|
|
if (length > kMaxReasonableSize) {
|
|
// be reasonable
|
|
WMSG1(" GFDBuffer refusing to allocate buffer size(long)=%ld bytes\n",
|
|
(long) length);
|
|
return kDIErrInvalidArg;
|
|
}
|
|
|
|
/* if buffer is nil, allocate it ourselves */
|
|
if (buffer == nil) {
|
|
fBuffer = (void*) new char[(int) length];
|
|
if (fBuffer == nil)
|
|
return kDIErrMalloc;
|
|
} else
|
|
fBuffer = buffer;
|
|
|
|
fLength = (long) length;
|
|
fAllocLength = (long) length;
|
|
fDoDelete = doDelete;
|
|
fDoExpand = doExpand;
|
|
fReadOnly = readOnly;
|
|
|
|
fCurrentOffset = 0;
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
DIError
|
|
GFDBuffer::Read(void* buf, size_t length, size_t* pActual)
|
|
{
|
|
if (fBuffer == nil)
|
|
return kDIErrNotReady;
|
|
if (length == 0)
|
|
return kDIErrInvalidArg;
|
|
|
|
if (fCurrentOffset + (long)length > fLength) {
|
|
if (pActual == nil) {
|
|
WMSG3(" GFDBuffer underrrun off=%ld len=%d flen=%ld\n",
|
|
(long) fCurrentOffset, length, (long) fLength);
|
|
return kDIErrDataUnderrun;
|
|
} else {
|
|
/* set *pActual and adjust "length" */
|
|
assert(fLength >= fCurrentOffset);
|
|
length = (size_t) (fLength - fCurrentOffset);
|
|
*pActual = length;
|
|
|
|
if (length == 0)
|
|
return kDIErrEOF;
|
|
}
|
|
}
|
|
if (pActual != nil)
|
|
*pActual = length;
|
|
|
|
memcpy(buf, (const char*)fBuffer + fCurrentOffset, length);
|
|
fCurrentOffset += length;
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
DIError
|
|
GFDBuffer::Write(const void* buf, size_t length, size_t* pActual)
|
|
{
|
|
if (fBuffer == nil)
|
|
return kDIErrNotReady;
|
|
assert(pActual == nil); // not handling this yet
|
|
if (fCurrentOffset + (long)length > fLength) {
|
|
if (!fDoExpand) {
|
|
WMSG3(" GFDBuffer overrun off=%ld len=%d flen=%ld\n",
|
|
(long) fCurrentOffset, length, (long) fLength);
|
|
return kDIErrDataOverrun;
|
|
}
|
|
|
|
/*
|
|
* Expand the buffer as needed.
|
|
*
|
|
* We delete the old buffer unless "doDelete" is not set, in
|
|
* which case we just drop the pointer. Anything we allocate
|
|
* here can and will be deleted; "doDelete" only applies to the
|
|
* pointer initially passed in.
|
|
*/
|
|
if (fCurrentOffset + (long)length <= fAllocLength) {
|
|
/* fits inside allocated space, so just extend length */
|
|
fLength = (long) fCurrentOffset + (long)length;
|
|
} else {
|
|
/* does not fit, realloc buffer */
|
|
fAllocLength = (long) fCurrentOffset + (long)length + 8*1024;
|
|
WMSG1("Reallocating buffer (new size = %ld)\n", fAllocLength);
|
|
assert(fAllocLength < kMaxReasonableSize);
|
|
char* newBuf = new char[(int) fAllocLength];
|
|
if (newBuf == nil)
|
|
return kDIErrMalloc;
|
|
|
|
memcpy(newBuf, fBuffer, fLength);
|
|
|
|
if (fDoDelete)
|
|
delete[] (char*)fBuffer;
|
|
else
|
|
fDoDelete = true; // future deletions are okay
|
|
|
|
fBuffer = newBuf;
|
|
fLength = (long) fCurrentOffset + (long)length;
|
|
}
|
|
}
|
|
|
|
memcpy((char*)fBuffer + fCurrentOffset, buf, length);
|
|
fCurrentOffset += length;
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
DIError
|
|
GFDBuffer::Seek(di_off_t offset, DIWhence whence)
|
|
{
|
|
if (fBuffer == nil)
|
|
return kDIErrNotReady;
|
|
|
|
switch (whence) {
|
|
case kSeekSet:
|
|
if (offset < 0 || offset >= fLength)
|
|
return kDIErrInvalidArg;
|
|
fCurrentOffset = offset;
|
|
break;
|
|
case kSeekEnd:
|
|
if (offset > 0 || offset < -fLength)
|
|
return kDIErrInvalidArg;
|
|
fCurrentOffset = fLength + offset;
|
|
break;
|
|
case kSeekCur:
|
|
if (offset < -fCurrentOffset ||
|
|
offset >= (fLength - fCurrentOffset))
|
|
{
|
|
return kDIErrInvalidArg;
|
|
}
|
|
fCurrentOffset += offset;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return kDIErrInvalidArg;
|
|
}
|
|
|
|
assert(fCurrentOffset >= 0 && fCurrentOffset <= fLength);
|
|
return kDIErrNone;
|
|
}
|
|
|
|
di_off_t
|
|
GFDBuffer::Tell(void)
|
|
{
|
|
if (fBuffer == nil)
|
|
return (di_off_t) -1;
|
|
return fCurrentOffset;
|
|
}
|
|
|
|
DIError
|
|
GFDBuffer::Close(void)
|
|
{
|
|
if (fBuffer == nil)
|
|
return kDIErrNone;
|
|
|
|
if (fDoDelete) {
|
|
WMSG0(" GFDBuffer closing and deleting\n");
|
|
delete[] (char*) fBuffer;
|
|
} else {
|
|
WMSG0(" GFDBuffer closing\n");
|
|
}
|
|
fBuffer = nil;
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
/*
|
|
* ===========================================================================
|
|
* GFDWinVolume
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* This class is intended for use with logical volumes under Win32. Such
|
|
* devices must be accessed on 512-byte boundaries, which means no arbitrary
|
|
* seeks or reads. The device driver doesn't seem too adept at figuring
|
|
* out how large the device is, either, so we need to work that out for
|
|
* ourselves. (The standard approach appears to involve examining the
|
|
* partition map for the logical or physical volume, but we don't have a
|
|
* partition map to look at.)
|
|
*/
|
|
|
|
/*
|
|
* Prepare a logical volume device for reading or writing. "deviceName"
|
|
* must have the form "N:\" for a logical volume or "80:\" for a physical
|
|
* volume.
|
|
*/
|
|
DIError
|
|
GFDWinVolume::Open(const char* deviceName, bool readOnly)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
HANDLE handle = nil;
|
|
//unsigned long kTwoGBBlocks;
|
|
|
|
if (fVolAccess.Ready())
|
|
return kDIErrAlreadyOpen;
|
|
if (deviceName == nil)
|
|
return kDIErrInvalidArg;
|
|
if (deviceName[0] == '\0')
|
|
return kDIErrInvalidArg;
|
|
|
|
delete[] fPathName;
|
|
fPathName = new char[strlen(deviceName) +1];
|
|
strcpy(fPathName, deviceName);
|
|
|
|
// Create a UNICODE representation of the device name. We may want
|
|
// to make the argument UNICODE instead, but most of diskimg is 8-bit
|
|
// character oriented.
|
|
size_t srcLen = strlen(deviceName) + 1;
|
|
WCHAR* wdeviceName = new WCHAR[srcLen];
|
|
size_t convertedChars;
|
|
mbstowcs_s(&convertedChars, wdeviceName, srcLen, deviceName, _TRUNCATE);
|
|
dierr = fVolAccess.Open(wdeviceName, readOnly);
|
|
delete[] wdeviceName;
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
fBlockSize = fVolAccess.GetBlockSize(); // must be power of 2
|
|
assert(fBlockSize > 0);
|
|
//kTwoGBBlocks = kTwoGB / fBlockSize;
|
|
|
|
unsigned long totalBlocks;
|
|
totalBlocks = fVolAccess.GetTotalBlocks();
|
|
fVolumeEOF = (di_off_t)totalBlocks * fBlockSize;
|
|
|
|
assert(fVolumeEOF > 0);
|
|
|
|
fReadOnly = readOnly;
|
|
|
|
bail:
|
|
return dierr;
|
|
}
|
|
|
|
DIError
|
|
GFDWinVolume::Read(void* buf, size_t length, size_t* pActual)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
unsigned char* blkBuf = nil;
|
|
|
|
//WMSG2(" GFDWinVolume: reading %ld bytes from offset %ld\n", length,
|
|
// fCurrentOffset);
|
|
|
|
if (!fVolAccess.Ready())
|
|
return kDIErrNotReady;
|
|
|
|
// don't allow reading past the end of file
|
|
if (fCurrentOffset + (long) length > fVolumeEOF) {
|
|
if (pActual == nil)
|
|
return kDIErrDataUnderrun;
|
|
length = (size_t) (fVolumeEOF - fCurrentOffset);
|
|
}
|
|
if (pActual != nil)
|
|
*pActual = length;
|
|
if (length == 0)
|
|
return kDIErrNone;
|
|
|
|
long advanceLen = length;
|
|
|
|
blkBuf = new unsigned char[fBlockSize]; // get this off the heap??
|
|
long blockIndex = (long) (fCurrentOffset / fBlockSize);
|
|
int bufOffset = (int) (fCurrentOffset % fBlockSize); // req power of 2
|
|
assert(blockIndex >= 0);
|
|
|
|
/*
|
|
* When possible, do multi-block reads directly into "buf". The first
|
|
* and last block may require special handling.
|
|
*/
|
|
while (length) {
|
|
assert(length > 0);
|
|
|
|
if (bufOffset != 0 || length < (size_t) fBlockSize) {
|
|
assert(bufOffset >= 0 && bufOffset < fBlockSize);
|
|
|
|
size_t thisCount;
|
|
|
|
dierr = fVolAccess.ReadBlocks(blockIndex, 1, blkBuf);
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
thisCount = fBlockSize - bufOffset;
|
|
if (thisCount > length)
|
|
thisCount = length;
|
|
|
|
//WMSG2(" Copying %d bytes from block %d\n",
|
|
// thisCount, blockIndex);
|
|
|
|
memcpy(buf, blkBuf + bufOffset, thisCount);
|
|
length -= thisCount;
|
|
buf = (char*) buf + thisCount;
|
|
|
|
bufOffset = 0;
|
|
blockIndex++;
|
|
} else {
|
|
assert(bufOffset == 0);
|
|
|
|
long blockCount = length / fBlockSize;
|
|
assert(blockCount < 32768);
|
|
|
|
dierr = fVolAccess.ReadBlocks(blockIndex, (short) blockCount, buf);
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
length -= blockCount * fBlockSize;
|
|
buf = (char*) buf + blockCount * fBlockSize;
|
|
|
|
blockIndex += blockCount;
|
|
}
|
|
|
|
}
|
|
|
|
fCurrentOffset += advanceLen;
|
|
|
|
bail:
|
|
delete[] blkBuf;
|
|
return dierr;
|
|
}
|
|
|
|
DIError
|
|
GFDWinVolume::Write(const void* buf, size_t length, size_t* pActual)
|
|
{
|
|
DIError dierr = kDIErrNone;
|
|
unsigned char* blkBuf = nil;
|
|
|
|
//WMSG2(" GFDWinVolume: writing %ld bytes at offset %ld\n", length,
|
|
// fCurrentOffset);
|
|
|
|
if (!fVolAccess.Ready())
|
|
return kDIErrNotReady;
|
|
if (fReadOnly)
|
|
return kDIErrAccessDenied;
|
|
|
|
// don't allow writing past the end of the volume
|
|
if (fCurrentOffset + (long) length > fVolumeEOF) {
|
|
if (pActual == nil)
|
|
return kDIErrDataOverrun;
|
|
length = (size_t) (fVolumeEOF - fCurrentOffset);
|
|
}
|
|
if (pActual != nil)
|
|
*pActual = length;
|
|
if (length == 0)
|
|
return kDIErrNone;
|
|
|
|
long advanceLen = length;
|
|
|
|
blkBuf = new unsigned char[fBlockSize]; // get this out of the heap??
|
|
long blockIndex = (long) (fCurrentOffset / fBlockSize);
|
|
int bufOffset = (int) (fCurrentOffset % fBlockSize); // req power of 2
|
|
assert(blockIndex >= 0);
|
|
|
|
/*
|
|
* When possible, do multi-block writes directly from "buf". The first
|
|
* and last block may require special handling.
|
|
*/
|
|
while (length) {
|
|
assert(length > 0);
|
|
|
|
if (bufOffset != 0 || length < (size_t) fBlockSize) {
|
|
assert(bufOffset >= 0 && bufOffset < fBlockSize);
|
|
|
|
size_t thisCount;
|
|
|
|
dierr = fVolAccess.ReadBlocks(blockIndex, 1, blkBuf);
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
thisCount = fBlockSize - bufOffset;
|
|
if (thisCount > length)
|
|
thisCount = length;
|
|
|
|
//WMSG3(" Copying %d bytes into block %d (off=%d)\n",
|
|
// thisCount, blockIndex, bufOffset);
|
|
|
|
memcpy(blkBuf + bufOffset, buf, thisCount);
|
|
length -= thisCount;
|
|
buf = (char*) buf + thisCount;
|
|
|
|
dierr = fVolAccess.WriteBlocks(blockIndex, 1, blkBuf);
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
bufOffset = 0;
|
|
blockIndex++;
|
|
} else {
|
|
assert(bufOffset == 0);
|
|
|
|
long blockCount = length / fBlockSize;
|
|
assert(blockCount < 32768);
|
|
|
|
dierr = fVolAccess.WriteBlocks(blockIndex, (short) blockCount, buf);
|
|
if (dierr != kDIErrNone)
|
|
goto bail;
|
|
|
|
length -= blockCount * fBlockSize;
|
|
buf = (char*) buf + blockCount * fBlockSize;
|
|
|
|
blockIndex += blockCount;
|
|
}
|
|
|
|
}
|
|
|
|
fCurrentOffset += advanceLen;
|
|
|
|
bail:
|
|
delete[] blkBuf;
|
|
return dierr;
|
|
}
|
|
|
|
DIError
|
|
GFDWinVolume::Seek(di_off_t offset, DIWhence whence)
|
|
{
|
|
if (!fVolAccess.Ready())
|
|
return kDIErrNotReady;
|
|
|
|
switch (whence) {
|
|
case kSeekSet:
|
|
if (offset < 0 || offset >= fVolumeEOF)
|
|
return kDIErrInvalidArg;
|
|
fCurrentOffset = offset;
|
|
break;
|
|
case kSeekEnd:
|
|
if (offset > 0 || offset < -fVolumeEOF)
|
|
return kDIErrInvalidArg;
|
|
fCurrentOffset = fVolumeEOF + offset;
|
|
break;
|
|
case kSeekCur:
|
|
if (offset < -fCurrentOffset ||
|
|
offset >= (fVolumeEOF - fCurrentOffset))
|
|
{
|
|
return kDIErrInvalidArg;
|
|
}
|
|
fCurrentOffset += offset;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return kDIErrInvalidArg;
|
|
}
|
|
|
|
assert(fCurrentOffset >= 0 && fCurrentOffset <= fVolumeEOF);
|
|
return kDIErrNone;
|
|
}
|
|
|
|
di_off_t
|
|
GFDWinVolume::Tell(void)
|
|
{
|
|
if (!fVolAccess.Ready())
|
|
return (di_off_t) -1;
|
|
return fCurrentOffset;
|
|
}
|
|
|
|
DIError
|
|
GFDWinVolume::Close(void)
|
|
{
|
|
if (!fVolAccess.Ready())
|
|
return kDIErrNotReady;
|
|
|
|
WMSG0(" GFDWinVolume closing\n");
|
|
fVolAccess.Close();
|
|
return kDIErrNone;
|
|
}
|
|
#endif /*_WIN32*/
|