#include "stdafx.h" #include "N64Rom.h" #include "SystemGlobals.h" #include #include #include #include #include #include #ifdef _WIN32 #include #endif CN64Rom::CN64Rom() : m_ROMImage(nullptr), m_ROMImageBase(nullptr), m_RomFileSize(0), m_ErrorMsg(EMPTY_STRING), m_Country(Country_Unknown), m_CicChip(CIC_UNKNOWN) { } CN64Rom::~CN64Rom() { UnallocateRomImage(); } bool CN64Rom::AllocateRomImage(uint32_t RomFileSize) { WriteTrace(TraceN64System, TraceDebug, "Allocating memory for ROM"); std::unique_ptr ImageBase(new uint8_t[RomFileSize + 0x2000]); if (ImageBase.get() == nullptr) { SetError(MSG_MEM_ALLOC_ERROR); WriteTrace(TraceN64System, TraceError, "Failed to allocate memory for ROM (size: 0x%X)", RomFileSize); return false; } uint8_t * Image = (uint8_t *)(((uint64_t)ImageBase.get() + 0xFFF) & ~0xFFF); // Start at beginning of memory page WriteTrace(TraceN64System, TraceDebug, "Allocated ROM memory (%p)", Image); // Save information about the ROM loaded m_ROMImageBase = ImageBase.release(); m_ROMImage = Image; m_RomFileSize = RomFileSize; return true; } bool CN64Rom::AllocateAndLoadN64Image(const char * FileLoc, bool LoadBootCodeOnly) { WriteTrace(TraceN64System, TraceDebug, "Trying to open %s", FileLoc); if (!m_RomFile.Open(FileLoc, CFileBase::modeRead)) { WriteTrace(TraceN64System, TraceError, "Failed to open %s", FileLoc); return false; } // Read the first 4 bytes and make sure it is a valid N64 image uint8_t Test[4]; m_RomFile.SeekToBegin(); if (m_RomFile.Read(Test, sizeof(Test)) != sizeof(Test)) { m_RomFile.Close(); WriteTrace(TraceN64System, TraceError, "Failed to read ident bytes"); return false; } if (!IsValidRomImage(Test)) { m_RomFile.Close(); WriteTrace(TraceN64System, TraceError, "Invalid image file %X %X %X %X", Test[0], Test[1], Test[2], Test[3]); return false; } uint32_t RomFileSize = m_RomFile.GetLength(); WriteTrace(TraceN64System, TraceDebug, "Successfully opened, size: 0x%X", RomFileSize); // If loading boot code then just load the first 0x1000 bytes if (LoadBootCodeOnly) { WriteTrace(TraceN64System, TraceDebug, "Loading boot code, so loading the first 0x1000 bytes", RomFileSize); RomFileSize = 0x1000; } if (!AllocateRomImage(RomFileSize)) { m_RomFile.Close(); return false; } // Load the N64 ROM into the allocated memory g_Notify->DisplayMessage(5, MSG_LOADING); m_RomFile.SeekToBegin(); uint32_t count, TotalRead = 0; for (count = 0; count < (int)RomFileSize; count += ReadFromRomSection) { uint32_t dwToRead = RomFileSize - count; if (dwToRead > ReadFromRomSection) { dwToRead = ReadFromRomSection; } if (m_RomFile.Read(&m_ROMImage[count], dwToRead) != dwToRead) { m_RomFile.Close(); SetError(MSG_FAIL_IMAGE); WriteTrace(TraceN64System, TraceError, "Failed to read file (TotalRead: 0x%X)", TotalRead); return false; } TotalRead += dwToRead; // Show message of how much of the ROM has been loaded (as a percent) g_Notify->DisplayMessage(0, stdstr_f("%s: %.2f%c", GS(MSG_LOADED), ((float)TotalRead / (float)RomFileSize) * 100.0f, '%').c_str()); } if (RomFileSize != TotalRead) { m_RomFile.Close(); SetError(MSG_FAIL_IMAGE); WriteTrace(TraceN64System, TraceError, "Expected to read: 0x%X, read: 0x%X", TotalRead, RomFileSize); return false; } g_Notify->DisplayMessage(5, MSG_BYTESWAP); ByteSwapRom(); // Protect the memory so that it can't be written to ProtectMemory(m_ROMImage, m_RomFileSize, MEM_READONLY); return true; } bool CN64Rom::AllocateAndLoadZipImage(const char * FileLoc, bool LoadBootCodeOnly) { unzFile file = unzOpen(FileLoc); if (file == nullptr) { return false; } int port = unzGoToFirstFile(file); bool FoundRom = false; // Scan through all files in zip until a suitable file is found while (port == UNZ_OK && !FoundRom) { unz_file_info info; char zname[260]; unzGetCurrentFileInfo(file, &info, zname, sizeof(zname), nullptr, 0, nullptr, 0); if (unzLocateFile(file, zname, 1) != UNZ_OK) { SetError(MSG_FAIL_ZIP); break; } if (unzOpenCurrentFile(file) != UNZ_OK) { SetError(MSG_FAIL_ZIP); break; } // Read the first 4 bytes to check magic number uint8_t Test[4]; unzReadCurrentFile(file, Test, sizeof(Test)); if (IsValidRomImage(Test)) { // Get the size of the ROM and try to allocate the memory needed uint32_t RomFileSize = info.uncompressed_size; if (LoadBootCodeOnly) { RomFileSize = 0x1000; } if (!AllocateRomImage(RomFileSize)) { m_RomFile.Close(); return false; } // Load the N64 ROM into the allocated memory g_Notify->DisplayMessage(5, MSG_LOADING); memcpy(m_ROMImage, Test, 4); uint32_t dwRead, count, TotalRead = 0; for (count = 4; count < (int)RomFileSize; count += ReadFromRomSection) { uint32_t dwToRead = RomFileSize - count; if (dwToRead > ReadFromRomSection) { dwToRead = ReadFromRomSection; } dwRead = unzReadCurrentFile(file, &m_ROMImage[count], dwToRead); if (dwRead == 0) { SetError(MSG_FAIL_ZIP); unzCloseCurrentFile(file); break; } TotalRead += dwRead; // Show message of how much of the ROM has been loaded (as a percent) g_Notify->DisplayMessage(5, stdstr_f("%s: %.2f%c", GS(MSG_LOADED), ((float)TotalRead / (float)RomFileSize) * 100.0f, '%').c_str()); } dwRead = TotalRead + 4; if (RomFileSize != dwRead) { unzCloseCurrentFile(file); SetError(MSG_FAIL_ZIP); g_Notify->DisplayMessage(1, ""); break; } FoundRom = true; g_Notify->DisplayMessage(5, MSG_BYTESWAP); ByteSwapRom(); // Protect the memory so that it can't be written to ProtectMemory(m_ROMImage, m_RomFileSize, MEM_READONLY); } unzCloseCurrentFile(file); if (!FoundRom) { port = unzGoToNextFile(file); } } unzClose(file); return FoundRom; } void CN64Rom::ByteSwapRom() { uint32_t count; switch (*((uint32_t *)&m_ROMImage[0])) { case 0x12408037: for (count = 0; count < m_RomFileSize; count += 4) { m_ROMImage[count] ^= m_ROMImage[count + 2]; m_ROMImage[count + 2] ^= m_ROMImage[count]; m_ROMImage[count] ^= m_ROMImage[count + 2]; m_ROMImage[count + 1] ^= m_ROMImage[count + 3]; m_ROMImage[count + 3] ^= m_ROMImage[count + 1]; m_ROMImage[count + 1] ^= m_ROMImage[count + 3]; } break; case 0x40072780: // 64DD IPL case 0x40123780: for (count = 0; count < m_RomFileSize; count += 4) { m_ROMImage[count] ^= m_ROMImage[count + 3]; m_ROMImage[count + 3] ^= m_ROMImage[count]; m_ROMImage[count] ^= m_ROMImage[count + 3]; m_ROMImage[count + 1] ^= m_ROMImage[count + 2]; m_ROMImage[count + 2] ^= m_ROMImage[count + 1]; m_ROMImage[count + 1] ^= m_ROMImage[count + 2]; } break; case 0x80371240: break; default: g_Notify->DisplayError(stdstr_f("ByteSwapRom: %X", m_ROMImage[0]).c_str()); } } CICChip CN64Rom::GetCicChipID(uint8_t * RomData, uint64_t * CRC) { uint64_t crc = 0; int32_t count; for (count = 0x40; count < 0x1000; count += 4) { crc += *(uint32_t *)(RomData + count); } if (CRC != nullptr) { *CRC = crc; } switch (crc) { case 0x000000D0027FDF31: return CIC_NUS_6101; case 0x000000CFFB631223: return CIC_NUS_6101; case 0x000000D057C85244: return CIC_NUS_6102; case 0x000000D6497E414B: return CIC_NUS_6103; case 0x0000011A49F60E96: return CIC_NUS_6105; case 0x000000D6D5BE5580: return CIC_NUS_6106; case 0x000001053BC19870: return CIC_NUS_5167; // 64DD conversion CIC case 0x000000D2E53EF008: return CIC_NUS_8303; // 64DD IPL case 0x000000D2E53EF39F: return CIC_NUS_DDTL; // 64DD IPL tool case 0x000000D2E53E5DDA: return CIC_NUS_DDUS; // 64DD IPL US (different CIC) default: return CIC_UNKNOWN; } } void CN64Rom::CalculateCicChip() { uint64_t CRC = 0; if (m_ROMImage == nullptr) { m_CicChip = CIC_UNKNOWN; return; } m_CicChip = GetCicChipID(m_ROMImage, &CRC); if (m_CicChip == CIC_UNKNOWN) { if (HaveDebugger()) { g_Notify->DisplayError(stdstr_f("Unknown CIC checksum:\n%I64X.", CRC).c_str()); } } } void CN64Rom::CalculateRomCrc() { uint32_t t0, t2, t3, t4, t5; uint32_t a0, a1, a2, a3; uint32_t s0; uint32_t v0, v1; uint32_t length = 0x00100000; // CIC_NUS_6101 at=0x5D588B65 , s6=0x3F // CIC_NUS_6102 at=0x5D588B65 , s6=0x3F // CIC_NUS_6103 at=0x6C078965 , s6=0x78 // CIC_NUS_6105 at=0x5d588b65 , s6=0x91 // CIC_NUS_6106 at=0x6C078965 , s6=0x85 // 64DD IPL (JPN) at=0x02E90EDD , s6=0xdd // 64DD IPL (USA) at=0x02E90EDD , s6=0xde // 64DD TOOL IPL at=0x0260BCD5 , s6=0xdd //v0 = 0xFFFFFFFF & (s6 * at) + 1; switch (m_CicChip) { case CIC_NUS_6101: case CIC_NUS_6102: v0 = 0xF8CA4DDC; break; case CIC_NUS_6103: v0 = 0xA3886759; break; case CIC_NUS_6105: v0 = 0xDF26F436; break; case CIC_NUS_6106: v0 = 0x1FEA617A; break; case CIC_NUS_DDUS: length = 0x000A0000; v0 = 0x861AE3A7; break; case CIC_NUS_8303: length = 0x000A0000; v0 = 0x8331D4CA; break; case CIC_NUS_DDTL: length = 0x000A0000; v0 = 0x0D8303E2; break; default: return; } ProtectMemory(m_ROMImage, m_RomFileSize, MEM_READWRITE); v1 = 0; t0 = 0; t5 = 0x20; a3 = v0; t2 = v0; t3 = v0; s0 = v0; a2 = v0; t4 = v0; for (t0 = 0; t0 < length; t0 += 4) { v0 = *(uint32_t *)(m_ROMImage + t0 + 0x1000); v1 = a3 + v0; a1 = v1; if (v1 < a3) { if (m_CicChip == CIC_NUS_DDUS || m_CicChip == CIC_NUS_8303) { t2 = t2 ^ t3; } else { t2 += 0x1; } } v1 = v0 & 0x001F; a0 = (v0 << v1) | (v0 >> (t5 - v1)); a3 = a1; t3 = t3 ^ v0; s0 = s0 + a0; if (a2 < v0) { a2 = a3 ^ v0 ^ a2; } else { if (m_CicChip == CIC_NUS_8303) a2 = a2 + a0; else a2 = a2 ^ a0; } if (m_CicChip == CIC_NUS_6105) { t4 = (v0 ^ (*(uint32_t *)(m_ROMImage + (0xFF & t0) + 0x750))) + t4; } else t4 = (v0 ^ s0) + t4; } if (m_CicChip == CIC_NUS_6103) { a3 = (a3 ^ t2) + t3; s0 = (s0 ^ a2) + t4; } else if (m_CicChip == CIC_NUS_6106) { a3 = 0xFFFFFFFF & (a3 * t2) + t3; s0 = 0xFFFFFFFF & (s0 * a2) + t4; } else { a3 = a3 ^ t2 ^ t3; s0 = s0 ^ a2 ^ t4; } *(uint32_t *)(m_ROMImage + 0x10) = a3; *(uint32_t *)(m_ROMImage + 0x14) = s0; ProtectMemory(m_ROMImage, m_RomFileSize, MEM_READONLY); } CICChip CN64Rom::CicChipID() { return m_CicChip; } bool CN64Rom::IsValidRomImage(uint8_t Test[4]) { if (*((uint32_t *)&Test[0]) == 0x40123780) { return true; } if (*((uint32_t *)&Test[0]) == 0x12408037) { return true; } if (*((uint32_t *)&Test[0]) == 0x80371240) { return true; } if (*((uint32_t *)&Test[0]) == 0x40072780) { return true; } // 64DD IPL return false; } bool CN64Rom::IsLoadedRomDDIPL() { switch (CicChipID()) { case CIC_NUS_8303: case CIC_NUS_DDUS: case CIC_NUS_DDTL: return true; default: return false; } } void CN64Rom::CleanRomName(char * RomName, bool byteswap) { if (byteswap) { for (int count = 0; count < 20; count += 4) { RomName[count] ^= RomName[count + 3]; RomName[count + 3] ^= RomName[count]; RomName[count] ^= RomName[count + 3]; RomName[count + 1] ^= RomName[count + 2]; RomName[count + 2] ^= RomName[count + 1]; RomName[count + 1] ^= RomName[count + 2]; } } // Truncate all the spaces at the end of the string for (int count = 19; count >= 0; count--) { if (RomName[count] == ' ') { RomName[count] = '\0'; } else if (RomName[count] == '\0') { } else { count = -1; } } RomName[20] = '\0'; // Remove all special characters from the string for (int count = 0; count < (int)strlen(RomName); count++) { switch (RomName[count]) { case '/': case '\\': RomName[count] = '-'; break; case ':': RomName[count] = ';'; break; } } } void CN64Rom::NotificationCB(const char * Status, CN64Rom * /*_this*/) { g_Notify->DisplayMessage(5, stdstr_f("%s", Status).c_str()); } bool CN64Rom::LoadN64Image(const char * FileLoc, bool LoadBootCodeOnly) { WriteTrace(TraceN64System, TraceDebug, "Start (FileLoc: \"%s\" LoadBootCodeOnly: %s)", FileLoc, LoadBootCodeOnly ? "true" : "false"); UnallocateRomImage(); m_ErrorMsg = EMPTY_STRING; stdstr ext = CPath(FileLoc).GetExtension(); bool Loaded7zFile = false; #ifdef _WIN32 if (strstr(FileLoc, "?") != nullptr || _stricmp(ext.c_str(), "7z") == 0) { stdstr FullPath = FileLoc; // This should be a 7-zip file char * SubFile = strstr(const_cast(FullPath.c_str()), "?"); if (SubFile != nullptr) { *SubFile = '\0'; SubFile += 1; } //else load first found file until dialog is implemented //{ // Pop up a dialog and select file // Allocate memory for sub name and copy selected file name to variable //} C7zip ZipFile(FullPath.c_str()); ZipFile.SetNotificationCallback((C7zip::LP7ZNOTIFICATION)NotificationCB, this); for (int i = 0; i < ZipFile.NumFiles(); i++) { CSzFileItem * f = ZipFile.FileItem(i); if (f->IsDir) { continue; } stdstr ZipFileName; ZipFileName.FromUTF16(ZipFile.FileNameIndex(i).c_str()); if (SubFile != nullptr) { if (_stricmp(ZipFileName.c_str(), SubFile) != 0) { continue; } } // Get the size of the ROM and try to allocate the memory needed uint32_t RomFileSize = (uint32_t)f->Size; // If loading boot code then just load the first 0x1000 bytes if (LoadBootCodeOnly) { RomFileSize = 0x1000; } if (!AllocateRomImage(RomFileSize)) { WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } // Load the N64 ROM to the allocated memory g_Notify->DisplayMessage(5, MSG_LOADING); if (!ZipFile.GetFile(i, m_ROMImage, RomFileSize)) { SetError(MSG_FAIL_IMAGE); WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } if (!IsValidRomImage(m_ROMImage)) { if (i < ZipFile.NumFiles() - 1) { UnallocateRomImage(); continue; } SetError(MSG_FAIL_IMAGE); WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } g_Notify->DisplayMessage(5, MSG_BYTESWAP); ByteSwapRom(); // Protect the memory so that it can't be written to ProtectMemory(m_ROMImage, m_RomFileSize, MEM_READONLY); Loaded7zFile = true; break; } if (!Loaded7zFile) { SetError(MSG_7Z_FILE_NOT_FOUND); WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } } #endif // Try to open the file as a zip file if (!Loaded7zFile) { if (!AllocateAndLoadZipImage(FileLoc, LoadBootCodeOnly)) { if (m_ErrorMsg != EMPTY_STRING) { WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } if (!AllocateAndLoadN64Image(FileLoc, LoadBootCodeOnly)) { WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } } } char RomName[260]; // Get the header from the ROM image memcpy(&RomName[0], (void *)(m_ROMImage + 0x20), 20); CN64Rom::CleanRomName(RomName); if (strlen(RomName) == 0) { strcpy(RomName, CPath(FileLoc).GetName().c_str()); CN64Rom::CleanRomName(RomName, false); } WriteTrace(TraceN64System, TraceDebug, "RomName %s", RomName); m_RomName = RomName; m_FileName = FileLoc; m_MD5 = ""; if (!LoadBootCodeOnly) { // Calculate files MD5 checksum m_MD5 = MD5((const unsigned char *)m_ROMImage, m_RomFileSize).hex_digest(); WriteTrace(TraceN64System, TraceDebug, "MD5: %s", m_MD5.c_str()); } m_Country = (Country)m_ROMImage[0x3D]; CalculateCicChip(); uint32_t CRC1, CRC2; if (IsLoadedRomDDIPL()) { // Handle CRC differently if it is a 64DD IPL CRC1 = (*(uint16_t *)(&m_ROMImage[0x608]) << 16) | *(uint16_t *)(&m_ROMImage[0x60C]); CRC2 = (*(uint16_t *)(&m_ROMImage[0x638]) << 16) | *(uint16_t *)(&m_ROMImage[0x63C]); } else { CRC1 = *(uint32_t *)(&m_ROMImage[0x10]); CRC2 = *(uint32_t *)(&m_ROMImage[0x14]); } m_RomIdent = stdstr_f("%08X-%08X-C:%X", CRC1, CRC2, m_ROMImage[0x3D]); { CIniFileBase::SectionList GameIdentifiers; CIniFile RomDatabase(g_Settings->LoadStringVal(SupportFile_RomDatabase).c_str()); RomDatabase.GetVectorOfSections(GameIdentifiers); if (GameIdentifiers.find(m_RomIdent.c_str()) == GameIdentifiers.end()) { char InternalName[22] = { 0 }; memcpy(InternalName, (void *)(m_ROMImage + 0x20), 20); CN64Rom::CleanRomName(InternalName); std::string AltIdentifier = stdstr_f("%s-C:%X", stdstr(InternalName).Trim().ToUpper().c_str(), m_Country); AltIdentifier = RomDatabase.GetString(AltIdentifier.c_str(), "Alt Identifier", ""); if (!AltIdentifier.empty()) { m_RomIdent = AltIdentifier; } } } WriteTrace(TraceN64System, TraceDebug, "Ident: %s", m_RomIdent.c_str()); if (!LoadBootCodeOnly && g_Rom == this) { g_Settings->SaveBool(GameRunning_LoadingInProgress, false); if (!g_Disk) { SaveRomSettingID(false); } else if (!IsLoadedRomDDIPL()) { g_Settings->SaveString(Game_GameName, m_RomName.c_str()); // Use base games save file if loaded in combo } } if (g_Settings->LoadBool(Game_CRC_Recalc)) { // Calculate ROM header CRC CalculateRomCrc(); } WriteTrace(TraceN64System, TraceDebug, "Done (res: true)"); return true; } bool CN64Rom::LoadN64ImageIPL(const char * FileLoc, bool LoadBootCodeOnly) { UnallocateRomImage(); m_ErrorMsg = EMPTY_STRING; stdstr ext = CPath(FileLoc).GetExtension(); bool Loaded7zFile = false; #ifdef _WIN32 if (strstr(FileLoc, "?") != nullptr || _stricmp(ext.c_str(), "7z") == 0) { stdstr FullPath = FileLoc; // This should be a 7-zip file char * SubFile = strstr(const_cast(FullPath.c_str()), "?"); if (SubFile != nullptr) { *SubFile = '\0'; SubFile += 1; } //else load first found file until dialog is implemented //{ // Pop up a dialog and select file // Allocate memory for sub name and copy selected file name to variable //} C7zip ZipFile(FullPath.c_str()); ZipFile.SetNotificationCallback((C7zip::LP7ZNOTIFICATION)NotificationCB, this); for (int i = 0; i < ZipFile.NumFiles(); i++) { CSzFileItem * f = ZipFile.FileItem(i); if (f->IsDir) { continue; } stdstr ZipFileName; ZipFileName.FromUTF16(ZipFile.FileNameIndex(i).c_str()); if (SubFile != nullptr) { if (_stricmp(ZipFileName.c_str(), SubFile) != 0) { continue; } } // Get the size of the ROM and try to allocate the memory needed uint32_t RomFileSize = (uint32_t)f->Size; // If loading boot code then just load the first 0x1000 bytes if (LoadBootCodeOnly) { RomFileSize = 0x1000; } if (!AllocateRomImage(RomFileSize)) { WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } // Load the N64 ROM to the allocated memory g_Notify->DisplayMessage(5, MSG_LOADING); if (!ZipFile.GetFile(i, m_ROMImage, RomFileSize)) { SetError(MSG_FAIL_IMAGE_IPL); WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } if (!IsValidRomImage(m_ROMImage)) { if (i < ZipFile.NumFiles() - 1) { UnallocateRomImage(); continue; } SetError(MSG_FAIL_IMAGE_IPL); WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } g_Notify->DisplayMessage(5, MSG_BYTESWAP); ByteSwapRom(); // Protect the memory so that it can't be written to ProtectMemory(m_ROMImage, m_RomFileSize, MEM_READONLY); Loaded7zFile = true; break; } if (!Loaded7zFile) { SetError(MSG_7Z_FILE_NOT_FOUND); WriteTrace(TraceN64System, TraceDebug, "Done (res: false)"); return false; } } #endif // Try to open the file as a zip file if (!Loaded7zFile) { if (!AllocateAndLoadZipImage(FileLoc, LoadBootCodeOnly)) { if (m_ErrorMsg != EMPTY_STRING) { return false; } if (!AllocateAndLoadN64Image(FileLoc, LoadBootCodeOnly)) { return false; } } } char RomName[260]; // Get the header from the ROM image memcpy(&RomName[0], (void *)(m_ROMImage + 0x20), 20); CN64Rom::CleanRomName(RomName); if (strlen(RomName) == 0) { strcpy(RomName, CPath(FileLoc).GetName().c_str()); CN64Rom::CleanRomName(RomName,false); } WriteTrace(TraceN64System, TraceDebug, "RomName %s", RomName); m_RomName = RomName; m_FileName = FileLoc; m_MD5 = ""; if (!LoadBootCodeOnly) { // Calculate files MD5 checksum m_MD5 = MD5((const unsigned char *)m_ROMImage, m_RomFileSize).hex_digest(); WriteTrace(TraceN64System, TraceDebug, "MD5: %s", m_MD5.c_str()); } m_Country = (Country)m_ROMImage[0x3D]; CalculateCicChip(); uint32_t CRC1, CRC2; if (IsLoadedRomDDIPL()) { // Handle CRC differently if it is a 64DD IPL CRC1 = (*(uint16_t *)(&m_ROMImage[0x608]) << 16) | *(uint16_t *)(&m_ROMImage[0x60C]); CRC2 = (*(uint16_t *)(&m_ROMImage[0x638]) << 16) | *(uint16_t *)(&m_ROMImage[0x63C]); } else { CRC1 = *(uint32_t *)(&m_ROMImage[0x10]); CRC2 = *(uint32_t *)(&m_ROMImage[0x14]); } m_RomIdent = stdstr_f("%08X-%08X-C:%X", CRC1, CRC2, m_ROMImage[0x3D]); WriteTrace(TraceN64System, TraceDebug, "Ident: %s", m_RomIdent.c_str()); if (!IsLoadedRomDDIPL()) { SetError(MSG_FAIL_IMAGE_IPL); return false; } if (!LoadBootCodeOnly && g_DDRom == this) { g_Settings->SaveBool(GameRunning_LoadingInProgress, false); } if (g_Settings->LoadBool(Game_CRC_Recalc)) { // Calculate ROM header CRC CalculateRomCrc(); } return true; } // Save the settings of the loaded ROM, so all loaded settings about ROM will be identified with this ROM void CN64Rom::SaveRomSettingID(bool temp) { g_Settings->SaveBool(Game_TempLoaded, temp); g_Settings->SaveString(Game_GameName, m_RomName.c_str()); g_Settings->SaveString(Game_IniKey, m_RomIdent.c_str()); g_Settings->SaveString(Game_UniqueSaveDir, stdstr_f("%s-%s", m_RomName.c_str(), m_MD5.c_str()).c_str()); g_Settings->SaveDword(Game_SystemType, IsPal() ? SYSTEM_PAL : SYSTEM_NTSC); } void CN64Rom::ClearRomSettingID() { g_Settings->SaveString(Game_GameName, ""); g_Settings->SaveString(Game_IniKey, ""); } void CN64Rom::SetError(LanguageStringID ErrorMsg) { m_ErrorMsg = ErrorMsg; } bool CN64Rom::IsPal() { switch (m_Country) { case Country_Germany: case Country_French: case Country_Italian: case Country_Europe: case Country_Spanish: case Country_Australia: case Country_EuropeanX_PAL: case Country_EuropeanY_PAL: return true; } return false; } void CN64Rom::UnallocateRomImage() { m_RomFile.Close(); if (m_ROMImageBase) { ProtectMemory(m_ROMImage, m_RomFileSize, MEM_READWRITE); delete[] m_ROMImageBase; m_ROMImageBase = nullptr; } m_ROMImage = nullptr; }