mirror of https://github.com/PCSX2/pcsx2.git
504 lines
11 KiB
C++
504 lines
11 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 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 PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
#include "MemoryCardProtocol.h"
|
|
#include "Sio.h"
|
|
|
|
#define MC_LOG_ENABLE 0
|
|
#define MC_LOG if (MC_LOG_ENABLE) DevCon
|
|
|
|
#define PS1_FAIL() if (this->PS1Fail()) return;
|
|
|
|
MemoryCardProtocol g_MemoryCardProtocol;
|
|
|
|
// Check if the memcard is for PS1, and if we are working on a command sent over SIO2.
|
|
// If so, return dead air.
|
|
bool MemoryCardProtocol::PS1Fail()
|
|
{
|
|
if (mcd->IsPSX() && sio2.commandLength > 0)
|
|
{
|
|
while (fifoOut.size() < sio2.commandLength)
|
|
{
|
|
fifoOut.push_back(0x00);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// A repeated pattern in memcard commands is to pad with zero bytes,
|
|
// then end with 0x2b and terminator bytes. This function is a shortcut for that.
|
|
void MemoryCardProtocol::The2bTerminator(size_t length)
|
|
{
|
|
while (fifoOut.size() < length - 2)
|
|
{
|
|
fifoOut.push_back(0x00);
|
|
}
|
|
|
|
fifoOut.push_back(0x2b);
|
|
fifoOut.push_back(mcd->term);
|
|
}
|
|
|
|
// After one read or write, the memcard is almost certainly going to be issued a new read or write
|
|
// for the next segment of the same sector. Bump the transferAddr to where that segment begins.
|
|
// If it is the end and a new sector is being accessed, the SetSector function will deal with
|
|
// both sectorAddr and transferAddr.
|
|
void MemoryCardProtocol::ReadWriteIncrement(size_t length)
|
|
{
|
|
mcd->transferAddr += length;
|
|
}
|
|
|
|
void MemoryCardProtocol::RecalculatePS1Addr()
|
|
{
|
|
mcd->sectorAddr = ((ps1McState.sectorAddrMSB << 8) | ps1McState.sectorAddrLSB);
|
|
mcd->goodSector = (mcd->sectorAddr <= 0x03ff);
|
|
mcd->transferAddr = 128 * mcd->sectorAddr;
|
|
}
|
|
|
|
void MemoryCardProtocol::ResetPS1State()
|
|
{
|
|
ps1McState.currentByte = 2;
|
|
ps1McState.sectorAddrMSB = 0;
|
|
ps1McState.sectorAddrLSB = 0;
|
|
ps1McState.checksum = 0;
|
|
ps1McState.expectedChecksum = 0;
|
|
memset(ps1McState.buf.data(), 0, ps1McState.buf.size());
|
|
}
|
|
|
|
void MemoryCardProtocol::Probe()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
The2bTerminator(4);
|
|
}
|
|
|
|
void MemoryCardProtocol::UnknownWriteDeleteEnd()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
The2bTerminator(4);
|
|
}
|
|
|
|
void MemoryCardProtocol::SetSector()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
const u8 sectorLSB = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
const u8 sector2nd = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
const u8 sector3rd = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
const u8 sectorMSB = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
const u8 expectedChecksum = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
|
|
u8 computedChecksum = sectorLSB ^ sector2nd ^ sector3rd ^ sectorMSB;
|
|
mcd->goodSector = (computedChecksum == expectedChecksum);
|
|
|
|
if (!mcd->goodSector)
|
|
{
|
|
Console.Warning("%s() Warning! Memcard sector checksum failed! (Expected %02X != Actual %02X) Please report to the PCSX2 team!", __FUNCTION__, expectedChecksum, computedChecksum);
|
|
}
|
|
|
|
u32 newSector = sectorLSB | (sector2nd << 8) | (sector3rd << 16) | (sectorMSB << 24);
|
|
mcd->sectorAddr = newSector;
|
|
|
|
McdSizeInfo info;
|
|
mcd->GetSizeInfo(info);
|
|
mcd->transferAddr = (info.SectorSize + 16) * mcd->sectorAddr;
|
|
|
|
The2bTerminator(9);
|
|
}
|
|
|
|
void MemoryCardProtocol::GetSpecs()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
//u8 checksum = 0x00;
|
|
McdSizeInfo info;
|
|
mcd->GetSizeInfo(info);
|
|
fifoOut.push_back(0x2b);
|
|
|
|
const u8 sectorSizeLSB = (info.SectorSize & 0xff);
|
|
//checksum ^= sectorSizeLSB;
|
|
fifoOut.push_back(sectorSizeLSB);
|
|
|
|
const u8 sectorSizeMSB = (info.SectorSize >> 8);
|
|
//checksum ^= sectorSizeMSB;
|
|
fifoOut.push_back(sectorSizeMSB);
|
|
|
|
const u8 eraseBlockSizeLSB = (info.EraseBlockSizeInSectors & 0xff);
|
|
//checksum ^= eraseBlockSizeLSB;
|
|
fifoOut.push_back(eraseBlockSizeLSB);
|
|
|
|
const u8 eraseBlockSizeMSB = (info.EraseBlockSizeInSectors >> 8);
|
|
//checksum ^= eraseBlockSizeMSB;
|
|
fifoOut.push_back(eraseBlockSizeMSB);
|
|
|
|
const u8 sectorCountLSB = (info.McdSizeInSectors & 0xff);
|
|
//checksum ^= sectorCountLSB;
|
|
fifoOut.push_back(sectorCountLSB);
|
|
|
|
const u8 sectorCount2nd = (info.McdSizeInSectors >> 8);
|
|
//checksum ^= sectorCount2nd;
|
|
fifoOut.push_back(sectorCount2nd);
|
|
|
|
const u8 sectorCount3rd = (info.McdSizeInSectors >> 16);
|
|
//checksum ^= sectorCount3rd;
|
|
fifoOut.push_back(sectorCount3rd);
|
|
|
|
const u8 sectorCountMSB = (info.McdSizeInSectors >> 24);
|
|
//checksum ^= sectorCountMSB;
|
|
fifoOut.push_back(sectorCountMSB);
|
|
|
|
fifoOut.push_back(info.Xor);
|
|
fifoOut.push_back(mcd->term);
|
|
}
|
|
|
|
void MemoryCardProtocol::SetTerminator()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
const u8 newTerminator = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
const u8 oldTerminator = mcd->term;
|
|
mcd->term = newTerminator;
|
|
fifoOut.push_back(0x00);
|
|
fifoOut.push_back(0x2b);
|
|
fifoOut.push_back(oldTerminator);
|
|
}
|
|
|
|
void MemoryCardProtocol::GetTerminator()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
fifoOut.push_back(0x2b);
|
|
fifoOut.push_back(mcd->term);
|
|
fifoOut.push_back(static_cast<u8>(Terminator::DEFAULT));
|
|
}
|
|
|
|
void MemoryCardProtocol::WriteData()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
fifoOut.push_back(0x00);
|
|
fifoOut.push_back(0x2b);
|
|
const u8 writeLength = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
u8 checksum = 0x00;
|
|
std::vector<u8> buf;
|
|
|
|
for (size_t writeCounter = 0; writeCounter < writeLength; writeCounter++)
|
|
{
|
|
const u8 writeByte = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
checksum ^= writeByte;
|
|
buf.push_back(writeByte);
|
|
fifoOut.push_back(0x00);
|
|
}
|
|
|
|
mcd->Write(buf.data(), buf.size());
|
|
fifoOut.push_back(checksum);
|
|
fifoOut.push_back(mcd->term);
|
|
|
|
ReadWriteIncrement(writeLength);
|
|
}
|
|
|
|
void MemoryCardProtocol::ReadData()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
const u8 readLength = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
fifoOut.push_back(0x00);
|
|
fifoOut.push_back(0x2b);
|
|
std::vector<u8> buf;
|
|
buf.resize(readLength);
|
|
mcd->Read(buf.data(), buf.size());
|
|
u8 checksum = 0x00;
|
|
|
|
for (const u8 readByte : buf)
|
|
{
|
|
checksum ^= readByte;
|
|
fifoOut.push_back(readByte);
|
|
}
|
|
|
|
fifoOut.push_back(checksum);
|
|
fifoOut.push_back(mcd->term);
|
|
|
|
ReadWriteIncrement(readLength);
|
|
}
|
|
|
|
u8 MemoryCardProtocol::PS1Read(u8 data)
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
bool sendAck = true;
|
|
u8 ret = 0;
|
|
|
|
switch (ps1McState.currentByte)
|
|
{
|
|
case 2:
|
|
ret = 0x5a;
|
|
break;
|
|
case 3:
|
|
ret = 0x5d;
|
|
break;
|
|
case 4:
|
|
ps1McState.sectorAddrMSB = data;
|
|
ret = 0x00;
|
|
break;
|
|
case 5:
|
|
ps1McState.sectorAddrLSB = data;
|
|
ret = 0x00;
|
|
RecalculatePS1Addr();
|
|
break;
|
|
case 6:
|
|
ret = 0x5c;
|
|
break;
|
|
case 7:
|
|
ret = 0x5d;
|
|
break;
|
|
case 8:
|
|
ret = ps1McState.sectorAddrMSB;
|
|
break;
|
|
case 9:
|
|
ret = ps1McState.sectorAddrLSB;
|
|
break;
|
|
case 138:
|
|
ret = ps1McState.checksum;
|
|
break;
|
|
case 139:
|
|
ret = 0x47;
|
|
sendAck = false;
|
|
break;
|
|
case 10:
|
|
ps1McState.checksum = ps1McState.sectorAddrMSB ^ ps1McState.sectorAddrLSB;
|
|
mcd->Read(ps1McState.buf.data(), ps1McState.buf.size());
|
|
[[fallthrough]];
|
|
default:
|
|
ret = ps1McState.buf.at(ps1McState.currentByte - 10);
|
|
ps1McState.checksum ^= ret;
|
|
break;
|
|
}
|
|
|
|
if (sendAck)
|
|
{
|
|
sio0.Acknowledge();
|
|
}
|
|
|
|
ps1McState.currentByte++;
|
|
return ret;
|
|
}
|
|
|
|
u8 MemoryCardProtocol::PS1State(u8 data)
|
|
{
|
|
DevCon.Error("%s(%02X) I do not exist, please change that ASAP.", __FUNCTION__, data);
|
|
assert(false);
|
|
return 0x00;
|
|
}
|
|
|
|
u8 MemoryCardProtocol::PS1Write(u8 data)
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
bool sendAck = true;
|
|
u8 ret = 0;
|
|
|
|
switch (ps1McState.currentByte)
|
|
{
|
|
case 2:
|
|
ret = 0x5a;
|
|
break;
|
|
case 3:
|
|
ret = 0x5d;
|
|
break;
|
|
case 4:
|
|
ps1McState.sectorAddrMSB = data;
|
|
ret = 0x00;
|
|
break;
|
|
case 5:
|
|
ps1McState.sectorAddrLSB = data;
|
|
ret = 0x00;
|
|
RecalculatePS1Addr();
|
|
break;
|
|
case 134:
|
|
ps1McState.expectedChecksum = data;
|
|
ret = 0;
|
|
break;
|
|
case 135:
|
|
ret = 0x5c;
|
|
break;
|
|
case 136:
|
|
ret = 0x5d;
|
|
break;
|
|
case 137:
|
|
if (!mcd->goodSector)
|
|
{
|
|
ret = 0xff;
|
|
}
|
|
else if (ps1McState.expectedChecksum != ps1McState.checksum)
|
|
{
|
|
ret = 0x4e;
|
|
}
|
|
else
|
|
{
|
|
mcd->Write(ps1McState.buf.data(), ps1McState.buf.size());
|
|
ret = 0x47;
|
|
// Clear the "directory unread" bit of the flag byte. Per no$psx, this is cleared
|
|
// on writes, not reads.
|
|
mcd->FLAG &= 0x07;
|
|
}
|
|
|
|
sendAck = false;
|
|
break;
|
|
case 6:
|
|
ps1McState.checksum = ps1McState.sectorAddrMSB ^ ps1McState.sectorAddrLSB;
|
|
[[fallthrough]];
|
|
default:
|
|
ps1McState.buf.at(ps1McState.currentByte - 6) = data;
|
|
ps1McState.checksum ^= data;
|
|
ret = 0x00;
|
|
break;
|
|
}
|
|
|
|
if (sendAck)
|
|
{
|
|
sio0.Acknowledge();
|
|
}
|
|
|
|
ps1McState.currentByte++;
|
|
return ret;
|
|
}
|
|
|
|
u8 MemoryCardProtocol::PS1Pocketstation(u8 data)
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
sio2.SetRecv1(Recv1::DISCONNECTED);
|
|
return 0x00;
|
|
}
|
|
|
|
void MemoryCardProtocol::ReadWriteEnd()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
The2bTerminator(4);
|
|
}
|
|
|
|
void MemoryCardProtocol::EraseBlock()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
mcd->EraseBlock();
|
|
The2bTerminator(4);
|
|
}
|
|
|
|
void MemoryCardProtocol::UnknownBoot()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
The2bTerminator(5);
|
|
}
|
|
|
|
void MemoryCardProtocol::AuthXor()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
const u8 modeByte = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
|
|
switch (modeByte)
|
|
{
|
|
// When encountered, the command length in RECV3 is guaranteed to be 14,
|
|
// and the PS2 is expecting us to XOR the data it is about to send.
|
|
case 0x01:
|
|
case 0x02:
|
|
case 0x04:
|
|
case 0x0f:
|
|
case 0x11:
|
|
case 0x13:
|
|
{
|
|
// Long + XOR
|
|
fifoOut.push_back(0x00);
|
|
fifoOut.push_back(0x2b);
|
|
u8 xorResult = 0x00;
|
|
|
|
for (size_t xorCounter = 0; xorCounter < 8; xorCounter++)
|
|
{
|
|
const u8 toXOR = fifoIn.front();
|
|
fifoIn.pop_front();
|
|
xorResult ^= toXOR;
|
|
fifoOut.push_back(0x00);
|
|
}
|
|
|
|
fifoOut.push_back(xorResult);
|
|
fifoOut.push_back(mcd->term);
|
|
break;
|
|
}
|
|
// When encountered, the command length in RECV3 is guaranteed to be 5,
|
|
// and there is no attempt to XOR anything.
|
|
case 0x00:
|
|
case 0x03:
|
|
case 0x05:
|
|
case 0x08:
|
|
case 0x09:
|
|
case 0x0a:
|
|
case 0x0c:
|
|
case 0x0d:
|
|
case 0x0e:
|
|
case 0x10:
|
|
case 0x12:
|
|
case 0x14:
|
|
{
|
|
// Short + No XOR
|
|
The2bTerminator(5);
|
|
break;
|
|
}
|
|
// When encountered, the command length in RECV3 is guaranteed to be 14,
|
|
// and the PS2 is about to send us data, BUT the PS2 does NOT want us
|
|
// to send the XOR, it wants us to send the 0x2b and terminator as the
|
|
// last two bytes.
|
|
case 0x06:
|
|
case 0x07:
|
|
case 0x0b:
|
|
{
|
|
// Long + No XOR
|
|
The2bTerminator(14);
|
|
break;
|
|
}
|
|
default:
|
|
Console.Warning("%s(queue) Unexpected modeByte (%02X), please report to the PCSX2 team", __FUNCTION__, modeByte);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MemoryCardProtocol::AuthF3()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
The2bTerminator(5);
|
|
}
|
|
|
|
void MemoryCardProtocol::AuthF7()
|
|
{
|
|
MC_LOG.WriteLn("%s", __FUNCTION__);
|
|
PS1_FAIL();
|
|
The2bTerminator(5);
|
|
}
|