1 //////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // Nestopia - NES/Famicom emulator written in C++ 4 // 5 // Copyright (C) 2003-2008 Martin Freij 6 // 7 // This file is part of Nestopia. 8 // 9 // Nestopia is free software; you can redistribute it and/or modify 10 // it under the terms of the GNU General Public License as published by 11 // the Free Software Foundation; either version 2 of the License, or 12 // (at your option) any later version. 13 // 14 // Nestopia is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with Nestopia; if not, write to the Free Software 21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 // 23 //////////////////////////////////////////////////////////////////////////////////////// 24 25 #include "NstIoFile.hpp" 26 #include "NstCollectionVector.hpp" 27 #include <windows.h> 28 29 namespace Nestopia 30 { 31 namespace Io 32 { 33 NST_COMPILE_ASSERT 34 ( 35 File::BEGIN == FILE_BEGIN && 36 File::CURRENT == FILE_CURRENT && 37 File::END == FILE_END 38 ); 39 File()40 File::File() 41 : handle(NULL) {} 42 File(const GenericString fileName,const uint mode)43 File::File(const GenericString fileName,const uint mode) 44 : handle(NULL) 45 { 46 Open( fileName, mode ); 47 } 48 ~File()49 File::~File() 50 { 51 Close(); 52 } 53 Open(const GenericString n,const uint mode)54 void File::Open(const GenericString n,const uint mode) 55 { 56 NST_ASSERT( (mode & (WRITE|READ)) && ((mode & WRITE) || !(mode & EMPTY)) && (mode & (RANDOM_ACCESS|SEQUENTIAL_ACCESS)) != (RANDOM_ACCESS|SEQUENTIAL_ACCESS) ); 57 58 Close(); 59 60 if (n.Empty()) 61 throw ERR_NOT_FOUND; 62 63 handle = ::CreateFile 64 ( 65 (name=n).Ptr(), 66 ( 67 (mode & (READ|WRITE)) == (READ|WRITE) ? (GENERIC_READ|GENERIC_WRITE) : 68 (mode & (READ|WRITE)) == (READ) ? (GENERIC_READ) : 69 (mode & (READ|WRITE)) == (WRITE) ? (GENERIC_WRITE) : 70 0 71 ), 72 ( 73 (mode & (READ|WRITE)) == (WRITE) ? 0 : FILE_SHARE_READ 74 ), 75 NULL, 76 ( 77 (mode & (EMPTY|EXISTING)) == (EMPTY) ? CREATE_ALWAYS : 78 (mode & (EMPTY|EXISTING)) == (EMPTY|EXISTING) ? TRUNCATE_EXISTING : 79 (mode & (EMPTY|EXISTING)) == (EXISTING) ? OPEN_EXISTING : 80 (mode & (WRITE)) ? OPEN_ALWAYS : 81 OPEN_EXISTING 82 ), 83 ( 84 ( 85 (mode & (WRITE|WRITE_THROUGH)) == (WRITE) ? (FILE_ATTRIBUTE_NORMAL) : 86 (mode & (WRITE|WRITE_THROUGH)) == (WRITE|WRITE_THROUGH) ? (FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH) : 0 87 ) 88 | 89 ( 90 (mode & (READ|RANDOM_ACCESS)) == (READ|RANDOM_ACCESS) ? FILE_FLAG_RANDOM_ACCESS : 91 (mode & (READ|SEQUENTIAL_ACCESS)) == (READ|SEQUENTIAL_ACCESS) ? FILE_FLAG_SEQUENTIAL_SCAN : 0 92 ) 93 ), 94 NULL 95 ); 96 97 if (handle && handle != INVALID_HANDLE_VALUE) 98 { 99 if (!(mode & EMPTY)) 100 { 101 try 102 { 103 Size(); 104 } 105 catch (...) 106 { 107 Close(); 108 throw; 109 } 110 } 111 } 112 else 113 { 114 handle = NULL; 115 name.Clear(); 116 117 switch (::GetLastError()) 118 { 119 case ERROR_FILE_NOT_FOUND: 120 case ERROR_PATH_NOT_FOUND: 121 122 throw ERR_NOT_FOUND; 123 124 case ERROR_ALREADY_EXISTS: 125 126 throw ERR_ALREADY_EXISTS; 127 128 case ERROR_ACCESS_DENIED: 129 130 if (mode & WRITE) 131 throw ERR_READ_ONLY; 132 133 default: 134 135 throw ERR_OPEN; 136 } 137 } 138 } 139 Close()140 void File::Close() 141 { 142 name.Clear(); 143 144 if (void* const tmp = handle) 145 { 146 handle = NULL; 147 ::CloseHandle( tmp ); 148 } 149 } 150 Write(const void * const data,const uint size) const151 void File::Write(const void* const data,const uint size) const 152 { 153 NST_ASSERT( IsOpen() && bool(data) >= bool(size) ); 154 155 if (size) 156 { 157 DWORD written = 0; 158 159 if (!::WriteFile( handle, data, size, &written, NULL ) || written != size) 160 throw ERR_WRITE; 161 } 162 } 163 WriteSome(const void * const data,const uint size) const164 uint File::WriteSome(const void* const data,const uint size) const 165 { 166 NST_ASSERT( IsOpen() && bool(data) >= bool(size) ); 167 168 if (size) 169 { 170 DWORD written = 0; 171 172 if (!::WriteFile( handle, data, size, &written, NULL )) 173 throw ERR_WRITE; 174 175 return written; 176 } 177 178 return 0; 179 } 180 Write8(uint data) const181 void File::Write8(uint data) const 182 { 183 const uchar buffer = data; 184 Write( &buffer, 1 ); 185 } 186 Write16(uint data) const187 void File::Write16(uint data) const 188 { 189 const uchar buffer[] = { data & 0xFF, data >> 8 }; 190 Write( buffer, 2 ); 191 } 192 Write32(uint data) const193 void File::Write32(uint data) const 194 { 195 const uchar buffer[] = { data & 0xFF, data >> 8 & 0xFF, data >> 16 & 0xFF, data >> 24 }; 196 Write( buffer, 4 ); 197 } 198 Read(void * const data,const uint size) const199 void File::Read(void* const data,const uint size) const 200 { 201 NST_ASSERT( IsOpen() && bool(data) >= bool(size) ); 202 203 if (size) 204 { 205 DWORD read = 0; 206 207 if (!::ReadFile( handle, data, size, &read, NULL ) || read != size) 208 throw ERR_READ; 209 } 210 } 211 ReadSome(void * const data,uint size) const212 uint File::ReadSome(void* const data,uint size) const 213 { 214 NST_ASSERT( IsOpen() && bool(data) >= bool(size) ); 215 216 if (size) 217 { 218 DWORD read = 0; 219 220 if (!::ReadFile( handle, data, size, &read, NULL )) 221 throw ERR_READ; 222 223 return read; 224 } 225 226 return 0; 227 } 228 Peek(void * const data,const uint size) const229 void File::Peek(void* const data,const uint size) const 230 { 231 const uint pos = Position(); 232 Read( data, size ); 233 Position() = pos; 234 } 235 Peek(const uint from,void * const data,const uint size) const236 void File::Peek(const uint from,void* const data,const uint size) const 237 { 238 const uint pos = Position(); 239 Position() = from; 240 Read( data, size ); 241 Position() = pos; 242 } 243 Peek8() const244 uint File::Peek8() const 245 { 246 uchar buffer; 247 Peek( &buffer, 1 ); 248 return buffer; 249 } 250 Peek16() const251 uint File::Peek16() const 252 { 253 uchar buffer[2]; 254 Peek( buffer, 2 ); 255 return buffer[0] | uint(buffer[1]) << 8; 256 } 257 Peek32() const258 uint File::Peek32() const 259 { 260 uchar buffer[4]; 261 Peek( buffer, 4 ); 262 return buffer[0] | uint(buffer[1]) << 8 | uint(buffer[2]) << 16 | uint(buffer[3]) << 24; 263 } 264 Seek(const Offset offset,const int distance) const265 uint File::Seek(const Offset offset,const int distance) const 266 { 267 NST_ASSERT( IsOpen() ); 268 269 const DWORD pos = ::SetFilePointer( handle, distance, NULL, offset ); 270 271 if (pos == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR) 272 throw ERR_SEEK; 273 274 return pos; 275 } 276 Read8() const277 uint File::Read8() const 278 { 279 uchar buffer; 280 Read( &buffer, 1 ); 281 return buffer; 282 } 283 Read16() const284 uint File::Read16() const 285 { 286 uchar buffer[2]; 287 Read( buffer, 2 ); 288 return buffer[0] | buffer[1] << 8; 289 } 290 Read32() const291 uint File::Read32() const 292 { 293 uchar buffer[4]; 294 Read( buffer, 4 ); 295 return buffer[0] | buffer[1] << 8 | uint(buffer[2]) << 16 | uint(buffer[3]) << 24; 296 } 297 Size() const298 uint File::Size() const 299 { 300 NST_ASSERT( IsOpen() ); 301 302 DWORD words[2]; 303 304 words[1] = 0; 305 words[0] = ::GetFileSize( handle, &words[1] ); 306 307 if (words[0] == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR) 308 throw ERR_SEEK; 309 310 if (words[1] || words[0] > MAX_SIZE) 311 throw ERR_TOO_BIG; 312 313 return words[0]; 314 } 315 Truncate() const316 void File::Truncate() const 317 { 318 NST_ASSERT( IsOpen() ); 319 320 if (!::SetEndOfFile( handle )) 321 throw ERR_SEEK; 322 } 323 Truncate(const uint pos) const324 void File::Truncate(const uint pos) const 325 { 326 Position() = pos; 327 Truncate(); 328 } 329 Flush() const330 void File::Flush() const 331 { 332 NST_ASSERT( IsOpen() ); 333 ::FlushFileBuffers( handle ); 334 } 335 ReadText(String::Heap<char> & string) const336 void File::ReadText(String::Heap<char>& string) const 337 { 338 string.Resize( Size() - Position() ); 339 340 if (string.Length()) 341 Read( string.Ptr(), string.Length() ); 342 } 343 ReadText(String::Heap<wchar_t> & string) const344 void File::ReadText(String::Heap<wchar_t>& string) const 345 { 346 String::Heap<char> buffer; 347 ReadText( buffer ); 348 ParseText( buffer.Ptr(), buffer.Length(), string ); 349 } 350 WriteText(cstring const string,const uint length,bool) const351 void File::WriteText(cstring const string,const uint length,bool) const 352 { 353 Write( string, length ); 354 } 355 WriteText(wcstring string,uint length,bool forceUnicode) const356 void File::WriteText(wcstring string,uint length,bool forceUnicode) const 357 { 358 if (length) 359 { 360 if (forceUnicode || String::Generic<wchar_t>(string,length).Wide()) 361 { 362 Collection::Buffer buffer( length * 2 + 2 ); 363 char* NST_RESTRICT dst = buffer.Ptr(); 364 365 *dst++ = UTF16_LE & 0xFF; 366 *dst++ = UTF16_LE >> 8; 367 368 do 369 { 370 *dst++ = *string & 0xFF; 371 *dst++ = *string++ >> 8; 372 } 373 while (--length); 374 375 Write( buffer.Ptr(), buffer.Size() ); 376 } 377 else 378 { 379 const String::Heap<char> tmp( string, length ); 380 Write( tmp.Ptr(), tmp.Length() ); 381 } 382 } 383 } 384 ParseText(cstring src,const uint length,String::Heap<char> & string)385 void File::ParseText(cstring src,const uint length,String::Heap<char>& string) 386 { 387 string.Assign( src, length ); 388 } 389 ParseText(cstring src,const uint length,String::Heap<wchar_t> & string)390 void File::ParseText(cstring src,const uint length,String::Heap<wchar_t>& string) 391 { 392 if (length) 393 { 394 const uint utf = (length >= 2) ? (src[0] | uint(src[1]) << 8) : 0; 395 396 if (utf == UTF16_LE || utf == UTF16_BE) 397 { 398 NST_VERIFY( length > 2 && length % 2 == 0 ); 399 400 string.Resize( length / 2 - 1 ); 401 402 if (string.Length()) 403 { 404 wchar_t* NST_RESTRICT dst = string.Ptr(); 405 const wchar_t* const end = dst + string.Length(); 406 407 if (utf == UTF16_LE) 408 { 409 do 410 { 411 src += 2; 412 *dst++ = src[0] | src[1] << 8; 413 } 414 while (dst != end); 415 } 416 else 417 { 418 do 419 { 420 src += 2; 421 *dst++ = src[1] | src[0] << 8; 422 } 423 while (dst != end); 424 } 425 } 426 } 427 else 428 { 429 string.Assign( src, length ); 430 } 431 } 432 else 433 { 434 string.Clear(); 435 } 436 } 437 Delete(wcstring const path)438 bool File::Delete(wcstring const path) 439 { 440 NST_ASSERT( path && *path ); 441 return ::DeleteFile( path ); 442 } 443 } 444 } 445