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 <new>
26 #include "../NstMachine.hpp"
27 #include "../NstStream.hpp"
28 #include "../NstChecksum.hpp"
29 #include "../NstCartridge.hpp"
30 #include "../NstImageDatabase.hpp"
31 #include "../NstCartridgeInes.hpp"
32 #include "NstApiMachine.hpp"
33 
34 namespace Nes
35 {
36 	namespace Api
37 	{
38 		Cartridge::ChooseProfileCaller Cartridge::chooseProfileCallback;
39 
40 		#ifdef NST_MSVC_OPTIMIZE
41 		#pragma optimize("s", on)
42 		#endif
43 
Hash()44 		Cartridge::Profile::Hash::Hash() throw()
45 		{
46 			Clear();
47 		}
48 
Hash(const char * sha1,const char * crc)49 		Cartridge::Profile::Hash::Hash(const char* sha1,const char* crc) throw()
50 		{
51 			Assign( sha1, crc );
52 		}
53 
Hash(const wchar_t * sha1,const wchar_t * crc)54 		Cartridge::Profile::Hash::Hash(const wchar_t* sha1,const wchar_t* crc) throw()
55 		{
56 			Assign( sha1, crc );
57 		}
58 
Hash(const dword * sha1,dword crc)59 		Cartridge::Profile::Hash::Hash(const dword* sha1,dword crc) throw()
60 		{
61 			Assign( sha1, crc );
62 		}
63 
Clear()64 		void Cartridge::Profile::Hash::Clear() throw()
65 		{
66 			Assign( NULL, dword(0) );
67 		}
68 
69 		template<typename T>
Set(dword & dst,const T * NST_RESTRICT src)70 		bool Cartridge::Profile::Hash::Set(dword& dst,const T* NST_RESTRICT src)
71 		{
72 			dword v = 0;
73 			uint i = 32;
74 
75 			do
76 			{
77 				i -= 4;
78 
79 				const T c = *src++;
80 
81 				if (c >= '0' && c <= '9')
82 				{
83 					v |= dword(c - '0') << i;
84 				}
85 				else if (c >= 'A' && c <= 'F')
86 				{
87 					v |= dword(c - 'A' + 0xA) << i;
88 				}
89 				else if (c >= 'a' && c <= 'f')
90 				{
91 					v |= dword(c - 'a' + 0xA) << i;
92 				}
93 				else
94 				{
95 					return false;
96 				}
97 			}
98 			while (i);
99 
100 			dst = v;
101 			return true;
102 		}
103 
104 		template<typename T>
Import(const T * sha1,const T * crc)105 		void Cartridge::Profile::Hash::Import(const T* sha1,const T* crc)
106 		{
107 			Clear();
108 
109 			if (crc && *crc)
110 				Set( data[0], crc );
111 
112 			if (sha1 && *sha1)
113 			{
114 				for (uint i=1; i < 1+SHA1_WORD_LENGTH; ++i, sha1 += 8)
115 				{
116 					if (!Set( data[i], sha1 ))
117 					{
118 						for (uint j=1; j < i; ++j)
119 							data[j] = 0;
120 
121 						break;
122 					}
123 				}
124 			}
125 		}
126 
Assign(const char * sha1,const char * crc)127 		void Cartridge::Profile::Hash::Assign(const char* sha1,const char* crc) throw()
128 		{
129 			Import( sha1, crc );
130 		}
131 
Assign(const wchar_t * sha1,const wchar_t * crc)132 		void Cartridge::Profile::Hash::Assign(const wchar_t* sha1,const wchar_t* crc) throw()
133 		{
134 			Import( sha1, crc );
135 		}
136 
Assign(const dword * sha1,dword crc)137 		void Cartridge::Profile::Hash::Assign(const dword* sha1,dword crc) throw()
138 		{
139 			data[0] = crc & 0xFFFFFFFF;
140 
141 			for (uint i=0; i < SHA1_WORD_LENGTH; ++i)
142 				data[1+i] = (sha1 ? sha1[i] & 0xFFFFFFFF : 0);
143 		}
144 
Get(char * sha1,char * crc) const145 		void Cartridge::Profile::Hash::Get(char* sha1,char* crc) const throw()
146 		{
147 			if (crc)
148 			{
149 				for (dword v=data[0], j=32; j; )
150 				{
151 					j -= 4;
152 					uint c = v >> j & 0xF;
153 					*crc++ = (c < 0xA ? '0' + c : 'A' + (c - 0xA) );
154 				}
155 			}
156 
157 			if (sha1)
158 			{
159 				for (uint i=1; i < 1+SHA1_WORD_LENGTH; ++i)
160 				{
161 					for (dword v=data[i], j=32; j; )
162 					{
163 						j -= 4;
164 						uint c = v >> j & 0xF;
165 						*sha1++ = (c < 0xA ? '0' + c : 'A' + (c - 0xA) );
166 					}
167 				}
168 			}
169 		}
170 
operator <(const Hash & c) const171 		bool Cartridge::Profile::Hash::operator < (const Hash& c) const throw()
172 		{
173 			for (const dword *a=data, *b=c.data, *end=data+(1+SHA1_WORD_LENGTH); a != end; ++a, ++b)
174 			{
175 				if (*a < *b)
176 					return true;
177 
178 				if (*a > *b)
179 					return false;
180 			}
181 
182 			return false;
183 		}
184 
operator ==(const Hash & c) const185 		bool Cartridge::Profile::Hash::operator == (const Hash& c) const throw()
186 		{
187 			for (const dword *a=data, *b=c.data, *end=data+(1+SHA1_WORD_LENGTH); a != end; ++a, ++b)
188 			{
189 				if (*a != *b)
190 					return false;
191 			}
192 
193 			return true;
194 		}
195 
operator !() const196 		bool Cartridge::Profile::Hash::operator ! () const throw()
197 		{
198 			for (uint i=0; i < 1+SHA1_WORD_LENGTH; ++i)
199 			{
200 				if (data[i])
201 					return false;
202 			}
203 
204 			return true;
205 		}
206 
Compute(const void * mem,ulong size)207 		void Cartridge::Profile::Hash::Compute(const void* mem,ulong size) throw()
208 		{
209 			const Core::Checksum checksum( static_cast<const byte*>(mem), size );
210 			Assign( checksum.GetSha1(), checksum.GetCrc() );
211 		}
212 
GetSha1() const213 		const dword* Cartridge::Profile::Hash::GetSha1() const throw()
214 		{
215 			return data+1;
216 		}
217 
GetCrc32() const218 		dword Cartridge::Profile::Hash::GetCrc32() const throw()
219 		{
220 			return data[0];
221 		}
222 
System()223 		Cartridge::Profile::System::System() throw()
224 		: type(NES_NTSC), cpu(CPU_RP2A03), ppu(PPU_RP2C02) {}
225 
Dump()226 		Cartridge::Profile::Dump::Dump() throw()
227 		: state(UNKNOWN) {}
228 
Game()229 		Cartridge::Profile::Game::Game() throw()
230 		: adapter(Input::ADAPTER_NES), players(0)
231 		{
232 			controllers[0] = Input::PAD1;
233 			controllers[1] = Input::PAD2;
234 			controllers[2] = Input::UNCONNECTED;
235 			controllers[3] = Input::UNCONNECTED;
236 			controllers[4] = Input::UNCONNECTED;
237 		}
238 
Pin()239 		Cartridge::Profile::Board::Pin::Pin() throw()
240 		: number(0) {}
241 
Sample()242 		Cartridge::Profile::Board::Sample::Sample() throw()
243 		: id(0) {}
244 
Rom()245 		Cartridge::Profile::Board::Rom::Rom() throw()
246 		: id(0), size(0) {}
247 
Ram()248 		Cartridge::Profile::Board::Ram::Ram() throw()
249 		: id(0), size(0), battery(false) {}
250 
Chip()251 		Cartridge::Profile::Board::Chip::Chip() throw()
252 		: battery(false) {}
253 
Board()254 		Cartridge::Profile::Board::Board() throw()
255 		: solderPads(0), mapper(NO_MAPPER), subMapper(0) {}
256 
~Board()257 		Cartridge::Profile::Board::~Board() throw()
258 		{
259 		}
260 
Profile()261 		Cartridge::Profile::Profile() throw()
262 		: multiRegion(false), patched(false)
263 		{
264 		}
265 
~Profile()266 		Cartridge::Profile::~Profile() throw()
267 		{
268 		}
269 
270 		template<typename T>
GetComponentSize(const T & components) const271 		dword Cartridge::Profile::Board::GetComponentSize(const T& components) const
272 		{
273 			dword size = 0;
274 
275 			for (typename T::const_iterator it(components.begin()), end(components.end()); it != end; ++it)
276 				size += it->size;
277 
278 			return size;
279 		}
280 
GetPrg() const281 		dword Cartridge::Profile::Board::GetPrg() const throw()
282 		{
283 			return GetComponentSize( prg );
284 		}
285 
GetChr() const286 		dword Cartridge::Profile::Board::GetChr() const throw()
287 		{
288 			return GetComponentSize( chr );
289 		}
290 
GetWram() const291 		dword Cartridge::Profile::Board::GetWram() const throw()
292 		{
293 			return GetComponentSize( wram );
294 		}
295 
GetVram() const296 		dword Cartridge::Profile::Board::GetVram() const throw()
297 		{
298 			return GetComponentSize( vram );
299 		}
300 
301 		template<typename T>
HasComponentBattery(const T & components) const302 		bool Cartridge::Profile::Board::HasComponentBattery(const T& components) const
303 		{
304 			for (typename T::const_iterator it(components.begin()), end(components.end()); it != end; ++it)
305 			{
306 				if (it->battery)
307 					return true;
308 			}
309 
310 			return false;
311 		}
312 
HasWramBattery() const313 		bool Cartridge::Profile::Board::HasWramBattery() const throw()
314 		{
315 			return HasComponentBattery( wram );
316 		}
317 
HasMmcBattery() const318 		bool Cartridge::Profile::Board::HasMmcBattery() const throw()
319 		{
320 			return HasComponentBattery( chips );
321 		}
322 
HasBattery() const323 		bool Cartridge::Profile::Board::HasBattery() const throw()
324 		{
325 			return HasWramBattery() || HasMmcBattery();
326 		}
327 
NesHeader()328 		Cartridge::NesHeader::NesHeader() throw()
329 		{
330 			Clear();
331 		}
332 
Clear()333 		void Cartridge::NesHeader::Clear() throw()
334 		{
335 			system = SYSTEM_CONSOLE;
336 			region = REGION_NTSC;
337 			prgRom = 0;
338 			prgRam = 0;
339 			prgNvRam = 0;
340 			chrRom = 0;
341 			chrRam = 0;
342 			chrNvRam = 0;
343 			ppu = PPU_RP2C02;
344 			mirroring = MIRRORING_VERTICAL;
345 			mapper = 0;
346 			subMapper = 0;
347 			security = 0;
348 			version = 0;
349 			trainer = false;
350 		}
351 
Import(const void * const data,const ulong length)352 		Result Cartridge::NesHeader::Import(const void* const data,const ulong length) throw()
353 		{
354 			return Core::Cartridge::Ines::ReadHeader( *this, static_cast<const byte*>(data), length );
355 		}
356 
Export(void * data,ulong length) const357 		Result Cartridge::NesHeader::Export(void* data,ulong length) const throw()
358 		{
359 			return Core::Cartridge::Ines::WriteHeader( *this, static_cast<byte*>(data), length );
360 		}
361 
IsLoaded() const362 		bool Cartridge::Database::IsLoaded() const throw()
363 		{
364 			return emulator.imageDatabase;
365 		}
366 
IsEnabled() const367 		bool Cartridge::Database::IsEnabled() const throw()
368 		{
369 			return emulator.imageDatabase && emulator.imageDatabase->Enabled();
370 		}
371 
Create()372 		bool Cartridge::Database::Create()
373 		{
374 			if (emulator.imageDatabase == NULL)
375 				emulator.imageDatabase = new (std::nothrow) Core::ImageDatabase;
376 
377 			return emulator.imageDatabase;
378 		}
379 
Load(std::istream & stream)380 		Result Cartridge::Database::Load(std::istream& stream) throw()
381 		{
382 			return Create() ? emulator.imageDatabase->Load( stream ) : RESULT_ERR_OUT_OF_MEMORY;
383 		}
384 
Load(std::istream & baseStream,std::istream & overloadStream)385 		Result Cartridge::Database::Load(std::istream& baseStream,std::istream& overloadStream) throw()
386 		{
387 			return Create() ? emulator.imageDatabase->Load( baseStream, overloadStream ) : RESULT_ERR_OUT_OF_MEMORY;
388 		}
389 
Unload()390 		void Cartridge::Database::Unload() throw()
391 		{
392 			if (emulator.imageDatabase)
393 				emulator.imageDatabase->Unload();
394 		}
395 
Enable(bool state)396 		Result Cartridge::Database::Enable(bool state) throw()
397 		{
398 			if (Create())
399 			{
400 				if (emulator.imageDatabase->Enabled() != state)
401 				{
402 					emulator.imageDatabase->Enable( state );
403 					return RESULT_OK;
404 				}
405 
406 				return RESULT_NOP;
407 			}
408 
409 			return RESULT_ERR_OUT_OF_MEMORY;
410 		}
411 
ReadRomset(std::istream & stream,Machine::FavoredSystem system,bool askProfile,Profile & profile)412 		Result Cartridge::ReadRomset(std::istream& stream,Machine::FavoredSystem system,bool askProfile,Profile& profile) throw()
413 		{
414 			try
415 			{
416 				Core::Cartridge::ReadRomset( stream, static_cast<Core::FavoredSystem>(system), askProfile, profile );
417 			}
418 			catch (Result result)
419 			{
420 				return result;
421 			}
422 			catch (...)
423 			{
424 				return RESULT_ERR_GENERIC;
425 			}
426 
427 			return RESULT_OK;
428 		}
429 
ReadInes(std::istream & stream,Machine::FavoredSystem system,Profile & profile)430 		Result Cartridge::ReadInes(std::istream& stream,Machine::FavoredSystem system,Profile& profile) throw()
431 		{
432 			try
433 			{
434 				Core::Cartridge::ReadInes( stream, static_cast<Core::FavoredSystem>(system), profile );
435 			}
436 			catch (Result result)
437 			{
438 				return result;
439 			}
440 			catch (...)
441 			{
442 				return RESULT_ERR_GENERIC;
443 			}
444 
445 			return RESULT_OK;
446 		}
447 
ReadUnif(std::istream & stream,Machine::FavoredSystem system,Profile & profile)448 		Result Cartridge::ReadUnif(std::istream& stream,Machine::FavoredSystem system,Profile& profile) throw()
449 		{
450 			try
451 			{
452 				Core::Cartridge::ReadUnif( stream, static_cast<Core::FavoredSystem>(system), profile );
453 			}
454 			catch (Result result)
455 			{
456 				return result;
457 			}
458 			catch (...)
459 			{
460 				return RESULT_ERR_GENERIC;
461 			}
462 
463 			return RESULT_OK;
464 		}
465 
GetProfile() const466 		const Cartridge::Profile* Cartridge::GetProfile() const throw()
467 		{
468 			if (emulator.Is(Machine::CARTRIDGE))
469 				return &static_cast<const Core::Cartridge*>(emulator.image)->GetProfile();
470 
471 			return NULL;
472 		}
473 
FindEntry(const Profile::Hash & hash,Machine::FavoredSystem system) const474 		Cartridge::Database::Entry Cartridge::Database::FindEntry(const Profile::Hash& hash,Machine::FavoredSystem system) const throw()
475 		{
476 			return emulator.imageDatabase ? emulator.imageDatabase->Search( hash, static_cast<Core::FavoredSystem>(system) ).Reference() : NULL;
477 		}
478 
FindEntry(const void * file,ulong length,Machine::FavoredSystem system) const479 		Cartridge::Database::Entry Cartridge::Database::FindEntry(const void* file,ulong length,Machine::FavoredSystem system) const throw()
480 		{
481 			if (emulator.imageDatabase)
482 			{
483 				Profile::Hash hash;
484 				hash.Compute( file, length );
485 				return emulator.imageDatabase->Search( hash, static_cast<Core::FavoredSystem>(system) ).Reference();
486 			}
487 			else
488 			{
489 				return NULL;
490 			}
491 		}
492 
GetProfile(Profile & profile) const493 		Result Cartridge::Database::Entry::GetProfile(Profile& profile) const throw()
494 		{
495 			Core::ImageDatabase::Entry entry(ref);
496 
497 			if (!entry)
498 				return RESULT_ERR_NOT_READY;
499 
500 			try
501 			{
502 				entry.Fill(profile);
503 			}
504 			catch (const std::bad_alloc&)
505 			{
506 				return RESULT_ERR_OUT_OF_MEMORY;
507 			}
508 			catch (...)
509 			{
510 				return RESULT_ERR_GENERIC;
511 			}
512 
513 			return RESULT_OK;
514 		}
515 
GetTitle() const516 		const wchar_t* Cartridge::Database::Entry::GetTitle() const throw()
517 		{
518 			return Core::ImageDatabase::Entry(ref).GetTitle();
519 		}
520 
GetHash() const521 		const Cartridge::Profile::Hash* Cartridge::Database::Entry::GetHash() const throw()
522 		{
523 			return Core::ImageDatabase::Entry(ref).GetHash();
524 		}
525 
GetRegion() const526 		const wchar_t* Cartridge::Database::Entry::GetRegion() const throw()
527 		{
528 			return Core::ImageDatabase::Entry(ref).GetRegion();
529 		}
530 
GetSystem() const531 		Cartridge::Profile::System::Type Cartridge::Database::Entry::GetSystem() const throw()
532 		{
533 			return Core::ImageDatabase::Entry(ref).GetSystem();
534 		}
535 
IsMultiRegion() const536 		bool Cartridge::Database::Entry::IsMultiRegion() const throw()
537 		{
538 			return Core::ImageDatabase::Entry(ref).IsMultiRegion();
539 		}
540 
GetPrgRom() const541 		dword Cartridge::Database::Entry::GetPrgRom() const throw()
542 		{
543 			return Core::ImageDatabase::Entry(ref).GetPrg();
544 		}
545 
GetChrRom() const546 		dword Cartridge::Database::Entry::GetChrRom() const throw()
547 		{
548 			return Core::ImageDatabase::Entry(ref).GetChr();
549 		}
550 
GetWram() const551 		uint Cartridge::Database::Entry::GetWram() const throw()
552 		{
553 			return Core::ImageDatabase::Entry(ref).GetWram();
554 		}
555 
GetVram() const556 		uint Cartridge::Database::Entry::GetVram() const throw()
557 		{
558 			return Core::ImageDatabase::Entry(ref).GetVram();
559 		}
560 
HasBattery() const561 		bool Cartridge::Database::Entry::HasBattery() const throw()
562 		{
563 			return Core::ImageDatabase::Entry(ref).HasBattery();
564 		}
565 
GetMapper() const566 		uint Cartridge::Database::Entry::GetMapper() const throw()
567 		{
568 			return Core::ImageDatabase::Entry(ref).GetMapper();
569 		}
570 
GetDumpState() const571 		Cartridge::Profile::Dump::State Cartridge::Database::Entry::GetDumpState() const throw()
572 		{
573 			return Core::ImageDatabase::Entry(ref).GetDumpState();
574 		}
575 
576 		#ifdef NST_MSVC_OPTIMIZE
577 		#pragma optimize("", on)
578 		#endif
579 	}
580 }
581