nestopia/source/win32/NstIoFile.cpp

444 lines
9.1 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia 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 2 of the License, or
// (at your option) any later version.
//
// Nestopia 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 Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#include "NstIoFile.hpp"
#include "NstCollectionVector.hpp"
#include <windows.h>
namespace Nestopia
{
namespace Io
{
NST_COMPILE_ASSERT
(
File::BEGIN == FILE_BEGIN &&
File::CURRENT == FILE_CURRENT &&
File::END == FILE_END
);
File::File()
: handle(NULL) {}
File::File(const GenericString fileName,const uint mode)
: handle(NULL)
{
Open( fileName, mode );
}
File::~File()
{
Close();
}
void File::Open(const GenericString n,const uint mode)
{
NST_ASSERT( (mode & (WRITE|READ)) && ((mode & WRITE) || !(mode & EMPTY)) && (mode & (RANDOM_ACCESS|SEQUENTIAL_ACCESS)) != (RANDOM_ACCESS|SEQUENTIAL_ACCESS) );
Close();
if (n.Empty())
throw ERR_NOT_FOUND;
handle = ::CreateFile
(
(name=n).Ptr(),
(
(mode & (READ|WRITE)) == (READ|WRITE) ? (GENERIC_READ|GENERIC_WRITE) :
(mode & (READ|WRITE)) == (READ) ? (GENERIC_READ) :
(mode & (READ|WRITE)) == (WRITE) ? (GENERIC_WRITE) :
0
),
(
(mode & (READ|WRITE)) == (WRITE) ? 0 : FILE_SHARE_READ
),
NULL,
(
(mode & (EMPTY|EXISTING)) == (EMPTY) ? CREATE_ALWAYS :
(mode & (EMPTY|EXISTING)) == (EMPTY|EXISTING) ? TRUNCATE_EXISTING :
(mode & (EMPTY|EXISTING)) == (EXISTING) ? OPEN_EXISTING :
(mode & (WRITE)) ? OPEN_ALWAYS :
OPEN_EXISTING
),
(
(
(mode & (WRITE|WRITE_THROUGH)) == (WRITE) ? (FILE_ATTRIBUTE_NORMAL) :
(mode & (WRITE|WRITE_THROUGH)) == (WRITE|WRITE_THROUGH) ? (FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH) : 0
)
|
(
(mode & (READ|RANDOM_ACCESS)) == (READ|RANDOM_ACCESS) ? FILE_FLAG_RANDOM_ACCESS :
(mode & (READ|SEQUENTIAL_ACCESS)) == (READ|SEQUENTIAL_ACCESS) ? FILE_FLAG_SEQUENTIAL_SCAN : 0
)
),
NULL
);
if (handle && handle != INVALID_HANDLE_VALUE)
{
if (!(mode & EMPTY))
{
try
{
Size();
}
catch (...)
{
Close();
throw;
}
}
}
else
{
handle = NULL;
name.Clear();
switch (::GetLastError())
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
throw ERR_NOT_FOUND;
case ERROR_ALREADY_EXISTS:
throw ERR_ALREADY_EXISTS;
case ERROR_ACCESS_DENIED:
if (mode & WRITE)
throw ERR_READ_ONLY;
default:
throw ERR_OPEN;
}
}
}
void File::Close()
{
name.Clear();
if (void* const tmp = handle)
{
handle = NULL;
::CloseHandle( tmp );
}
}
void File::Write(const void* const data,const uint size) const
{
NST_ASSERT( IsOpen() && bool(data) >= bool(size) );
if (size)
{
DWORD written = 0;
if (!::WriteFile( handle, data, size, &written, NULL ) || written != size)
throw ERR_WRITE;
}
}
uint File::WriteSome(const void* const data,const uint size) const
{
NST_ASSERT( IsOpen() && bool(data) >= bool(size) );
if (size)
{
DWORD written = 0;
if (!::WriteFile( handle, data, size, &written, NULL ))
throw ERR_WRITE;
return written;
}
return 0;
}
void File::Write8(uint data) const
{
const uchar buffer = data;
Write( &buffer, 1 );
}
void File::Write16(uint data) const
{
const uchar buffer[] = { data & 0xFF, data >> 8 };
Write( buffer, 2 );
}
void File::Write32(uint data) const
{
const uchar buffer[] = { data & 0xFF, data >> 8 & 0xFF, data >> 16 & 0xFF, data >> 24 };
Write( buffer, 4 );
}
void File::Read(void* const data,const uint size) const
{
NST_ASSERT( IsOpen() && bool(data) >= bool(size) );
if (size)
{
DWORD read = 0;
if (!::ReadFile( handle, data, size, &read, NULL ) || read != size)
throw ERR_READ;
}
}
uint File::ReadSome(void* const data,uint size) const
{
NST_ASSERT( IsOpen() && bool(data) >= bool(size) );
if (size)
{
DWORD read = 0;
if (!::ReadFile( handle, data, size, &read, NULL ))
throw ERR_READ;
return read;
}
return 0;
}
void File::Peek(void* const data,const uint size) const
{
const uint pos = Position();
Read( data, size );
Position() = pos;
}
void File::Peek(const uint from,void* const data,const uint size) const
{
const uint pos = Position();
Position() = from;
Read( data, size );
Position() = pos;
}
uint File::Peek8() const
{
uchar buffer;
Peek( &buffer, 1 );
return buffer;
}
uint File::Peek16() const
{
uchar buffer[2];
Peek( buffer, 2 );
return buffer[0] | uint(buffer[1]) << 8;
}
uint File::Peek32() const
{
uchar buffer[4];
Peek( buffer, 4 );
return buffer[0] | uint(buffer[1]) << 8 | uint(buffer[2]) << 16 | uint(buffer[3]) << 24;
}
uint File::Seek(const Offset offset,const int distance) const
{
NST_ASSERT( IsOpen() );
const DWORD pos = ::SetFilePointer( handle, distance, NULL, offset );
if (pos == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR)
throw ERR_SEEK;
return pos;
}
uint File::Read8() const
{
uchar buffer;
Read( &buffer, 1 );
return buffer;
}
uint File::Read16() const
{
uchar buffer[2];
Read( buffer, 2 );
return buffer[0] | buffer[1] << 8;
}
uint File::Read32() const
{
uchar buffer[4];
Read( buffer, 4 );
return buffer[0] | buffer[1] << 8 | uint(buffer[2]) << 16 | uint(buffer[3]) << 24;
}
uint File::Size() const
{
NST_ASSERT( IsOpen() );
DWORD words[2];
words[1] = 0;
words[0] = ::GetFileSize( handle, &words[1] );
if (words[0] == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR)
throw ERR_SEEK;
if (words[1] || words[0] > MAX_SIZE)
throw ERR_TOO_BIG;
return words[0];
}
void File::Truncate() const
{
NST_ASSERT( IsOpen() );
if (!::SetEndOfFile( handle ))
throw ERR_SEEK;
}
void File::Truncate(const uint pos) const
{
Position() = pos;
Truncate();
}
void File::Flush() const
{
NST_ASSERT( IsOpen() );
::FlushFileBuffers( handle );
}
void File::ReadText(String::Heap<char>& string) const
{
string.Resize( Size() - Position() );
if (string.Length())
Read( string.Ptr(), string.Length() );
}
void File::ReadText(String::Heap<wchar_t>& string) const
{
String::Heap<char> buffer;
ReadText( buffer );
ParseText( buffer.Ptr(), buffer.Length(), string );
}
void File::WriteText(cstring const string,const uint length,bool) const
{
Write( string, length );
}
void File::WriteText(wcstring string,uint length,bool forceUnicode) const
{
if (length)
{
if (forceUnicode || String::Generic<wchar_t>(string,length).Wide())
{
Collection::Buffer buffer( length * 2 + 2 );
char* NST_RESTRICT dst = buffer.Ptr();
*dst++ = UTF16_LE & 0xFF;
*dst++ = UTF16_LE >> 8;
do
{
*dst++ = *string & 0xFF;
*dst++ = *string++ >> 8;
}
while (--length);
Write( buffer.Ptr(), buffer.Size() );
}
else
{
const String::Heap<char> tmp( string, length );
Write( tmp.Ptr(), tmp.Length() );
}
}
}
void File::ParseText(cstring src,const uint length,String::Heap<char>& string)
{
string.Assign( src, length );
}
void File::ParseText(cstring src,const uint length,String::Heap<wchar_t>& string)
{
if (length)
{
const uint utf = (length >= 2) ? (src[0] | uint(src[1]) << 8) : 0;
if (utf == UTF16_LE || utf == UTF16_BE)
{
NST_VERIFY( length > 2 && length % 2 == 0 );
string.Resize( length / 2 - 1 );
if (string.Length())
{
wchar_t* NST_RESTRICT dst = string.Ptr();
const wchar_t* const end = dst + string.Length();
if (utf == UTF16_LE)
{
do
{
src += 2;
*dst++ = src[0] | src[1] << 8;
}
while (dst != end);
}
else
{
do
{
src += 2;
*dst++ = src[1] | src[0] << 8;
}
while (dst != end);
}
}
}
else
{
string.Assign( src, length );
}
}
else
{
string.Clear();
}
}
bool File::Delete(wcstring const path)
{
NST_ASSERT( path && *path );
return ::DeleteFile( path );
}
}
}