This commit is contained in:
_AG 2019-06-17 21:06:49 +02:00
commit f83c7392cc
7 changed files with 613 additions and 7 deletions

529
src/CdStream.cpp Normal file
View File

@ -0,0 +1,529 @@
#include <Windows.h>
#include "common.h"
#include "patcher.h"
#include "CdStream.h"
#include "rwcore.h"
#include "RwHelper.h"
#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", __VA_ARGS__)
#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", __VA_ARGS__)
struct CdReadInfo
{
uint32 nSectorOffset;
uint32 nSectorsToRead;
void *pBuffer;
char field_C;
bool bLocked;
bool bInUse;
char _pad0;
int32 nStatus;
HANDLE hSemaphore;
HANDLE hFile;
OVERLAPPED Overlapped;
};
VALIDATE_SIZE(CdReadInfo, 0x30);
char gCdImageNames[MAX_CDIMAGES+1][64];
int32 gNumImages;
int32 gNumChannels;
HANDLE gImgFiles[MAX_CDIMAGES];
HANDLE _gCdStreamThread;
HANDLE gCdStreamSema;
DWORD _gCdStreamThreadId;
CdReadInfo *gpReadInfo;
Queue gChannelRequestQ;
int32 lastPosnRead;
BOOL _gbCdStreamOverlapped;
BOOL _gbCdStreamAsync;
DWORD _gdwCdStreamFlags;
void
CdStreamInitThread(void)
{
SetLastError(0);
if ( gNumChannels > 0 )
{
for ( int32 i = 0; i < gNumChannels; i++ )
{
gpReadInfo[i].hSemaphore = CreateSemaphore(NULL, 0, 2, NULL);
if ( gpReadInfo[i].hSemaphore == NULL )
{
CDTRACE("failed to create sync semaphore");
ASSERT(0);
return;
}
}
}
gChannelRequestQ.items = (int32 *)LocalAlloc(LMEM_ZEROINIT, sizeof(int32) * (gNumChannels + 1));
gChannelRequestQ.head = 0;
gChannelRequestQ.tail = 0;
gChannelRequestQ.size = gNumChannels + 1;
ASSERT(gChannelRequestQ.items != NULL );
gCdStreamSema = CreateSemaphore(NULL, 0, 5, "CdStream");
if ( gCdStreamSema == NULL )
{
CDTRACE("failed to create stream semaphore");
ASSERT(0);
return;
}
_gCdStreamThread = CreateThread(NULL, 64*1024/*64KB*/, CdStreamThread, NULL, CREATE_SUSPENDED, &_gCdStreamThreadId);
if ( _gCdStreamThread == NULL )
{
CDTRACE("failed to create streaming thread");
ASSERT(0);
return;
}
SetThreadPriority(_gCdStreamThread, GetThreadPriority(GetCurrentThread()) - 1);
ResumeThread(_gCdStreamThread);
}
void
CdStreamInit(int32 numChannels)
{
DWORD SectorsPerCluster;
DWORD BytesPerSector;
DWORD NumberOfFreeClusters;
DWORD TotalNumberOfClusters;
GetDiskFreeSpace(NULL, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters);
_gdwCdStreamFlags = 0;
if ( BytesPerSector <= CDSTREAM_SECTOR_SIZE )
{
_gdwCdStreamFlags |= FILE_FLAG_NO_BUFFERING;
debug("Using no buffered loading for streaming\n");
}
_gbCdStreamOverlapped = TRUE;
_gdwCdStreamFlags |= FILE_FLAG_OVERLAPPED;
_gbCdStreamAsync = FALSE;
void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, BytesPerSector);
ASSERT( pBuffer != NULL );
SetLastError(0);
gNumImages = 0;
gNumChannels = numChannels;
gpReadInfo = (CdReadInfo *)LocalAlloc(LMEM_ZEROINIT, sizeof(CdReadInfo) * numChannels);
ASSERT( gpReadInfo != NULL );
CDDEBUG("read info %p", gpReadInfo);
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();
ASSERT( pBuffer != NULL );
RwFreeAlign(pBuffer);
}
uint32
GetGTA3ImgSize(void)
{
ASSERT( gImgFiles[0] != NULL );
return (uint32)GetFileSize(gImgFiles[0], NULL);
}
void
CdStreamShutdown(void)
{
if ( _gbCdStreamAsync )
{
LocalFree(gChannelRequestQ.items);
CloseHandle(gCdStreamSema);
CloseHandle(_gCdStreamThread);
for ( int32 i = 0; i < gNumChannels; i++ )
CloseHandle(gpReadInfo[i].hSemaphore);
}
LocalFree(gpReadInfo);
}
int32
CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size)
{
ASSERT( channel < gNumChannels );
ASSERT( buffer != NULL );
lastPosnRead = size + offset;
ASSERT( _GET_INDEX(offset) < MAX_CDIMAGES );
HANDLE hImage = gImgFiles[_GET_INDEX(offset)];
ASSERT( hImage != NULL );
CdReadInfo *pChannel = &gpReadInfo[channel];
ASSERT( pChannel != NULL );
pChannel->hFile = hImage;
SetLastError(0);
if ( _gbCdStreamAsync )
{
if ( pChannel->nSectorsToRead != 0 || pChannel->bInUse )
return STREAM_NONE;
pChannel->nStatus = STREAM_NONE;
pChannel->nSectorOffset = _GET_OFFSET(offset);
pChannel->nSectorsToRead = size;
pChannel->pBuffer = buffer;
pChannel->bLocked = 0;
AddToQueue(&gChannelRequestQ, channel);
if ( !ReleaseSemaphore(gCdStreamSema, 1, NULL) )
printf("Signal Sema Error\n");
return STREAM_SUCCESS;
}
if ( _gbCdStreamOverlapped )
{
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel];
ASSERT( pChannel != NULL );
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;
}
SetFilePointer(hImage, _GET_OFFSET(offset) * CDSTREAM_SECTOR_SIZE, NULL, FILE_BEGIN);
DWORD NumberOfBytesRead;
if ( !ReadFile(hImage, buffer, size * CDSTREAM_SECTOR_SIZE, &NumberOfBytesRead, NULL) )
return STREAM_NONE;
else
return STREAM_SUCCESS;
}
int32
CdStreamGetStatus(int32 channel)
{
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel];
ASSERT( pChannel != NULL );
if ( _gbCdStreamAsync )
{
if ( pChannel->bInUse )
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 )
{
ASSERT( pChannel->hFile != NULL );
if ( WaitForSingleObjectEx(pChannel->hFile, 0, TRUE) == WAIT_OBJECT_0 )
return STREAM_NONE;
else
return STREAM_READING;
}
return STREAM_NONE;
}
int32
CdStreamGetLastPosn(void)
{
return lastPosnRead;
}
int32
CdStreamSync(int32 channel)
{
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel];
ASSERT( pChannel != NULL );
if ( _gbCdStreamAsync )
{
if ( pChannel->nSectorsToRead != 0 )
{
pChannel->bLocked = true;
ASSERT( pChannel->hSemaphore != NULL );
WaitForSingleObject(pChannel->hSemaphore, INFINITE);
}
pChannel->bInUse = false;
return pChannel->nStatus;
}
DWORD NumberOfBytesTransferred;
if ( _gbCdStreamOverlapped && pChannel->hFile )
{
ASSERT(pChannel->hFile != NULL );
if ( GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) )
return STREAM_NONE;
else
return STREAM_ERROR;
}
return STREAM_NONE;
}
void
AddToQueue(Queue *queue, int32 item)
{
ASSERT( queue != NULL );
ASSERT( queue->items != NULL );
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)
{
ASSERT( queue != NULL );
if ( queue->head == queue->tail )
return -1;
ASSERT( queue->items != NULL );
return queue->items[queue->head];
}
void
RemoveFirstInQueue(Queue *queue)
{
ASSERT( queue != NULL );
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];
ASSERT( pChannel != NULL );
pChannel->bInUse = true;
if ( pChannel->nStatus == STREAM_NONE )
{
if ( _gbCdStreamOverlapped )
{
pChannel->Overlapped.Offset = pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE;
ASSERT(pChannel->hFile != NULL );
ASSERT(pChannel->pBuffer != NULL );
DWORD NumberOfBytesTransferred;
if ( ReadFile(pChannel->hFile,
pChannel->pBuffer,
pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE,
NULL,
&pChannel->Overlapped) )
{
pChannel->nStatus = STREAM_NONE;
}
else if ( GetLastError() == ERROR_IO_PENDING
&& GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) )
{
pChannel->nStatus = STREAM_NONE;
}
else
{
pChannel->nStatus = STREAM_ERROR;
}
}
else
{
ASSERT(pChannel->hFile != NULL );
ASSERT(pChannel->pBuffer != NULL );
SetFilePointer(pChannel->hFile, pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE, NULL, FILE_BEGIN);
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 )
{
ASSERT( pChannel->hSemaphore != NULL );
ReleaseSemaphore(pChannel->hSemaphore, 1, NULL);
}
pChannel->bInUse = false;
}
}
bool
CdStreamAddImage(char const *path)
{
ASSERT(path != NULL);
ASSERT(gNumImages < MAX_CDIMAGES);
SetLastError(0);
gImgFiles[gNumImages] = CreateFile(path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
_gdwCdStreamFlags | FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_READONLY,
NULL);
ASSERT( gImgFiles[gNumImages] != NULL );
if ( gImgFiles[gNumImages] == NULL )
return false;
strcpy(gCdImageNames[gNumImages], path);
gNumImages++;
return true;
}
char *
CdStreamGetImageName(int32 cd)
{
ASSERT(cd < MAX_CDIMAGES);
if ( gImgFiles[cd] != NULL )
return gCdImageNames[cd];
return NULL;
}
void
CdStreamRemoveImages(void)
{
for ( int32 i = 0; i < gNumChannels; i++ )
CdStreamSync(i);
for ( int32 i = 0; i < gNumImages; i++ )
{
SetLastError(0);
CloseHandle(gImgFiles[i]);
gImgFiles[i] = NULL;
}
gNumImages = 0;
}
int32
CdStreamGetNumImages(void)
{
return gNumImages;
}
STARTPATCHES
InjectHook(0x405B50, CdStreamInitThread, PATCH_JUMP);
InjectHook(0x405C80, CdStreamInit, PATCH_JUMP);
//InjectHook(0x405DB0, debug, PATCH_JUMP);
InjectHook(0x405DC0, GetGTA3ImgSize, PATCH_JUMP);
InjectHook(0x405DD0, CdStreamShutdown, PATCH_JUMP);
InjectHook(0x405E40, CdStreamRead, PATCH_JUMP);
InjectHook(0x405F90, CdStreamGetStatus, PATCH_JUMP);
InjectHook(0x406000, CdStreamGetLastPosn, PATCH_JUMP);
InjectHook(0x406010, CdStreamSync, PATCH_JUMP);
InjectHook(0x4060B0, AddToQueue, PATCH_JUMP);
InjectHook(0x4060F0, GetFirstInQueue, PATCH_JUMP);
InjectHook(0x406110, RemoveFirstInQueue, PATCH_JUMP);
InjectHook(0x406140, CdStreamThread, PATCH_JUMP);
InjectHook(0x406270, CdStreamAddImage, PATCH_JUMP);
InjectHook(0x4062E0, CdStreamGetImageName, PATCH_JUMP);
InjectHook(0x406300, CdStreamRemoveImages, PATCH_JUMP);
InjectHook(0x406370, CdStreamGetNumImages, PATCH_JUMP);
ENDPATCHES

47
src/CdStream.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#define MAX_CDIMAGES 8
#define CDSTREAM_SECTOR_SIZE 2048
#define _GET_INDEX(a) (a >> 24)
#define _GET_OFFSET(a) (a & 0xFFFFFF)
enum
{
STREAM_NONE = uint8( 0),
STREAM_SUCCESS = uint8( 1),
STREAM_READING = uint8(-1), // 0xFF,
STREAM_ERROR = uint8(-2), // 0xFE,
STREAM_ERROR_NOCD = uint8(-3), // 0xFD,
STREAM_ERROR_WRONGCD = uint8(-4), // 0xFC,
STREAM_ERROR_OPENCD = uint8(-5), // 0xFB,
STREAM_WAITING = uint8(-6) // 0xFA,
};
struct Queue
{
int32 *items;
int32 head;
int32 tail;
int32 size;
};
VALIDATE_SIZE(Queue, 0x10);
void CdStreamInitThread(void);
void CdStreamInit(int32 numChannels);
uint32 GetGTA3ImgSize(void);
void CdStreamShutdown(void);
int32 CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size);
int32 CdStreamGetStatus(int32 channel);
int32 CdStreamGetLastPosn(void);
int32 CdStreamSync(int32 channel);
void AddToQueue(Queue *queue, int32 item);
int32 GetFirstInQueue(Queue *queue);
void RemoveFirstInQueue(Queue *queue);
DWORD WINAPI CdStreamThread(LPVOID lpThreadParameter);
bool CdStreamAddImage(char const *path);
char *CdStreamGetImageName(int32 cd);
void CdStreamRemoveImages(void);
int32 CdStreamGetNumImages(void);

View File

@ -1995,7 +1995,10 @@ STARTPATCHES
InjectHook(0x492720, CPad::UpdatePads, PATCH_JUMP);
InjectHook(0x492C60, &CPad::ProcessPCSpecificStuff, PATCH_JUMP);
InjectHook(0x492C70, &CPad::Update, PATCH_JUMP);
#pragma warning( push )
#pragma warning( disable : 4573)
InjectHook(0x492F00, (void (*)())CPad::DoCheats, PATCH_JUMP);
#pragma warning( pop )
InjectHook(0x492F20, (void (CPad::*)(int16))&CPad::DoCheats, PATCH_JUMP);
InjectHook(0x492F30, CPad::StopPadsShaking, PATCH_JUMP);
InjectHook(0x492F50, &CPad::StopShaking, PATCH_JUMP);

View File

@ -4,6 +4,34 @@
#include "TimeCycle.h"
#include "skeleton.h"
void *
RwMallocAlign(RwUInt32 size, RwUInt32 align)
{
void *mem = (void *)malloc(size + align);
ASSERT(mem != NULL);
void *addr = (void *)((((RwUInt32)mem) + align) & ~(align - 1));
ASSERT(addr != NULL);
*(((void **)addr) - 1) = mem;
return addr;
}
void
RwFreeAlign(void *mem)
{
ASSERT(mem != NULL);
void *addr = *(((void **)mem) - 1);
ASSERT(addr != NULL);
free(addr);
}
void
DefinedState(void)
{

View File

@ -1,5 +1,8 @@
#pragma once
void *RwMallocAlign(RwUInt32 size, RwUInt32 align);
void RwFreeAlign(void *mem);
void DefinedState(void);
RwFrame *GetFirstChild(RwFrame *frame);
RwObject *GetFirstObject(RwFrame *frame);

View File

@ -5,10 +5,7 @@
#include "RpAnimBlend.h"
#include "AnimManager.h"
#include "AnimBlendAssociation.h"
// TODO: implement those
#define RwFreeAlign RwFree
#define RwMallocAlign(sz, algn) RwMalloc(sz)
#include "RwHelper.h"
CAnimBlendAssociation::CAnimBlendAssociation(void)
{
@ -57,6 +54,7 @@ CAnimBlendAssociation::AllocateAnimBlendNodeArray(int n)
void
CAnimBlendAssociation::FreeAnimBlendNodeArray(void)
{
ASSERT( nodes != NULL );
RwFreeAlign(nodes);
}

View File

@ -1,10 +1,8 @@
#include "common.h"
#include "patcher.h"
#include "AnimBlendClumpData.h"
#include "RwHelper.h"
// TODO: implement those
#define RwFreeAlign RwFree
#define RwMallocAlign(sz, algn) RwMalloc(sz)
CAnimBlendClumpData::CAnimBlendClumpData(void)
{