re3/src/core/CdStream.cpp

539 lines
11 KiB
C++
Raw Normal View History

2020-05-11 02:55:57 +00:00
#ifdef _WIN32
2020-04-21 10:28:06 +00:00
#define WITHWINDOWS
2019-06-17 13:32:38 +00:00
#include "common.h"
2020-04-17 13:31:11 +00:00
2019-06-17 13:32:38 +00:00
#include "CdStream.h"
#include "rwcore.h"
#include "RwHelper.h"
2020-11-28 15:16:15 +00:00
#include "MemoryMgr.h"
2019-06-17 13:32:38 +00:00
struct CdReadInfo
{
uint32 nSectorOffset;
uint32 nSectorsToRead;
void *pBuffer;
char field_C;
bool bLocked;
2021-02-16 14:11:12 +00:00
bool bReading;
2019-06-17 13:32:38 +00:00
int32 nStatus;
2021-02-16 14:11:12 +00:00
HANDLE pDoneSemaphore; // used for CdStreamSync
2019-06-17 13:32:38 +00:00
HANDLE hFile;
OVERLAPPED Overlapped;
};
2020-05-10 13:54:37 +00:00
2019-06-17 13:32:38 +00:00
VALIDATE_SIZE(CdReadInfo, 0x30);
char gCdImageNames[MAX_CDIMAGES+1][64];
int32 gNumImages;
int32 gNumChannels;
HANDLE gImgFiles[MAX_CDIMAGES];
HANDLE _gCdStreamThread;
2020-05-11 02:55:57 +00:00
HANDLE gCdStreamSema; // released when we have new thing to read(so channel is set)
2019-06-17 13:32:38 +00:00
DWORD _gCdStreamThreadId;
CdReadInfo *gpReadInfo;
Queue gChannelRequestQ;
int32 lastPosnRead;
BOOL _gbCdStreamOverlapped;
BOOL _gbCdStreamAsync;
DWORD _gdwCdStreamFlags;
2020-03-27 19:53:47 +00:00
DWORD WINAPI CdStreamThread(LPVOID lpThreadParameter);
2019-06-17 13:32:38 +00:00
void
CdStreamInitThread(void)
{
SetLastError(0);
if ( gNumChannels > 0 )
{
for ( int32 i = 0; i < gNumChannels; i++ )
{
2021-02-16 14:11:12 +00:00
gpReadInfo[i].pDoneSemaphore = CreateSemaphore(nil, 0, 2, nil);
2019-06-17 13:32:38 +00:00
2021-02-16 14:11:12 +00:00
if ( gpReadInfo[i].pDoneSemaphore == nil )
2019-06-17 13:32:38 +00:00
{
printf("%s: failed to create sync semaphore\n", "cdvd_stream");
2019-06-17 13:32:38 +00:00
ASSERT(0);
return;
}
}
}
gChannelRequestQ.items = (int32 *)LocalAlloc(LMEM_ZEROINIT, sizeof(int32) * (gNumChannels + 1));
gChannelRequestQ.head = 0;
gChannelRequestQ.tail = 0;
gChannelRequestQ.size = gNumChannels + 1;
2019-06-30 10:53:39 +00:00
ASSERT(gChannelRequestQ.items != nil );
2019-06-17 13:32:38 +00:00
2020-12-28 20:08:47 +00:00
#ifdef FIX_BUGS
gCdStreamSema = CreateSemaphore(nil, 0, 5, nil);
#else
2019-06-30 10:53:39 +00:00
gCdStreamSema = CreateSemaphore(nil, 0, 5, "CdStream");
2020-12-28 20:08:47 +00:00
#endif
2019-06-17 13:32:38 +00:00
2019-06-30 10:53:39 +00:00
if ( gCdStreamSema == nil )
2019-06-17 13:32:38 +00:00
{
printf("%s: failed to create stream semaphore\n", "cdvd_stream");
2019-06-17 13:32:38 +00:00
ASSERT(0);
return;
}
2019-06-30 10:53:39 +00:00
_gCdStreamThread = CreateThread(nil, 64*1024/*64KB*/, CdStreamThread, nil, CREATE_SUSPENDED, &_gCdStreamThreadId);
2019-06-17 13:32:38 +00:00
2019-06-30 10:53:39 +00:00
if ( _gCdStreamThread == nil )
2019-06-17 13:32:38 +00:00
{
printf("%s: failed to create streaming thread\n", "cdvd_stream");
2019-06-17 13:32:38 +00:00
ASSERT(0);
return;
}
SetThreadPriority(_gCdStreamThread, GetThreadPriority(GetCurrentThread()) - 1);
ResumeThread(_gCdStreamThread);
}
void
CdStreamInit(int32 numChannels)
{
DWORD SectorsPerCluster;
DWORD BytesPerSector;
DWORD NumberOfFreeClusters;
DWORD TotalNumberOfClusters;
2019-06-30 10:53:39 +00:00
GetDiskFreeSpace(nil, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters);
2019-06-17 13:32:38 +00:00
_gdwCdStreamFlags = 0;
2020-09-26 10:30:22 +00:00
#ifndef FIX_BUGS // this just slows down streaming
2019-06-17 13:32:38 +00:00
if ( BytesPerSector <= CDSTREAM_SECTOR_SIZE )
{
_gdwCdStreamFlags |= FILE_FLAG_NO_BUFFERING;
debug("Using no buffered loading for streaming\n");
}
2020-09-26 10:30:22 +00:00
#endif
2019-06-17 13:32:38 +00:00
_gbCdStreamOverlapped = TRUE;
_gdwCdStreamFlags |= FILE_FLAG_OVERLAPPED;
_gbCdStreamAsync = FALSE;
void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, BytesPerSector);
2019-06-30 10:53:39 +00:00
ASSERT( pBuffer != nil );
2019-06-17 13:32:38 +00:00
SetLastError(0);
gNumImages = 0;
gNumChannels = numChannels;
gpReadInfo = (CdReadInfo *)LocalAlloc(LMEM_ZEROINIT, sizeof(CdReadInfo) * numChannels);
2019-06-30 10:53:39 +00:00
ASSERT( gpReadInfo != nil );
2019-06-17 13:32:38 +00:00
2021-01-22 20:27:18 +00:00
debug("%s: read info %p\n", "cdvd_stream", gpReadInfo);
2019-06-17 13:32:38 +00:00
CdStreamAddImage("MODELS\\GTA3.IMG");
int32 nStatus = CdStreamRead(0, pBuffer, 0, 1);
CdStreamRemoveImages();
if ( nStatus == STREAM_SUCCESS )
{
_gbCdStreamAsync = TRUE;
debug("Using async loading for streaming\n");
}
else
{
_gdwCdStreamFlags &= ~FILE_FLAG_OVERLAPPED;
_gbCdStreamOverlapped = FALSE;
_gbCdStreamAsync = TRUE;
debug("Using sync loading for streaming\n");
}
CdStreamInitThread();
2019-06-30 10:53:39 +00:00
ASSERT( pBuffer != nil );
2019-06-17 13:32:38 +00:00
RwFreeAlign(pBuffer);
}
uint32
GetGTA3ImgSize(void)
{
2019-06-30 10:53:39 +00:00
ASSERT( gImgFiles[0] != nil );
return (uint32)GetFileSize(gImgFiles[0], nil);
2019-06-17 13:32:38 +00:00
}
void
CdStreamShutdown(void)
{
if ( _gbCdStreamAsync )
{
LocalFree(gChannelRequestQ.items);
CloseHandle(gCdStreamSema);
CloseHandle(_gCdStreamThread);
for ( int32 i = 0; i < gNumChannels; i++ )
2021-02-16 14:11:12 +00:00
CloseHandle(gpReadInfo[i].pDoneSemaphore);
2019-06-17 13:32:38 +00:00
}
LocalFree(gpReadInfo);
}
int32
CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size)
{
ASSERT( channel < gNumChannels );
2019-06-30 10:53:39 +00:00
ASSERT( buffer != nil );
2019-06-17 13:32:38 +00:00
lastPosnRead = size + offset;
ASSERT( _GET_INDEX(offset) < MAX_CDIMAGES );
HANDLE hImage = gImgFiles[_GET_INDEX(offset)];
2019-06-30 10:53:39 +00:00
ASSERT( hImage != nil );
2019-06-17 13:32:38 +00:00
CdReadInfo *pChannel = &gpReadInfo[channel];
2019-06-30 10:53:39 +00:00
ASSERT( pChannel != nil );
2019-06-17 13:32:38 +00:00
pChannel->hFile = hImage;
SetLastError(0);
if ( _gbCdStreamAsync )
{
2021-02-16 14:11:12 +00:00
if ( pChannel->nSectorsToRead != 0 || pChannel->bReading )
2019-06-17 13:32:38 +00:00
return STREAM_NONE;
pChannel->nStatus = STREAM_NONE;
pChannel->nSectorOffset = _GET_OFFSET(offset);
pChannel->nSectorsToRead = size;
pChannel->pBuffer = buffer;
pChannel->bLocked = 0;
AddToQueue(&gChannelRequestQ, channel);
2019-06-30 10:53:39 +00:00
if ( !ReleaseSemaphore(gCdStreamSema, 1, nil) )
2019-06-17 13:32:38 +00:00
printf("Signal Sema Error\n");
return STREAM_SUCCESS;
}
if ( _gbCdStreamOverlapped )
{
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel];
2019-06-30 10:53:39 +00:00
ASSERT( pChannel != nil );
2019-06-17 13:32:38 +00:00
pChannel->Overlapped.Offset = _GET_OFFSET(offset) * CDSTREAM_SECTOR_SIZE;
if ( !ReadFile(hImage, buffer, size * CDSTREAM_SECTOR_SIZE, NULL, &pChannel->Overlapped)
&& GetLastError() != ERROR_IO_PENDING )
return STREAM_NONE;
else
return STREAM_SUCCESS;
}
#ifdef BIG_IMG
LARGE_INTEGER liDistanceToMove;
liDistanceToMove.QuadPart = _GET_OFFSET(offset);
liDistanceToMove.QuadPart *= CDSTREAM_SECTOR_SIZE;
SetFilePointerEx(hImage, liDistanceToMove, nil, FILE_BEGIN);
#else
2019-06-30 10:53:39 +00:00
SetFilePointer(hImage, _GET_OFFSET(offset) * CDSTREAM_SECTOR_SIZE, nil, FILE_BEGIN);
#endif
2019-06-17 13:32:38 +00:00
DWORD NumberOfBytesRead;
2019-06-30 10:53:39 +00:00
if ( !ReadFile(hImage, buffer, size * CDSTREAM_SECTOR_SIZE, &NumberOfBytesRead, nil) )
2019-06-17 13:32:38 +00:00
return STREAM_NONE;
else
return STREAM_SUCCESS;
}
int32
CdStreamGetStatus(int32 channel)
{
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel];
2019-06-30 10:53:39 +00:00
ASSERT( pChannel != nil );
2019-06-17 13:32:38 +00:00
if ( _gbCdStreamAsync )
{
2021-02-16 14:11:12 +00:00
if ( pChannel->bReading )
2019-06-17 13:32:38 +00:00
return STREAM_READING;
if ( pChannel->nSectorsToRead != 0 )
return STREAM_WAITING;
if ( pChannel->nStatus != STREAM_NONE )
{
int32 status = pChannel->nStatus;
pChannel->nStatus = STREAM_NONE;
return status;
}
return STREAM_NONE;
}
if ( _gbCdStreamOverlapped )
{
2019-06-30 10:53:39 +00:00
ASSERT( pChannel->hFile != nil );
2019-06-17 13:32:38 +00:00
if ( WaitForSingleObjectEx(pChannel->hFile, 0, TRUE) == WAIT_OBJECT_0 )
return STREAM_NONE;
else
return STREAM_READING;
}
return STREAM_NONE;
}
int32
CdStreamGetLastPosn(void)
{
return lastPosnRead;
}
2020-05-11 02:55:57 +00:00
// wait for channel to finish reading
2019-06-17 13:32:38 +00:00
int32
CdStreamSync(int32 channel)
{
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel];
2019-06-30 10:53:39 +00:00
ASSERT( pChannel != nil );
2019-06-17 13:32:38 +00:00
if ( _gbCdStreamAsync )
{
if ( pChannel->nSectorsToRead != 0 )
{
pChannel->bLocked = true;
2021-02-16 14:11:12 +00:00
ASSERT( pChannel->pDoneSemaphore != nil );
2019-06-17 13:32:38 +00:00
2021-02-16 14:11:12 +00:00
// Deadlock fix 1
#ifdef FIX_BUGS
// This is while loop on Posix streamer, for spurious wakeups
if (pChannel->bLocked && pChannel->nSectorsToRead != 0){
WaitForSingleObject(pChannel->pDoneSemaphore, INFINITE);
}
pChannel->bLocked = false;
#else
WaitForSingleObject(pChannel->pDoneSemaphore, INFINITE);
#endif
2019-06-17 13:32:38 +00:00
}
2021-02-16 14:11:12 +00:00
pChannel->bReading = false;
2019-06-17 13:32:38 +00:00
return pChannel->nStatus;
}
DWORD NumberOfBytesTransferred;
if ( _gbCdStreamOverlapped && pChannel->hFile )
{
2019-06-30 10:53:39 +00:00
ASSERT(pChannel->hFile != nil );
2020-05-11 02:55:57 +00:00
// Beware: This is blocking call (because of last parameter)
2019-06-17 13:32:38 +00:00
if ( GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) )
return STREAM_NONE;
else
return STREAM_ERROR;
}
return STREAM_NONE;
}
void
AddToQueue(Queue *queue, int32 item)
{
2019-06-30 10:53:39 +00:00
ASSERT( queue != nil );
ASSERT( queue->items != nil );
2019-06-17 13:32:38 +00:00
queue->items[queue->tail] = item;
queue->tail = (queue->tail + 1) % queue->size;
if ( queue->head == queue->tail )
debug("Queue is full\n");
}
int32
GetFirstInQueue(Queue *queue)
{
2019-06-30 10:53:39 +00:00
ASSERT( queue != nil );
2019-06-17 13:32:38 +00:00
if ( queue->head == queue->tail )
return -1;
2019-06-30 10:53:39 +00:00
ASSERT( queue->items != nil );
2019-06-17 13:32:38 +00:00
return queue->items[queue->head];
}
void
RemoveFirstInQueue(Queue *queue)
{
2019-06-30 10:53:39 +00:00
ASSERT( queue != nil );
2019-06-17 13:32:38 +00:00
if ( queue->head == queue->tail )
{
debug("Queue is empty\n");
return;
}
queue->head = (queue->head + 1) % queue->size;
}
DWORD
WINAPI CdStreamThread(LPVOID lpThreadParameter)
{
debug("Created cdstream thread\n");
while ( true )
{
WaitForSingleObject(gCdStreamSema, INFINITE);
int32 channel = GetFirstInQueue(&gChannelRequestQ);
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel];
2019-06-30 10:53:39 +00:00
ASSERT( pChannel != nil );
2019-06-17 13:32:38 +00:00
2021-02-16 14:11:12 +00:00
pChannel->bReading = true;
2019-06-17 13:32:38 +00:00
if ( pChannel->nStatus == STREAM_NONE )
{
if ( _gbCdStreamOverlapped )
{
pChannel->Overlapped.Offset = pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE;
2019-06-30 10:53:39 +00:00
ASSERT(pChannel->hFile != nil );
ASSERT(pChannel->pBuffer != nil );
2019-06-17 13:32:38 +00:00
DWORD NumberOfBytesTransferred;
if ( ReadFile(pChannel->hFile,
pChannel->pBuffer,
pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE,
NULL,
&pChannel->Overlapped) )
{
pChannel->nStatus = STREAM_NONE;
}
2020-05-11 02:55:57 +00:00
// Beware: This is blocking call (because of last parameter)
2019-06-17 13:32:38 +00:00
else if ( GetLastError() == ERROR_IO_PENDING
&& GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) )
{
pChannel->nStatus = STREAM_NONE;
}
else
{
pChannel->nStatus = STREAM_ERROR;
}
}
else
{
2019-06-30 10:53:39 +00:00
ASSERT(pChannel->hFile != nil );
ASSERT(pChannel->pBuffer != nil );
2019-06-17 13:32:38 +00:00
2019-06-30 10:53:39 +00:00
SetFilePointer(pChannel->hFile, pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE, nil, FILE_BEGIN);
2019-06-17 13:32:38 +00:00
DWORD NumberOfBytesRead;
if ( ReadFile(pChannel->hFile,
pChannel->pBuffer,
pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE,
&NumberOfBytesRead,
NULL) )
{
pChannel->nStatus = STREAM_NONE;
}
}
}
RemoveFirstInQueue(&gChannelRequestQ);
pChannel->nSectorsToRead = 0;
if ( pChannel->bLocked )
{
2021-02-16 14:11:12 +00:00
ASSERT( pChannel->pDoneSemaphore != nil );
// Deadlock fix 2
#ifdef FIX_BUGS
pChannel->bLocked = 0;
#endif
ReleaseSemaphore(pChannel->pDoneSemaphore, 1, NULL);
2019-06-17 13:32:38 +00:00
}
2021-02-16 14:11:12 +00:00
pChannel->bReading = false;
2019-06-17 13:32:38 +00:00
}
}
bool
CdStreamAddImage(char const *path)
{
2019-06-30 10:53:39 +00:00
ASSERT(path != nil);
2019-06-17 13:32:38 +00:00
ASSERT(gNumImages < MAX_CDIMAGES);
SetLastError(0);
gImgFiles[gNumImages] = CreateFile(path,
2019-06-18 07:50:26 +00:00
GENERIC_READ,
FILE_SHARE_READ,
2019-06-30 10:53:39 +00:00
nil,
2019-06-18 07:50:26 +00:00
OPEN_EXISTING,
_gdwCdStreamFlags | FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_READONLY,
2019-06-30 10:53:39 +00:00
nil);
2019-06-17 13:32:38 +00:00
2019-06-30 10:53:39 +00:00
ASSERT( gImgFiles[gNumImages] != nil );
2019-06-17 13:32:38 +00:00
if ( gImgFiles[gNumImages] == NULL )
return false;
strcpy(gCdImageNames[gNumImages], path);
gNumImages++;
return true;
}
char *
CdStreamGetImageName(int32 cd)
{
ASSERT(cd < MAX_CDIMAGES);
2019-06-30 10:53:39 +00:00
if ( gImgFiles[cd] != nil )
2019-06-17 13:32:38 +00:00
return gCdImageNames[cd];
2019-06-30 10:53:39 +00:00
return nil;
2019-06-17 13:32:38 +00:00
}
void
CdStreamRemoveImages(void)
{
for ( int32 i = 0; i < gNumChannels; i++ )
CdStreamSync(i);
for ( int32 i = 0; i < gNumImages; i++ )
{
SetLastError(0);
CloseHandle(gImgFiles[i]);
2019-06-30 10:53:39 +00:00
gImgFiles[i] = nil;
2019-06-17 13:32:38 +00:00
}
gNumImages = 0;
}
int32
CdStreamGetNumImages(void)
{
return gNumImages;
}
2020-05-11 02:55:57 +00:00
#endif