summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Streaming.cpp403
-rw-r--r--src/Streaming.h18
2 files changed, 309 insertions, 112 deletions
diff --git a/src/Streaming.cpp b/src/Streaming.cpp
index 14582e0c..62451526 100644
--- a/src/Streaming.cpp
+++ b/src/Streaming.cpp
@@ -23,7 +23,7 @@ CStreamingInfo &CStreaming::ms_startRequestedList = *(CStreamingInfo*)0x8F1B3C;
CStreamingInfo &CStreaming::ms_endRequestedList = *(CStreamingInfo*)0x940738;
int32 &CStreaming::ms_oldSectorX = *(int32*)0x8F2C84;
int32 &CStreaming::ms_oldSectorY = *(int32*)0x8F2C88;
-uint32 &CStreaming::ms_streamingBufferSize = *(uint32*)0x942FB0;
+int32 &CStreaming::ms_streamingBufferSize = *(int32*)0x942FB0;
int8 **CStreaming::ms_pStreamingBuffer = (int8**)0x87F818;
int32 &CStreaming::ms_memoryUsed = *(int32*)0x940568;
CStreamingChannel *CStreaming::ms_channel = (CStreamingChannel*)0x727EE0;
@@ -240,7 +240,7 @@ CStreaming::LoadCdDirectory(const char *dirname, int n)
while(CFileMgr::Read(fd, (char*)&direntry, sizeof(direntry))){
dot = strchr(direntry.name, '.');
if(dot) *dot = '\0';
- if(direntry.size > ms_streamingBufferSize)
+ if(direntry.size > (uint32)ms_streamingBufferSize)
ms_streamingBufferSize = direntry.size;
if(strcmp(dot+1, "DFF") == 0 || strcmp(dot+1, "dff") == 0){
@@ -490,7 +490,7 @@ CStreaming::RequestModel(int32 id, int32 flags)
if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){
// updgrade to priority
- if(flags & STREAMFLAGS_PRIORITY && (ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY) == 0){
+ if(flags & STREAMFLAGS_PRIORITY && !ms_aInfoForModel[id].IsPriority()){
ms_numPriorityRequests++;
ms_aInfoForModel[id].m_flags |= STREAMFLAGS_PRIORITY;
}
@@ -654,6 +654,16 @@ CStreaming::RequestSpecialChar(int32 charId, const char *modelName, int32 flags)
}
void
+CStreaming::DecrementRef(int32 id)
+{
+ ms_numModelsRequested--;
+ if(ms_aInfoForModel[id].IsPriority()){
+ ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY;
+ ms_numPriorityRequests--;
+ }
+}
+
+void
CStreaming::RemoveModel(int32 id)
{
int i;
@@ -671,13 +681,8 @@ CStreaming::RemoveModel(int32 id)
if(ms_aInfoForModel[id].m_next){
// Remove from list, model is neither loaded nor requested
- if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){
- ms_numModelsRequested--;
- if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY){
- ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY;
- ms_numPriorityRequests--;
- }
- }
+ if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE)
+ DecrementRef(id);
ms_aInfoForModel[id].RemoveFromList();
}else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_READING){
for(i = 0; i < 4; i++){
@@ -1120,7 +1125,223 @@ CStreaming::LoadInitialVehicles(void)
}
+// Find starting offset of the cdimage we next want to read
+// Not useful at all on PC...
+int32
+CStreaming::GetCdImageOffset(int32 lastPosn)
+{
+ int offset, off;
+ int i, img;
+ int dist, mindist;
+
+ img = -1;
+ mindist = INT_MAX;
+ offset = ms_imageOffsets[ms_lastImageRead];
+ if(lastPosn <= offset || lastPosn > offset + ms_imageSize){
+ // last read position is not in last image
+ for(i = 0; i < NUMCDIMAGES; i++){
+ off = ms_imageOffsets[i];
+ if(off == -1) continue;
+ if((uint32)lastPosn > (uint32)off)
+ // after start of image, get distance from end
+ // negative if before end!
+ dist = lastPosn - (off + ms_imageSize);
+ else
+ // before image, get offset to start
+ // this will never be negative
+ dist = off - lastPosn;
+ if(dist < mindist){
+ img = i;
+ mindist = dist;
+ }
+ }
+ assert(img >= 0);
+ offset = ms_imageOffsets[img];
+ ms_lastImageRead = img;
+ }
+ return offset;
+}
+
+inline bool
+TxdAvailable(int32 txdId)
+{
+ CStreamingInfo *si = &CStreaming::ms_aInfoForModel[txdId + STREAM_OFFSET_TXD];
+ return si->m_loadState == STREAMSTATE_LOADED || si->m_loadState == STREAMSTATE_READING;
+}
+
+// Find stream id of next requested file in cdimage
+int32
+CStreaming::GetNextFileOnCd(int32 lastPosn, bool priority)
+{
+ CStreamingInfo *si, *next;
+ int streamId;
+ uint32 posn, size;
+ int streamIdFirst, streamIdNext;
+ uint32 posnFirst, posnNext;
+
+ streamIdFirst = -1;
+ streamIdNext = -1;
+ posnFirst = UINT_MAX;
+ posnNext = UINT_MAX;
+
+ for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){
+ next = si->m_next;
+ streamId = si - ms_aInfoForModel;
+
+ // only priority requests if there are any
+ if(priority && ms_numPriorityRequests != 0 && !si->IsPriority())
+ continue;
+
+ // request Txd if necessary
+ if(streamId < STREAM_OFFSET_TXD &&
+ !TxdAvailable(CModelInfo::GetModelInfo(streamId)->GetTxdSlot())){
+ ReRequestTxd(CModelInfo::GetModelInfo(streamId)->GetTxdSlot());
+ }else if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){
+ if(posn < posnFirst){
+ // find first requested file in image
+ streamIdFirst = streamId;
+ posnFirst = posn;
+ }
+ if(posn < posnNext && posn >= (uint32)lastPosn){
+ // find first requested file after last read position
+ streamIdNext = streamId;
+ posnNext = posn;
+ }
+ }else{
+ // empty file
+ DecrementRef(streamId);
+ si->RemoveFromList();
+ si->m_loadState = STREAMSTATE_LOADED;
+ }
+ }
+
+ // wrap around
+ if(streamIdNext == -1)
+ streamIdNext = streamIdFirst;
+
+ if(streamIdNext == -1 && ms_numPriorityRequests != 0){
+ // try non-priority files
+ ms_numPriorityRequests = 0;
+ streamIdNext = GetNextFileOnCd(lastPosn, false);
+ }
+
+ return streamIdNext;
+}
+
+/*
+ * Streaming buffer size is half of the largest file.
+ * Files larger than the buffer size can only be loaded by channel 0,
+ * which then uses both buffers, while channel 1 is idle.
+ * ms_bLoadingBigModel is set to true to indicate this state.
+ *
+ * TODO: two-part files
+ */
+
+// Make channel read from disc
+void
+CStreaming::RequestModelStream(int32 ch)
+{
+ int lastPosn, imgOffset, streamId;
+ int totalSize;
+ uint32 posn, size, unused;
+ int i;
+ int haveBigFile, havePed;
+
+ lastPosn = CdStreamGetLastPosn();
+ imgOffset = GetCdImageOffset(lastPosn);
+ streamId = GetNextFileOnCd(lastPosn - imgOffset, true);
+
+ if(streamId == -1)
+ return;
+
+ // remove Txds that aren't requested anymore
+ while(streamId >= STREAM_OFFSET_TXD){
+ if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY ||
+ IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD))
+ break;
+ RemoveModel(streamId);
+ // so try next file
+ ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size);
+ streamId = GetNextFileOnCd(posn + size, true);
+ }
+
+ if(streamId == -1)
+ return;
+
+ ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size);
+ if(size > (uint32)ms_streamingBufferSize){
+ // Can only load big models on channel 0, and 1 has to be idle
+ if(ch == 1 || ms_channel[1].state != CHANNELSTATE_IDLE)
+ return;
+ ms_bLoadingBigModel = true;
+ }
+
+ // Load up to 4 adjacent files
+ haveBigFile = 0;
+ havePed = 0;
+ totalSize = 0;
+ for(i = 0; i < 4; i++){
+ // no more files we can read
+ if(streamId == -1 || ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_INQUEUE)
+ break;
+
+ // also stop at non-priority files
+ ms_aInfoForModel[streamId].GetCdPosnAndSize(unused, size);
+ if(ms_numPriorityRequests != 0 && !ms_aInfoForModel[streamId].IsPriority())
+ break;
+
+ // Can't load certain combinations of files together
+ if(streamId < STREAM_OFFSET_TXD){
+ if(havePed && CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_PED ||
+ haveBigFile && CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_VEHICLE ||
+ !TxdAvailable(CModelInfo::GetModelInfo(streamId)->GetTxdSlot()))
+ break;
+ }else{
+ if(haveBigFile && size > 200)
+ break;
+ }
+
+ // Now add the file
+ ms_channel[ch].streamIds[i] = streamId;
+ ms_channel[ch].offsets[i] = totalSize;
+ totalSize += size;
+
+ // To big for buffer, remove again
+ if(totalSize > ms_streamingBufferSize && i > 0){
+ totalSize -= size;
+ break;
+ }
+ if(streamId < STREAM_OFFSET_TXD){
+ if(CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_PED)
+ havePed = 1;
+ if(CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_VEHICLE)
+ haveBigFile = 1;
+ }else{
+ if(size > 200)
+ haveBigFile = 1;
+ }
+ ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING;
+ ms_aInfoForModel[streamId].RemoveFromList();
+ DecrementRef(streamId);
+
+ streamId = ms_aInfoForModel[streamId].m_nextID;
+ }
+
+ // clear remaining slots
+ for(; i < 4; i++)
+ ms_channel[ch].streamIds[i] = -1;
+ // Now read the data
+ assert(!(ms_bLoadingBigModel && ch == 1)); // this would clobber the buffer
+ if(CdStreamRead(ch, ms_pStreamingBuffer[ch], imgOffset+posn, totalSize) == STREAM_NONE)
+ debug("FUCKFUCKFUCK\n");
+ ms_channel[ch].state = CHANNELSTATE_READING;
+ ms_channel[ch].field24 = 0;
+ ms_channel[ch].size = totalSize;
+ ms_channel[ch].position = imgOffset+posn;
+ ms_channel[ch].numTries = 0;
+}
+// Load data previously read from disc
bool
CStreaming::ProcessLoadingChannel(int32 ch)
{
@@ -1192,75 +1413,81 @@ CStreaming::ProcessLoadingChannel(int32 ch)
return true;
}
-inline bool
-TxdAvailable(int32 txdId)
+void
+CStreaming::LoadRequestedModels(void)
{
- CStreamingInfo *si = &CStreaming::ms_aInfoForModel[txdId + STREAM_OFFSET_TXD];
- return si->m_loadState == STREAMSTATE_LOADED || si->m_loadState == STREAMSTATE_READING;
+ static int currentChannel = 0;
+
+ // We can't read with channel 1 while channel 0 is using its buffer
+ if(ms_bLoadingBigModel)
+ currentChannel = 0;
+
+ // We have data, load
+ if(ms_channel[currentChannel].state == CHANNELSTATE_READING ||
+ ms_channel[currentChannel].state == CHANNELSTATE_STARTED)
+ ProcessLoadingChannel(currentChannel);
+
+ if(ms_channelError == -1){
+ // Channel is idle, read more data
+ if(ms_channel[currentChannel].state == CHANNELSTATE_IDLE)
+ RequestModelStream(currentChannel);
+ // Switch channel
+ if(ms_channel[currentChannel].state != CHANNELSTATE_STARTED)
+ currentChannel = 1 - currentChannel;
+ }
}
-// Find stream id of next requested file in cdimage
-int32
-CStreaming::GetNextFileOnCd(int32 lastPosn, bool priority)
+void
+CStreaming::LoadAllRequestedModels(bool priority)
{
- CStreamingInfo *si, *next;
- int streamId;
+ static bool bInsideLoadAll = false;
+ int imgOffset, streamId, status;
+ int i;
uint32 posn, size;
- int streamIdFirst, streamIdNext;
- uint32 posnFirst, posnNext;
-
- streamIdFirst = -1;
- streamIdNext = -1;
- posnFirst = UINT_MAX;
- posnNext = UINT_MAX;
-
- for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){
- next = si->m_next;
- streamId = si - ms_aInfoForModel;
- // only priority requests if there are any
- if(priority && ms_numPriorityRequests != 0 &&
- (si->m_flags & STREAMFLAGS_PRIORITY) == 0)
- continue;
+ if(bInsideLoadAll)
+ return;
- // request Txd if necessary
- if(streamId < STREAM_OFFSET_TXD &&
- !TxdAvailable(CModelInfo::GetModelInfo(streamId)->GetTxdSlot())){
- ReRequestTxd(CModelInfo::GetModelInfo(streamId)->GetTxdSlot());
- }else if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){
- if(posn < posnFirst){
- // find first requested file in image
- streamIdFirst = streamId;
- posnFirst = posn;
- }
- if(posn < posnNext && posn >= (uint32)lastPosn){
- // find first requested file after last read position
- streamIdNext = streamId;
- posnNext = posn;
+ FlushChannels();
+ imgOffset = GetCdImageOffset(CdStreamGetLastPosn());
+
+ while(ms_endRequestedList.m_prev != &ms_startRequestedList){
+ streamId = GetNextFileOnCd(0, priority);
+ if(streamId == -1)
+ break;
+
+ ms_aInfoForModel[streamId].RemoveFromList();
+ DecrementRef(streamId);
+
+ if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){
+ do
+ status = CdStreamRead(0, ms_pStreamingBuffer[0], imgOffset+posn, size);
+ while(CdStreamSync(0) || status == STREAM_NONE);
+ ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING;
+
+ MakeSpaceFor(size * CDSTREAM_SECTOR_SIZE);
+ ConvertBufferToObject(ms_pStreamingBuffer[0], streamId);
+ if(ms_aInfoForModel[streamId].m_loadState == STREAMSTATE_STARTED)
+ FinishLoadingLargeFile(ms_pStreamingBuffer[0], streamId);
+
+ if(streamId < STREAM_OFFSET_TXD){
+ CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamId);
+ if(mi->IsSimple())
+ mi->m_alpha = 255;
}
}else{
- // empty file
- ms_numModelsRequested--;
- if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_PRIORITY){
- ms_aInfoForModel[streamId].m_flags &= ~STREAMFLAGS_PRIORITY;
- ms_numPriorityRequests--;
- }
- si->RemoveFromList();
- si->m_loadState = STREAMSTATE_LOADED;
+ // empty
+ ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED;
}
}
- // wrap around
- if(streamIdNext == -1)
- streamIdNext = streamIdFirst;
-
- if(streamIdNext == -1 && ms_numPriorityRequests){
- // try non-priority files
- ms_numPriorityRequests = 0;
- streamIdNext = GetNextFileOnCd(lastPosn, false);
+ ms_bLoadingBigModel = false;
+ for(i = 0; i < 4; i++){
+ ms_channel[1].streamIds[i] = -1;
+ ms_channel[1].offsets[i] = -1;
}
-
- return streamIdNext;
+ ms_channel[1].state = CHANNELSTATE_IDLE;
+ bInsideLoadAll = false;
}
void
@@ -1269,14 +1496,14 @@ CStreaming::FlushChannels(void)
if(ms_channel[1].state == CHANNELSTATE_STARTED)
ProcessLoadingChannel(1);
- if(ms_channel[0].state == CHANNELSTATE_UNK1){
+ if(ms_channel[0].state == CHANNELSTATE_READING){
CdStreamSync(0);
ProcessLoadingChannel(0);
}
if(ms_channel[0].state == CHANNELSTATE_STARTED)
ProcessLoadingChannel(0);
- if(ms_channel[1].state == CHANNELSTATE_UNK1){
+ if(ms_channel[1].state == CHANNELSTATE_READING){
CdStreamSync(1);
ProcessLoadingChannel(1);
}
@@ -1296,43 +1523,6 @@ CStreaming::FlushRequestList(void)
FlushChannels();
}
-// Find starting offset of the cdimage we next want to read
-// Not useful at all on PC...
-int32
-CStreaming::GetCdImageOffset(int32 lastPosn)
-{
- int offset, off;
- int i, img;
- int dist, mindist;
-
- img = -1;
- mindist = INT_MAX;
- offset = ms_imageOffsets[ms_lastImageRead];
- if(lastPosn <= offset || lastPosn > offset + ms_imageSize){
- // last read position is not in last image
- for(i = 0; i < NUMCDIMAGES; i++){
- off = ms_imageOffsets[i];
- if(off == -1) continue;
- if((uint32)lastPosn > (uint32)off)
- // after start of image, get distance from end
- // negative if before end!
- dist = lastPosn - (off + ms_imageSize);
- else
- // before image, get offset to start
- // this will never be negative
- dist = off - lastPosn;
- if(dist < mindist){
- img = i;
- mindist = dist;
- }
- }
- assert(img >= 0);
- offset = ms_imageOffsets[img];
- ms_lastImageRead = img;
- }
- return offset;
-}
-
void
CStreaming::ImGonnaUseStreamingMemory(void)
@@ -1431,6 +1621,9 @@ STARTPATCHES
InjectHook(0x40A680, CStreaming::FlushRequestList, PATCH_JUMP);
InjectHook(0x409FF0, CStreaming::GetCdImageOffset, PATCH_JUMP);
InjectHook(0x409E50, CStreaming::GetNextFileOnCd, PATCH_JUMP);
+ InjectHook(0x40A060, CStreaming::RequestModelStream, PATCH_JUMP);
+ InjectHook(0x40A390, CStreaming::LoadRequestedModels, PATCH_JUMP);
+ InjectHook(0x40A440, CStreaming::LoadAllRequestedModels, PATCH_JUMP);
InjectHook(0x4063E0, &CStreamingInfo::GetCdPosnAndSize, PATCH_JUMP);
InjectHook(0x406410, &CStreamingInfo::SetCdPosnAndSize, PATCH_JUMP);
diff --git a/src/Streaming.h b/src/Streaming.h
index 62d0c2fb..571a7194 100644
--- a/src/Streaming.h
+++ b/src/Streaming.h
@@ -24,14 +24,14 @@ enum StreamLoadState
STREAMSTATE_NOTLOADED = 0,
STREAMSTATE_LOADED = 1,
STREAMSTATE_INQUEUE = 2,
- STREAMSTATE_READING = 3, // what is this?
- STREAMSTATE_STARTED = 4, // first part read
+ STREAMSTATE_READING = 3, // channel is reading
+ STREAMSTATE_STARTED = 4, // first part loaded
};
enum ChannelState
{
CHANNELSTATE_IDLE = 0,
- CHANNELSTATE_UNK1 = 1,
+ CHANNELSTATE_READING = 1,
CHANNELSTATE_STARTED = 2,
CHANNELSTATE_ERROR = 3,
};
@@ -53,6 +53,7 @@ public:
void AddToList(CStreamingInfo *link);
void RemoveFromList(void);
uint32 GetCdSize(void) { return m_size; }
+ bool IsPriority(void) { return !!(m_flags & STREAMFLAGS_PRIORITY); }
};
struct CStreamingChannel
@@ -63,7 +64,7 @@ struct CStreamingChannel
int32 field24;
int32 position;
int32 size;
- int32 field30;
+ int32 numTries;
int32 status; // from CdStream
};
@@ -83,7 +84,7 @@ public:
static CStreamingInfo &ms_endRequestedList;
static int32 &ms_oldSectorX;
static int32 &ms_oldSectorY;
- static uint32 &ms_streamingBufferSize;
+ static int32 &ms_streamingBufferSize;
static int8 **ms_pStreamingBuffer; //[2]
static int32 &ms_memoryUsed;
static CStreamingChannel *ms_channel; //[2]
@@ -118,6 +119,7 @@ public:
static void RequestIslands(eLevelName level);
static void RequestSpecialModel(int32 modelId, const char *modelName, int32 flags);
static void RequestSpecialChar(int32 charId, const char *modelName, int32 flags);
+ static void DecrementRef(int32 id);
static void RemoveModel(int32 id);
static void RemoveTxd(int32 id) { RemoveModel(id + STREAM_OFFSET_TXD); }
static void RemoveUnusedBuildings(eLevelName level);
@@ -139,12 +141,14 @@ public:
static void SetModelTxdIsDeletable(int32 id);
static void SetMissionDoesntRequireModel(int32 id);
+ static int32 GetCdImageOffset(int32 lastPosn);
static int32 GetNextFileOnCd(int32 position, bool priority);
- static bool ProcessLoadingChannel(int32 ch);
static void RequestModelStream(int32 ch);
+ static bool ProcessLoadingChannel(int32 ch);
+ static void LoadRequestedModels(void);
+ static void LoadAllRequestedModels(bool priority);
static void FlushChannels(void);
static void FlushRequestList(void);
- static int32 GetCdImageOffset(int32 lastPosn);
static void MakeSpaceFor(int32 size);
static void ImGonnaUseStreamingMemory(void);