nintendo-64.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. struct Nintendo64 : Cartridge {
  2. auto name() -> string override { return "Nintendo 64"; }
  3. auto extensions() -> vector<string> override { return {"n64", "v64", "z64"}; }
  4. auto export(string location) -> vector<uint8_t> override;
  5. auto heuristics(vector<uint8_t>& data, string location) -> string override;
  6. };
  7. auto Nintendo64::export(string location) -> vector<uint8_t> {
  8. vector<uint8_t> data;
  9. append(data, {location, "program.rom"});
  10. return data;
  11. }
  12. auto Nintendo64::heuristics(vector<uint8_t>& data, string location) -> string {
  13. if(data.size() < 0x1000) {
  14. //too small
  15. return {};
  16. } else if(data[0] == 0x80 && data[1] == 0x37 && data[2] == 0x12 && data[3] == 0x40) {
  17. //big endian
  18. } else if(data[0] == 0x37 && data[1] == 0x80 && data[2] == 0x40 && data[3] == 0x12) {
  19. //byte-swapped
  20. for(uint index = 0; index < data.size(); index += 2) {
  21. uint8_t d0 = data[index + 0];
  22. uint8_t d1 = data[index + 1];
  23. data[index + 0] = d1;
  24. data[index + 1] = d0;
  25. }
  26. } else if(data[0] == 0x40 && data[1] == 0x12 && data[2] == 0x37 && data[3] == 0x80) {
  27. //little endian
  28. for(uint index = 0; index < data.size(); index += 4) {
  29. uint8_t d0 = data[index + 0];
  30. uint8_t d1 = data[index + 1];
  31. uint8_t d2 = data[index + 2];
  32. uint8_t d3 = data[index + 3];
  33. data[index + 0] = d3;
  34. data[index + 1] = d2;
  35. data[index + 2] = d1;
  36. data[index + 3] = d0;
  37. }
  38. } else {
  39. //unrecognized
  40. return {};
  41. }
  42. string region = "NTSC";
  43. switch(data[0x3e]) {
  44. case 'A': region = "NTSC"; break; //Asia
  45. case 'B': region = "NTSC"; break; //Brazil
  46. case 'C': region = "NTSC"; break; //China
  47. case 'D': region = "PAL"; break; //Germany
  48. case 'E': region = "NTSC"; break; //North America
  49. case 'F': region = "PAL"; break; //France
  50. case 'G': region = "NTSC"; break; //Gateway 64 (NTSC)
  51. case 'H': region = "PAL"; break; //Netherlands
  52. case 'I': region = "PAL"; break; //Italy
  53. case 'J': region = "NTSC"; break; //Japan
  54. case 'K': region = "NTSC"; break; //Korea
  55. case 'L': region = "PAL"; break; //Gateway 64 (PAL)
  56. case 'N': region = "NTSC"; break; //Canada
  57. case 'P': region = "PAL"; break; //Europe
  58. case 'S': region = "PAL"; break; //Spain
  59. case 'U': region = "PAL"; break; //Australia
  60. case 'W': region = "PAL"; break; //Scandanavia
  61. case 'X': region = "PAL"; break; //Europe
  62. case 'Y': region = "PAL"; break; //Europe
  63. }
  64. //detect the CIC used for a given gamepak based on checksumming its bootcode
  65. //note: NTSC 6101 = PAL 7102 and NTSC 6102 = PAL 7101 (IDs are swapped)
  66. //note: NTSC 6104 / PAL 7104 was never officially used
  67. bool ntsc = region == "NTSC";
  68. string cic = ntsc ? "CIC-NUS-6102" : "CIC-NUS-7101"; //fallback; most common
  69. uint32_t crc32 = Hash::CRC32({&data[0x40], 0x9c0}).value();
  70. if(crc32 == 0x1deb51a9) cic = ntsc ? "CIC-NUS-6101" : "CIC-NUS-7102";
  71. if(crc32 == 0xc08e5bd6) cic = ntsc ? "CIC-NUS-6102" : "CIC-NUS-7101";
  72. if(crc32 == 0x03b8376a) cic = ntsc ? "CIC-NUS-6103" : "CIC-NUS-7103";
  73. if(crc32 == 0xcf7f41dc) cic = ntsc ? "CIC-NUS-6105" : "CIC-NUS-7105";
  74. if(crc32 == 0xd1059c6a) cic = ntsc ? "CIC-NUS-6106" : "CIC-NUS-7106";
  75. string s;
  76. s += "game\n";
  77. s +={" name: ", Media::name(location), "\n"};
  78. s +={" label: ", Media::name(location), "\n"};
  79. s +={" region: ", region, "\n"};
  80. s += " board\n";
  81. s +={" cic: ", cic, "\n"};
  82. s += " memory\n";
  83. s += " type: ROM\n";
  84. s +={" size: 0x", hex(data.size()), "\n"};
  85. s += " content: Program\n";
  86. return s;
  87. }