1017 lines
30 KiB
C++
1017 lines
30 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "N64Disk.h"
|
|
#include "SystemGlobals.h"
|
|
#include <Common/MemoryManagement.h>
|
|
#include <Common/Platform.h>
|
|
#include <Common/md5.h>
|
|
#include <Project64-core/N64System/Mips/Register.h>
|
|
#include <memory>
|
|
|
|
CN64Disk::CN64Disk() :
|
|
m_DiskImage(nullptr),
|
|
m_DiskImageBase(nullptr),
|
|
m_DiskHeader(nullptr),
|
|
m_DiskHeaderBase(nullptr),
|
|
m_ErrorMsg(EMPTY_STRING),
|
|
m_DiskBufAddress(0),
|
|
m_DiskSysAddress(0),
|
|
m_DiskIDAddress(0),
|
|
m_DiskRomAddress(0),
|
|
m_DiskRamAddress(0),
|
|
m_isShadowDisk(false)
|
|
{
|
|
}
|
|
|
|
CN64Disk::~CN64Disk()
|
|
{
|
|
}
|
|
|
|
bool CN64Disk::LoadDiskImage(const char * FileLoc)
|
|
{
|
|
UnallocateDiskImage();
|
|
m_ErrorMsg = EMPTY_STRING;
|
|
|
|
// Assume the file extension is *.ndd or *.d64
|
|
stdstr ext = CPath(FileLoc).GetExtension();
|
|
stdstr ShadowFile = FileLoc;
|
|
ShadowFile[ShadowFile.length() - 1] = 'r';
|
|
|
|
WriteTrace(TraceN64System, TraceDebug, "Attempting to load shadow file");
|
|
if (!AllocateAndLoadDiskImage(ShadowFile.c_str()))
|
|
{
|
|
m_isShadowDisk = false;
|
|
WriteTrace(TraceN64System, TraceDebug, "Loading shadow file failed");
|
|
UnallocateDiskImage();
|
|
if (!AllocateAndLoadDiskImage(FileLoc))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_isShadowDisk = true;
|
|
}
|
|
|
|
char RomName[5];
|
|
m_FileName = FileLoc;
|
|
uint32_t crc1 = CalculateCrc();
|
|
uint32_t crc2 = ~crc1;
|
|
m_DiskIdent.Format("%08X-%08X-C:%X", crc1, crc2, GetDiskAddressID()[0]);
|
|
// Get the disk ID from the disk image
|
|
if (*(uint32_t *)(&GetDiskAddressID()[0]) != 0)
|
|
{
|
|
// If not 0x00000000
|
|
RomName[0] = (char)*(GetDiskAddressID() + 3);
|
|
RomName[1] = (char)*(GetDiskAddressID() + 2);
|
|
RomName[2] = (char)*(GetDiskAddressID() + 1);
|
|
RomName[3] = (char)*(GetDiskAddressID() + 0);
|
|
RomName[4] = '\0';
|
|
}
|
|
else
|
|
{
|
|
// If 0x00000000 then use a made up one
|
|
RomName[0] = m_DiskIdent[12];
|
|
RomName[1] = m_DiskIdent[11];
|
|
RomName[2] = m_DiskIdent[10];
|
|
RomName[3] = m_DiskIdent[9];
|
|
RomName[4] = '\0';
|
|
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
{
|
|
m_DiskHeader[0x20 + (i ^ 3)] = (uint8_t)m_DiskIdent[9 + i];
|
|
}
|
|
}
|
|
m_RomName = RomName;
|
|
m_Country = GetDiskCountryCode();
|
|
m_DiskType = GetDiskAddressSys()[5 ^ 3] & 0x0F;
|
|
|
|
GenerateLBAToPhysTable();
|
|
InitSysDataD64();
|
|
DetectRamAddress();
|
|
LoadDiskRAMImage();
|
|
|
|
if (g_Disk == this)
|
|
{
|
|
g_Settings->SaveBool(GameRunning_LoadingInProgress, false);
|
|
SaveDiskSettingID(false);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CN64Disk::SaveDiskImage()
|
|
{
|
|
DeinitSysDataD64();
|
|
|
|
// No need to save if disk type is 6
|
|
if (m_DiskType == 6)
|
|
{
|
|
m_DiskFile.Close();
|
|
WriteTrace(TraceN64System, TraceDebug, "Loaded disk type is 6. No RAM area. Shadow file is not needed.");
|
|
return true;
|
|
}
|
|
|
|
// Assume the file extension is *.ndd / *.d64
|
|
if (m_DiskFormat == DiskFormatMAME || m_isShadowDisk || g_Settings->LoadDword(Setting_DiskSaveType) == SaveDisk_ShadowFile)
|
|
{
|
|
// Shadow file
|
|
stdstr ShadowFile = m_FileName;
|
|
ShadowFile[ShadowFile.length() - 1] = 'r';
|
|
|
|
WriteTrace(TraceN64System, TraceDebug, "Trying to open %s (shadow file)", ShadowFile.c_str());
|
|
m_DiskFile.Close();
|
|
if (!m_DiskFile.Open(ShadowFile.c_str(), CFileBase::modeWrite | CFileBase::modeCreate | CFileBase::modeNoTruncate))
|
|
{
|
|
WriteTrace(TraceN64System, TraceError, "Failed to open %s (shadow file)", ShadowFile.c_str());
|
|
return false;
|
|
}
|
|
|
|
m_DiskFile.SeekToBegin();
|
|
ForceByteSwapDisk();
|
|
|
|
if (!m_DiskFile.Write(m_DiskImage, m_DiskFileSize))
|
|
{
|
|
m_DiskFile.Close();
|
|
WriteTrace(TraceN64System, TraceError, "Failed to write file");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// RAM file
|
|
if (m_DiskFileSize <= m_DiskRamAddress || m_DiskRamAddress == 0)
|
|
{
|
|
m_DiskFile.Close();
|
|
return true;
|
|
}
|
|
|
|
stdstr ShadowFile = m_FileName;
|
|
ShadowFile[ShadowFile.length() - 1] = 'm';
|
|
ShadowFile[ShadowFile.length() - 2] = 'a';
|
|
ShadowFile[ShadowFile.length() - 3] = 'r';
|
|
|
|
WriteTrace(TraceN64System, TraceDebug, "Trying to open %s (RAM file)", ShadowFile.c_str());
|
|
m_DiskFile.Close();
|
|
if (!m_DiskFile.Open(ShadowFile.c_str(), CFileBase::modeWrite | CFileBase::modeCreate | CFileBase::modeNoTruncate))
|
|
{
|
|
WriteTrace(TraceN64System, TraceError, "Failed to open %s (RAM file)", ShadowFile.c_str());
|
|
return false;
|
|
}
|
|
|
|
m_DiskFile.SeekToBegin();
|
|
ForceByteSwapDisk();
|
|
|
|
if (!m_DiskFile.Write(GetDiskAddressRam(), m_DiskFileSize - m_DiskRamAddress))
|
|
{
|
|
m_DiskFile.Close();
|
|
WriteTrace(TraceN64System, TraceError, "Failed to write file");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_DiskFile.Close();
|
|
return true;
|
|
}
|
|
|
|
void CN64Disk::SwapDiskImage(const char * FileLoc)
|
|
{
|
|
g_Reg->ASIC_STATUS &= ~DD_STATUS_DISK_PRES;
|
|
LoadDiskImage(FileLoc);
|
|
}
|
|
|
|
bool CN64Disk::IsValidDiskImage(uint8_t Test[0x20])
|
|
{
|
|
// Basic system data check (first 0x20 bytes is enough)
|
|
// Disk type
|
|
if ((Test[0x05] & 0xEF) > 6) return false;
|
|
|
|
// IPL load block
|
|
uint16_t ipl_load_blk = ((Test[0x06] << 8) | Test[0x07]);
|
|
if (ipl_load_blk > 0x10C3 || ipl_load_blk == 0x0000) return false;
|
|
|
|
// IPL load address
|
|
uint32_t ipl_load_addr = (Test[0x1C] << 24) | (Test[0x1D] << 16) | (Test[0x1E] << 8) | Test[0x1F];
|
|
if (ipl_load_addr < 0x80000000 && ipl_load_addr >= 0x80800000) return false;
|
|
|
|
// Country code
|
|
if (*((uint32_t *)&Test[0]) == 0x16D348E8)
|
|
{
|
|
return true;
|
|
}
|
|
else if (*((uint32_t *)&Test[0]) == 0x56EE6322)
|
|
{
|
|
return true;
|
|
}
|
|
else if (*((uint32_t *)&Test[0]) == 0x00000000)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Save the settings of the loaded ROM, so all loaded settings about ROM will be identified with this ROM
|
|
|
|
void CN64Disk::SaveDiskSettingID(bool temp)
|
|
{
|
|
g_Settings->SaveBool(Game_TempLoaded, temp);
|
|
g_Settings->SaveString(Game_GameName, m_RomName.c_str());
|
|
g_Settings->SaveString(Game_IniKey, m_DiskIdent.c_str());
|
|
//g_Settings->SaveString(Game_UniqueSaveDir, stdstr_f("%s-%s", m_RomName.c_str(), m_MD5.c_str()).c_str());
|
|
|
|
switch (GetCountry())
|
|
{
|
|
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:
|
|
g_Settings->SaveDword(Game_SystemType, SYSTEM_PAL);
|
|
break;
|
|
default:
|
|
g_Settings->SaveDword(Game_SystemType, SYSTEM_NTSC);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CN64Disk::ClearDiskSettingID()
|
|
{
|
|
g_Settings->SaveString(Game_GameName, "");
|
|
g_Settings->SaveString(Game_IniKey, "");
|
|
}
|
|
|
|
bool CN64Disk::AllocateDiskImage(uint32_t DiskFileSize)
|
|
{
|
|
WriteTrace(TraceN64System, TraceDebug, "Allocating memory for disk");
|
|
std::unique_ptr<uint8_t> ImageBase(new uint8_t[DiskFileSize + 0x1000]);
|
|
if (ImageBase.get() == nullptr)
|
|
{
|
|
SetError(MSG_MEM_ALLOC_ERROR);
|
|
WriteTrace(TraceN64System, TraceError, "Failed to allocate memory for disk (size: 0x%X)", DiskFileSize);
|
|
return false;
|
|
}
|
|
uint8_t * Image = (uint8_t *)(((uint64_t)ImageBase.get() + 0xFFF) & ~0xFFF); // Start at beginning of memory page
|
|
WriteTrace(TraceN64System, TraceDebug, "Allocated disk memory (%p)", Image);
|
|
|
|
// Save information about the disk loaded
|
|
m_DiskImageBase = ImageBase.release();
|
|
m_DiskImage = Image;
|
|
m_DiskFileSize = DiskFileSize;
|
|
return true;
|
|
}
|
|
|
|
bool CN64Disk::AllocateDiskHeader()
|
|
{
|
|
WriteTrace(TraceN64System, TraceDebug, "Allocating memory for disk header forge");
|
|
std::unique_ptr<uint8_t> HeaderBase(new uint8_t[0x40 + 0x1000]);
|
|
if (HeaderBase.get() == nullptr)
|
|
{
|
|
SetError(MSG_MEM_ALLOC_ERROR);
|
|
WriteTrace(TraceN64System, TraceError, "Failed to allocate memory for disk header forge (size: 0x40)");
|
|
return false;
|
|
}
|
|
uint8_t * Header = (uint8_t *)(((uint64_t)HeaderBase.get() + 0xFFF) & ~0xFFF); // Start at beginning of memory page
|
|
WriteTrace(TraceN64System, TraceDebug, "Allocated disk memory (%p)", Header);
|
|
|
|
// Save information about the disk loaded
|
|
m_DiskHeaderBase = HeaderBase.release();
|
|
m_DiskHeader = Header;
|
|
return true;
|
|
}
|
|
|
|
bool CN64Disk::AllocateAndLoadDiskImage(const char * FileLoc)
|
|
{
|
|
WriteTrace(TraceN64System, TraceDebug, "Trying to open %s", FileLoc);
|
|
if (!m_DiskFile.Open(FileLoc, CFileBase::modeRead))
|
|
{
|
|
WriteTrace(TraceN64System, TraceError, "Failed to open %s", FileLoc);
|
|
return false;
|
|
}
|
|
|
|
// Make sure it is a valid disk image
|
|
uint8_t Test[0x100];
|
|
bool isValidDisk = false;
|
|
|
|
const uint8_t blocks[8] = {0, 1, 2, 3, 8, 9, 10, 11};
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
m_DiskFile.Seek(0x4D08 * blocks[i], CFileBase::SeekPosition::begin);
|
|
if (m_DiskFile.Read(Test, sizeof(Test)) != sizeof(Test))
|
|
{
|
|
m_DiskFile.Close();
|
|
WriteTrace(TraceN64System, TraceError, "Failed to read ident bytes");
|
|
return false;
|
|
}
|
|
|
|
isValidDisk = IsValidDiskImage(Test);
|
|
if (isValidDisk)
|
|
break;
|
|
}
|
|
if (!isValidDisk)
|
|
{
|
|
m_DiskFile.Close();
|
|
WriteTrace(TraceN64System, TraceError, "Invalid disk image file");
|
|
return false;
|
|
}
|
|
uint32_t DiskFileSize = m_DiskFile.GetLength();
|
|
stdstr ext = CPath(FileLoc).GetExtension();
|
|
WriteTrace(TraceN64System, TraceDebug, "Successfully opened, size: 0x%X", DiskFileSize);
|
|
|
|
// Check disk file format
|
|
if (((DiskFileSize == MameFormatSize) || (DiskFileSize == SDKFormatSize)) && (ext.compare("ndr") || ext.compare("ndd")))
|
|
{
|
|
if (DiskFileSize == MameFormatSize)
|
|
{
|
|
// If disk is MAME Format (size is constant, it should be the same for every file), then continue
|
|
m_DiskFormat = DiskFormatMAME;
|
|
WriteTrace(TraceN64System, TraceDebug, "Disk file is MAME format");
|
|
}
|
|
else
|
|
{
|
|
// If disk is SDK format (made with SDK-based dumpers like LuigiBlood's, or Nintendo's, size is also constant)
|
|
m_DiskFormat = DiskFormatSDK;
|
|
WriteTrace(TraceN64System, TraceDebug, "Disk file is SDK format");
|
|
}
|
|
|
|
if (!AllocateDiskImage(DiskFileSize))
|
|
{
|
|
m_DiskFile.Close();
|
|
return false;
|
|
}
|
|
|
|
// Load the N64 disk to the allocated memory
|
|
g_Notify->DisplayMessage(5, MSG_LOADING);
|
|
m_DiskFile.SeekToBegin();
|
|
|
|
uint32_t count, TotalRead = 0;
|
|
for (count = 0; count < (int)DiskFileSize; count += ReadFromRomSection)
|
|
{
|
|
uint32_t dwToRead = DiskFileSize - count;
|
|
if (dwToRead > ReadFromRomSection)
|
|
{
|
|
dwToRead = ReadFromRomSection;
|
|
}
|
|
|
|
if (m_DiskFile.Read(&m_DiskImage[count], dwToRead) != dwToRead)
|
|
{
|
|
m_DiskFile.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 percentage)
|
|
g_Notify->DisplayMessage(0, stdstr_f("%s: %.2f%c", GS(MSG_LOADED), ((float)TotalRead / (float)DiskFileSize) * 100.0f, '%').c_str());
|
|
}
|
|
|
|
if (DiskFileSize != TotalRead)
|
|
{
|
|
m_DiskFile.Close();
|
|
SetError(MSG_FAIL_IMAGE);
|
|
WriteTrace(TraceN64System, TraceError, "Expected to read: 0x%X, read: 0x%X", TotalRead, DiskFileSize);
|
|
return false;
|
|
}
|
|
|
|
DetectSystemArea();
|
|
|
|
g_Notify->DisplayMessage(5, MSG_BYTESWAP);
|
|
ByteSwapDisk();
|
|
}
|
|
else if ((DiskFileSize > 0x4F08) && (ext.compare("d6r") || ext.compare("d64")))
|
|
{
|
|
m_DiskFormat = DiskFormatD64;
|
|
WriteTrace(TraceN64System, TraceDebug, "Disk file is D64 format");
|
|
|
|
m_DiskType = Test[5];
|
|
uint16_t ROM_LBA_END = (Test[0xE0] << 8) | Test[0xE1];
|
|
uint16_t RAM_LBA_START = (Test[0xE2] << 8) | Test[0xE3];
|
|
uint16_t RAM_LBA_END = (Test[0xE4] << 8) | Test[0xE5];
|
|
|
|
if ((ROM_LBA_END + SYSTEM_LBAS) >= RAM_START_LBA[m_DiskType] ||
|
|
((RAM_LBA_START + SYSTEM_LBAS) != RAM_START_LBA[m_DiskType] && RAM_LBA_START != 0xFFFF))
|
|
{
|
|
m_DiskFile.Close();
|
|
SetError(MSG_FAIL_IMAGE);
|
|
WriteTrace(TraceN64System, TraceError, "Malformed D64 disk image");
|
|
return false;
|
|
}
|
|
|
|
uint32_t ROM_SIZE = LBAToByte(SYSTEM_LBAS, ROM_LBA_END + 1);
|
|
uint32_t RAM_SIZE = 0;
|
|
if (RAM_LBA_START != 0xFFFF && RAM_LBA_END != 0xFFFF)
|
|
RAM_SIZE = LBAToByte(SYSTEM_LBAS + RAM_LBA_START, RAM_LBA_END + 1 - RAM_LBA_START);
|
|
uint32_t FULL_RAM_SIZE = RAM_SIZES[m_DiskType];
|
|
|
|
if ((0x200 + ROM_SIZE + RAM_SIZE) != DiskFileSize)
|
|
{
|
|
m_DiskFile.Close();
|
|
SetError(MSG_FAIL_IMAGE);
|
|
WriteTrace(TraceN64System, TraceError, "Malformed D64 disk image, expected file size of 0x200 + 0x%X + 0x%X = %08X, actual file size: %08X", ROM_SIZE, RAM_SIZE, (0x200 + ROM_SIZE + RAM_SIZE), DiskFileSize);
|
|
return false;
|
|
}
|
|
|
|
// Allocate file with maximum size RAM area
|
|
WriteTrace(TraceN64System, TraceError, "Allocate D64 ROM %08X + RAM %08X", ROM_SIZE, FULL_RAM_SIZE);
|
|
if (!AllocateDiskImage(0x200 + ROM_SIZE + FULL_RAM_SIZE))
|
|
{
|
|
m_DiskFile.Close();
|
|
return false;
|
|
}
|
|
|
|
// Load the N64 disk to the allocated memory
|
|
g_Notify->DisplayMessage(5, MSG_LOADING);
|
|
m_DiskFile.SeekToBegin();
|
|
|
|
uint32_t count, TotalRead = 0;
|
|
for (count = 0; count < (int)DiskFileSize; count += ReadFromRomSection)
|
|
{
|
|
uint32_t dwToRead = DiskFileSize - count;
|
|
if (dwToRead > ReadFromRomSection)
|
|
{
|
|
dwToRead = ReadFromRomSection;
|
|
}
|
|
|
|
if (m_DiskFile.Read(&m_DiskImage[count], dwToRead) != dwToRead)
|
|
{
|
|
m_DiskFile.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 percentage)
|
|
g_Notify->DisplayMessage(0, stdstr_f("%s: %.2f%c", GS(MSG_LOADED), ((float)TotalRead / (float)DiskFileSize) * 100.0f, '%').c_str());
|
|
}
|
|
|
|
if (DiskFileSize != TotalRead)
|
|
{
|
|
m_DiskFile.Close();
|
|
SetError(MSG_FAIL_IMAGE);
|
|
WriteTrace(TraceN64System, TraceError, "Expected to read: 0x%X, read: 0x%X", TotalRead, DiskFileSize);
|
|
return false;
|
|
}
|
|
|
|
DetectSystemArea();
|
|
|
|
g_Notify->DisplayMessage(5, MSG_BYTESWAP);
|
|
ForceByteSwapDisk();
|
|
}
|
|
else
|
|
{
|
|
// Otherwise the disk file is invalid
|
|
m_DiskFile.Close();
|
|
WriteTrace(TraceN64System, TraceError, "Disk file is invalid, unexpected size");
|
|
return false;
|
|
}
|
|
|
|
ProtectMemory(m_DiskImage, m_DiskFileSize, MEM_READWRITE);
|
|
|
|
AllocateDiskHeader();
|
|
memcpy(m_DiskHeader, GetDiskAddressSys(), 0x20);
|
|
memcpy(m_DiskHeader + 0x20, GetDiskAddressID(), 0x20);
|
|
memcpy(m_DiskHeader + 0x3B, GetDiskAddressID(), 5);
|
|
return true;
|
|
}
|
|
|
|
bool CN64Disk::LoadDiskRAMImage()
|
|
{
|
|
if (g_Settings->LoadDword(Setting_DiskSaveType) == DISKSAVE_SHADOW || m_DiskFormat == DiskFormatMAME ||
|
|
m_isShadowDisk || m_DiskFileSize <= m_DiskRamAddress || m_DiskRamAddress == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
CFile ramfile;
|
|
stdstr filename = m_FileName;
|
|
|
|
filename[filename.length() - 1] = 'm';
|
|
filename[filename.length() - 2] = 'a';
|
|
filename[filename.length() - 3] = 'r';
|
|
|
|
WriteTrace(TraceN64System, TraceDebug, "Trying to open %s", filename.c_str());
|
|
if (!ramfile.Open(filename.c_str(), CFileBase::modeRead))
|
|
{
|
|
WriteTrace(TraceN64System, TraceError, "Failed to open %s", filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (ramfile.GetLength() != m_DiskFileSize - m_DiskRamAddress)
|
|
{
|
|
ramfile.Close();
|
|
WriteTrace(TraceN64System, TraceError, "RAM save file is the wrong size");
|
|
return false;
|
|
}
|
|
|
|
ForceByteSwapDisk();
|
|
ramfile.SeekToBegin();
|
|
if (ramfile.Read(GetDiskAddressRam(), m_DiskFileSize - m_DiskRamAddress) != (m_DiskFileSize - m_DiskRamAddress))
|
|
{
|
|
ramfile.Close();
|
|
WriteTrace(TraceN64System, TraceError, "Failed to read RAM save data");
|
|
return false;
|
|
}
|
|
|
|
ForceByteSwapDisk();
|
|
return true;
|
|
}
|
|
|
|
void CN64Disk::ByteSwapDisk()
|
|
{
|
|
uint32_t count;
|
|
|
|
switch (*((uint32_t *)&GetDiskAddressSys()[8]))
|
|
{
|
|
case 0x281E140A:
|
|
case 0x3024180C:
|
|
for (count = 0; count < m_DiskFileSize; count += 4)
|
|
{
|
|
m_DiskImage[count] ^= m_DiskImage[count + 3];
|
|
m_DiskImage[count + 3] ^= m_DiskImage[count];
|
|
m_DiskImage[count] ^= m_DiskImage[count + 3];
|
|
m_DiskImage[count + 1] ^= m_DiskImage[count + 2];
|
|
m_DiskImage[count + 2] ^= m_DiskImage[count + 1];
|
|
m_DiskImage[count + 1] ^= m_DiskImage[count + 2];
|
|
}
|
|
break;
|
|
case 0x0A141E28: break;
|
|
case 0x0C182430: break;
|
|
default:
|
|
g_Notify->DisplayError(stdstr_f("ByteSwapDisk: %08X - %08X", *((uint32_t *)&GetDiskAddressSys()[8]), m_DiskSysAddress).c_str());
|
|
}
|
|
}
|
|
|
|
void CN64Disk::ForceByteSwapDisk()
|
|
{
|
|
uint32_t count;
|
|
|
|
for (count = 0; count < m_DiskFileSize; count += 4)
|
|
{
|
|
m_DiskImage[count] ^= m_DiskImage[count + 3];
|
|
m_DiskImage[count + 3] ^= m_DiskImage[count];
|
|
m_DiskImage[count] ^= m_DiskImage[count + 3];
|
|
m_DiskImage[count + 1] ^= m_DiskImage[count + 2];
|
|
m_DiskImage[count + 2] ^= m_DiskImage[count + 1];
|
|
m_DiskImage[count + 1] ^= m_DiskImage[count + 2];
|
|
}
|
|
}
|
|
|
|
void CN64Disk::SetError(LanguageStringID ErrorMsg)
|
|
{
|
|
m_ErrorMsg = ErrorMsg;
|
|
}
|
|
|
|
void CN64Disk::UnallocateDiskImage()
|
|
{
|
|
m_DiskFile.Close();
|
|
|
|
if (m_DiskHeaderBase)
|
|
{
|
|
ProtectMemory(m_DiskHeader, 0x40, MEM_READWRITE);
|
|
delete[] m_DiskHeaderBase;
|
|
m_DiskHeaderBase = nullptr;
|
|
}
|
|
m_DiskHeader = nullptr;
|
|
|
|
if (m_DiskImageBase)
|
|
{
|
|
ProtectMemory(m_DiskImage, m_DiskFileSize, MEM_READWRITE);
|
|
delete[] m_DiskImageBase;
|
|
m_DiskImageBase = nullptr;
|
|
}
|
|
m_DiskImage = nullptr;
|
|
}
|
|
|
|
uint32_t CN64Disk::CalculateCrc()
|
|
{
|
|
// Custom CRC
|
|
uint32_t crc = 0;
|
|
for (int i = 0; i < 0x4D08; i += 4)
|
|
{
|
|
crc += *(uint32_t *)(&GetDiskAddressRom()[i]);
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
uint32_t CN64Disk::GetDiskAddressBlock(uint16_t head, uint16_t track, uint16_t block, uint16_t sector, uint16_t sectorsize)
|
|
{
|
|
uint32_t offset = 0;
|
|
if (m_DiskFormat == DiskFormatMAME)
|
|
{
|
|
// MAME
|
|
uint32_t tr_off = 0;
|
|
uint16_t dd_zone = 0;
|
|
|
|
if (track >= 0x425)
|
|
{
|
|
dd_zone = 7 + head;
|
|
tr_off = track - 0x425;
|
|
}
|
|
else if (track >= 0x390)
|
|
{
|
|
dd_zone = 6 + head;
|
|
tr_off = track - 0x390;
|
|
}
|
|
else if (track >= 0x2FB)
|
|
{
|
|
dd_zone = 5 + head;
|
|
tr_off = track - 0x2FB;
|
|
}
|
|
else if (track >= 0x266)
|
|
{
|
|
dd_zone = 4 + head;
|
|
tr_off = track - 0x266;
|
|
}
|
|
else if (track >= 0x1D1)
|
|
{
|
|
dd_zone = 3 + head;
|
|
tr_off = track - 0x1D1;
|
|
}
|
|
else if (track >= 0x13C)
|
|
{
|
|
dd_zone = 2 + head;
|
|
tr_off = track - 0x13C;
|
|
}
|
|
else if (track >= 0x9E)
|
|
{
|
|
dd_zone = 1 + head;
|
|
tr_off = track - 0x9E;
|
|
}
|
|
else
|
|
{
|
|
dd_zone = 0 + head;
|
|
tr_off = track;
|
|
}
|
|
|
|
offset = MAMEStartOffset[dd_zone] + tr_off * TRACKSIZE(dd_zone) + block * BLOCKSIZE(dd_zone) + sector * sectorsize;
|
|
|
|
if (offset < (BLOCKSIZE(0) * SYSTEM_LBAS) && sector == 0)
|
|
{
|
|
uint16_t AddressBlock = (uint16_t)(offset / (BLOCKSIZE(0)));
|
|
uint16_t block_sys = (uint16_t)(m_DiskSysAddress / (BLOCKSIZE(0)));
|
|
uint16_t block_id = (uint16_t)(m_DiskIDAddress / (BLOCKSIZE(0)));
|
|
|
|
if (AddressBlock < 12 && AddressBlock != block_sys)
|
|
{
|
|
offset = 0xFFFFFFFF;
|
|
}
|
|
else if (AddressBlock > 12 && AddressBlock < 16 && AddressBlock != block_id)
|
|
{
|
|
offset = 0xFFFFFFFF;
|
|
}
|
|
}
|
|
}
|
|
else if (m_DiskFormat == DiskFormatSDK)
|
|
{
|
|
// SDK
|
|
offset = LBAToByte(0, PhysToLBA(head, track, block)) + sector * sectorsize;
|
|
|
|
if (offset < (BLOCKSIZE(0) * SYSTEM_LBAS) && sector == 0)
|
|
{
|
|
uint16_t AddressBlock = (uint16_t)(offset / (BLOCKSIZE(0)));
|
|
uint16_t block_sys = (uint16_t)(m_DiskSysAddress / (BLOCKSIZE(0)));
|
|
uint16_t block_id = (uint16_t)(m_DiskIDAddress / (BLOCKSIZE(0)));
|
|
|
|
if (AddressBlock < 12 && AddressBlock != block_sys)
|
|
{
|
|
offset = 0xFFFFFFFF;
|
|
}
|
|
else if (AddressBlock > 12 && AddressBlock < 16 && AddressBlock != block_id)
|
|
{
|
|
offset = 0xFFFFFFFF;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// D64
|
|
uint16_t ROM_LBA_END = *(uint16_t *)(&GetDiskAddressSys()[0xE2]);
|
|
uint16_t RAM_LBA_START = *(uint16_t *)(&GetDiskAddressSys()[0xE0]);
|
|
uint16_t RAM_LBA_END = *(uint16_t *)(&GetDiskAddressSys()[0xE6]);
|
|
uint16_t LBA = PhysToLBA(head, track, block);
|
|
if (LBA < DISKID_LBA)
|
|
{
|
|
offset = m_DiskSysAddress;
|
|
}
|
|
else if ((LBA >= DISKID_LBA) && (LBA < SYSTEM_LBAS))
|
|
{
|
|
offset = m_DiskIDAddress;
|
|
}
|
|
else if (LBA <= (ROM_LBA_END + SYSTEM_LBAS))
|
|
{
|
|
offset = 0x200 + LBAToByte(SYSTEM_LBAS, LBA - SYSTEM_LBAS) + (sector * sectorsize);
|
|
}
|
|
else if (((LBA - SYSTEM_LBAS) <= RAM_LBA_END) && ((LBA - SYSTEM_LBAS) >= RAM_LBA_START))
|
|
{
|
|
offset = 0x200 + LBAToByte(SYSTEM_LBAS, ROM_LBA_END + 1);
|
|
offset += LBAToByte(RAM_LBA_START + SYSTEM_LBAS, LBA - RAM_LBA_START - SYSTEM_LBAS) + (sector * sectorsize);
|
|
}
|
|
else
|
|
{
|
|
offset = 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
if (offset >= m_DiskFileSize)
|
|
{
|
|
offset = 0xFFFFFFFF;
|
|
}
|
|
if (sector == 0)
|
|
{
|
|
WriteTrace(TraceN64System, TraceDebug, "Head %d Track %d Block %d - LBA %d - Address %08X", head, track, block, PhysToLBA(head, track, block), offset);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
void CN64Disk::DetectSystemArea()
|
|
{
|
|
if ((m_DiskFormat == DiskFormatMAME) || (m_DiskFormat == DiskFormatSDK))
|
|
{
|
|
// MAME / SDK (system area can be handled identically)
|
|
m_DiskSysAddress = 0;
|
|
m_DiskIDAddress = DISKID_LBA * 0x4D08;
|
|
m_DiskRomAddress = SYSTEM_LBAS * 0x4D08;
|
|
|
|
// Handle system data
|
|
const uint16_t sysblocks[4] = {9, 8, 1, 0};
|
|
// Check if disk is development disk
|
|
bool isDevDisk = false;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (IsSysSectorGood(sysblocks[i] + 2, 0xC0))
|
|
{
|
|
m_DiskSysAddress = ((sysblocks[i] + 2) * 0x4D08);
|
|
isDevDisk = true;
|
|
}
|
|
}
|
|
|
|
if (!isDevDisk)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (IsSysSectorGood(sysblocks[i], 0xE8))
|
|
{
|
|
m_DiskSysAddress = (sysblocks[i] * 0x4D08);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle disk ID
|
|
for (int i = 2; i > 0; i--)
|
|
{
|
|
// There are two disk ID blocks
|
|
if (IsSysSectorGood(DISKID_LBA + i, 0xE8))
|
|
{
|
|
m_DiskIDAddress = ((DISKID_LBA + i) * 0x4D08);
|
|
}
|
|
}
|
|
}
|
|
else //if (m_DiskFormat == DiskFormatD64)
|
|
{
|
|
// D64 (uses fixed addresses)
|
|
m_DiskSysAddress = 0x000;
|
|
m_DiskIDAddress = 0x100;
|
|
m_DiskRomAddress = 0x200;
|
|
}
|
|
}
|
|
|
|
bool CN64Disk::IsSysSectorGood(uint32_t block, uint32_t sectorsize)
|
|
{
|
|
// Checks if all sectors are identical (meant only to be used for system area for MAME and SDK formats)
|
|
for (int j = 1; j < SECTORS_PER_BLOCK; j++)
|
|
{
|
|
for (uint32_t k = 0; k < sectorsize; k++)
|
|
{
|
|
if (m_DiskImage[(block * 0x4D08) + (j * sectorsize) + k] != m_DiskImage[(block * 0x4D08) + k])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (block < DISKID_LBA)
|
|
{
|
|
// Check system data
|
|
|
|
// System format
|
|
if (m_DiskImage[(block * 0x4D08) + 4] != 0x10)
|
|
return false;
|
|
|
|
// Disk format
|
|
if ((m_DiskImage[(block * 0x4D08) + 5] & 0xF0) != 0x10)
|
|
return false;
|
|
|
|
// Always 0xFFFFFFFF
|
|
if (*(uint32_t *)&m_DiskImage[(block * 0x4D08) + 0x18] != 0xFFFFFFFF)
|
|
return false;
|
|
|
|
uint8_t alt = 0xC; // Retail
|
|
if ((block & 2) != 0)
|
|
{
|
|
alt = 0xA; // Development
|
|
}
|
|
|
|
// Alternate tracks offsets (always the same)
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
if (m_DiskImage[(block * 0x4D08) + 8 + i] != ((i + 1) * alt))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Country CN64Disk::GetDiskCountryCode()
|
|
{
|
|
switch (*(uint32_t *)&GetDiskAddressSys()[0])
|
|
{
|
|
case DISK_COUNTRY_JPN:
|
|
return Country_Japan;
|
|
case DISK_COUNTRY_USA:
|
|
return Country_NorthAmerica;
|
|
case DISK_COUNTRY_DEV:
|
|
default:
|
|
return Country_Unknown;
|
|
}
|
|
}
|
|
|
|
void CN64Disk::InitSysDataD64()
|
|
{
|
|
// Otherwise the disk will not work properly
|
|
if (m_DiskFormat != DiskFormatD64)
|
|
return;
|
|
|
|
GetDiskAddressSys()[4 ^ 3] = 0x10;
|
|
GetDiskAddressSys()[5 ^ 3] |= 0x10;
|
|
|
|
// Expand RAM area for file format consistency
|
|
if (m_DiskType < 6)
|
|
{
|
|
*(uint16_t *)&GetDiskAddressSys()[0xE2 ^ 2] = RAM_START_LBA[m_DiskType] - SYSTEM_LBAS;
|
|
*(uint16_t *)&GetDiskAddressSys()[0xE4 ^ 2] = MAX_LBA - SYSTEM_LBAS;
|
|
}
|
|
else
|
|
{
|
|
*(uint16_t *)&GetDiskAddressSys()[0xE2 ^ 2] = 0xFFFF;
|
|
*(uint16_t *)&GetDiskAddressSys()[0xE4 ^ 2] = 0xFFFF;
|
|
}
|
|
}
|
|
|
|
void CN64Disk::DeinitSysDataD64()
|
|
{
|
|
// Restore the data
|
|
if (m_DiskFormat != DiskFormatD64)
|
|
return;
|
|
|
|
GetDiskAddressSys()[4 ^ 3] = 0x00;
|
|
GetDiskAddressSys()[5 ^ 3] &= 0x0F;
|
|
}
|
|
|
|
void CN64Disk::GenerateLBAToPhysTable()
|
|
{
|
|
for (uint32_t lba = 0; lba < SIZE_LBA; lba++)
|
|
{
|
|
LBAToPhysTable[lba] = LBAToPhys(lba);
|
|
}
|
|
}
|
|
|
|
void CN64Disk::DetectRamAddress()
|
|
{
|
|
if (m_DiskFormat == DiskFormatMAME)
|
|
{
|
|
// Not supported
|
|
m_DiskRamAddress = 0;
|
|
}
|
|
else if (m_DiskFormat == DiskFormatSDK)
|
|
{
|
|
m_DiskRamAddress = LBAToByte(0, RAM_START_LBA[m_DiskType]);
|
|
}
|
|
else //if (m_DiskFormat == DiskFormatD64)
|
|
{
|
|
m_DiskRamAddress = m_DiskRomAddress + LBAToByte(SYSTEM_LBAS, *(uint16_t *)(&GetDiskAddressSys()[0xE0 ^ 2]) + 1);
|
|
}
|
|
}
|
|
|
|
uint32_t CN64Disk::LBAToVZone(uint32_t lba)
|
|
{
|
|
for (uint32_t vzone = 0; vzone < 16; vzone++)
|
|
{
|
|
if (lba < VZONE_LBA_TBL[m_DiskType][vzone])
|
|
{
|
|
return vzone;
|
|
}
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
uint32_t CN64Disk::LBAToByte(uint32_t lba, uint32_t nlbas)
|
|
{
|
|
bool init_flag = true;
|
|
uint32_t totalbytes = 0;
|
|
uint32_t blocksize = 0;
|
|
uint32_t vzone = 0, pzone = 0;
|
|
if (nlbas != 0)
|
|
{
|
|
for (; nlbas != 0; nlbas--)
|
|
{
|
|
if ((init_flag == true) || (VZONE_LBA_TBL[m_DiskType][vzone] == lba))
|
|
{
|
|
vzone = LBAToVZone(lba);
|
|
pzone = VZoneToPZone(vzone, m_DiskType);
|
|
if (7 < pzone)
|
|
{
|
|
pzone -= 7;
|
|
}
|
|
blocksize = SECTORSIZE_P[pzone] * SECTORS_PER_BLOCK;
|
|
}
|
|
|
|
totalbytes += blocksize;
|
|
lba++;
|
|
init_flag = false;
|
|
if (((nlbas - 1) != 0) && (lba > MAX_LBA))
|
|
{
|
|
return 0xFFFFFFFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
return totalbytes;
|
|
}
|
|
|
|
uint16_t CN64Disk::LBAToPhys(uint32_t lba)
|
|
{
|
|
uint8_t * sys_data = GetDiskAddressSys();
|
|
|
|
// Get block 0/1 on disk track
|
|
uint8_t block = 1;
|
|
if (((lba & 3) == 0) || ((lba & 3) == 3))
|
|
block = 0;
|
|
|
|
// Get virtual and physical disk zones
|
|
uint16_t vzone = (uint16_t)LBAToVZone(lba);
|
|
uint16_t pzone = VZoneToPZone(vzone, m_DiskType);
|
|
|
|
// Get disk head
|
|
uint16_t head = (7 < pzone);
|
|
|
|
// Get disk zone
|
|
uint16_t disk_zone = pzone;
|
|
if (disk_zone != 0)
|
|
disk_zone = pzone - 7;
|
|
|
|
// Get virtual zone LBA start, if zone 0, it's LBA 0
|
|
uint16_t vzone_lba = 0;
|
|
if (vzone != 0)
|
|
vzone_lba = VZONE_LBA_TBL[m_DiskType][vzone - 1];
|
|
|
|
// Calculate physical track
|
|
uint16_t track = (uint16_t)((lba - vzone_lba) >> 1);
|
|
|
|
// Get the start track from current zone
|
|
uint16_t track_zone_start = SCYL_ZONE_TBL[0][pzone];
|
|
if (head != 0)
|
|
{
|
|
// If head 1, count from the other way around
|
|
track = -track;
|
|
track_zone_start = OUTERCYL_TBL[disk_zone - 1];
|
|
}
|
|
track += SCYL_ZONE_TBL[0][pzone];
|
|
|
|
// Get the relative offset to defect tracks for the current zone (if zone 0, then it's 0)
|
|
uint16_t defect_offset = 0;
|
|
if (pzone != 0)
|
|
defect_offset = sys_data[(8 + pzone - 1) ^ 3];
|
|
|
|
// Get amount of defect tracks for the current zone
|
|
uint16_t defect_amount = sys_data[(8 + pzone) ^ 3] - defect_offset;
|
|
|
|
// Skip defect tracks
|
|
while ((defect_amount != 0) && ((sys_data[(0x20 + defect_offset) ^ 3] + track_zone_start) <= track))
|
|
{
|
|
track++;
|
|
defect_offset++;
|
|
defect_amount--;
|
|
}
|
|
|
|
return track | (head * 0x1000) | (block * 0x2000);
|
|
}
|
|
|
|
uint16_t CN64Disk::PhysToLBA(uint16_t head, uint16_t track, uint16_t block)
|
|
{
|
|
uint16_t expectedvalue = track | (head * 0x1000) | (block * 0x2000);
|
|
|
|
for (uint16_t lba = 0; lba < SIZE_LBA; lba++)
|
|
{
|
|
if (LBAToPhysTable[lba] == expectedvalue)
|
|
{
|
|
return lba;
|
|
}
|
|
}
|
|
return 0xFFFF;
|
|
}
|