pcsx2/plugins/cdvdGigaherz/src/ReadThread.cpp

430 lines
9.9 KiB
C++
Raw Normal View History

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2014 David Quintana [gigaherz]
*
* 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 "CDVD.h"
const s32 prefetch_max_blocks = 16;
s32 prefetch_mode = 0;
s32 prefetch_last_lba = 0;
s32 prefetch_last_mode = 0;
s32 prefetch_left = 0;
HANDLE hNotify = nullptr;
HANDLE hThread = nullptr;
HANDLE hRequestComplete = nullptr;
CRITICAL_SECTION CacheMutex;
DWORD pidThread = 0;
typedef struct
{
int lsn;
int mode;
char data[2352 * 16]; //we will read in blocks of 16 sectors
} SectorInfo;
//bits: 12 would use 1<<12 entries, or 4096*16 sectors ~ 128MB
#define CACHE_SIZE 12
const s32 CacheSize = (1 << CACHE_SIZE);
SectorInfo Cache[CacheSize];
bool threadRequestPending;
SectorInfo threadRequestInfo;
u32 cdvdSectorHash(int lsn, int mode)
{
u32 t = 0;
int i = 32;
int m = CacheSize - 1;
while (i >= 0) {
t ^= lsn & m;
lsn >>= CACHE_SIZE;
i -= CACHE_SIZE;
}
return (t ^ mode) & m;
}
void cdvdCacheUpdate(int lsn, int mode, char *data)
{
EnterCriticalSection(&CacheMutex);
u32 entry = cdvdSectorHash(lsn, mode);
memcpy(Cache[entry].data, data, 2352 * 16);
Cache[entry].lsn = lsn;
Cache[entry].mode = mode;
LeaveCriticalSection(&CacheMutex);
}
bool cdvdCacheFetch(int lsn, int mode, char *data)
{
EnterCriticalSection(&CacheMutex);
u32 entry = cdvdSectorHash(lsn, mode);
if ((Cache[entry].lsn == lsn) &&
(Cache[entry].mode == mode)) {
memcpy(data, Cache[entry].data, 2352 * 16);
LeaveCriticalSection(&CacheMutex);
return true;
}
//printf("NOT IN CACHE\n");
LeaveCriticalSection(&CacheMutex);
return false;
}
void cdvdCacheReset()
{
EnterCriticalSection(&CacheMutex);
for (int i = 0; i < CacheSize; i++) {
Cache[i].lsn = -1;
Cache[i].mode = -1;
}
LeaveCriticalSection(&CacheMutex);
}
void cdvdCallNewDiscCB()
{
weAreInNewDiskCB = true;
newDiscCB();
weAreInNewDiskCB = false;
}
bool cdvdUpdateDiscStatus()
{
int change = src->DiscChanged();
if (change == -1) { //error getting status (no disc in drive?)
//try to recreate the device
src->Reopen();
if (src->IsOK()) {
change = 1;
} else {
curDiskType = CDVD_TYPE_NODISC;
curTrayStatus = CDVD_TRAY_OPEN;
return true;
}
}
if (change == 1) {
if (!disc_has_changed) {
disc_has_changed = true;
curDiskType = CDVD_TYPE_NODISC;
curTrayStatus = CDVD_TRAY_OPEN;
cdvdCallNewDiscCB();
}
} else {
if (disc_has_changed) {
curDiskType = CDVD_TYPE_NODISC;
curTrayStatus = CDVD_TRAY_CLOSE;
// just a test
src->Reopen();
disc_has_changed = false;
cdvdRefreshData();
cdvdCallNewDiscCB();
}
}
return (change != 0);
}
DWORD CALLBACK cdvdThread(PVOID param)
{
printf(" * CDVD: IO thread started...\n");
while (cdvd_is_open) {
if (!src)
break;
if (cdvdUpdateDiscStatus()) {
// Need to sleep some to avoid an aggressive spin that sucks the cpu dry.
Sleep(10);
continue;
}
if (prefetch_left)
WaitForSingleObject(hNotify, 1);
else
WaitForSingleObject(hNotify, 250);
// check again to make sure we're not done here...
if (!cdvd_is_open)
break;
static SectorInfo info;
bool handlingRequest = false;
if (threadRequestPending) {
info = threadRequestInfo;
handlingRequest = true;
} else {
info.lsn = prefetch_last_lba;
info.mode = prefetch_last_mode;
}
if (threadRequestPending || prefetch_left) {
s32 ret = -1;
s32 tries = 5;
s32 count = 16;
s32 left = src->GetSectorCount() - info.lsn;
if (left < count)
count = left;
do {
if (info.mode == CDVD_MODE_2048)
ret = src->ReadSectors2048(info.lsn, count, info.data);
else
ret = src->ReadSectors2352(info.lsn, count, info.data);
if (ret == 0)
break;
tries--;
} while ((ret < 0) && (tries > 0));
cdvdCacheUpdate(info.lsn, info.mode, info.data);
if (handlingRequest) {
threadRequestInfo = info;
handlingRequest = false;
threadRequestPending = false;
PulseEvent(hRequestComplete);
prefetch_last_lba = info.lsn;
prefetch_last_mode = info.mode;
prefetch_left = prefetch_max_blocks;
} else {
prefetch_last_lba += 16;
prefetch_left--;
}
}
}
printf(" * CDVD: IO thread finished.\n");
return 0;
}
s32 cdvdStartThread()
{
InitializeCriticalSection(&CacheMutex);
hNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hNotify == nullptr)
return -1;
hRequestComplete = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hRequestComplete == nullptr)
return -1;
cdvd_is_open = true;
hThread = CreateThread(NULL, 0, cdvdThread, NULL, 0, &pidThread);
if (hThread == nullptr)
return -1;
SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL);
cdvdCacheReset();
return 0;
}
void cdvdStopThread()
{
cdvd_is_open = false;
PulseEvent(hNotify);
if (WaitForSingleObject(hThread, 4000) == WAIT_TIMEOUT) {
TerminateThread(hThread, 0);
}
CloseHandle(hThread);
CloseHandle(hNotify);
CloseHandle(hRequestComplete);
DeleteCriticalSection(&CacheMutex);
}
s32 cdvdRequestSector(u32 sector, s32 mode)
{
if (sector >= src->GetSectorCount())
return -1;
sector &= ~15; //align to 16-sector block
threadRequestInfo.lsn = sector;
threadRequestInfo.mode = mode;
threadRequestPending = false;
if (cdvdCacheFetch(sector, mode, threadRequestInfo.data)) {
return 0;
}
threadRequestPending = true;
ResetEvent(hRequestComplete);
PulseEvent(hNotify);
return 0;
}
s32 cdvdRequestComplete()
{
return !threadRequestPending;
}
s8 *cdvdGetSector(s32 sector, s32 mode)
{
while (threadRequestPending) {
WaitForSingleObject(hRequestComplete, 10);
}
s32 offset;
if (mode == CDVD_MODE_2048) {
offset = 2048 * (sector - threadRequestInfo.lsn);
return threadRequestInfo.data + offset;
}
offset = 2352 * (sector - threadRequestInfo.lsn);
s8 *data = threadRequestInfo.data + offset;
switch (mode) {
case CDVD_MODE_2328:
return data + 24;
case CDVD_MODE_2340:
return data + 12;
}
return data;
}
s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer)
{
static char data[16 * 2352];
if ((u32)first >= src->GetSectorCount())
return -1;
s32 sector = first & (~15); //align to 16-sector block
EnterCriticalSection(&CacheMutex);
if (!cdvdCacheFetch(sector, mode, data)) {
s32 ret = -1;
s32 tries = 5;
s32 count = 16;
s32 left = src->GetSectorCount() - sector;
if (left < count)
count = left;
do {
if (mode == CDVD_MODE_2048)
ret = src->ReadSectors2048(sector, count, data);
else
ret = src->ReadSectors2352(sector, count, data);
if (ret == 0)
break;
tries--;
} while ((ret < 0) && (tries > 0));
cdvdCacheUpdate(sector, mode, data);
}
LeaveCriticalSection(&CacheMutex);
s32 offset;
if (mode == CDVD_MODE_2048) {
offset = 2048 * (first - sector);
memcpy(buffer, data + offset, 2048);
return 0;
}
offset = 2352 * (first - sector);
s8 *bfr = data + offset;
switch (mode) {
case CDVD_MODE_2328:
memcpy(buffer, bfr + 24, 2328);
return 0;
case CDVD_MODE_2340:
memcpy(buffer, bfr + 12, 2340);
return 0;
default:
memcpy(buffer, bfr + 12, 2352);
return 0;
}
}
s32 cdvdGetMediaType()
{
return src->GetMediaType();
}
s32 cdvdRefreshData()
{
char *diskTypeName = "Unknown";
//read TOC from device
cdvdParseTOC();
if ((etrack == 0) || (strack > etrack)) {
curDiskType = CDVD_TYPE_NODISC;
} else {
s32 mt = cdvdGetMediaType();
if (mt < 0)
curDiskType = CDVD_TYPE_DETCTCD;
else if (mt == 0)
curDiskType = CDVD_TYPE_DETCTDVDS;
else
curDiskType = CDVD_TYPE_DETCTDVDD;
}
curTrayStatus = CDVD_TRAY_CLOSE;
switch (curDiskType) {
case CDVD_TYPE_DETCTDVDD:
diskTypeName = "Double-Layer DVD";
break;
case CDVD_TYPE_DETCTDVDS:
diskTypeName = "Single-Layer DVD";
break;
case CDVD_TYPE_DETCTCD:
diskTypeName = "CD-ROM";
break;
case CDVD_TYPE_NODISC:
diskTypeName = "No Disc";
break;
}
printf(" * CDVD: Disk Type: %s\n", diskTypeName);
cdvdCacheReset();
return 0;
}