CDVD: Implement Disc Swapping (#4860)

Inspired by the work by fldef, fixes up swapping support for ISO's and physical discs

CDVD: Set the disk type to none/detecting when seeking.
Also disable the skip BIOS flag if we're not skipping the BIOS
CDVD: Fix up swapping behaviour, Singstar etc
This commit is contained in:
refractionpcsx2 2021-10-03 17:27:15 +01:00 committed by GitHub
parent 759b869c1c
commit 8d92c0668c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 139 additions and 55 deletions

View File

@ -441,7 +441,7 @@ void cdvdReloadElfInfo(wxString elfoverride)
// called from context of executing VM code (recompilers), so we need to trap exceptions
// and route them through the VM's exception handler. (needed for non-SEH platforms, such
// as Linux/GCC)
DevCon.WriteLn(Color_Green, L"Reload ELF");
try
{
if (!elfoverride.IsEmpty())
@ -576,15 +576,33 @@ s32 cdvdReadSubQ(s32 lsn, cdvdSubQ* subq)
static void cdvdDetectDisk()
{
cdvd.Type = DoCDVDdetectDiskType();
cdvdReloadElfInfo();
}
s32 cdvdCtrlTrayOpen()
{
DevCon.WriteLn(Color_Green, L"Open virtual disk tray");
// If we switch using a source change we need to pretend it's a new disc
if (CDVDsys_GetSourceType() == CDVD_SourceType::Disc)
{
cdvdNewDiskCB();
return 0;
}
cdvdDetectDisk();
DiscSwapTimerSeconds = cdvd.RTC.second; // remember the PS2 time when this happened
cdvd.Status = CDVD_STATUS_TRAY_OPEN;
cdvd.Ready = CDVD_NOTREADY;
cdvd.Ready = CDVD_DRIVENOTREADY;
cdvd.mediaChanged = true;
if (cdvd.Type > 0)
{
cdvd.Tray.cdvdActionSeconds = 3;
cdvd.Tray.trayState = CDVD_DISC_EJECT;
DevCon.WriteLn(Color_Green, L"Simulating ejected media");
}
return 0; // needs to be 0 for success according to homebrew test "CDVD"
}
@ -592,38 +610,28 @@ s32 cdvdCtrlTrayOpen()
s32 cdvdCtrlTrayClose()
{
DevCon.WriteLn(Color_Green, L"Close virtual disk tray");
cdvd.Status = CDVD_STATUS_PAUSE;
cdvd.Ready = CDVD_READY1;
cdvd.TrayTimeout = 0; // Reset so it can't get closed twice by cdvdVsync()
if (!g_GameStarted && g_SkipBiosHack)
{
DevCon.WriteLn(Color_Green, L"Media already loaded (fast boot)");
cdvd.Ready = CDVD_READY1;
cdvd.Status = CDVD_STATUS_PAUSE;
cdvd.Tray.trayState = CDVD_DISC_ENGAGED;
cdvd.Tray.cdvdActionSeconds = 0;
}
else
{
DevCon.WriteLn(Color_Green, L"Seeking media");
cdvd.Ready = CDVD_DRIVENOTREADY;
cdvd.Status = CDVD_STATUS_SEEK;
cdvd.Tray.trayState = CDVD_DISC_SEEKING;
cdvd.Tray.cdvdActionSeconds = 3;
}
cdvdDetectDisk();
return 0; // needs to be 0 for success according to homebrew test "CDVD"
}
// Some legacy function, not used anymore
s32 cdvdGetTrayStatus()
{
/*s32 ret = CDVD->getTrayStatus();
if (ret == -1)
return(CDVD_TRAY_CLOSE);
else
return(ret);*/
return -1;
}
// Note: Is tray status being kept as a var here somewhere?
// Yep, and sceCdTrayReq needs it to detect tray state changes (rama)
// cdvdNewDiskCB() can update it's status as well...
// Modified by (efp) - 16/01/2006
static __fi void cdvdGetDiskType()
{
cdvd.Type = DoCDVDdetectDiskType();
}
// check whether disc is single or dual layer
// if its dual layer, check what the disctype is and what sector number
// layer1 starts at
@ -697,6 +705,7 @@ void cdvdReset()
cdvd.sDataIn = 0x40;
cdvd.Ready = CDVD_READY2;
cdvd.Status = CDVD_STATUS_PAUSE;
cdvd.Speed = 4;
cdvd.BlockSize = 2064;
cdvd.Action = cdvdAction_None;
@ -711,6 +720,12 @@ void cdvdReset()
cdvd.RTC.day = (u8)curtime.GetDay(wxDateTime::GMT9);
cdvd.RTC.month = (u8)curtime.GetMonth(wxDateTime::GMT9) + 1; // WX returns Jan as "0"
cdvd.RTC.year = (u8)(curtime.GetYear(wxDateTime::GMT9) - 2000);
g_GameStarted = false;
g_GameLoading = false;
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
cdvdCtrlTrayClose();
}
struct Freeze_v10Compat
@ -741,10 +756,35 @@ void cdvdNewDiskCB()
{
ScopedTryLock lock(Mutex_NewDiskCB);
if (lock.Failed())
{
DevCon.WriteLn(Color_Red, L"NewDiskCB Lock Failed");
return;
}
DoCDVDresetDiskTypeCache();
cdvdDetectDisk();
// If not ejected but we've swapped source pretend it got ejected
if ((g_GameStarted || !g_SkipBiosHack) && cdvd.Tray.trayState != CDVD_DISC_EJECT)
{
DevCon.WriteLn(Color_Green, L"Ejecting media");
cdvd.Status = CDVD_STATUS_TRAY_OPEN;
cdvd.Ready = CDVD_DRIVENOTREADY;
cdvd.Tray.trayState = CDVD_DISC_EJECT;
cdvd.mediaChanged = true;
// If it really got ejected, the DVD Reader will report Type 0, so no need to simulate ejection
if (cdvd.Type > 0)
cdvd.Tray.cdvdActionSeconds = 3;
}
else if(cdvd.Type > 0)
{
DevCon.WriteLn(Color_Green, L"Seeking new media");
cdvd.Ready = CDVD_DRIVENOTREADY;
cdvd.Status = CDVD_STATUS_SEEK;
cdvd.Tray.trayState = CDVD_DISC_SEEKING;
cdvd.Tray.cdvdActionSeconds = 3;
}
}
static void mechaDecryptBytes(u32 madr, int size)
@ -933,7 +973,7 @@ __fi void cdvdReadInterrupt()
{
//Console.WriteLn("cdvdReadInterrupt %x %x %x %x %x", cpuRegs.interrupt, cdvd.Readed, cdvd.Reading, cdvd.nSectors, (HW_DMA3_BCR_H16 * HW_DMA3_BCR_L16) *4);
cdvd.Ready = CDVD_NOTREADY;
cdvd.Ready = CDVD_NCMDNOTREADY;
cdvd.Status = CDVD_STATUS_READ | CDVD_STATUS_SPIN;
if (!cdvd.Readed)
{
@ -1038,7 +1078,7 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
uint delta = abs((s32)(cdvd.SeekToSector - cdvd.Sector));
uint seektime;
cdvd.Ready = CDVD_NOTREADY;
cdvd.Ready = CDVD_NCMDNOTREADY;
cdvd.Reading = 0;
cdvd.Readed = 0;
@ -1098,6 +1138,30 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
u8 monthmap[13] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void cdvdUpdateTrayState()
{
if (cdvd.Tray.cdvdActionSeconds > 0)
{
if (--cdvd.Tray.cdvdActionSeconds == 0)
{
switch (cdvd.Tray.trayState)
{
case CDVD_DISC_EJECT:
cdvdCtrlTrayClose();
break;
case CDVD_DISC_SEEKING:
case CDVD_DISC_ENGAGED:
cdvd.mediaChanged = true;
DevCon.WriteLn(Color_Green, L"Media ready to read");
cdvd.Tray.trayState = CDVD_DISC_ENGAGED;
cdvd.Status = CDVD_STATUS_PAUSE;
cdvd.Ready = CDVD_READY1;
break;
}
}
}
}
void cdvdVsync()
{
cdvd.RTCcount++;
@ -1105,15 +1169,7 @@ void cdvdVsync()
return;
cdvd.RTCcount = 0;
if (cdvd.Status == CDVD_STATUS_TRAY_OPEN)
{
cdvd.TrayTimeout++;
}
if (cdvd.TrayTimeout > 3)
{
cdvdCtrlTrayClose();
cdvd.TrayTimeout = 0;
}
cdvdUpdateTrayState();
cdvd.RTC.second++;
if (cdvd.RTC.second < 60)
@ -1190,13 +1246,13 @@ u8 cdvdRead(u8 key)
CDVD_LOG("cdvdRead0A(Status) %x", cdvd.Status);
return cdvd.Status;
case 0x0B: // TRAY-STATE (if tray has been opened)
case 0x0B: // MEDIA CHANGED (Set when disc is ejected or detected, aka cdvd.type changes)
{
CDVD_LOG("cdvdRead0B(Tray) (1 open, 0 closed): %x", cdvd.Status);
if (cdvd.Status == CDVD_STATUS_TRAY_OPEN)
return 1;
else
return 0;
CDVD_LOG("cdvdRead0B(Media Change) (1 Changed, 0 Not Changed): %x", cdvd.mediaChanged);
bool ret = cdvd.mediaChanged;
cdvd.mediaChanged = false;
return ret;
}
case 0x0C: // CRT MINUTE
CDVD_LOG("cdvdRead0C(Min) %x", itob((u8)(cdvd.Sector / (60 * 75))));
@ -1211,9 +1267,16 @@ u8 cdvdRead(u8 key)
return itob((u8)(cdvd.Sector % 75));
case 0x0F: // TYPE
CDVD_LOG("cdvdRead0F(Disc Type) %x", cdvd.Type);
cdvdGetDiskType();
if (cdvd.Tray.trayState == CDVD_DISC_ENGAGED)
{
CDVD_LOG("cdvdRead0F(Disc Type) Engaged %x", cdvd.Type);
return cdvd.Type;
}
else
{
CDVD_LOG("cdvdRead0F(Disc Type) Detecting %x", (cdvd.Tray.trayState == CDVD_DISC_SEEKING) ? 1 : 0);
return (cdvd.Tray.trayState == CDVD_DISC_SEEKING) ? 1 : 0; // Detecting Disc / No Disc
}
case 0x13: // UNKNOWN
CDVD_LOG("cdvdRead13(Unknown) %x", 4);
@ -1609,7 +1672,7 @@ static __fi void cdvdWrite07(u8 rt) // BREAK
CDVD_LOG("cdvdWrite07(Break) %x", rt);
// If we're already in a Ready state or already Breaking, then do nothing:
if ((cdvd.Ready != CDVD_NOTREADY) || (cdvd.Action == cdvdAction_Break))
if ((cdvd.Ready != CDVD_NCMDNOTREADY) || (cdvd.Action == cdvdAction_Break))
return;
DbgCon.WriteLn("*PCSX2*: CDVD BREAK %x", rt);
@ -1696,7 +1759,12 @@ static void cdvdWrite16(u8 rt) // SCOMMAND
SetResultSize(4);
cdvdGetMechaVer(&cdvd.Result[0]);
break;
case 0x30:
SetResultSize(2);
cdvd.Result[0] = cdvd.Status;
cdvd.Result[1] = (cdvd.Status & 0x1) ? 8 : 0;
//Console.Warning("Tray check param[1]=%02X", cdvd.Param[1]);
break;
case 0x44: // write console ID (9:1)
SetResultSize(1);
cdvdWriteConsoleID(&cdvd.Param[1]);

View File

@ -76,6 +76,19 @@ struct cdvdRTC
u8 year;
};
enum TrayStates
{
CDVD_DISC_ENGAGED,
CDVD_DISC_SEEKING,
CDVD_DISC_EJECT
};
struct cdvdTrayTimer
{
u32 cdvdActionSeconds;
TrayStates trayState;
};
struct cdvdStruct
{
u8 nCommand;
@ -135,6 +148,8 @@ struct cdvdStruct
u32 SeekToSector; // Holds the destination sector during seek operations.
u32 ReadTime; // Avg. time to read one block of data (in Iop cycles)
bool Spinning; // indicates if the Cdvd is spinning or needs a spinup delay
bool mediaChanged;
cdvdTrayTimer Tray;
};
extern cdvdStruct cdvd;

View File

@ -72,7 +72,8 @@ enum cdvdStatus
enum cdvdready
{
CDVD_NOTREADY = 0x06,
CDVD_DRIVENOTREADY = 0x02,
CDVD_NCMDNOTREADY = 0x06,
CDVD_READY1 = 0x42,
CDVD_READY2 = 0x42 // This is used in a few places for some reason.
//It would be worth checking if this was just a typo made at some point.

View File

@ -101,8 +101,6 @@ void cpuReset()
extern void Deci2Reset(); // lazy, no good header for it yet.
Deci2Reset();
g_GameStarted = false;
g_GameLoading = false;
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
ElfCRC = 0;
@ -692,6 +690,8 @@ void __fastcall eeloadHook()
{
if (disctype == 2)
elftoload = discelf.ToUTF8();
else
g_SkipBiosHack = false; // We're not fast booting, so disable it (Fixes some weirdness with the BIOS)
}
// When fast-booting, we insert the game's ELF name into EELOAD so that the game is called instead of the default call of

View File

@ -597,8 +597,8 @@ void AppCoreThread::OnResumeInThread(SystemsMask systemsToReinstate)
if (m_resetCdvd)
{
CDVDsys_ChangeSource(g_Conf->CdvdSource);
cdvdCtrlTrayOpen();
DoCDVDopen();
cdvdCtrlTrayOpen();
m_resetCdvd = false;
}
else if (systemsToReinstate & System_CDVD)