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