2017-01-22 19:34:59 +00:00
|
|
|
/*
|
2022-01-09 01:15:50 +00:00
|
|
|
Copyright 2016-2022 melonDS team
|
2017-01-22 19:34:59 +00:00
|
|
|
|
|
|
|
This file is part of melonDS.
|
|
|
|
|
|
|
|
melonDS is free software: you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free
|
|
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
|
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include "NDS.h"
|
2019-07-03 10:37:34 +00:00
|
|
|
#include "DSi.h"
|
2017-01-22 19:34:59 +00:00
|
|
|
#include "NDSCart.h"
|
2017-06-25 15:35:45 +00:00
|
|
|
#include "ARM.h"
|
2022-01-07 13:00:43 +00:00
|
|
|
#include "CRC32.h"
|
2019-08-07 10:57:12 +00:00
|
|
|
#include "DSi_AES.h"
|
2019-03-27 03:23:03 +00:00
|
|
|
#include "Platform.h"
|
2020-05-29 22:28:21 +00:00
|
|
|
#include "ROMList.h"
|
2020-09-03 18:28:07 +00:00
|
|
|
#include "melonDLDI.h"
|
2023-06-30 11:28:52 +00:00
|
|
|
#include "xxhash/xxhash.h"
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2023-03-23 17:04:38 +00:00
|
|
|
using Platform::Log;
|
|
|
|
using Platform::LogLevel;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
|
|
|
namespace NDSCart
|
2017-01-31 16:34:17 +00:00
|
|
|
{
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
// SRAM TODO: emulate write delays???
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u16 SPICnt;
|
|
|
|
u32 ROMCnt;
|
2017-02-03 15:57:31 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 SPIData;
|
|
|
|
u32 SPIDataPos;
|
|
|
|
bool SPIHold;
|
|
|
|
|
|
|
|
u8 ROMCommand[8];
|
|
|
|
u32 ROMData;
|
|
|
|
|
|
|
|
u8 TransferData[0x4000];
|
|
|
|
u32 TransferPos;
|
|
|
|
u32 TransferLen;
|
|
|
|
u32 TransferDir;
|
|
|
|
u8 TransferCmd[8];
|
2017-02-03 15:57:31 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
std::unique_ptr<CartCommon> Cart;
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u32 Key1_KeyBuf[0x412];
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u64 Key2_X;
|
|
|
|
u64 Key2_Y;
|
2017-02-03 15:57:31 +00:00
|
|
|
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u32 ByteSwap(u32 val)
|
2017-01-31 16:34:17 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
|
2017-02-07 21:23:46 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void Key1_Encrypt(u32* data)
|
2017-02-07 21:23:46 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
u32 y = data[0];
|
|
|
|
u32 x = data[1];
|
|
|
|
u32 z;
|
|
|
|
|
|
|
|
for (u32 i = 0x0; i <= 0xF; i++)
|
|
|
|
{
|
|
|
|
z = Key1_KeyBuf[i] ^ x;
|
|
|
|
x = Key1_KeyBuf[0x012 + (z >> 24) ];
|
|
|
|
x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)];
|
|
|
|
x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)];
|
|
|
|
x += Key1_KeyBuf[0x312 + (z & 0xFF)];
|
|
|
|
x ^= y;
|
|
|
|
y = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[0] = x ^ Key1_KeyBuf[0x10];
|
|
|
|
data[1] = y ^ Key1_KeyBuf[0x11];
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void Key1_Decrypt(u32* data)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
u32 y = data[0];
|
|
|
|
u32 x = data[1];
|
|
|
|
u32 z;
|
|
|
|
|
|
|
|
for (u32 i = 0x11; i >= 0x2; i--)
|
|
|
|
{
|
|
|
|
z = Key1_KeyBuf[i] ^ x;
|
|
|
|
x = Key1_KeyBuf[0x012 + (z >> 24) ];
|
|
|
|
x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)];
|
|
|
|
x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)];
|
|
|
|
x += Key1_KeyBuf[0x312 + (z & 0xFF)];
|
|
|
|
x ^= y;
|
|
|
|
y = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[0] = x ^ Key1_KeyBuf[0x1];
|
|
|
|
data[1] = y ^ Key1_KeyBuf[0x0];
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void Key1_ApplyKeycode(u32* keycode, u32 mod)
|
|
|
|
{
|
|
|
|
Key1_Encrypt(&keycode[1]);
|
|
|
|
Key1_Encrypt(&keycode[0]);
|
|
|
|
|
|
|
|
u32 temp[2] = {0,0};
|
|
|
|
|
|
|
|
for (u32 i = 0; i <= 0x11; i++)
|
|
|
|
{
|
|
|
|
Key1_KeyBuf[i] ^= ByteSwap(keycode[i % mod]);
|
|
|
|
}
|
|
|
|
for (u32 i = 0; i <= 0x410; i+=2)
|
|
|
|
{
|
|
|
|
Key1_Encrypt(temp);
|
|
|
|
Key1_KeyBuf[i ] = temp[1];
|
|
|
|
Key1_KeyBuf[i+1] = temp[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength)
|
2022-01-07 13:00:43 +00:00
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
if (externalBios)
|
2022-01-07 13:00:43 +00:00
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 expected_bios_length = dsi ? 0x10000 : 0x4000;
|
|
|
|
if (biosLength != expected_bios_length)
|
|
|
|
{
|
|
|
|
Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got %u bytes\n", expected_bios_length, biosLength);
|
|
|
|
}
|
|
|
|
else if (bios == nullptr)
|
2022-01-08 10:20:21 +00:00
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got nullptr\n", expected_bios_length);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy(Key1_KeyBuf, bios + (dsi ? 0xC6D0 : 0x0030), 0x1048);
|
|
|
|
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n");
|
2022-01-08 10:20:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// well
|
|
|
|
memset(Key1_KeyBuf, 0, 0x1048);
|
2023-06-30 11:28:52 +00:00
|
|
|
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n");
|
2022-01-07 13:00:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
Key1_LoadKeyBuf(dsi, Platform::GetConfigBool(Platform::ExternalBIOSEnable), bios, biosLength);
|
2021-04-24 22:48:02 +00:00
|
|
|
|
|
|
|
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
|
|
|
|
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
|
|
|
|
if (level >= 2) Key1_ApplyKeycode(keycode, mod);
|
|
|
|
if (level >= 3)
|
|
|
|
{
|
|
|
|
keycode[1] <<= 1;
|
|
|
|
keycode[2] >>= 1;
|
|
|
|
Key1_ApplyKeycode(keycode, mod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Key2_Encrypt(u8* data, u32 len)
|
|
|
|
{
|
|
|
|
for (u32 i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
Key2_X = (((Key2_X >> 5) ^
|
|
|
|
(Key2_X >> 17) ^
|
|
|
|
(Key2_X >> 18) ^
|
|
|
|
(Key2_X >> 31)) & 0xFF)
|
|
|
|
+ (Key2_X << 8);
|
|
|
|
Key2_Y = (((Key2_Y >> 5) ^
|
|
|
|
(Key2_Y >> 23) ^
|
|
|
|
(Key2_Y >> 18) ^
|
|
|
|
(Key2_Y >> 31)) & 0xFF)
|
|
|
|
+ (Key2_Y << 8);
|
|
|
|
|
|
|
|
Key2_X &= 0x0000007FFFFFFFFFULL;
|
|
|
|
Key2_Y &= 0x0000007FFFFFFFFFULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
|
|
|
ROM = rom;
|
|
|
|
ROMLength = len;
|
|
|
|
ChipID = chipid;
|
2023-06-30 11:28:52 +00:00
|
|
|
ROMParams = romparams;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
memcpy(&Header, rom, sizeof(Header));
|
|
|
|
IsDSi = Header.IsDSi() && !badDSiDump;
|
|
|
|
DSiBase = Header.DSiRegionStart << 19;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CartCommon::~CartCommon()
|
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
delete[] ROM;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 CartCommon::Checksum() const
|
2022-01-07 13:00:43 +00:00
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
const NDSHeader& header = GetHeader();
|
2022-01-07 13:00:43 +00:00
|
|
|
u32 crc = CRC32(ROM, 0x40);
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
crc = CRC32(&ROM[header.ARM9ROMOffset], header.ARM9Size, crc);
|
|
|
|
crc = CRC32(&ROM[header.ARM7ROMOffset], header.ARM7Size, crc);
|
2022-01-07 13:00:43 +00:00
|
|
|
|
|
|
|
if (IsDSi)
|
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
crc = CRC32(&ROM[header.DSiARM9iROMOffset], header.DSiARM9iSize, crc);
|
|
|
|
crc = CRC32(&ROM[header.DSiARM7iROMOffset], header.DSiARM7iSize, crc);
|
2022-01-07 13:00:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return crc;
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartCommon::Reset()
|
|
|
|
{
|
|
|
|
CmdEncMode = 0;
|
|
|
|
DataEncMode = 0;
|
2021-07-22 19:37:34 +00:00
|
|
|
DSiMode = false;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
2023-04-28 15:05:34 +00:00
|
|
|
void CartCommon::SetupDirectBoot(const std::string& romname)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
|
|
|
CmdEncMode = 2;
|
|
|
|
DataEncMode = 2;
|
2022-01-07 13:00:43 +00:00
|
|
|
DSiMode = IsDSi && (NDS::ConsoleType==1);
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CartCommon::DoSavestate(Savestate* file)
|
2018-10-17 22:54:21 +00:00
|
|
|
{
|
|
|
|
file->Section("NDCS");
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
file->Var32(&CmdEncMode);
|
|
|
|
file->Var32(&DataEncMode);
|
2021-07-22 19:37:34 +00:00
|
|
|
file->Bool32(&DSiMode);
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
void CartCommon::SetupSave(u32 type)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
void CartCommon::LoadSave(const u8* savedata, u32 savelen)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len)
|
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
if (CmdEncMode == 0)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
switch (cmd[0])
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
case 0x9F:
|
|
|
|
memset(data, 0xFF, len);
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2021-04-30 15:29:04 +00:00
|
|
|
case 0x00:
|
|
|
|
memset(data, 0, len);
|
|
|
|
if (len > 0x1000)
|
|
|
|
{
|
|
|
|
ReadROM(0, 0x1000, data, 0);
|
|
|
|
for (u32 pos = 0x1000; pos < len; pos += 0x1000)
|
|
|
|
memcpy(data+pos, data, 0x1000);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ReadROM(0, len, data, 0);
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2021-04-30 15:29:04 +00:00
|
|
|
case 0x90:
|
|
|
|
for (u32 pos = 0; pos < len; pos += 4)
|
|
|
|
*(u32*)&data[pos] = ChipID;
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2021-04-30 15:29:04 +00:00
|
|
|
case 0x3C:
|
|
|
|
CmdEncMode = 1;
|
2023-06-30 11:28:52 +00:00
|
|
|
Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
|
2021-07-22 19:37:34 +00:00
|
|
|
DSiMode = false;
|
2021-04-30 15:29:04 +00:00
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2021-04-30 15:29:04 +00:00
|
|
|
case 0x3D:
|
|
|
|
if (IsDSi)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
2021-07-22 19:37:34 +00:00
|
|
|
CmdEncMode = 1;
|
2023-06-30 11:28:52 +00:00
|
|
|
Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS));
|
2021-07-22 19:37:34 +00:00
|
|
|
DSiMode = true;
|
2021-04-30 15:29:04 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2021-04-30 15:29:04 +00:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2021-07-22 19:37:34 +00:00
|
|
|
else if (CmdEncMode == 1)
|
2021-04-30 15:29:04 +00:00
|
|
|
{
|
|
|
|
// decrypt the KEY1 command as needed
|
|
|
|
// (KEY2 commands do not need decrypted because KEY2 is handled entirely by hardware,
|
|
|
|
// but KEY1 is not, so DS software is responsible for encrypting KEY1 commands)
|
|
|
|
u8 cmddec[8];
|
|
|
|
*(u32*)&cmddec[0] = ByteSwap(*(u32*)&cmd[4]);
|
|
|
|
*(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmd[0]);
|
|
|
|
Key1_Decrypt((u32*)cmddec);
|
|
|
|
u32 tmp = ByteSwap(*(u32*)&cmddec[4]);
|
|
|
|
*(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmddec[0]);
|
|
|
|
*(u32*)&cmddec[0] = tmp;
|
|
|
|
|
|
|
|
// TODO eventually: verify all the command parameters and shit
|
|
|
|
|
|
|
|
switch (cmddec[0] & 0xF0)
|
|
|
|
{
|
|
|
|
case 0x40:
|
|
|
|
DataEncMode = 2;
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2021-04-30 15:29:04 +00:00
|
|
|
case 0x10:
|
|
|
|
for (u32 pos = 0; pos < len; pos += 4)
|
|
|
|
*(u32*)&data[pos] = ChipID;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0x20:
|
|
|
|
{
|
|
|
|
u32 addr = (cmddec[2] & 0xF0) << 8;
|
2021-07-22 19:37:34 +00:00
|
|
|
if (DSiMode)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
// the DSi region starts with 0x3000 unreadable bytes
|
|
|
|
// similarly to how the DS region starts at 0x1000 with 0x3000 unreadable bytes
|
|
|
|
// these contain data for KEY1 crypto
|
|
|
|
addr -= 0x1000;
|
2021-07-22 19:37:34 +00:00
|
|
|
addr += DSiBase;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
2021-04-30 15:29:04 +00:00
|
|
|
ReadROM(addr, 0x1000, data, 0);
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
2021-04-30 15:29:04 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0xA0:
|
|
|
|
CmdEncMode = 2;
|
|
|
|
return 0;
|
2021-04-30 15:34:41 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CmdEncMode == 2)
|
|
|
|
{
|
|
|
|
switch (cmd[0])
|
|
|
|
{
|
|
|
|
case 0xB8:
|
|
|
|
for (u32 pos = 0; pos < len; pos += 4)
|
|
|
|
*(u32*)&data[pos] = ChipID;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-30 15:29:04 +00:00
|
|
|
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CartCommon::ROMCommandFinish(u8* cmd, u8* data, u32 len)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last)
|
|
|
|
{
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CartCommon::SetIRQ()
|
|
|
|
{
|
|
|
|
NDS::SetIRQ(0, NDS::IRQ_CartIREQMC);
|
|
|
|
NDS::SetIRQ(1, NDS::IRQ_CartIREQMC);
|
|
|
|
}
|
|
|
|
|
2023-03-27 20:36:26 +00:00
|
|
|
u8 *CartCommon::GetSaveMemory() const
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 CartCommon::GetSaveMemoryLength() const
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset)
|
|
|
|
{
|
|
|
|
if (addr >= ROMLength) return;
|
|
|
|
if ((addr+len) > ROMLength)
|
|
|
|
len = ROMLength - addr;
|
|
|
|
|
|
|
|
memcpy(data+offset, ROM+addr, len);
|
|
|
|
}
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
const NDSBanner* CartCommon::Banner() const
|
|
|
|
{
|
|
|
|
const NDSHeader& header = GetHeader();
|
|
|
|
size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40;
|
|
|
|
if (header.BannerOffset >= 0x200 && header.BannerOffset < (ROMLength - bannersize))
|
|
|
|
{
|
|
|
|
return reinterpret_cast<const NDSBanner*>(ROM + header.BannerOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) : CartCommon(rom, len, chipid, badDSiDump, romparams)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
|
|
|
SRAM = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CartRetail::~CartRetail()
|
|
|
|
{
|
|
|
|
if (SRAM) delete[] SRAM;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CartRetail::Reset()
|
|
|
|
{
|
|
|
|
CartCommon::Reset();
|
|
|
|
|
|
|
|
SRAMCmd = 0;
|
|
|
|
SRAMAddr = 0;
|
|
|
|
SRAMStatus = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CartRetail::DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
CartCommon::DoSavestate(file);
|
|
|
|
|
2018-12-11 19:39:07 +00:00
|
|
|
// we reload the SRAM contents.
|
2022-01-07 13:00:43 +00:00
|
|
|
// it should be the same file, but the contents may change
|
2018-10-17 22:54:21 +00:00
|
|
|
|
|
|
|
u32 oldlen = SRAMLength;
|
|
|
|
|
|
|
|
file->Var32(&SRAMLength);
|
|
|
|
if (SRAMLength != oldlen)
|
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Warn, "savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength);
|
|
|
|
Log(LogLevel::Warn, "oh well. loading it anyway. adsfgdsf\n");
|
2018-10-17 22:54:21 +00:00
|
|
|
|
|
|
|
if (oldlen) delete[] SRAM;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAM = nullptr;
|
2018-10-17 22:54:21 +00:00
|
|
|
if (SRAMLength) SRAM = new u8[SRAMLength];
|
|
|
|
}
|
|
|
|
if (SRAMLength)
|
|
|
|
{
|
|
|
|
file->VarArray(SRAM, SRAMLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
// SPI status shito
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
file->Var8(&SRAMCmd);
|
|
|
|
file->Var32(&SRAMAddr);
|
|
|
|
file->Var8(&SRAMStatus);
|
2021-02-23 01:46:02 +00:00
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
if ((!file->Saving) && SRAM)
|
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength);
|
2018-10-17 22:54:21 +00:00
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
void CartRetail::SetupSave(u32 type)
|
2017-01-31 16:34:17 +00:00
|
|
|
{
|
|
|
|
if (SRAM) delete[] SRAM;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAM = nullptr;
|
2017-02-03 15:57:31 +00:00
|
|
|
|
2021-05-01 13:06:08 +00:00
|
|
|
if (type > 10) type = 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
int sramlen[] =
|
|
|
|
{
|
|
|
|
0,
|
|
|
|
512,
|
|
|
|
8192, 65536, 128*1024,
|
|
|
|
256*1024, 512*1024, 1024*1024,
|
2021-04-29 23:13:35 +00:00
|
|
|
8192*1024, 16384*1024, 65536*1024
|
2021-04-24 22:48:02 +00:00
|
|
|
};
|
|
|
|
SRAMLength = sramlen[type];
|
|
|
|
|
|
|
|
if (SRAMLength)
|
2017-01-31 16:34:17 +00:00
|
|
|
{
|
|
|
|
SRAM = new u8[SRAMLength];
|
2021-04-24 22:48:02 +00:00
|
|
|
memset(SRAM, 0xFF, SRAMLength);
|
|
|
|
}
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (type)
|
2017-01-31 16:34:17 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
case 1: SRAMType = 1; break; // EEPROM, small
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4: SRAMType = 2; break; // EEPROM, regular
|
|
|
|
case 5:
|
|
|
|
case 6:
|
|
|
|
case 7: SRAMType = 3; break; // FLASH
|
|
|
|
case 8:
|
2021-04-29 23:13:35 +00:00
|
|
|
case 9:
|
|
|
|
case 10: SRAMType = 4; break; // NAND
|
2021-04-24 22:48:02 +00:00
|
|
|
default: SRAMType = 0; break; // ...whatever else
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
void CartRetail::LoadSave(const u8* savedata, u32 savelen)
|
2018-10-23 20:54:09 +00:00
|
|
|
{
|
2022-01-07 13:00:43 +00:00
|
|
|
if (!SRAM) return;
|
2018-10-23 20:54:09 +00:00
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
u32 len = std::min(savelen, SRAMLength);
|
|
|
|
memcpy(SRAM, savedata, len);
|
|
|
|
Platform::WriteNDSSave(savedata, len, 0, len);
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
2017-02-03 15:57:31 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len);
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (cmd[0])
|
2017-03-23 22:47:55 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0xB7:
|
2017-03-23 22:47:55 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
|
|
|
|
memset(data, 0, len);
|
|
|
|
|
|
|
|
if (((addr + len - 1) >> 12) != (addr >> 12))
|
|
|
|
{
|
|
|
|
u32 len1 = 0x1000 - (addr & 0xFFF);
|
|
|
|
ReadROM_B7(addr, len1, data, 0);
|
|
|
|
ReadROM_B7(addr+len1, len-len1, data, len1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ReadROM_B7(addr, len, data, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return CartCommon::ROMCommandStart(cmd, data, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)
|
|
|
|
{
|
|
|
|
if (SRAMType == 0) return 0;
|
|
|
|
|
|
|
|
if (pos == 0)
|
|
|
|
{
|
|
|
|
// handle generic commands with no parameters
|
|
|
|
switch (val)
|
|
|
|
{
|
|
|
|
case 0x04: // write disable
|
|
|
|
SRAMStatus &= ~(1<<1);
|
2022-03-14 17:08:29 +00:00
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x06: // write enable
|
|
|
|
SRAMStatus |= (1<<1);
|
2022-03-14 17:08:29 +00:00
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
SRAMCmd = val;
|
|
|
|
SRAMAddr = 0;
|
|
|
|
}
|
|
|
|
|
2022-03-14 17:08:29 +00:00
|
|
|
return 0xFF;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (SRAMType)
|
|
|
|
{
|
|
|
|
case 1: return SRAMWrite_EEPROMTiny(val, pos, last);
|
|
|
|
case 2: return SRAMWrite_EEPROM(val, pos, last);
|
|
|
|
case 3: return SRAMWrite_FLASH(val, pos, last);
|
2022-03-14 17:08:29 +00:00
|
|
|
default: return 0xFF;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-27 20:36:26 +00:00
|
|
|
u8 *CartRetail::GetSaveMemory() const
|
|
|
|
{
|
|
|
|
return SRAM;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 CartRetail::GetSaveMemoryLength() const
|
|
|
|
{
|
|
|
|
return SRAMLength;
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
|
|
|
|
{
|
|
|
|
addr &= (ROMLength-1);
|
|
|
|
|
|
|
|
if (addr < 0x8000)
|
|
|
|
addr = 0x8000 + (addr & 0x1FF);
|
|
|
|
|
2021-07-22 19:37:34 +00:00
|
|
|
if (IsDSi && (addr >= DSiBase))
|
|
|
|
{
|
|
|
|
// for DSi carts:
|
|
|
|
// * in DSi mode: block the first 0x3000 bytes of the DSi area
|
|
|
|
// * in DS mode: block the entire DSi area
|
|
|
|
|
|
|
|
if ((!DSiMode) || (addr < (DSiBase+0x3000)))
|
|
|
|
addr = 0x8000 + (addr & 0x1FF);
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
|
|
|
|
memcpy(data+offset, ROM+addr, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
|
|
|
|
{
|
|
|
|
switch (SRAMCmd)
|
|
|
|
{
|
|
|
|
case 0x01: // write status register
|
|
|
|
// TODO: WP bits should be nonvolatile!
|
|
|
|
if (pos == 1)
|
|
|
|
SRAMStatus = (SRAMStatus & 0x01) | (val & 0x0C);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0x05: // read status register
|
|
|
|
return SRAMStatus | 0xF0;
|
|
|
|
|
|
|
|
case 0x02: // write low
|
|
|
|
case 0x0A: // write high
|
|
|
|
if (pos < 2)
|
|
|
|
{
|
|
|
|
SRAMAddr = val;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAMFirstAddr = SRAMAddr;
|
2017-03-23 22:47:55 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
// TODO: implement WP bits!
|
|
|
|
if (SRAMStatus & (1<<1))
|
|
|
|
{
|
|
|
|
SRAM[(SRAMAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF] = val;
|
|
|
|
}
|
|
|
|
SRAMAddr++;
|
2017-03-23 22:47:55 +00:00
|
|
|
}
|
2022-01-07 13:00:43 +00:00
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
SRAMStatus &= ~(1<<1);
|
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength,
|
|
|
|
(SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr);
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
2017-03-23 22:47:55 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x03: // read low
|
|
|
|
case 0x0B: // read high
|
|
|
|
if (pos < 2)
|
2017-03-23 22:47:55 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
SRAMAddr = val;
|
|
|
|
return 0;
|
2017-03-23 22:47:55 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 ret = SRAM[(SRAMAddr + ((SRAMCmd==0x0B)?0x100:0)) & 0x1FF];
|
|
|
|
SRAMAddr++;
|
|
|
|
return ret;
|
2017-03-23 22:47:55 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x9F: // read JEDEC ID
|
|
|
|
return 0xFF;
|
2017-03-23 22:47:55 +00:00
|
|
|
|
|
|
|
default:
|
2021-04-24 22:48:02 +00:00
|
|
|
if (pos == 1)
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Warn, "unknown tiny EEPROM save command %02X\n", SRAMCmd);
|
2022-03-14 17:08:29 +00:00
|
|
|
return 0xFF;
|
2017-03-23 22:47:55 +00:00
|
|
|
}
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2018-12-31 13:17:58 +00:00
|
|
|
u32 addrsize = 2;
|
|
|
|
if (SRAMLength > 65536) addrsize++;
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (SRAMCmd)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x01: // write status register
|
|
|
|
// TODO: WP bits should be nonvolatile!
|
|
|
|
if (pos == 1)
|
|
|
|
SRAMStatus = (SRAMStatus & 0x01) | (val & 0x0C);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0x05: // read status register
|
|
|
|
return SRAMStatus;
|
|
|
|
|
|
|
|
case 0x02: // write
|
|
|
|
if (pos <= addrsize)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAMFirstAddr = SRAMAddr;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
// TODO: implement WP bits
|
|
|
|
if (SRAMStatus & (1<<1))
|
|
|
|
{
|
|
|
|
SRAM[SRAMAddr & (SRAMLength-1)] = val;
|
|
|
|
}
|
|
|
|
SRAMAddr++;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
2022-01-07 13:00:43 +00:00
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
SRAMStatus &= ~(1<<1);
|
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength,
|
|
|
|
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
2017-02-03 15:57:31 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x03: // read
|
|
|
|
if (pos <= addrsize)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
|
|
|
return 0;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
// TODO: size limit!!
|
|
|
|
u8 ret = SRAM[SRAMAddr & (SRAMLength-1)];
|
|
|
|
SRAMAddr++;
|
|
|
|
return ret;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x9F: // read JEDEC ID
|
|
|
|
// TODO: GBAtek implies it's not always all FF (FRAM)
|
|
|
|
return 0xFF;
|
2017-02-03 15:57:31 +00:00
|
|
|
|
|
|
|
default:
|
2021-04-24 22:48:02 +00:00
|
|
|
if (pos == 1)
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Warn, "unknown EEPROM save command %02X\n", SRAMCmd);
|
2022-03-14 17:08:29 +00:00
|
|
|
return 0xFF;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (SRAMCmd)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x05: // read status register
|
|
|
|
return SRAMStatus;
|
|
|
|
|
|
|
|
case 0x02: // page program
|
|
|
|
if (pos <= 3)
|
2017-03-29 20:12:53 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAMFirstAddr = SRAMAddr;
|
2017-03-29 20:12:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
if (SRAMStatus & (1<<1))
|
|
|
|
{
|
|
|
|
// CHECKME: should it be &=~val ??
|
|
|
|
SRAM[SRAMAddr & (SRAMLength-1)] = 0;
|
|
|
|
}
|
|
|
|
SRAMAddr++;
|
2017-03-29 20:12:53 +00:00
|
|
|
}
|
2022-01-07 13:00:43 +00:00
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
SRAMStatus &= ~(1<<1);
|
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength,
|
|
|
|
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
2017-03-29 20:12:53 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x03: // read
|
|
|
|
if (pos <= 3)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
|
|
|
return 0;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 ret = SRAM[SRAMAddr & (SRAMLength-1)];
|
|
|
|
SRAMAddr++;
|
|
|
|
return ret;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x0A: // page write
|
|
|
|
if (pos <= 3)
|
2017-02-03 15:57:31 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAMFirstAddr = SRAMAddr;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
if (SRAMStatus & (1<<1))
|
|
|
|
{
|
|
|
|
SRAM[SRAMAddr & (SRAMLength-1)] = val;
|
|
|
|
}
|
|
|
|
SRAMAddr++;
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
2022-01-07 13:00:43 +00:00
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
SRAMStatus &= ~(1<<1);
|
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength,
|
|
|
|
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
2017-02-03 15:57:31 +00:00
|
|
|
|
2021-04-30 01:01:52 +00:00
|
|
|
case 0x0B: // fast read
|
|
|
|
if (pos <= 3)
|
|
|
|
{
|
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (pos == 4)
|
|
|
|
{
|
|
|
|
// dummy byte
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u8 ret = SRAM[SRAMAddr & (SRAMLength-1)];
|
|
|
|
SRAMAddr++;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x9F: // read JEDEC IC
|
|
|
|
// GBAtek says it should be 0xFF. verify?
|
|
|
|
return 0xFF;
|
|
|
|
|
|
|
|
case 0xD8: // sector erase
|
|
|
|
if (pos <= 3)
|
|
|
|
{
|
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAMFirstAddr = SRAMAddr;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
if ((pos == 3) && (SRAMStatus & (1<<1)))
|
|
|
|
{
|
|
|
|
for (u32 i = 0; i < 0x10000; i++)
|
|
|
|
{
|
|
|
|
SRAM[SRAMAddr & (SRAMLength-1)] = 0;
|
|
|
|
SRAMAddr++;
|
|
|
|
}
|
|
|
|
}
|
2022-01-07 13:00:43 +00:00
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
SRAMStatus &= ~(1<<1);
|
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength,
|
|
|
|
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0xDB: // page erase
|
|
|
|
if (pos <= 3)
|
|
|
|
{
|
|
|
|
SRAMAddr <<= 8;
|
|
|
|
SRAMAddr |= val;
|
2022-01-07 13:00:43 +00:00
|
|
|
SRAMFirstAddr = SRAMAddr;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
if ((pos == 3) && (SRAMStatus & (1<<1)))
|
|
|
|
{
|
|
|
|
for (u32 i = 0; i < 0x100; i++)
|
|
|
|
{
|
|
|
|
SRAM[SRAMAddr & (SRAMLength-1)] = 0;
|
|
|
|
SRAMAddr++;
|
|
|
|
}
|
|
|
|
}
|
2022-01-07 13:00:43 +00:00
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
SRAMStatus &= ~(1<<1);
|
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength,
|
|
|
|
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (pos == 1)
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Warn, "unknown FLASH save command %02X\n", SRAMCmd);
|
2022-03-14 17:08:29 +00:00
|
|
|
return 0xFF;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CartRetailNAND::~CartRetailNAND()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CartRetailNAND::Reset()
|
|
|
|
{
|
|
|
|
CartRetail::Reset();
|
|
|
|
|
|
|
|
SRAMAddr = 0;
|
|
|
|
SRAMStatus = 0x20;
|
|
|
|
SRAMWindow = 0;
|
|
|
|
|
|
|
|
// ROM header 94/96 = SRAM addr start / 0x20000
|
|
|
|
SRAMBase = *(u16*)&ROM[0x96] << 17;
|
|
|
|
|
|
|
|
memset(SRAMWriteBuffer, 0, 0x800);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CartRetailNAND::DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
CartRetail::DoSavestate(file);
|
|
|
|
|
|
|
|
file->Var32(&SRAMBase);
|
|
|
|
file->Var32(&SRAMWindow);
|
|
|
|
|
|
|
|
file->VarArray(SRAMWriteBuffer, 0x800);
|
|
|
|
file->Var32(&SRAMWritePos);
|
|
|
|
|
|
|
|
if (!file->Saving)
|
|
|
|
BuildSRAMID();
|
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
2022-01-07 13:00:43 +00:00
|
|
|
CartRetail::LoadSave(savedata, savelen);
|
2021-04-24 22:48:02 +00:00
|
|
|
BuildSRAMID();
|
|
|
|
}
|
|
|
|
|
|
|
|
int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len)
|
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len);
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (cmd[0])
|
|
|
|
{
|
|
|
|
case 0x81: // write data
|
|
|
|
if ((SRAMStatus & (1<<4)) && SRAMWindow >= SRAMBase && SRAMWindow < (SRAMBase+SRAMLength))
|
|
|
|
{
|
|
|
|
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
|
|
|
|
|
|
|
|
if (addr >= SRAMWindow && addr < (SRAMWindow+0x20000))
|
|
|
|
{
|
|
|
|
// the command is issued 4 times, each with the same address
|
|
|
|
// seems they use the one from the first command (CHECKME)
|
|
|
|
if (!SRAMAddr)
|
|
|
|
SRAMAddr = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SRAMAddr = 0;
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case 0x82: // commit write
|
|
|
|
if (SRAMAddr && SRAMWritePos)
|
|
|
|
{
|
|
|
|
if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000))
|
|
|
|
{
|
|
|
|
memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800);
|
2022-01-07 13:00:43 +00:00
|
|
|
Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800);
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SRAMAddr = 0;
|
|
|
|
SRAMWritePos = 0;
|
|
|
|
}
|
|
|
|
SRAMStatus &= ~(1<<4);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0x84: // discard write buffer
|
|
|
|
SRAMAddr = 0;
|
|
|
|
SRAMWritePos = 0;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0x85: // write enable
|
|
|
|
if (SRAMWindow)
|
|
|
|
{
|
|
|
|
SRAMStatus |= (1<<4);
|
|
|
|
SRAMWritePos = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0x8B: // revert to ROM read mode
|
|
|
|
SRAMWindow = 0;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0x94: // return ID data
|
|
|
|
{
|
|
|
|
// TODO: check what the data really is. probably the NAND chip's ID.
|
|
|
|
// also, might be different between different games or even between different carts.
|
|
|
|
// this was taken from a Jam with the Band cart.
|
|
|
|
u8 iddata[0x30] =
|
|
|
|
{
|
|
|
|
0xEC, 0xF1, 0x00, 0x95, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
};
|
|
|
|
|
|
|
|
if (SRAMLength) memcpy(&iddata[0x18], &SRAM[SRAMLength - 0x800], 16);
|
|
|
|
|
|
|
|
memset(data, 0, len);
|
|
|
|
memcpy(data, iddata, std::min(len, 0x30u));
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0xB2: // set window for accessing SRAM
|
|
|
|
{
|
|
|
|
u32 addr = (cmd[1]<<24) | ((cmd[2]&0xFE)<<16);
|
|
|
|
|
|
|
|
// window is 0x20000 bytes, address is aligned to that boundary
|
|
|
|
// NAND remains stuck 'busy' forever if this is less than the starting SRAM address
|
|
|
|
// TODO.
|
2023-03-23 17:04:38 +00:00
|
|
|
if (addr < SRAMBase) Log(LogLevel::Warn,"NAND: !! BAD ADDR %08X < %08X\n", addr, SRAMBase);
|
|
|
|
if (addr >= (SRAMBase+SRAMLength)) Log(LogLevel::Warn,"NAND: !! BAD ADDR %08X > %08X\n", addr, SRAMBase+SRAMLength);
|
2017-02-03 15:57:31 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
SRAMWindow = addr;
|
2017-03-29 20:12:53 +00:00
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0xB7:
|
2017-03-29 20:12:53 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
|
|
|
|
|
|
|
|
if (SRAMWindow == 0)
|
2017-03-29 20:12:53 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
// regular ROM mode
|
|
|
|
memset(data, 0, len);
|
|
|
|
|
|
|
|
if (((addr + len - 1) >> 12) != (addr >> 12))
|
|
|
|
{
|
|
|
|
u32 len1 = 0x1000 - (addr & 0xFFF);
|
|
|
|
ReadROM_B7(addr, len1, data, 0);
|
|
|
|
ReadROM_B7(addr+len1, len-len1, data, len1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ReadROM_B7(addr, len, data, 0);
|
2017-03-29 20:12:53 +00:00
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// SRAM mode
|
|
|
|
memset(data, 0xFF, len);
|
2017-03-29 20:12:53 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
if (SRAMWindow >= SRAMBase && SRAMWindow < (SRAMBase+SRAMLength) &&
|
|
|
|
addr >= SRAMWindow && addr < (SRAMWindow+0x20000))
|
|
|
|
{
|
|
|
|
memcpy(data, &SRAM[addr - SRAMBase], len);
|
|
|
|
}
|
|
|
|
}
|
2017-03-29 20:12:53 +00:00
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0xD6: // read NAND status
|
2017-03-29 20:12:53 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
// status bits
|
|
|
|
// bit5: ready
|
|
|
|
// bit4: write enable
|
|
|
|
|
|
|
|
for (u32 i = 0; i < len; i+=4)
|
|
|
|
*(u32*)&data[i] = SRAMStatus * 0x01010101;
|
2017-03-29 20:12:53 +00:00
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0;
|
2017-03-29 20:12:53 +00:00
|
|
|
|
2017-02-03 15:57:31 +00:00
|
|
|
default:
|
2021-04-24 22:48:02 +00:00
|
|
|
return CartRetail::ROMCommandStart(cmd, data, len);
|
2017-02-03 15:57:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartRetailNAND::ROMCommandFinish(u8* cmd, u8* data, u32 len)
|
2017-01-31 16:34:17 +00:00
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (cmd[0])
|
2017-01-31 16:34:17 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x81: // write data
|
|
|
|
if (SRAMAddr)
|
|
|
|
{
|
|
|
|
if ((SRAMWritePos + len) > 0x800)
|
|
|
|
len = 0x800 - SRAMWritePos;
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
memcpy(&SRAMWriteBuffer[SRAMWritePos], data, len);
|
|
|
|
SRAMWritePos += len;
|
|
|
|
}
|
2017-01-31 16:34:17 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
2021-04-24 22:48:02 +00:00
|
|
|
return CartCommon::ROMCommandFinish(cmd, data, len);
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
2020-11-11 12:38:05 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 CartRetailNAND::SPIWrite(u8 val, u32 pos, bool last)
|
2020-11-11 12:38:05 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
return 0xFF;
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartRetailNAND::BuildSRAMID()
|
|
|
|
{
|
|
|
|
// the last 128K of the SRAM are read-only.
|
|
|
|
// most of it is FF, except for the NAND ID at the beginning
|
|
|
|
// of the last 0x800 bytes.
|
|
|
|
|
|
|
|
if (SRAMLength > 0x20000)
|
|
|
|
{
|
|
|
|
memset(&SRAM[SRAMLength - 0x20000], 0xFF, 0x20000);
|
|
|
|
|
|
|
|
// TODO: check what the data is all about!
|
|
|
|
// this was pulled from a Jam with the Band cart. may be different on other carts.
|
|
|
|
// WarioWare DIY may have different data or not have this at all.
|
|
|
|
// the ID data is also found in the response to command 94, and JwtB checks it.
|
|
|
|
// WarioWare doesn't seem to care.
|
|
|
|
// there is also more data here, but JwtB doesn't seem to care.
|
|
|
|
u8 iddata[0x10] = {0xEC, 0x00, 0x9E, 0xA1, 0x51, 0x65, 0x34, 0x35, 0x30, 0x35, 0x30, 0x31, 0x19, 0x19, 0x02, 0x0A};
|
|
|
|
memcpy(&SRAM[SRAMLength - 0x800], iddata, 16);
|
|
|
|
}
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams) : CartRetail(rom, len, chipid, badDSiDump, romparams)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
IRVersion = irversion;
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
CartRetailIR::~CartRetailIR()
|
|
|
|
{
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartRetailIR::Reset()
|
|
|
|
{
|
|
|
|
CartRetail::Reset();
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
IRCmd = 0;
|
|
|
|
}
|
2020-09-03 18:28:07 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartRetailIR::DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
CartRetail::DoSavestate(file);
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
file->Var8(&IRCmd);
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last)
|
|
|
|
{
|
|
|
|
if (pos == 0)
|
|
|
|
{
|
|
|
|
IRCmd = val;
|
|
|
|
return 0;
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
// TODO: emulate actual IR comm
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (IRCmd)
|
|
|
|
{
|
|
|
|
case 0x00: // pass-through
|
|
|
|
return CartRetail::SPIWrite(val, pos-1, last);
|
2018-12-12 19:59:08 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0x08: // ID
|
|
|
|
return 0xAA;
|
|
|
|
}
|
2021-06-20 00:23:45 +00:00
|
|
|
|
|
|
|
return 0;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
2018-12-12 19:59:08 +00:00
|
|
|
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Info,"POKETYPE CART\n");
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
CartRetailBT::~CartRetailBT()
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartRetailBT::Reset()
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
CartRetail::Reset();
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartRetailBT::DoSavestate(Savestate* file)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
CartRetail::DoSavestate(file);
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Debug,"POKETYPE SPI: %02X %d %d - %08X\n", val, pos, last, NDS::GetPC(0));
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
/*if (pos == 0)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
// TODO do something with it??
|
|
|
|
if(val==0xFF)SetIRQ();
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
if(pos==7)SetIRQ();*/
|
|
|
|
|
|
|
|
return 0;
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartCommon(rom, len, chipid, false, romparams)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2022-01-07 13:00:43 +00:00
|
|
|
SD = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CartHomebrew::~CartHomebrew()
|
|
|
|
{
|
|
|
|
if (SD)
|
|
|
|
{
|
|
|
|
SD->Close();
|
|
|
|
delete SD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CartHomebrew::Reset()
|
|
|
|
{
|
|
|
|
CartCommon::Reset();
|
|
|
|
|
2021-10-28 16:47:13 +00:00
|
|
|
ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly);
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
if (SD)
|
|
|
|
{
|
|
|
|
SD->Close();
|
|
|
|
delete SD;
|
|
|
|
}
|
|
|
|
|
2021-10-28 16:47:13 +00:00
|
|
|
if (Platform::GetConfigBool(Platform::DLDI_Enable))
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-10-28 16:47:13 +00:00
|
|
|
std::string folderpath;
|
|
|
|
if (Platform::GetConfigBool(Platform::DLDI_FolderSync))
|
|
|
|
folderpath = Platform::GetConfigString(Platform::DLDI_FolderPath);
|
|
|
|
else
|
|
|
|
folderpath = "";
|
|
|
|
|
|
|
|
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), ReadOnly);
|
|
|
|
SD = new FATStorage(Platform::GetConfigString(Platform::DLDI_ImagePath),
|
|
|
|
(u64)Platform::GetConfigInt(Platform::DLDI_ImageSize) * 1024 * 1024,
|
|
|
|
ReadOnly,
|
|
|
|
folderpath);
|
|
|
|
SD->Open();
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
else
|
2021-10-28 16:47:13 +00:00
|
|
|
SD = nullptr;
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2023-04-28 15:05:34 +00:00
|
|
|
void CartHomebrew::SetupDirectBoot(const std::string& romname)
|
2021-04-24 22:48:02 +00:00
|
|
|
{
|
2022-01-07 13:00:43 +00:00
|
|
|
CartCommon::SetupDirectBoot(romname);
|
2020-09-03 18:28:07 +00:00
|
|
|
|
2021-10-28 16:47:13 +00:00
|
|
|
if (SD)
|
|
|
|
{
|
|
|
|
// add the ROM to the SD volume
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
if (!SD->InjectFile(romname, ROM, ROMLength))
|
2021-10-28 16:47:13 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// setup argv command line
|
|
|
|
|
|
|
|
char argv[512] = {0};
|
|
|
|
int argvlen;
|
|
|
|
|
|
|
|
strncpy(argv, "fat:/", 511);
|
2022-01-07 13:00:43 +00:00
|
|
|
strncat(argv, romname.c_str(), 511);
|
2021-10-28 16:47:13 +00:00
|
|
|
argvlen = strlen(argv);
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
const NDSHeader& header = GetHeader();
|
2021-10-28 16:47:13 +00:00
|
|
|
void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32;
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 argvbase = header.ARM9RAMAddress + header.ARM9Size;
|
2021-10-28 16:47:13 +00:00
|
|
|
argvbase = (argvbase + 0xF) & ~0xF;
|
|
|
|
|
|
|
|
for (u32 i = 0; i <= argvlen; i+=4)
|
|
|
|
writefn(argvbase+i, *(u32*)&argv[i]);
|
|
|
|
|
|
|
|
writefn(0x02FFFE70, 0x5F617267);
|
|
|
|
writefn(0x02FFFE74, argvbase);
|
|
|
|
writefn(0x02FFFE78, argvlen+1);
|
|
|
|
}
|
2017-02-07 21:23:46 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartHomebrew::DoSavestate(Savestate* file)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
CartCommon::DoSavestate(file);
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len);
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (cmd[0])
|
|
|
|
{
|
|
|
|
case 0xB7:
|
|
|
|
{
|
|
|
|
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
|
|
|
|
memset(data, 0, len);
|
|
|
|
|
|
|
|
if (((addr + len - 1) >> 12) != (addr >> 12))
|
|
|
|
{
|
|
|
|
u32 len1 = 0x1000 - (addr & 0xFFF);
|
|
|
|
ReadROM_B7(addr, len1, data, 0);
|
|
|
|
ReadROM_B7(addr+len1, len-len1, data, len1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ReadROM_B7(addr, len, data, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0xC0: // SD read
|
|
|
|
{
|
|
|
|
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
|
2021-10-28 16:47:13 +00:00
|
|
|
if (SD) SD->ReadSectors(sector, len>>9, data);
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2018-12-12 19:59:08 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
case 0xC1: // SD write
|
|
|
|
return 1;
|
2019-08-04 14:46:02 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
default:
|
|
|
|
return CartCommon::ROMCommandStart(cmd, data, len);
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
|
2018-10-17 22:54:21 +00:00
|
|
|
{
|
2021-04-30 15:29:04 +00:00
|
|
|
if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
// TODO: delayed SD writing? like we have for SRAM
|
2018-10-17 22:54:21 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
switch (cmd[0])
|
|
|
|
{
|
|
|
|
case 0xC1:
|
|
|
|
{
|
|
|
|
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
|
2021-10-28 16:47:13 +00:00
|
|
|
if (SD && (!ReadOnly)) SD->WriteSectors(sector, len>>9, data);
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
break;
|
2018-10-17 22:54:21 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
default:
|
|
|
|
return CartCommon::ROMCommandFinish(cmd, data, len);
|
|
|
|
}
|
2018-10-17 22:54:21 +00:00
|
|
|
}
|
|
|
|
|
2022-03-12 20:52:29 +00:00
|
|
|
void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly)
|
2018-04-26 22:17:05 +00:00
|
|
|
{
|
|
|
|
if (patch[0x0D] > binary[dldioffset+0x0F])
|
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Error, "DLDI driver ain't gonna fit, sorry\n");
|
2018-04-26 22:17:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Info, "existing driver is: %s\n", &binary[dldioffset+0x10]);
|
|
|
|
Log(LogLevel::Info, "new driver is: %s\n", &patch[0x10]);
|
2018-04-26 22:17:05 +00:00
|
|
|
|
|
|
|
u32 memaddr = *(u32*)&binary[dldioffset+0x40];
|
|
|
|
if (memaddr == 0)
|
|
|
|
memaddr = *(u32*)&binary[dldioffset+0x68] - 0x80;
|
|
|
|
|
|
|
|
u32 patchbase = *(u32*)&patch[0x40];
|
|
|
|
u32 delta = memaddr - patchbase;
|
|
|
|
|
|
|
|
u32 patchsize = 1 << patch[0x0D];
|
|
|
|
u32 patchend = patchbase + patchsize;
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
memcpy(&binary[dldioffset], patch, patchlen);
|
2018-04-26 22:17:05 +00:00
|
|
|
|
|
|
|
*(u32*)&binary[dldioffset+0x40] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x44] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x48] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x4C] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x50] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x54] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x58] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x5C] += delta;
|
|
|
|
|
|
|
|
*(u32*)&binary[dldioffset+0x68] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x6C] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x70] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x74] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x78] += delta;
|
|
|
|
*(u32*)&binary[dldioffset+0x7C] += delta;
|
|
|
|
|
|
|
|
u8 fixmask = patch[0x0E];
|
|
|
|
|
|
|
|
if (fixmask & 0x01)
|
|
|
|
{
|
|
|
|
u32 fixstart = *(u32*)&patch[0x40] - patchbase;
|
|
|
|
u32 fixend = *(u32*)&patch[0x44] - patchbase;
|
|
|
|
|
|
|
|
for (u32 addr = fixstart; addr < fixend; addr+=4)
|
|
|
|
{
|
|
|
|
u32 val = *(u32*)&binary[dldioffset+addr];
|
|
|
|
if (val >= patchbase && val < patchend)
|
|
|
|
*(u32*)&binary[dldioffset+addr] += delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fixmask & 0x02)
|
|
|
|
{
|
|
|
|
u32 fixstart = *(u32*)&patch[0x48] - patchbase;
|
|
|
|
u32 fixend = *(u32*)&patch[0x4C] - patchbase;
|
|
|
|
|
|
|
|
for (u32 addr = fixstart; addr < fixend; addr+=4)
|
|
|
|
{
|
|
|
|
u32 val = *(u32*)&binary[dldioffset+addr];
|
|
|
|
if (val >= patchbase && val < patchend)
|
|
|
|
*(u32*)&binary[dldioffset+addr] += delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fixmask & 0x04)
|
|
|
|
{
|
|
|
|
u32 fixstart = *(u32*)&patch[0x50] - patchbase;
|
|
|
|
u32 fixend = *(u32*)&patch[0x54] - patchbase;
|
|
|
|
|
|
|
|
for (u32 addr = fixstart; addr < fixend; addr+=4)
|
|
|
|
{
|
|
|
|
u32 val = *(u32*)&binary[dldioffset+addr];
|
|
|
|
if (val >= patchbase && val < patchend)
|
|
|
|
*(u32*)&binary[dldioffset+addr] += delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fixmask & 0x08)
|
|
|
|
{
|
|
|
|
u32 fixstart = *(u32*)&patch[0x58] - patchbase;
|
|
|
|
u32 fixend = *(u32*)&patch[0x5C] - patchbase;
|
|
|
|
|
|
|
|
memset(&binary[dldioffset+fixstart], 0, fixend-fixstart);
|
|
|
|
}
|
|
|
|
|
2021-10-28 16:47:13 +00:00
|
|
|
if (readonly)
|
|
|
|
{
|
|
|
|
// clear the can-write feature flag
|
|
|
|
binary[dldioffset+0x64] &= ~0x02;
|
|
|
|
|
|
|
|
// make writeSectors() return failure
|
|
|
|
u32 writesec_addr = *(u32*)&binary[dldioffset+0x74];
|
|
|
|
writesec_addr -= memaddr;
|
|
|
|
writesec_addr += dldioffset;
|
|
|
|
*(u32*)&binary[writesec_addr+0x00] = 0xE3A00000; // mov r0, #0
|
|
|
|
*(u32*)&binary[writesec_addr+0x04] = 0xE12FFF1E; // bx lr
|
|
|
|
}
|
|
|
|
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Debug, "applied DLDI patch at %08X\n", dldioffset);
|
2022-03-12 20:52:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
|
|
|
|
{
|
|
|
|
if (*(u32*)&patch[0] != 0xBF8DA5ED ||
|
|
|
|
*(u32*)&patch[4] != 0x69684320 ||
|
|
|
|
*(u32*)&patch[8] != 0x006D6873)
|
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Error, "bad DLDI patch\n");
|
2022-03-12 20:52:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 offset = *(u32*)&ROM[0x20];
|
|
|
|
u32 size = *(u32*)&ROM[0x2C];
|
|
|
|
|
|
|
|
u8* binary = &ROM[offset];
|
|
|
|
|
|
|
|
for (u32 i = 0; i < size; )
|
|
|
|
{
|
|
|
|
if (*(u32*)&binary[i ] == 0xBF8DA5ED &&
|
|
|
|
*(u32*)&binary[i+4] == 0x69684320 &&
|
|
|
|
*(u32*)&binary[i+8] == 0x006D6873)
|
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Debug, "DLDI structure found at %08X (%08X)\n", i, offset+i);
|
2022-03-12 20:52:29 +00:00
|
|
|
ApplyDLDIPatchAt(binary, i, patch, patchlen, readonly);
|
|
|
|
i += patchlen;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
i++;
|
|
|
|
}
|
2018-04-26 22:17:05 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
|
|
|
|
{
|
|
|
|
// TODO: how strict should this be for homebrew?
|
|
|
|
|
|
|
|
addr &= (ROMLength-1);
|
|
|
|
|
|
|
|
memcpy(data+offset, ROM+addr, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Init()
|
|
|
|
{
|
|
|
|
Cart = nullptr;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
Cart = nullptr;
|
2021-04-24 22:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
ResetCart();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
file->Section("NDSC");
|
|
|
|
|
|
|
|
file->Var16(&SPICnt);
|
|
|
|
file->Var32(&ROMCnt);
|
|
|
|
|
|
|
|
file->Var8(&SPIData);
|
|
|
|
file->Var32(&SPIDataPos);
|
|
|
|
file->Bool32(&SPIHold);
|
|
|
|
|
|
|
|
file->VarArray(ROMCommand, 8);
|
|
|
|
file->Var32(&ROMData);
|
|
|
|
|
|
|
|
file->VarArray(TransferData, 0x4000);
|
|
|
|
file->Var32(&TransferPos);
|
|
|
|
file->Var32(&TransferLen);
|
|
|
|
file->Var32(&TransferDir);
|
|
|
|
file->VarArray(TransferCmd, 8);
|
|
|
|
|
|
|
|
// cart inserted/len/ROM/etc should be already populated
|
|
|
|
// savestate should be loaded after the right game is loaded
|
|
|
|
// (TODO: system to verify that indeed the right ROM is loaded)
|
|
|
|
// (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing)
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
u32 carttype = 0;
|
|
|
|
u32 cartchk = 0;
|
|
|
|
if (Cart)
|
|
|
|
{
|
|
|
|
carttype = Cart->Type();
|
|
|
|
cartchk = Cart->Checksum();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file->Saving)
|
|
|
|
{
|
|
|
|
file->Var32(&carttype);
|
|
|
|
file->Var32(&cartchk);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u32 savetype;
|
|
|
|
file->Var32(&savetype);
|
|
|
|
if (savetype != carttype) return;
|
|
|
|
|
|
|
|
u32 savechk;
|
|
|
|
file->Var32(&savechk);
|
|
|
|
if (savechk != cartchk) return;
|
|
|
|
}
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
if (Cart) Cart->DoSavestate(file);
|
|
|
|
}
|
|
|
|
|
2018-04-26 22:17:05 +00:00
|
|
|
|
2020-05-29 22:28:21 +00:00
|
|
|
bool ReadROMParams(u32 gamecode, ROMListEntry* params)
|
2018-12-11 18:47:03 +00:00
|
|
|
{
|
|
|
|
u32 offset = 0;
|
2023-08-28 18:01:15 +00:00
|
|
|
u32 chk_size = ROMListEntryCount >> 1;
|
2018-12-11 18:47:03 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
2018-12-31 03:35:58 +00:00
|
|
|
u32 key = 0;
|
2023-06-30 11:28:52 +00:00
|
|
|
const ROMListEntry* curentry = &ROMList[offset + chk_size];
|
2020-05-29 22:28:21 +00:00
|
|
|
key = curentry->GameCode;
|
2018-12-11 18:47:03 +00:00
|
|
|
|
2018-12-31 03:35:58 +00:00
|
|
|
if (key == gamecode)
|
2018-12-11 18:47:03 +00:00
|
|
|
{
|
2020-05-29 22:28:21 +00:00
|
|
|
memcpy(params, curentry, sizeof(ROMListEntry));
|
2018-12-11 18:47:03 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-31 03:35:58 +00:00
|
|
|
if (key < gamecode)
|
2018-12-11 18:47:03 +00:00
|
|
|
{
|
|
|
|
if (chk_size == 0)
|
2020-05-29 22:28:21 +00:00
|
|
|
offset++;
|
2018-12-11 18:47:03 +00:00
|
|
|
else
|
2020-05-29 22:28:21 +00:00
|
|
|
offset += chk_size;
|
2018-12-11 18:47:03 +00:00
|
|
|
}
|
|
|
|
else if (chk_size == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
chk_size >>= 1;
|
|
|
|
}
|
|
|
|
|
2023-08-28 18:01:15 +00:00
|
|
|
if (offset >= ROMListEntryCount)
|
2018-12-11 18:47:03 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-30 09:04:50 +00:00
|
|
|
void DecryptSecureArea(u8* out)
|
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
const NDSHeader& header = Cart->GetHeader();
|
|
|
|
const u8* cartrom = Cart->GetROM();
|
|
|
|
|
|
|
|
u32 gamecode = header.GameCodeAsU32();
|
|
|
|
u32 arm9base = header.ARM9ROMOffset;
|
2020-03-30 09:04:50 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
memcpy(out, &cartrom[arm9base], 0x800);
|
2020-03-30 09:04:50 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
Key1_InitKeycode(false, gamecode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
|
2020-03-30 09:04:50 +00:00
|
|
|
Key1_Decrypt((u32*)&out[0]);
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
Key1_InitKeycode(false, gamecode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
|
2020-03-30 09:04:50 +00:00
|
|
|
for (u32 i = 0; i < 0x800; i += 8)
|
|
|
|
Key1_Decrypt((u32*)&out[i]);
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
XXH64_hash_t hash = XXH64(out, 0x800, 0);
|
|
|
|
Log(LogLevel::Debug, "Secure area post-decryption xxh64 hash: %zx\n", hash);
|
|
|
|
|
2020-03-30 11:28:51 +00:00
|
|
|
if (!strncmp((const char*)out, "encryObj", 8))
|
2020-03-30 09:04:50 +00:00
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Info, "Secure area decryption OK\n");
|
2020-03-30 09:04:50 +00:00
|
|
|
*(u32*)&out[0] = 0xE7FFDEFF;
|
|
|
|
*(u32*)&out[4] = 0xE7FFDEFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Warn, "Secure area decryption failed\n");
|
2020-03-30 09:04:50 +00:00
|
|
|
for (u32 i = 0; i < 0x800; i += 4)
|
|
|
|
*(u32*)&out[i] = 0xE7FFDEFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
if (romdata == nullptr)
|
|
|
|
{
|
|
|
|
Log(LogLevel::Error, "NDSCart: romdata is null\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
2022-01-07 13:00:43 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
if (romlen == 0)
|
|
|
|
{
|
|
|
|
Log(LogLevel::Error, "NDSCart: romlen is zero\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
merge local_wifi (#1516)
* attempt at betterer wifi
* add preliminary sync mechanism
* fix gaps in wifi implementation
* move local-MP comm to its own module instead of cramping Platform.cpp
* remove some stupid cruft
* as you wish, Sorer
(starting work on shared-memory system)
* shared-memory IPC that actually works (albeit Windows-only for now)
* shut up logging from NULL writes on ARM7 (ffs Nintendo learn to code)
* get this somewhat good
* leave client sync mode when host deauths. makes download play actually work.
* start implementing MP-comm error handling
* * add MP-reply error counters
* feeble attempt at fixing slowdown/desync/etc problems
* somewhat better exchange/sync method
* * when entering power-saving mode, be sure to finish transferring the current frame first
* fix misc bug due to old cruft leftover
makes for a more stable connection
* remove a bunch of cruft
* set wifi time interval to 34 cycles instead of 33. games seem sensitive to the general timing of wifi vs the rest of the system, and this seems to make things run better, atleast until I rewrite this to use a proper scheduler.
* more graceful handling of disconnects
* deal with FIFO overflow more gracefully
* BAHAHAHAHAHAHAHAHHHH
THE SNEAKY BASTARDS
so, when the DS receives a beacon with the right BSSID
that beacon's timestamp is copied to USCOUNTER
* attempt at making the connection process smoother for weird games
* * begin adding POWCNT2, only applies to wifi for now
* begin work on wifi scheduler
* implement the shitty timers
* add the RF wakeup thing
* begin work on receiving frames. for now it can just receive melonAP beacons, but hey, it's a start.
* add enough TX functionality that online wifi is a possibility again.
* there are problems with this scheduler thing. committing it anyway
* kind of a rollback... we're gonna work out a compromise on this, I guess
* don't transmit shit if RXCNT.bit15 isn't set
* move RX-finish to its own function. more accurate filtering. implement RXFILTER.
* remove some cruft
* fix some of the shittiness when trying to connect more than two players
* fix some more shittiness
* fix more wifi shittiness (mainly don't try to receive shit while sending a frame)
* run wifi every 8µs. improves performance.
* fix IRQ14/IRQ15
* make this work under Linux
* Make it work on macOS, for now using a custom sem_timedwait
implementation.
If anyone knows anything about mach ports and have an idea for how to
make this work using mach IPC, please do let me know.
* 25ms seems like a good timeout
* begin work on proper multiplayer UI shito.
for now, determine a global instance ID, and derivate the system MAC from it. remove 'randomize MAC' option.
* finish removing RandomizeMAC
* lay groundwork for instance-unique config
* work some on the UI... make it not labelled Fart
* more UI work: make it explicit that some things are instance-unique
* separate firmware files for multiplayer instances
* make instances save to different save files, too
* more UI work, make things somewhat less shitty
* lay base for the multiplayer settings dialog
* actually hook up most of that dialog
* actually implement the fun audio settings
* ensure all the wifi shit is properly savestated and reset. properly update timings for the wifi region when wifi is disabled.
* add more fun labels
* * ignore WEP frames if WEP is off
* implement RX_LEN_CROP
* fake enough of WEP processing to make Inazuma Eleven work
* * do not copy more ROM banner data than actually needed
* avoid trying to read out of bounds if the banner offset is bad
* Fix oversight with the preferences action causing the build to fail on macOS
Co-authored-by: Nadia Holmquist Pedersen <nadia@nhp.sh>
2022-09-22 18:32:27 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 cartromsize = 0x200;
|
|
|
|
while (cartromsize < romlen)
|
|
|
|
cartromsize <<= 1; // ROM size must be a power of 2
|
2022-01-07 13:00:43 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
u8* cartrom = nullptr;
|
2022-01-07 13:00:43 +00:00
|
|
|
try
|
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
cartrom = new u8[cartromsize];
|
2022-01-07 13:00:43 +00:00
|
|
|
}
|
|
|
|
catch (const std::bad_alloc& e)
|
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
Log(LogLevel::Error, "NDSCart: failed to allocate memory for ROM (%d bytes)\n", cartromsize);
|
|
|
|
|
|
|
|
return nullptr;
|
2022-01-07 13:00:43 +00:00
|
|
|
}
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
// copy romdata into cartrom then zero out the remaining space
|
|
|
|
memcpy(cartrom, romdata, romlen);
|
|
|
|
memset(cartrom + romlen, 0, cartromsize - romlen);
|
2022-01-07 13:00:43 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
NDSHeader header {};
|
|
|
|
memcpy(&header, cartrom, sizeof(header));
|
2022-09-02 10:47:12 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
bool dsi = header.IsDSi();
|
2023-05-11 11:24:01 +00:00
|
|
|
bool badDSiDump = false;
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 dsiRegion = header.DSiRegionMask;
|
2023-05-11 11:24:01 +00:00
|
|
|
if (dsi && dsiRegion == 0)
|
|
|
|
{
|
|
|
|
Log(LogLevel::Info, "DS header indicates DSi, but region is zero. Going in bad dump mode.\n");
|
|
|
|
badDSiDump = true;
|
|
|
|
dsi = false;
|
|
|
|
}
|
merge local_wifi (#1516)
* attempt at betterer wifi
* add preliminary sync mechanism
* fix gaps in wifi implementation
* move local-MP comm to its own module instead of cramping Platform.cpp
* remove some stupid cruft
* as you wish, Sorer
(starting work on shared-memory system)
* shared-memory IPC that actually works (albeit Windows-only for now)
* shut up logging from NULL writes on ARM7 (ffs Nintendo learn to code)
* get this somewhat good
* leave client sync mode when host deauths. makes download play actually work.
* start implementing MP-comm error handling
* * add MP-reply error counters
* feeble attempt at fixing slowdown/desync/etc problems
* somewhat better exchange/sync method
* * when entering power-saving mode, be sure to finish transferring the current frame first
* fix misc bug due to old cruft leftover
makes for a more stable connection
* remove a bunch of cruft
* set wifi time interval to 34 cycles instead of 33. games seem sensitive to the general timing of wifi vs the rest of the system, and this seems to make things run better, atleast until I rewrite this to use a proper scheduler.
* more graceful handling of disconnects
* deal with FIFO overflow more gracefully
* BAHAHAHAHAHAHAHAHHHH
THE SNEAKY BASTARDS
so, when the DS receives a beacon with the right BSSID
that beacon's timestamp is copied to USCOUNTER
* attempt at making the connection process smoother for weird games
* * begin adding POWCNT2, only applies to wifi for now
* begin work on wifi scheduler
* implement the shitty timers
* add the RF wakeup thing
* begin work on receiving frames. for now it can just receive melonAP beacons, but hey, it's a start.
* add enough TX functionality that online wifi is a possibility again.
* there are problems with this scheduler thing. committing it anyway
* kind of a rollback... we're gonna work out a compromise on this, I guess
* don't transmit shit if RXCNT.bit15 isn't set
* move RX-finish to its own function. more accurate filtering. implement RXFILTER.
* remove some cruft
* fix some of the shittiness when trying to connect more than two players
* fix some more shittiness
* fix more wifi shittiness (mainly don't try to receive shit while sending a frame)
* run wifi every 8µs. improves performance.
* fix IRQ14/IRQ15
* make this work under Linux
* Make it work on macOS, for now using a custom sem_timedwait
implementation.
If anyone knows anything about mach ports and have an idea for how to
make this work using mach IPC, please do let me know.
* 25ms seems like a good timeout
* begin work on proper multiplayer UI shito.
for now, determine a global instance ID, and derivate the system MAC from it. remove 'randomize MAC' option.
* finish removing RandomizeMAC
* lay groundwork for instance-unique config
* work some on the UI... make it not labelled Fart
* more UI work: make it explicit that some things are instance-unique
* separate firmware files for multiplayer instances
* make instances save to different save files, too
* more UI work, make things somewhat less shitty
* lay base for the multiplayer settings dialog
* actually hook up most of that dialog
* actually implement the fun audio settings
* ensure all the wifi shit is properly savestated and reset. properly update timings for the wifi region when wifi is disabled.
* add more fun labels
* * ignore WEP frames if WEP is off
* implement RX_LEN_CROP
* fake enough of WEP processing to make Inazuma Eleven work
* * do not copy more ROM banner data than actually needed
* avoid trying to read out of bounds if the banner offset is bad
* Fix oversight with the preferences action causing the build to fail on macOS
Co-authored-by: Nadia Holmquist Pedersen <nadia@nhp.sh>
2022-09-22 18:32:27 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 gamecode = header.GameCodeAsU32();
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 arm9base = header.ARM9ROMOffset;
|
|
|
|
bool homebrew = header.IsHomebrew();
|
2019-08-04 14:46:02 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
ROMListEntry romparams {};
|
2020-05-29 22:28:21 +00:00
|
|
|
if (!ReadROMParams(gamecode, &romparams))
|
2018-12-11 18:47:03 +00:00
|
|
|
{
|
|
|
|
// set defaults
|
2023-06-30 11:28:52 +00:00
|
|
|
Log(LogLevel::Warn, "ROM entry not found for gamecode %d\n", gamecode);
|
2018-12-11 19:39:07 +00:00
|
|
|
|
2020-05-29 22:28:21 +00:00
|
|
|
romparams.GameCode = gamecode;
|
2023-06-30 11:28:52 +00:00
|
|
|
romparams.ROMSize = cartromsize;
|
2022-01-07 13:00:43 +00:00
|
|
|
if (homebrew)
|
2020-05-29 22:28:21 +00:00
|
|
|
romparams.SaveMemType = 0; // no saveRAM for homebrew
|
2018-12-11 19:39:07 +00:00
|
|
|
else
|
2020-05-29 22:28:21 +00:00
|
|
|
romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME)
|
2018-12-11 18:47:03 +00:00
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
if (romparams.ROMSize != romlen)
|
2023-06-30 11:28:52 +00:00
|
|
|
Log(LogLevel::Warn, "!! bad ROM size %d (expected %d) rounded to %d\n", romlen, romparams.ROMSize, cartromsize);
|
2018-12-12 19:59:08 +00:00
|
|
|
|
2017-10-10 22:01:05 +00:00
|
|
|
// generate a ROM ID
|
|
|
|
// note: most games don't check the actual value
|
|
|
|
// it just has to stay the same throughout gameplay
|
2023-06-30 11:28:52 +00:00
|
|
|
u32 cartid = 0x000000C2;
|
2018-12-12 19:59:08 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
if (cartromsize >= 1024 * 1024 && cartromsize <= 128 * 1024 * 1024)
|
|
|
|
cartid |= ((cartromsize >> 20) - 1) << 8;
|
2018-12-12 19:59:08 +00:00
|
|
|
else
|
2023-06-30 11:28:52 +00:00
|
|
|
cartid |= (0x100 - (cartromsize >> 28)) << 8;
|
2018-12-12 19:59:08 +00:00
|
|
|
|
2021-04-29 23:13:35 +00:00
|
|
|
if (romparams.SaveMemType >= 8 && romparams.SaveMemType <= 10)
|
2023-06-30 11:28:52 +00:00
|
|
|
cartid |= 0x08000000; // NAND flag
|
2018-12-12 19:59:08 +00:00
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
if (dsi)
|
2023-06-30 11:28:52 +00:00
|
|
|
cartid |= 0x40000000;
|
2019-08-04 14:46:02 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
// cart ID for Jam with the Band
|
|
|
|
// TODO: this kind of ID triggers different KEY1 phase
|
|
|
|
// (repeats commands a bunch of times)
|
2023-06-30 11:28:52 +00:00
|
|
|
//cartid = 0x88017FEC;
|
|
|
|
//cartid = 0x80007FC2; // pokémon typing adventure
|
2021-04-24 22:48:02 +00:00
|
|
|
|
|
|
|
u32 irversion = 0;
|
|
|
|
if ((gamecode & 0xFF) == 'I')
|
2019-08-07 10:57:12 +00:00
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
if (((gamecode >> 8) & 0xFF) < 'P')
|
|
|
|
irversion = 1; // Active Health / Walk with Me
|
|
|
|
else
|
|
|
|
irversion = 2; // Pokémon HG/SS, B/W, B2/W2
|
2017-01-23 01:26:05 +00:00
|
|
|
}
|
2019-08-07 10:57:12 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
std::unique_ptr<CartCommon> cart;
|
2022-01-07 13:00:43 +00:00
|
|
|
if (homebrew)
|
2023-06-30 11:28:52 +00:00
|
|
|
cart = std::make_unique<CartHomebrew>(cartrom, cartromsize, cartid, romparams);
|
|
|
|
else if (cartid & 0x08000000)
|
|
|
|
cart = std::make_unique<CartRetailNAND>(cartrom, cartromsize, cartid, romparams);
|
2021-04-24 22:48:02 +00:00
|
|
|
else if (irversion != 0)
|
2023-06-30 11:28:52 +00:00
|
|
|
cart = std::make_unique<CartRetailIR>(cartrom, cartromsize, cartid, irversion, badDSiDump, romparams);
|
2021-04-24 22:48:02 +00:00
|
|
|
else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx
|
2023-06-30 11:28:52 +00:00
|
|
|
cart = std::make_unique<CartRetailBT>(cartrom, cartromsize, cartid, romparams);
|
2020-03-30 09:04:50 +00:00
|
|
|
else
|
2023-06-30 11:28:52 +00:00
|
|
|
cart = std::make_unique<CartRetail>(cartrom, cartromsize, cartid, badDSiDump, romparams);
|
|
|
|
|
|
|
|
if (romparams.SaveMemType > 0)
|
|
|
|
cart->SetupSave(romparams.SaveMemType);
|
|
|
|
|
|
|
|
return cart;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Why a move function? Because the Cart object is polymorphic,
|
|
|
|
// and cloning polymorphic objects without knowing the underlying type is annoying.
|
|
|
|
bool InsertROM(std::unique_ptr<CartCommon>&& cart)
|
|
|
|
{
|
|
|
|
if (!cart) {
|
|
|
|
Log(LogLevel::Error, "Failed to insert invalid cart; existing cart (if any) was not ejected.\n");
|
|
|
|
return false;
|
|
|
|
}
|
2021-04-24 22:48:02 +00:00
|
|
|
|
|
|
|
if (Cart)
|
2023-06-30 11:28:52 +00:00
|
|
|
EjectCart();
|
|
|
|
|
|
|
|
Cart = std::move(cart);
|
2019-08-07 10:57:12 +00:00
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
Cart->Reset();
|
|
|
|
|
|
|
|
const NDSHeader& header = Cart->GetHeader();
|
|
|
|
const ROMListEntry romparams = Cart->GetROMParams();
|
|
|
|
const u8* cartrom = Cart->GetROM();
|
|
|
|
if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000)
|
|
|
|
{
|
|
|
|
// reencrypt secure area if needed
|
|
|
|
if (*(u32*)&cartrom[header.ARM9ROMOffset] == 0xE7FFDEFF && *(u32*)&cartrom[header.ARM9ROMOffset + 0x10] != 0xE7FFDEFF)
|
|
|
|
{
|
|
|
|
Log(LogLevel::Debug, "Re-encrypting cart secure area\n");
|
|
|
|
|
|
|
|
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
|
|
|
|
|
|
|
|
Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
|
|
|
|
for (u32 i = 0; i < 0x800; i += 8)
|
|
|
|
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
|
|
|
|
|
|
|
|
Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
|
|
|
|
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
|
|
|
|
|
|
|
|
Log(LogLevel::Debug, "Re-encrypted cart secure area\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Log(LogLevel::Debug, "No need to re-encrypt cart secure area\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Log(LogLevel::Info, "Inserted cart with game code: %.4s\n", header.GameCode);
|
|
|
|
Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID());
|
|
|
|
Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
|
|
|
|
|
|
|
|
DSi::SetCartInserted(true);
|
2020-09-03 18:28:07 +00:00
|
|
|
|
2017-02-07 22:31:21 +00:00
|
|
|
return true;
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2023-06-30 11:28:52 +00:00
|
|
|
bool LoadROM(const u8* romdata, u32 romlen)
|
|
|
|
{
|
|
|
|
std::unique_ptr<CartCommon> cart = ParseROM(romdata, romlen);
|
|
|
|
|
|
|
|
return InsertROM(std::move(cart));
|
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
void LoadSave(const u8* savedata, u32 savelen)
|
2021-01-22 10:22:32 +00:00
|
|
|
{
|
2022-01-07 13:00:43 +00:00
|
|
|
if (Cart)
|
|
|
|
Cart->LoadSave(savedata, savelen);
|
2021-01-22 10:22:32 +00:00
|
|
|
}
|
|
|
|
|
2023-04-28 15:05:34 +00:00
|
|
|
void SetupDirectBoot(const std::string& romname)
|
2021-01-22 10:22:32 +00:00
|
|
|
{
|
2022-01-07 13:00:43 +00:00
|
|
|
if (Cart)
|
|
|
|
Cart->SetupDirectBoot(romname);
|
2021-01-22 10:22:32 +00:00
|
|
|
}
|
|
|
|
|
2023-03-27 20:36:26 +00:00
|
|
|
u8* GetSaveMemory()
|
|
|
|
{
|
|
|
|
return Cart ? Cart->GetSaveMemory() : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GetSaveMemoryLength()
|
|
|
|
{
|
|
|
|
return Cart ? Cart->GetSaveMemoryLength() : 0;
|
|
|
|
}
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
void EjectCart()
|
2018-10-23 20:54:09 +00:00
|
|
|
{
|
2023-06-30 11:28:52 +00:00
|
|
|
if (!Cart) return;
|
2018-10-23 20:54:09 +00:00
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
// ejecting the cart triggers the gamecard IRQ
|
|
|
|
NDS::SetIRQ(0, NDS::IRQ_CartIREQMC);
|
|
|
|
NDS::SetIRQ(1, NDS::IRQ_CartIREQMC);
|
2020-11-11 12:38:05 +00:00
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
Cart = nullptr;
|
|
|
|
|
2022-08-21 20:11:22 +00:00
|
|
|
DSi::SetCartInserted(false);
|
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
// CHECKME: does an eject imply anything for the ROM/SPI transfer registers?
|
2020-09-11 01:08:06 +00:00
|
|
|
}
|
|
|
|
|
2019-08-04 14:46:02 +00:00
|
|
|
void ResetCart()
|
|
|
|
{
|
|
|
|
// CHECKME: what if there is a transfer in progress?
|
|
|
|
|
|
|
|
SPICnt = 0;
|
|
|
|
ROMCnt = 0;
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
SPIData = 0;
|
|
|
|
SPIDataPos = 0;
|
|
|
|
SPIHold = false;
|
|
|
|
|
2019-08-04 14:46:02 +00:00
|
|
|
memset(ROMCommand, 0, 8);
|
2020-09-03 18:28:07 +00:00
|
|
|
ROMData = 0;
|
2019-08-04 14:46:02 +00:00
|
|
|
|
|
|
|
Key2_X = 0;
|
|
|
|
Key2_Y = 0;
|
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
memset(TransferData, 0, 0x4000);
|
|
|
|
TransferPos = 0;
|
|
|
|
TransferLen = 0;
|
|
|
|
TransferDir = 0;
|
|
|
|
memset(TransferCmd, 0, 8);
|
|
|
|
TransferCmd[0] = 0xFF;
|
2019-08-04 14:46:02 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
if (Cart) Cart->Reset();
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-11 18:21:31 +00:00
|
|
|
void ROMEndTransfer(u32 param)
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
|
|
|
ROMCnt &= ~(1<<31);
|
|
|
|
|
|
|
|
if (SPICnt & (1<<14))
|
2021-04-24 22:48:02 +00:00
|
|
|
NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartXferDone);
|
2020-09-03 18:28:07 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
if (Cart)
|
|
|
|
Cart->ROMCommandFinish(TransferCmd, TransferData, TransferLen);
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ROMPrepareData(u32 param)
|
|
|
|
{
|
2020-09-03 18:28:07 +00:00
|
|
|
if (TransferDir == 0)
|
|
|
|
{
|
|
|
|
if (TransferPos >= TransferLen)
|
|
|
|
ROMData = 0;
|
|
|
|
else
|
|
|
|
ROMData = *(u32*)&TransferData[TransferPos];
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
TransferPos += 4;
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
|
|
|
|
ROMCnt |= (1<<23);
|
2017-04-27 16:45:43 +00:00
|
|
|
|
|
|
|
if (NDS::ExMemCnt[0] & (1<<11))
|
|
|
|
NDS::CheckDMAs(1, 0x12);
|
|
|
|
else
|
|
|
|
NDS::CheckDMAs(0, 0x05);
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2017-01-31 16:34:17 +00:00
|
|
|
void WriteROMCnt(u32 val)
|
2018-04-27 18:00:53 +00:00
|
|
|
{
|
2022-03-13 23:27:03 +00:00
|
|
|
u32 xferstart = (val & ~ROMCnt) & (1<<31);
|
|
|
|
ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x20800000);
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
// all this junk would only really be useful if melonDS was interfaced to
|
|
|
|
// a DS cart reader
|
2017-01-22 19:34:59 +00:00
|
|
|
if (val & (1<<15))
|
|
|
|
{
|
|
|
|
u32 snum = (NDS::ExMemCnt[0]>>8)&0x8;
|
|
|
|
u64 seed0 = *(u32*)&NDS::ROMSeed0[snum] | ((u64)NDS::ROMSeed0[snum+4] << 32);
|
|
|
|
u64 seed1 = *(u32*)&NDS::ROMSeed1[snum] | ((u64)NDS::ROMSeed1[snum+4] << 32);
|
|
|
|
|
|
|
|
Key2_X = 0;
|
|
|
|
Key2_Y = 0;
|
|
|
|
for (u32 i = 0; i < 39; i++)
|
|
|
|
{
|
|
|
|
if (seed0 & (1ULL << i)) Key2_X |= (1ULL << (38-i));
|
|
|
|
if (seed1 & (1ULL << i)) Key2_Y |= (1ULL << (38-i));
|
|
|
|
}
|
|
|
|
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Debug, "seed0: %02X%08X\n", (u32)(seed0>>32), (u32)seed0);
|
|
|
|
Log(LogLevel::Debug, "seed1: %02X%08X\n", (u32)(seed1>>32), (u32)seed1);
|
|
|
|
Log(LogLevel::Debug, "key2 X: %02X%08X\n", (u32)(Key2_X>>32), (u32)Key2_X);
|
|
|
|
Log(LogLevel::Debug, "key2 Y: %02X%08X\n", (u32)(Key2_Y>>32), (u32)Key2_Y);
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2022-03-13 23:27:03 +00:00
|
|
|
// transfers will only start when bit31 changes from 0 to 1
|
|
|
|
// and if AUXSPICNT is configured correctly
|
|
|
|
if (!(SPICnt & (1<<15))) return;
|
|
|
|
if (SPICnt & (1<<13)) return;
|
|
|
|
if (!xferstart) return;
|
2017-01-22 19:34:59 +00:00
|
|
|
|
|
|
|
u32 datasize = (ROMCnt >> 24) & 0x7;
|
|
|
|
if (datasize == 7)
|
|
|
|
datasize = 4;
|
|
|
|
else if (datasize > 0)
|
|
|
|
datasize = 0x100 << datasize;
|
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
TransferPos = 0;
|
|
|
|
TransferLen = datasize;
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
*(u32*)&TransferCmd[0] = *(u32*)&ROMCommand[0];
|
|
|
|
*(u32*)&TransferCmd[4] = *(u32*)&ROMCommand[4];
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2022-01-07 13:00:43 +00:00
|
|
|
memset(TransferData, 0xFF, TransferLen);
|
|
|
|
|
2019-10-19 14:03:59 +00:00
|
|
|
/*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n",
|
2017-01-22 19:34:59 +00:00
|
|
|
SPICnt, ROMCnt,
|
2021-04-24 22:48:02 +00:00
|
|
|
TransferCmd[0], TransferCmd[1], TransferCmd[2], TransferCmd[3],
|
|
|
|
TransferCmd[4], TransferCmd[5], TransferCmd[6], TransferCmd[7],
|
2019-10-19 14:03:59 +00:00
|
|
|
datasize);*/
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
// default is read
|
|
|
|
// commands that do writes will change this
|
|
|
|
TransferDir = 0;
|
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
if (Cart)
|
|
|
|
TransferDir = Cart->ROMCommandStart(TransferCmd, TransferData, TransferLen);
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2021-05-03 12:58:45 +00:00
|
|
|
if ((datasize > 0) && (((ROMCnt >> 30) & 0x1) != TransferDir))
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Debug, "NDSCART: !! BAD TRANSFER DIRECTION FOR CMD %02X, DIR=%d, ROMCNT=%08X\n", ROMCommand[0], TransferDir, ROMCnt);
|
2021-05-03 12:58:45 +00:00
|
|
|
|
2017-04-11 18:21:31 +00:00
|
|
|
ROMCnt &= ~(1<<23);
|
|
|
|
|
|
|
|
// ROM transfer timings
|
|
|
|
// the bus is parallel with 8 bits
|
|
|
|
// thus a command would take 8 cycles to be transferred
|
|
|
|
// and it would take 4 cycles to receive a word of data
|
2017-04-27 16:45:43 +00:00
|
|
|
// TODO: advance read position if bit28 is set
|
2022-03-13 23:27:03 +00:00
|
|
|
// TODO: during a write transfer, bit23 is set immediately when beginning the transfer(?)
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2017-04-11 18:21:31 +00:00
|
|
|
u32 xfercycle = (ROMCnt & (1<<27)) ? 8 : 5;
|
2019-03-26 17:34:01 +00:00
|
|
|
u32 cmddelay = 8;
|
|
|
|
|
|
|
|
// delays are only applied when the WR bit is cleared
|
2021-04-24 22:48:02 +00:00
|
|
|
// CHECKME: do the delays apply at the end (instead of start) when WR is set?
|
2019-03-26 17:34:01 +00:00
|
|
|
if (!(ROMCnt & (1<<30)))
|
|
|
|
{
|
|
|
|
cmddelay += (ROMCnt & 0x1FFF);
|
|
|
|
if (datasize) cmddelay += ((ROMCnt >> 16) & 0x3F);
|
|
|
|
}
|
2017-04-25 00:26:37 +00:00
|
|
|
|
2017-01-22 19:34:59 +00:00
|
|
|
if (datasize == 0)
|
2017-04-25 00:26:37 +00:00
|
|
|
NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*cmddelay, ROMEndTransfer, 0);
|
2017-01-22 19:34:59 +00:00
|
|
|
else
|
2019-01-05 04:28:58 +00:00
|
|
|
NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMPrepareData, 0);
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
void AdvanceROMTransfer()
|
|
|
|
{
|
|
|
|
ROMCnt &= ~(1<<23);
|
|
|
|
|
|
|
|
if (TransferPos < TransferLen)
|
|
|
|
{
|
|
|
|
u32 xfercycle = (ROMCnt & (1<<27)) ? 8 : 5;
|
|
|
|
u32 delay = 4;
|
|
|
|
if (!(ROMCnt & (1<<30)))
|
|
|
|
{
|
|
|
|
if (!(TransferPos & 0x1FF))
|
|
|
|
delay += ((ROMCnt >> 16) & 0x3F);
|
|
|
|
}
|
|
|
|
|
|
|
|
NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*delay, ROMPrepareData, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ROMEndTransfer(0);
|
|
|
|
}
|
|
|
|
|
2017-01-31 16:34:17 +00:00
|
|
|
u32 ReadROMData()
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2021-05-03 12:58:45 +00:00
|
|
|
if (ROMCnt & (1<<30)) return 0;
|
|
|
|
|
2017-04-11 18:21:31 +00:00
|
|
|
if (ROMCnt & (1<<23))
|
2017-01-22 19:34:59 +00:00
|
|
|
{
|
2020-09-03 18:28:07 +00:00
|
|
|
AdvanceROMTransfer();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ROMData;
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
void WriteROMData(u32 val)
|
|
|
|
{
|
2021-05-03 12:58:45 +00:00
|
|
|
if (!(ROMCnt & (1<<30))) return;
|
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
ROMData = val;
|
|
|
|
|
|
|
|
if (ROMCnt & (1<<23))
|
|
|
|
{
|
|
|
|
if (TransferDir == 1)
|
2017-04-11 18:21:31 +00:00
|
|
|
{
|
2020-09-03 18:28:07 +00:00
|
|
|
if (TransferPos < TransferLen)
|
|
|
|
*(u32*)&TransferData[TransferPos] = ROMData;
|
2017-04-25 00:26:37 +00:00
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
TransferPos += 4;
|
2017-04-11 18:21:31 +00:00
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
|
2020-09-03 18:28:07 +00:00
|
|
|
AdvanceROMTransfer();
|
|
|
|
}
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
2017-01-31 16:34:17 +00:00
|
|
|
|
|
|
|
void WriteSPICnt(u16 val)
|
|
|
|
{
|
2021-04-24 22:48:02 +00:00
|
|
|
if ((SPICnt & 0x2040) == 0x2040 && (val & 0x2000) == 0x0000)
|
|
|
|
{
|
|
|
|
// forcefully reset SPI hold
|
|
|
|
SPIHold = false;
|
|
|
|
}
|
|
|
|
|
2017-01-31 16:34:17 +00:00
|
|
|
SPICnt = (SPICnt & 0x0080) | (val & 0xE043);
|
2022-03-13 23:27:03 +00:00
|
|
|
|
|
|
|
// AUXSPICNT can be changed during a transfer
|
|
|
|
// in this case, the transfer continues until the end, even if bit13 or bit15 are cleared
|
|
|
|
// if the transfer speed is changed, the transfer continues at the new speed (TODO)
|
2017-07-15 17:29:10 +00:00
|
|
|
if (SPICnt & (1<<7))
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Debug, "!! CHANGING AUXSPICNT DURING TRANSFER: %04X\n", val);
|
2017-07-15 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SPITransferDone(u32 param)
|
|
|
|
{
|
|
|
|
SPICnt &= ~(1<<7);
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 ReadSPIData()
|
|
|
|
{
|
|
|
|
if (!(SPICnt & (1<<15))) return 0;
|
|
|
|
if (!(SPICnt & (1<<13))) return 0;
|
2017-07-15 17:29:10 +00:00
|
|
|
if (SPICnt & (1<<7)) return 0; // checkme
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2021-04-24 22:48:02 +00:00
|
|
|
return SPIData;
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void WriteSPIData(u8 val)
|
|
|
|
{
|
|
|
|
if (!(SPICnt & (1<<15))) return;
|
|
|
|
if (!(SPICnt & (1<<13))) return;
|
2022-03-13 23:27:03 +00:00
|
|
|
if (SPICnt & (1<<7)) return;
|
2017-01-31 16:34:17 +00:00
|
|
|
|
2017-07-15 17:29:10 +00:00
|
|
|
SPICnt |= (1<<7);
|
2021-04-24 22:48:02 +00:00
|
|
|
|
|
|
|
bool hold = SPICnt&(1<<6);
|
|
|
|
bool islast = false;
|
|
|
|
if (!hold)
|
|
|
|
{
|
|
|
|
if (SPIHold) SPIDataPos++;
|
|
|
|
else SPIDataPos = 0;
|
|
|
|
islast = true;
|
|
|
|
SPIHold = false;
|
|
|
|
}
|
|
|
|
else if (hold && (!SPIHold))
|
|
|
|
{
|
|
|
|
SPIHold = true;
|
|
|
|
SPIDataPos = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SPIDataPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Cart) SPIData = Cart->SPIWrite(val, SPIDataPos, islast);
|
|
|
|
else SPIData = 0;
|
2017-07-15 17:29:10 +00:00
|
|
|
|
|
|
|
// SPI transfers one bit per cycle -> 8 cycles per byte
|
|
|
|
u32 delay = 8 * (8 << (SPICnt & 0x3));
|
|
|
|
NDS::ScheduleEvent(NDS::Event_ROMSPITransfer, false, delay, SPITransferDone, 0);
|
2017-01-31 16:34:17 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 19:34:59 +00:00
|
|
|
}
|