|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xFile.cpp
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include "../../Pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
#include "pnAceW9xInt.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace W9x {
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* FileOp
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
struct FileOp {
|
|
|
|
EAsyncNotifyFile code;
|
|
|
|
bool notify;
|
|
|
|
union {
|
|
|
|
AsyncNotifyFileFlush flush;
|
|
|
|
AsyncNotifyFileRead read;
|
|
|
|
AsyncNotifyFileSequence sequence;
|
|
|
|
AsyncNotifyFileWrite write;
|
|
|
|
} data;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* CFile
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
class CFile : public CThreadDispObject {
|
|
|
|
private:
|
|
|
|
CCritSect m_critSect;
|
|
|
|
HANDLE m_handle;
|
|
|
|
FAsyncNotifyFileProc m_notifyProc;
|
|
|
|
void * m_userState;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void Complete (void * op, CCritSect * critSect, AsyncId asyncId);
|
|
|
|
void Delete (void * op);
|
|
|
|
|
|
|
|
public:
|
|
|
|
CFile (
|
|
|
|
HANDLE handle,
|
|
|
|
FAsyncNotifyFileProc notifyProc,
|
|
|
|
void * userState
|
|
|
|
);
|
|
|
|
~CFile ();
|
|
|
|
|
|
|
|
void Read (
|
|
|
|
qword offset,
|
|
|
|
void * buffer,
|
|
|
|
unsigned bytes
|
|
|
|
);
|
|
|
|
|
|
|
|
void SetLastWriteTime (qword lastWriteTime);
|
|
|
|
|
|
|
|
void Truncate (qword size);
|
|
|
|
|
|
|
|
void Write (
|
|
|
|
qword offset,
|
|
|
|
const void * buffer,
|
|
|
|
unsigned bytes
|
|
|
|
);
|
|
|
|
|
|
|
|
bool Seek (qword offset, EFileSeekFrom from);
|
|
|
|
};
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CFile::CFile (
|
|
|
|
HANDLE handle,
|
|
|
|
FAsyncNotifyFileProc notifyProc,
|
|
|
|
void * userState
|
|
|
|
) :
|
|
|
|
m_handle(handle),
|
|
|
|
m_notifyProc(notifyProc),
|
|
|
|
m_userState(userState)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CFile::~CFile () {
|
|
|
|
CloseHandle(m_handle);
|
|
|
|
m_handle = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CFile::Complete (void * op, CCritSect * critSect, AsyncId asyncId) {
|
|
|
|
FileOp * fileOp = (FileOp *)op;
|
|
|
|
|
|
|
|
// Enter our local critical section and leave the global one
|
|
|
|
m_critSect.Enter();
|
|
|
|
critSect->Leave();
|
|
|
|
|
|
|
|
// Complete the operation
|
|
|
|
switch (fileOp->code) {
|
|
|
|
|
|
|
|
case kNotifyFileFlush: {
|
|
|
|
if (fileOp->data.flush.truncateSize != kAsyncFileDontTruncate)
|
|
|
|
Truncate(fileOp->data.flush.truncateSize);
|
|
|
|
BOOL result = FlushFileBuffers(m_handle);
|
|
|
|
fileOp->data.flush.error = result ? kFileSuccess : AsyncGetLastFileError();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kNotifyFileRead:
|
|
|
|
Read(
|
|
|
|
fileOp->data.read.offset,
|
|
|
|
fileOp->data.read.buffer,
|
|
|
|
fileOp->data.read.bytes
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kNotifyFileWrite:
|
|
|
|
Write(
|
|
|
|
fileOp->data.write.offset,
|
|
|
|
fileOp->data.write.buffer,
|
|
|
|
fileOp->data.write.bytes
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leave our local critical section
|
|
|
|
m_critSect.Leave();
|
|
|
|
|
|
|
|
// Dispatch a completion notification
|
|
|
|
if (fileOp->notify) {
|
|
|
|
fileOp->data.flush.asyncId = asyncId;
|
|
|
|
m_notifyProc(
|
|
|
|
(AsyncFile)this,
|
|
|
|
fileOp->code,
|
|
|
|
&fileOp->data.flush,
|
|
|
|
&m_userState
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CFile::Delete (void * op) {
|
|
|
|
FileOp * fileOp = (FileOp *)op;
|
|
|
|
DEL(fileOp);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CFile::Read (
|
|
|
|
qword offset,
|
|
|
|
void * buffer,
|
|
|
|
unsigned bytes
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Seek to the start of the read
|
|
|
|
Seek(offset, kFileSeekFromBegin);
|
|
|
|
|
|
|
|
// Perform the read
|
|
|
|
DWORD bytesRead;
|
|
|
|
BOOL result = ReadFile(
|
|
|
|
m_handle,
|
|
|
|
buffer,
|
|
|
|
bytes,
|
|
|
|
&bytesRead,
|
|
|
|
nil // overlapped
|
|
|
|
);
|
|
|
|
|
|
|
|
// Handle errors
|
|
|
|
if (bytesRead != bytes)
|
|
|
|
MemZero((byte *)buffer + bytesRead, bytes - bytesRead);
|
|
|
|
if ( (!result && (GetLastError() != ERROR_IO_PENDING)) ||
|
|
|
|
(bytesRead != bytes) )
|
|
|
|
LogMsg(kLogFatal, "failed: ReadFile");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CFile::Seek (qword offset, EFileSeekFrom from) {
|
|
|
|
COMPILER_ASSERT(kFileSeekFromBegin == FILE_BEGIN);
|
|
|
|
COMPILER_ASSERT(kFileSeekFromCurrent == FILE_CURRENT);
|
|
|
|
COMPILER_ASSERT(kFileSeekFromEnd == FILE_END);
|
|
|
|
|
|
|
|
LONG low = (LONG)(offset % 0x100000000ul);
|
|
|
|
LONG high = (LONG)(offset / 0x100000000ul);
|
|
|
|
dword result = SetFilePointer(m_handle, low, &high, from);
|
|
|
|
if ((result == (dword)-1) && (GetLastError() != NO_ERROR)) {
|
|
|
|
LogMsg(kLogFatal, "failed: SetFilePointer");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CFile::SetLastWriteTime (qword lastWriteTime) {
|
|
|
|
COMPILER_ASSERT(sizeof(lastWriteTime) == sizeof(FILETIME));
|
|
|
|
SetFileTime(m_handle, nil, nil, (const FILETIME *)&lastWriteTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CFile::Truncate (qword size) {
|
|
|
|
ASSERT(size != kAsyncFileDontTruncate);
|
|
|
|
|
|
|
|
if (Seek(size, kFileSeekFromBegin) && !SetEndOfFile(m_handle))
|
|
|
|
LogMsg(kLogFatal, "failed: SetEndOfFile");
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CFile::Write (
|
|
|
|
qword offset,
|
|
|
|
const void * buffer,
|
|
|
|
unsigned bytes
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Seek to the start of the write
|
|
|
|
Seek(offset, kFileSeekFromBegin);
|
|
|
|
|
|
|
|
// Perform the write
|
|
|
|
DWORD bytesWritten;
|
|
|
|
BOOL result = WriteFile(
|
|
|
|
m_handle,
|
|
|
|
buffer,
|
|
|
|
bytes,
|
|
|
|
&bytesWritten,
|
|
|
|
nil // overlapped
|
|
|
|
);
|
|
|
|
|
|
|
|
// Handle errors
|
|
|
|
if ( (!result && (GetLastError() != ERROR_IO_PENDING)) ||
|
|
|
|
(bytesWritten != bytes) ) {
|
|
|
|
LogMsg(kLogFatal, "failed: WriteFile");
|
|
|
|
if (!result && (GetLastError() == ERROR_DISK_FULL)) {
|
|
|
|
MessageBox(nil, "Disk full!", "Error", MB_ICONSTOP | MB_SYSTEMMODAL);
|
|
|
|
// DebugDisableLeakChecking();
|
|
|
|
ExitProcess(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* Exported functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void W9xFileClose (
|
|
|
|
AsyncFile file,
|
|
|
|
qword truncateSize
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Dereference the object
|
|
|
|
CFile * object = (CFile *)file;
|
|
|
|
|
|
|
|
// If requested, truncate the file
|
|
|
|
if (truncateSize != kAsyncFileDontTruncate)
|
|
|
|
object->Truncate(truncateSize);
|
|
|
|
|
|
|
|
// Close the file object
|
|
|
|
object->Close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
AsyncId W9xFileCreateSequence (
|
|
|
|
AsyncFile file,
|
|
|
|
bool notify,
|
|
|
|
void * param
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Dereference the object
|
|
|
|
CFile * object = (CFile *)file;
|
|
|
|
|
|
|
|
// Queue an operation
|
|
|
|
FileOp * op = NEW(FileOp);
|
|
|
|
op->code = kNotifyFileSequence;
|
|
|
|
op->notify = notify;
|
|
|
|
op->data.flush.param = param;
|
|
|
|
return object->Queue(op);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
AsyncId W9xFileFlushBuffers (
|
|
|
|
AsyncFile file,
|
|
|
|
qword truncateSize,
|
|
|
|
bool notify,
|
|
|
|
void * param
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Dereference the object
|
|
|
|
CFile * object = (CFile *)file;
|
|
|
|
|
|
|
|
// Queue an operation
|
|
|
|
FileOp * op = NEW(FileOp);
|
|
|
|
op->code = kNotifyFileFlush;
|
|
|
|
op->notify = notify;
|
|
|
|
op->data.flush.param = param;
|
|
|
|
op->data.flush.truncateSize = truncateSize;
|
|
|
|
// op->data.flush.error filled in upon completion
|
|
|
|
return object->Queue(op);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
AsyncFile W9xFileOpen (
|
|
|
|
const wchar fullPath[],
|
|
|
|
FAsyncNotifyFileProc notifyProc,
|
|
|
|
EFileError * error,
|
|
|
|
unsigned desiredAccess,
|
|
|
|
unsigned openMode,
|
|
|
|
unsigned shareModeFlags,
|
|
|
|
void * userState,
|
|
|
|
qword * fileSize,
|
|
|
|
qword * fileLastWriteTime
|
|
|
|
) {
|
|
|
|
HANDLE fileHandle = CreateFileW(
|
|
|
|
fullPath,
|
|
|
|
desiredAccess,
|
|
|
|
shareModeFlags,
|
|
|
|
nil, // plSecurityAttributes
|
|
|
|
openMode,
|
|
|
|
0, // attributeFlags
|
|
|
|
nil // hTemplateFile
|
|
|
|
);
|
|
|
|
*error = AsyncGetLastFileError();
|
|
|
|
|
|
|
|
if (INVALID_HANDLE_VALUE == fileHandle)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
// don't allow users to open devices like "LPT1", etc.
|
|
|
|
if (GetFileType(fileHandle) != FILE_TYPE_DISK) {
|
|
|
|
LogMsg(kLogFatal, "failed: !FILE_TYPE_DISK");
|
|
|
|
*error = kFileErrorFileNotFound;
|
|
|
|
CloseHandle(fileHandle);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the file size
|
|
|
|
DWORD sizeHi, sizeLo = GetFileSize(fileHandle, &sizeHi);
|
|
|
|
if ((sizeLo == (DWORD) -1) && (NO_ERROR != GetLastError())) {
|
|
|
|
*error = AsyncGetLastFileError();
|
|
|
|
LogMsg(kLogFatal, "failed: GetFileSize");
|
|
|
|
CloseHandle(fileHandle);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
const qword size = ((qword) sizeHi << (qword) 32) | (qword) sizeLo;
|
|
|
|
|
|
|
|
qword lastWriteTime;
|
|
|
|
ASSERT(sizeof(lastWriteTime) >= sizeof(FILETIME));
|
|
|
|
GetFileTime(fileHandle, nil, nil, (FILETIME *) &lastWriteTime);
|
|
|
|
|
|
|
|
// Create a file object
|
|
|
|
CFile * object = NEW(CFile)(
|
|
|
|
fileHandle,
|
|
|
|
notifyProc,
|
|
|
|
userState
|
|
|
|
);
|
|
|
|
|
|
|
|
// return out parameters
|
|
|
|
if (fileSize)
|
|
|
|
*fileSize = size;
|
|
|
|
if (fileLastWriteTime)
|
|
|
|
*fileLastWriteTime = lastWriteTime;
|
|
|
|
return (AsyncFile)object;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
AsyncId W9xFileRead (
|
|
|
|
AsyncFile file,
|
|
|
|
qword offset,
|
|
|
|
void * buffer,
|
|
|
|
unsigned bytes,
|
|
|
|
unsigned flags,
|
|
|
|
void * param
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Dereference the object
|
|
|
|
CFile * object = (CFile *)file;
|
|
|
|
|
|
|
|
// Perform synchronous operations immediately
|
|
|
|
if (flags & kAsyncFileRwSync) {
|
|
|
|
object->Read(offset, buffer, bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Queue asynchronous operations
|
|
|
|
else {
|
|
|
|
FileOp * op = NEW(FileOp);
|
|
|
|
op->code = kNotifyFileRead;
|
|
|
|
op->notify = (flags & kAsyncFileRwNotify) != 0;
|
|
|
|
op->data.read.param = param;
|
|
|
|
op->data.read.offset = offset;
|
|
|
|
op->data.read.buffer = (byte *)buffer;
|
|
|
|
op->data.read.bytes = bytes;
|
|
|
|
return object->Queue(op);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void W9xFileSetLastWriteTime (
|
|
|
|
AsyncFile file,
|
|
|
|
qword lastWriteTime
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Dereference the object
|
|
|
|
CFile * object = (CFile *)file;
|
|
|
|
|
|
|
|
// Set the file time
|
|
|
|
object->SetLastWriteTime(lastWriteTime);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
qword W9xFileGetLastWriteTime (
|
|
|
|
const wchar fileName[]
|
|
|
|
) {
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA info;
|
|
|
|
bool f = GetFileAttributesExW(fileName, GetFileExInfoStandard, &info);
|
|
|
|
return f ? *((qword *) &info.ftLastWriteTime) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
AsyncId W9xFileWrite (
|
|
|
|
AsyncFile file,
|
|
|
|
qword offset,
|
|
|
|
const void * buffer,
|
|
|
|
unsigned bytes,
|
|
|
|
unsigned flags,
|
|
|
|
void * param
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Dereference the object
|
|
|
|
CFile * object = (CFile *)file;
|
|
|
|
|
|
|
|
// Perform synchronous operations immediately
|
|
|
|
if (flags & kAsyncFileRwSync) {
|
|
|
|
object->Write(offset, buffer, bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Queue asynchronous operations
|
|
|
|
else {
|
|
|
|
FileOp * op = NEW(FileOp);
|
|
|
|
op->code = kNotifyFileWrite;
|
|
|
|
op->notify = (flags & kAsyncFileRwNotify) != 0;
|
|
|
|
op->data.write.param = param;
|
|
|
|
op->data.write.offset = offset;
|
|
|
|
op->data.write.buffer = (byte *)buffer;
|
|
|
|
op->data.write.bytes = bytes;
|
|
|
|
return object->Queue(op);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool W9xFileSeek (
|
|
|
|
AsyncFile file,
|
|
|
|
qword distance,
|
|
|
|
EFileSeekFrom from
|
|
|
|
) {
|
|
|
|
CFile * object = (CFile *)file;
|
|
|
|
return object->Seek(distance, from);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace W9x
|