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