Merge pull request #743 from erorcun/master

Fix some sanitizer errors, improve POSIX streamer
This commit is contained in:
erorcun 2020-10-08 16:15:55 +03:00 committed by GitHub
commit bf98dcbc3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 301 additions and 158 deletions

View File

@ -1509,7 +1509,7 @@ void CGarage::UpdateCrusherShake(float X, float Y)
} }
// This is dumb but there is no way to avoid goto. What was there originally even? // This is dumb but there is no way to avoid goto. What was there originally even?
static bool DoINeedToRefreshPointer(CEntity * pDoor, bool bIsDummy, int8 nIndex) static bool DoINeedToRefreshPointer(CEntity * pDoor, bool bIsDummy, uint8 nIndex)
{ {
bool bNeedToFindDoorEntities = false; bool bNeedToFindDoorEntities = false;
if (pDoor) { if (pDoor) {

View File

@ -34,7 +34,7 @@ struct CdReadInfo
bool bReading; bool bReading;
int32 nStatus; int32 nStatus;
#ifdef ONE_THREAD_PER_CHANNEL #ifdef ONE_THREAD_PER_CHANNEL
int8 nThreadStatus; // 0: created 1:initalized 2:abort now int8 nThreadStatus; // 0: created 1:priority set up 2:abort now
pthread_t pChannelThread; pthread_t pChannelThread;
sem_t *pStartSemaphore; sem_t *pStartSemaphore;
#endif #endif
@ -52,7 +52,7 @@ char *gImgNames[MAX_CDIMAGES];
#ifndef ONE_THREAD_PER_CHANNEL #ifndef ONE_THREAD_PER_CHANNEL
pthread_t _gCdStreamThread; pthread_t _gCdStreamThread;
sem_t *gCdStreamSema; // released when we have new thing to read(so channel is set) sem_t *gCdStreamSema; // released when we have new thing to read(so channel is set)
int8 gCdStreamThreadStatus; // 0: created 1:initalized 2:abort now int8 gCdStreamThreadStatus; // 0: created 1:priority set up 2:abort now
Queue gChannelRequestQ; Queue gChannelRequestQ;
bool _gbCdStreamOverlapped; bool _gbCdStreamOverlapped;
#endif #endif
@ -69,7 +69,7 @@ void
CdStreamInitThread(void) CdStreamInitThread(void)
{ {
int status; int status;
char semName[20];
#ifndef ONE_THREAD_PER_CHANNEL #ifndef ONE_THREAD_PER_CHANNEL
gChannelRequestQ.items = (int32 *)calloc(gNumChannels + 1, sizeof(int32)); gChannelRequestQ.items = (int32 *)calloc(gNumChannels + 1, sizeof(int32));
gChannelRequestQ.head = 0; gChannelRequestQ.head = 0;
@ -77,7 +77,6 @@ CdStreamInitThread(void)
gChannelRequestQ.size = gNumChannels + 1; gChannelRequestQ.size = gNumChannels + 1;
ASSERT(gChannelRequestQ.items != nil ); ASSERT(gChannelRequestQ.items != nil );
gCdStreamSema = sem_open("/semaphore_cd_stream", O_CREAT, 0644, 1); gCdStreamSema = sem_open("/semaphore_cd_stream", O_CREAT, 0644, 1);
#endif
if (gCdStreamSema == SEM_FAILED) { if (gCdStreamSema == SEM_FAILED) {
@ -85,13 +84,14 @@ CdStreamInitThread(void)
ASSERT(0); ASSERT(0);
return; return;
} }
#endif
if ( gNumChannels > 0 ) if ( gNumChannels > 0 )
{ {
for ( int32 i = 0; i < gNumChannels; i++ ) for ( int32 i = 0; i < gNumChannels; i++ )
{ {
gpReadInfo[i].pDoneSemaphore = sem_open("/semaphore_done", O_CREAT, 0644, 1); sprintf(semName,"/semaphore_done%d",i);
gpReadInfo[i].pDoneSemaphore = sem_open(semName, O_CREAT, 0644, 1);
if (gpReadInfo[i].pDoneSemaphore == SEM_FAILED) if (gpReadInfo[i].pDoneSemaphore == SEM_FAILED)
{ {
@ -100,7 +100,8 @@ CdStreamInitThread(void)
return; return;
} }
#ifdef ONE_THREAD_PER_CHANNEL #ifdef ONE_THREAD_PER_CHANNEL
gpReadInfo[i].pStartSemaphore = sem_open("/semaphore_start", O_CREAT, 0644, 1); sprintf(semName,"/semaphore_start%d",i);
gpReadInfo[i].pStartSemaphore = sem_open(semName, O_CREAT, 0644, 1);
if (gpReadInfo[i].pStartSemaphore == SEM_FAILED) if (gpReadInfo[i].pStartSemaphore == SEM_FAILED)
{ {
@ -125,8 +126,8 @@ CdStreamInitThread(void)
#ifndef ONE_THREAD_PER_CHANNEL #ifndef ONE_THREAD_PER_CHANNEL
debug("Using one streaming thread for all channels\n"); debug("Using one streaming thread for all channels\n");
status = pthread_create(&_gCdStreamThread, NULL, CdStreamThread, nil);
gCdStreamThreadStatus = 0; gCdStreamThreadStatus = 0;
status = pthread_create(&_gCdStreamThread, NULL, CdStreamThread, nil);
if (status == -1) if (status == -1)
{ {
@ -204,7 +205,7 @@ GetGTA3ImgSize(void)
ASSERT(0); ASSERT(0);
return 0; return 0;
} }
ok: ok:
return (uint32)statbuf.st_size; return (uint32)statbuf.st_size;
} }
@ -215,12 +216,10 @@ CdStreamShutdown(void)
#ifndef ONE_THREAD_PER_CHANNEL #ifndef ONE_THREAD_PER_CHANNEL
gCdStreamThreadStatus = 2; gCdStreamThreadStatus = 2;
sem_post(gCdStreamSema); sem_post(gCdStreamSema);
#endif #else
#ifdef ONE_THREAD_PER_CHANNEL
for ( int32 i = 0; i < gNumChannels; i++ ) { for ( int32 i = 0; i < gNumChannels; i++ ) {
gpReadInfo[i].nThreadStatus = 2; gpReadInfo[i].nThreadStatus = 2;
sem_post(&gpReadInfo[i].pStartSemaphore); sem_post(gpReadInfo[i].pStartSemaphore);
} }
#endif #endif
} }
@ -243,8 +242,11 @@ CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size)
pChannel->hFile = hImage - 1; pChannel->hFile = hImage - 1;
if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) {
return STREAM_NONE; flushStream[channel] = 1;
CdStreamSync(channel);
//return STREAM_NONE;
}
pChannel->nStatus = STREAM_NONE; pChannel->nStatus = STREAM_NONE;
pChannel->nSectorOffset = _GET_OFFSET(offset); pChannel->nSectorOffset = _GET_OFFSET(offset);
@ -257,7 +259,7 @@ CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size)
if ( sem_post(gCdStreamSema) != 0 ) if ( sem_post(gCdStreamSema) != 0 )
printf("Signal Sema Error\n"); printf("Signal Sema Error\n");
#else #else
if ( sem_post(&gpReadInfo[channel].pStartSemaphore) != 0 ) if ( sem_post(pChannel->pStartSemaphore) != 0 )
printf("Signal Sema Error\n"); printf("Signal Sema Error\n");
#endif #endif
@ -314,13 +316,17 @@ CdStreamSync(int32 channel)
if (flushStream[channel]) { if (flushStream[channel]) {
#ifdef ONE_THREAD_PER_CHANNEL #ifdef ONE_THREAD_PER_CHANNEL
pChannel->nSectorsToRead = 0; pChannel->nSectorsToRead = 0;
pthread_kill(gpReadInfo[channel].pChannelThread, SIGINT); pthread_kill(pChannel->pChannelThread, SIGINT);
#else
if (pChannel->bReading) { if (pChannel->bReading) {
pChannel->bLocked = true;
sem_wait(pChannel->pDoneSemaphore);
}
#else
pChannel->nSectorsToRead = 0; pChannel->nSectorsToRead = 0;
if (pChannel->bReading) {
pChannel->bLocked = true;
pthread_kill(_gCdStreamThread, SIGINT); pthread_kill(_gCdStreamThread, SIGINT);
} else { sem_wait(pChannel->pDoneSemaphore);
pChannel->nSectorsToRead = 0;
} }
#endif #endif
pChannel->bReading = false; pChannel->bReading = false;
@ -388,33 +394,31 @@ void *CdStreamThread(void *param)
#else #else
int channel = *((int*)param); int channel = *((int*)param);
while (gpReadInfo[channel].nThreadStatus != 2){ while (gpReadInfo[channel].nThreadStatus != 2){
sem_wait(&gpReadInfo[channel].pStartSemaphore); sem_wait(gpReadInfo[channel].pStartSemaphore);
#endif #endif
ASSERT( channel < gNumChannels );
CdReadInfo *pChannel = &gpReadInfo[channel]; CdReadInfo *pChannel = &gpReadInfo[channel];
ASSERT( pChannel != nil ); ASSERT( pChannel != nil );
#ifdef ONE_THREAD_PER_CHANNEL
if (gpReadInfo[channel].nThreadStatus == 0){
gpReadInfo[channel].nThreadStatus = 1;
#else
if (gCdStreamThreadStatus == 0){
gCdStreamThreadStatus = 1;
#endif
#ifdef __linux__
pid_t tid = syscall(SYS_gettid);
int ret = setpriority(PRIO_PROCESS, tid, getpriority(PRIO_PROCESS, getpid()) + 1);
#endif
}
// spurious wakeup or we sent interrupt signal for flushing // spurious wakeup or we sent interrupt signal for flushing
if(pChannel->nSectorsToRead == 0) if(pChannel->nSectorsToRead == 0)
continue; continue;
pChannel->bReading = true; pChannel->bReading = true;
// Not standard POSIX :shrug:
#ifdef __linux__
#ifdef ONE_THREAD_PER_CHANNEL
if (gpReadInfo[channel].nThreadStatus == 0){
gpReadInfo[channel].nThreadStatus = 1;
#else
if (gCdStreamThreadStatus == 0){
gCdStreamThreadStatus = 1;
#endif
pid_t tid = syscall(SYS_gettid);
int ret = setpriority(PRIO_PROCESS, tid, getpriority(PRIO_PROCESS, getpid()) + 1);
}
#endif
if ( pChannel->nStatus == STREAM_NONE ) if ( pChannel->nStatus == STREAM_NONE )
{ {
ASSERT(pChannel->hFile >= 0); ASSERT(pChannel->hFile >= 0);
@ -423,7 +427,8 @@ void *CdStreamThread(void *param)
lseek(pChannel->hFile, pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE, SEEK_SET); lseek(pChannel->hFile, pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE, SEEK_SET);
if (read(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE) == -1) { if (read(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE) == -1) {
// pChannel->nSectorsToRead == 0 at this point means we wanted to flush channel // pChannel->nSectorsToRead == 0 at this point means we wanted to flush channel
pChannel->nStatus = pChannel->nSectorsToRead == 0 ? STREAM_NONE : STREAM_ERROR; // STREAM_WAITING is a little hack to make CStreaming not process this data
pChannel->nStatus = pChannel->nSectorsToRead == 0 ? STREAM_WAITING : STREAM_ERROR;
} else { } else {
pChannel->nStatus = STREAM_NONE; pChannel->nStatus = STREAM_NONE;
} }
@ -441,22 +446,29 @@ void *CdStreamThread(void *param)
} }
pChannel->bReading = false; pChannel->bReading = false;
} }
char semName[20];
#ifndef ONE_THREAD_PER_CHANNEL #ifndef ONE_THREAD_PER_CHANNEL
for ( int32 i = 0; i < gNumChannels; i++ ) for ( int32 i = 0; i < gNumChannels; i++ )
{ {
sem_close(gpReadInfo[i].pDoneSemaphore); sem_close(gpReadInfo[i].pDoneSemaphore);
sem_unlink("/semaphore_done"); sprintf(semName,"/semaphore_done%d",i);
sem_unlink(semName);
} }
sem_close(gCdStreamSema); sem_close(gCdStreamSema);
sem_unlink("/semaphore_cd_stream"); sem_unlink("/semaphore_cd_stream");
free(gChannelRequestQ.items); free(gChannelRequestQ.items);
#else #else
sem_close(gpReadInfo[channel].pStartSemaphore); sem_close(gpReadInfo[channel].pStartSemaphore);
sem_unlink("/semaphore_start"); sprintf(semName,"/semaphore_start%d",channel);
sem_unlink(semName);
sem_close(gpReadInfo[channel].pDoneSemaphore); sem_close(gpReadInfo[channel].pDoneSemaphore);
sem_unlink("/semaphore_done"); sprintf(semName,"/semaphore_done%d",channel);
sem_unlink(semName);
#endif #endif
if (gpReadInfo)
free(gpReadInfo); free(gpReadInfo);
gpReadInfo = nil;
pthread_exit(nil); pthread_exit(nil);
} }
@ -506,8 +518,10 @@ CdStreamGetImageName(int32 cd)
void void
CdStreamRemoveImages(void) CdStreamRemoveImages(void)
{ {
for ( int32 i = 0; i < gNumChannels; i++ ) for ( int32 i = 0; i < gNumChannels; i++ ) {
flushStream[i] = 1;
CdStreamSync(i); CdStreamSync(i);
}
for ( int32 i = 0; i < gNumImages; i++ ) for ( int32 i = 0; i < gNumImages; i++ )
{ {

View File

@ -980,7 +980,11 @@ CFileLoader::Load2dEffect(const char *line)
&effect->attractor.dir.z, &effect->attractor.dir.z,
&probability); &probability);
effect->attractor.type = flags; effect->attractor.type = flags;
#ifdef FIX_BUGS
effect->attractor.probability = clamp(probability, 0, 255);
#else
effect->attractor.probability = probability; effect->attractor.probability = probability;
#endif
break; break;
} }

View File

@ -142,17 +142,17 @@ static size_t
myfread(void *buf, size_t elt, size_t n, int fd) myfread(void *buf, size_t elt, size_t n, int fd)
{ {
if(myfiles[fd].isText){ if(myfiles[fd].isText){
char *p; unsigned char *p;
size_t i; size_t i;
int c; int c;
n *= elt; n *= elt;
p = (char*)buf; p = (unsigned char*)buf;
for(i = 0; i < n; i++){ for(i = 0; i < n; i++){
c = myfgetc(fd); c = myfgetc(fd);
if(c == EOF) if(c == EOF)
break; break;
*p++ = c; *p++ = (unsigned char)c;
} }
return i / elt; return i / elt;
} }
@ -163,12 +163,12 @@ static size_t
myfwrite(void *buf, size_t elt, size_t n, int fd) myfwrite(void *buf, size_t elt, size_t n, int fd)
{ {
if(myfiles[fd].isText){ if(myfiles[fd].isText){
char *p; unsigned char *p;
size_t i; size_t i;
int c; int c;
n *= elt; n *= elt;
p = (char*)buf; p = (unsigned char*)buf;
for(i = 0; i < n; i++){ for(i = 0; i < n; i++){
c = *p++; c = *p++;
myfputc(c, fd); myfputc(c, fd);

View File

@ -45,7 +45,11 @@ CStreamingInfo CStreaming::ms_endRequestedList;
int32 CStreaming::ms_oldSectorX; int32 CStreaming::ms_oldSectorX;
int32 CStreaming::ms_oldSectorY; int32 CStreaming::ms_oldSectorY;
int32 CStreaming::ms_streamingBufferSize; int32 CStreaming::ms_streamingBufferSize;
#ifndef ONE_THREAD_PER_CHANNEL
int8 *CStreaming::ms_pStreamingBuffer[2]; int8 *CStreaming::ms_pStreamingBuffer[2];
#else
int8 *CStreaming::ms_pStreamingBuffer[4];
#endif
size_t CStreaming::ms_memoryUsed; size_t CStreaming::ms_memoryUsed;
CStreamingChannel CStreaming::ms_channel[2]; CStreamingChannel CStreaming::ms_channel[2];
int32 CStreaming::ms_channelError; int32 CStreaming::ms_channelError;
@ -198,6 +202,10 @@ CStreaming::Init2(void)
ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE);
ms_streamingBufferSize /= 2; ms_streamingBufferSize /= 2;
ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE;
#ifdef ONE_THREAD_PER_CHANNEL
ms_pStreamingBuffer[2] = (int8*)RwMallocAlign(ms_streamingBufferSize*2*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE);
ms_pStreamingBuffer[3] = ms_pStreamingBuffer[2] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE;
#endif
debug("Streaming buffer size is %d sectors", ms_streamingBufferSize); debug("Streaming buffer size is %d sectors", ms_streamingBufferSize);
// PC only, figure out how much memory we got // PC only, figure out how much memory we got
@ -1831,6 +1839,109 @@ CStreaming::LoadRequestedModels(void)
} }
} }
// Let's load models first, then process it. Unfortunately processing models are still single-threaded.
// Currently only supported on POSIX streamer.
#ifdef ONE_THREAD_PER_CHANNEL
void
CStreaming::LoadAllRequestedModels(bool priority)
{
static bool bInsideLoadAll = false;
int imgOffset, streamId, status;
int i;
uint32 posn, size;
if(bInsideLoadAll)
return;
FlushChannels();
imgOffset = GetCdImageOffset(CdStreamGetLastPosn());
int streamIds[ARRAY_SIZE(ms_pStreamingBuffer)];
int streamSizes[ARRAY_SIZE(ms_pStreamingBuffer)];
int streamPoses[ARRAY_SIZE(ms_pStreamingBuffer)];
bool first = true;
int processI = 0;
while (true) {
// Enumerate files and start reading
for (int i=0; i<ARRAY_SIZE(ms_pStreamingBuffer); i++) {
if (!first && streamIds[i] != -1) {
processI = i;
continue;
}
if(ms_endRequestedList.m_prev != &ms_startRequestedList){
streamId = GetNextFileOnCd(0, priority);
if(streamId == -1){
streamIds[i] = -1;
break;
}
if (ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)) {
streamIds[i] = -1;
if (size > (uint32)ms_streamingBufferSize) {
if (i + 1 == ARRAY_SIZE(ms_pStreamingBuffer))
continue;
else if (!first && streamIds[i+1] != -1)
continue;
} else {
if (i != 0 && streamIds[i-1] != -1 && streamSizes[i-1] > (uint32)ms_streamingBufferSize)
continue;
}
ms_aInfoForModel[streamId].RemoveFromList();
DecrementRef(streamId);
streamIds[i] = streamId;
streamSizes[i] = size;
streamPoses[i] = posn;
CdStreamRead(i, ms_pStreamingBuffer[i], imgOffset+posn, size);
processI = i;
} else {
ms_aInfoForModel[streamId].RemoveFromList();
DecrementRef(streamId);
ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED;
streamIds[i] = -1;
}
} else
streamIds[i] = -1;
}
first = false;
// Now process
if (streamIds[processI] == -1)
break;
// Try again on error
while (CdStreamSync(processI) != STREAM_NONE) {
CdStreamRead(processI, ms_pStreamingBuffer[processI], imgOffset+streamPoses[processI], streamSizes[processI]);
}
ms_aInfoForModel[streamIds[processI]].m_loadState = STREAMSTATE_READING;
MakeSpaceFor(streamSizes[processI] * CDSTREAM_SECTOR_SIZE);
ConvertBufferToObject(ms_pStreamingBuffer[processI], streamIds[processI]);
if(ms_aInfoForModel[streamIds[processI]].m_loadState == STREAMSTATE_STARTED)
FinishLoadingLargeFile(ms_pStreamingBuffer[processI], streamIds[processI]);
if(streamIds[processI] < STREAM_OFFSET_TXD){
CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamIds[processI]);
if(mi->IsSimple())
mi->m_alpha = 255;
}
streamIds[processI] = -1;
}
ms_bLoadingBigModel = false;
for(i = 0; i < 4; i++){
ms_channel[1].streamIds[i] = -1;
ms_channel[1].offsets[i] = -1;
}
ms_channel[1].state = CHANNELSTATE_IDLE;
bInsideLoadAll = false;
}
#else
void void
CStreaming::LoadAllRequestedModels(bool priority) CStreaming::LoadAllRequestedModels(bool priority)
{ {
@ -1883,6 +1994,7 @@ CStreaming::LoadAllRequestedModels(bool priority)
ms_channel[1].state = CHANNELSTATE_IDLE; ms_channel[1].state = CHANNELSTATE_IDLE;
bInsideLoadAll = false; bInsideLoadAll = false;
} }
#endif
void void
CStreaming::FlushChannels(void) CStreaming::FlushChannels(void)
@ -1914,6 +2026,14 @@ CStreaming::FlushRequestList(void)
next = si->m_next; next = si->m_next;
RemoveModel(si - ms_aInfoForModel); RemoveModel(si - ms_aInfoForModel);
} }
#ifndef _WIN32
if(ms_channel[0].state == CHANNELSTATE_READING) {
flushStream[0] = 1;
}
if(ms_channel[1].state == CHANNELSTATE_READING) {
flushStream[1] = 1;
}
#endif
FlushChannels(); FlushChannels();
} }

View File

@ -106,7 +106,7 @@ typedef uint16_t wchar;
inline uint32 dpb(uint32 b, uint32 p, uint32 s, uint32 w) inline uint32 dpb(uint32 b, uint32 p, uint32 s, uint32 w)
{ {
uint32 m = MASK(p,s); uint32 m = MASK(p,s);
return w & ~m | b<<p & m; return (w & ~m) | ((b<<p) & m);
} }
inline uint32 ldb(uint32 p, uint32 s, uint32 w) inline uint32 ldb(uint32 p, uint32 s, uint32 w)
{ {

View File

@ -56,7 +56,7 @@ GetNumberOfMenuOptions(int screen)
uint8 uint8
GetLastMenuScreen() GetLastMenuScreen()
{ {
uint8 page = -1; int8 page = -1;
for (int i = 0; i < MENUPAGES; i++) { for (int i = 0; i < MENUPAGES; i++) {
if (strcmp(aScreens[i].m_ScreenName, "") == 0 && aScreens[i].unk == 0) if (strcmp(aScreens[i].m_ScreenName, "") == 0 && aScreens[i].unk == 0)
break; break;

View File

@ -22,8 +22,13 @@ uint32 CTheCarGenerators::CurrentActiveCount;
void CCarGenerator::SwitchOff() void CCarGenerator::SwitchOff()
{ {
#ifdef FIX_BUGS
if (m_nUsesRemaining != 0)
#endif
{
m_nUsesRemaining = 0; m_nUsesRemaining = 0;
--CTheCarGenerators::CurrentActiveCount; --CTheCarGenerators::CurrentActiveCount;
}
} }
void CCarGenerator::SwitchOn() void CCarGenerator::SwitchOn()

View File

@ -111,7 +111,7 @@ public:
CAutoPilot AutoPilot; CAutoPilot AutoPilot;
uint8 m_currentColour1; uint8 m_currentColour1;
uint8 m_currentColour2; uint8 m_currentColour2;
uint8 m_aExtras[2]; int8 m_aExtras[2];
int16 m_nAlarmState; int16 m_nAlarmState;
int16 m_nMissionValue; int16 m_nMissionValue;
CPed *pDriver; CPed *pDriver;

View File

@ -160,17 +160,17 @@ CWeaponInfo::LoadWeaponData(void)
ms_apWeaponInfos[weaponType].m_fAnimFrameFire = delayBetweenAnimAndFire / 30.0f; ms_apWeaponInfos[weaponType].m_fAnimFrameFire = delayBetweenAnimAndFire / 30.0f;
ms_apWeaponInfos[weaponType].m_fAnim2FrameFire = delayBetweenAnim2AndFire / 30.0f; ms_apWeaponInfos[weaponType].m_fAnim2FrameFire = delayBetweenAnim2AndFire / 30.0f;
ms_apWeaponInfos[weaponType].m_nModelId = modelId; ms_apWeaponInfos[weaponType].m_nModelId = modelId;
ms_apWeaponInfos[weaponType].m_bUseGravity = flags; ms_apWeaponInfos[weaponType].m_bUseGravity = flags & 1;
ms_apWeaponInfos[weaponType].m_bSlowsDown = flags >> 1; ms_apWeaponInfos[weaponType].m_bSlowsDown = (flags >> 1) & 1;
ms_apWeaponInfos[weaponType].m_bDissipates = flags >> 2; ms_apWeaponInfos[weaponType].m_bDissipates = (flags >> 2) & 1;
ms_apWeaponInfos[weaponType].m_bRandSpeed = flags >> 3; ms_apWeaponInfos[weaponType].m_bRandSpeed = (flags >> 3) & 1;
ms_apWeaponInfos[weaponType].m_bExpands = flags >> 4; ms_apWeaponInfos[weaponType].m_bExpands = (flags >> 4) & 1;
ms_apWeaponInfos[weaponType].m_bExplodes = flags >> 5; ms_apWeaponInfos[weaponType].m_bExplodes = (flags >> 5) & 1;
ms_apWeaponInfos[weaponType].m_bCanAim = flags >> 6; ms_apWeaponInfos[weaponType].m_bCanAim = (flags >> 6) & 1;
ms_apWeaponInfos[weaponType].m_bCanAimWithArm = flags >> 7; ms_apWeaponInfos[weaponType].m_bCanAimWithArm = (flags >> 7) & 1;
ms_apWeaponInfos[weaponType].m_b1stPerson = flags >> 8; ms_apWeaponInfos[weaponType].m_b1stPerson = (flags >> 8) & 1;
ms_apWeaponInfos[weaponType].m_bHeavy = flags >> 9; ms_apWeaponInfos[weaponType].m_bHeavy = (flags >> 9) & 1;
ms_apWeaponInfos[weaponType].m_bThrow = flags >> 10; ms_apWeaponInfos[weaponType].m_bThrow = (flags >> 10) & 1;
} }
} }