mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
444 lines
9.1 KiB
C++
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 );
|
|
}
|
|
}
|
|
}
|