summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/re3_msvc_amd64.yml2
-rw-r--r--gamefiles/TEXT/american.gxtbin220570 -> 220642 bytes
-rw-r--r--gamefiles/TEXT/french.gxtbin246544 -> 246616 bytes
-rw-r--r--gamefiles/TEXT/german.gxtbin242608 -> 242680 bytes
-rw-r--r--gamefiles/TEXT/italian.gxtbin241978 -> 242050 bytes
-rwxr-xr-xgamefiles/TEXT/polish.gxtbin241610 -> 241682 bytes
-rw-r--r--gamefiles/TEXT/russian.gxtbin222396 -> 222468 bytes
-rw-r--r--gamefiles/TEXT/spanish.gxtbin235542 -> 235614 bytes
-rw-r--r--premake5.lua9
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/animation/AnimBlendAssociation.h2
-rw-r--r--src/animation/AnimBlendClumpData.cpp1
-rw-r--r--src/audio/AudioManager.cpp2
-rw-r--r--src/audio/oal/stream.cpp565
-rw-r--r--src/audio/oal/stream.h90
-rw-r--r--src/audio/sampman.h110
-rw-r--r--src/audio/sampman_miles.cpp132
-rw-r--r--src/audio/sampman_oal.cpp186
-rw-r--r--src/control/AutoPilot.cpp58
-rw-r--r--src/control/CarCtrl.cpp2
-rw-r--r--src/control/Garages.cpp115
-rw-r--r--src/control/Garages.h22
-rw-r--r--src/control/Phones.cpp45
-rw-r--r--src/control/Pickups.cpp33
-rw-r--r--src/control/Script5.cpp36
-rw-r--r--src/core/Fire.cpp2
-rw-r--r--src/core/Frontend.cpp98
-rw-r--r--src/core/Frontend.h29
-rw-r--r--src/core/Game.cpp6
-rw-r--r--src/core/IniFile.cpp4
-rw-r--r--src/core/IniFile.h3
-rw-r--r--src/core/MenuScreensCustom.cpp27
-rw-r--r--src/core/Pools.cpp10
-rw-r--r--src/core/Zones.cpp78
-rw-r--r--src/core/config.h186
-rw-r--r--src/core/re3.cpp135
-rw-r--r--src/entities/Entity.cpp4
-rw-r--r--src/entities/Physical.cpp4
-rw-r--r--src/extras/frontendoption.h2
-rw-r--r--src/extras/ini.h761
-rw-r--r--src/extras/ini_parser.hpp333
-rw-r--r--src/objects/ParticleObject.cpp109
-rw-r--r--src/peds/Ped.cpp13
-rw-r--r--src/peds/PlayerPed.cpp6
-rw-r--r--src/peds/Population.cpp8
-rw-r--r--src/save/GenericGameStorage.cpp546
-rw-r--r--src/save/GenericGameStorage.h5
-rw-r--r--src/save/PCSave.cpp7
-rw-r--r--src/save/PCSave.h2
-rw-r--r--src/save/SaveBuf.h9
-rw-r--r--src/skel/crossplatform.cpp23
-rw-r--r--src/skel/crossplatform.h3
-rw-r--r--src/vehicles/Automobile.cpp4
-rw-r--r--src/vehicles/Boat.cpp2
-rw-r--r--src/vehicles/Cranes.cpp86
-rw-r--r--src/vehicles/Vehicle.cpp99
-rw-r--r--src/weapons/Weapon.cpp2
-rw-r--r--utils/gxt/american.txt6
-rw-r--r--utils/gxt/french.txt6
-rw-r--r--utils/gxt/german.txt6
-rw-r--r--utils/gxt/italian.txt6
-rwxr-xr-xutils/gxt/polish.txt6
-rw-r--r--utils/gxt/russian.txt6
-rw-r--r--utils/gxt/spanish.txt6
64 files changed, 3119 insertions, 941 deletions
diff --git a/.github/workflows/re3_msvc_amd64.yml b/.github/workflows/re3_msvc_amd64.yml
index 428da540..014ac4f7 100644
--- a/.github/workflows/re3_msvc_amd64.yml
+++ b/.github/workflows/re3_msvc_amd64.yml
@@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
platform: [win-amd64-librw_d3d9-oal, win-amd64-librw_gl3_glfw-oal]
- buildtype: [Debug, Release, Vanilla]
+ buildtype: [Debug, Release]
steps:
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.2
diff --git a/gamefiles/TEXT/american.gxt b/gamefiles/TEXT/american.gxt
index ebd1ac39..d4034411 100644
--- a/gamefiles/TEXT/american.gxt
+++ b/gamefiles/TEXT/american.gxt
Binary files differ
diff --git a/gamefiles/TEXT/french.gxt b/gamefiles/TEXT/french.gxt
index 5b4c9e05..16c7a716 100644
--- a/gamefiles/TEXT/french.gxt
+++ b/gamefiles/TEXT/french.gxt
Binary files differ
diff --git a/gamefiles/TEXT/german.gxt b/gamefiles/TEXT/german.gxt
index 1d6ec988..c3309d61 100644
--- a/gamefiles/TEXT/german.gxt
+++ b/gamefiles/TEXT/german.gxt
Binary files differ
diff --git a/gamefiles/TEXT/italian.gxt b/gamefiles/TEXT/italian.gxt
index 746f07da..b30b74f4 100644
--- a/gamefiles/TEXT/italian.gxt
+++ b/gamefiles/TEXT/italian.gxt
Binary files differ
diff --git a/gamefiles/TEXT/polish.gxt b/gamefiles/TEXT/polish.gxt
index 5519c290..d771427b 100755
--- a/gamefiles/TEXT/polish.gxt
+++ b/gamefiles/TEXT/polish.gxt
Binary files differ
diff --git a/gamefiles/TEXT/russian.gxt b/gamefiles/TEXT/russian.gxt
index 90c8b13d..0075c691 100644
--- a/gamefiles/TEXT/russian.gxt
+++ b/gamefiles/TEXT/russian.gxt
Binary files differ
diff --git a/gamefiles/TEXT/spanish.gxt b/gamefiles/TEXT/spanish.gxt
index be659528..8980eb4d 100644
--- a/gamefiles/TEXT/spanish.gxt
+++ b/gamefiles/TEXT/spanish.gxt
Binary files differ
diff --git a/premake5.lua b/premake5.lua
index 72ff4d09..0a8faa07 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -68,7 +68,7 @@ end
workspace "re3"
language "C++"
- configurations { "Debug", "Release", "Vanilla" }
+ configurations { "Debug", "Release" }
startproject "re3"
location "build"
symbols "Full"
@@ -80,6 +80,7 @@ workspace "re3"
end
filter { "system:windows" }
+ configurations { "Vanilla" }
platforms {
"win-x86-RW33_d3d8-mss",
"win-x86-librw_d3d9-mss",
@@ -123,9 +124,6 @@ workspace "re3"
flags { "LinkTimeOptimization" }
end
- filter "configurations:Vanilla"
- defines { "VANILLA_DEFINES" }
-
filter { "platforms:win*" }
system "windows"
@@ -315,6 +313,9 @@ project "re3"
includedirs { "vendor/opusfile/include" }
end
+ filter "configurations:Vanilla"
+ defines { "VANILLA_DEFINES" }
+
filter "platforms:*mss"
defines { "AUDIO_MSS" }
includedirs { "vendor/milessdk/include" }
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 35b7ec11..28090d7e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -39,7 +39,7 @@ target_compile_definitions(${EXECUTABLE}
PRIVATE
$<IF:$<CONFIG:DEBUG>,DEBUG,NDEBUG>
LIBRW
- ${PROJECT}_NO_AUTOLINK
+ CMAKE_NO_AUTOLINK
)
if(LIBRW_PLATFORM_D3D9)
diff --git a/src/animation/AnimBlendAssociation.h b/src/animation/AnimBlendAssociation.h
index 80927da2..45720b6f 100644
--- a/src/animation/AnimBlendAssociation.h
+++ b/src/animation/AnimBlendAssociation.h
@@ -35,7 +35,7 @@ public:
CAnimBlendLink link;
- int numNodes; // taken from CAnimBlendClumpData::numFrames
+ int32 numNodes; // taken from CAnimBlendClumpData::numFrames
// NB: Order of these depends on order of nodes in Clump this was built from
CAnimBlendNode *nodes;
CAnimBlendHierarchy *hierarchy;
diff --git a/src/animation/AnimBlendClumpData.cpp b/src/animation/AnimBlendClumpData.cpp
index 702ee811..b333a449 100644
--- a/src/animation/AnimBlendClumpData.cpp
+++ b/src/animation/AnimBlendClumpData.cpp
@@ -3,7 +3,6 @@
#include "AnimBlendClumpData.h"
#include "MemoryMgr.h"
-
CAnimBlendClumpData::CAnimBlendClumpData(void)
{
numFrames = 0;
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index 2e391349..a113cc93 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -993,4 +993,4 @@ cAudioManager::ComputeEmittingVolume(uint8 emittingVolume, float intensity, floa
return (quatIntensity - (dist - diffIntensity)) * (float)emittingVolume / quatIntensity;
return emittingVolume;
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/audio/oal/stream.cpp b/src/audio/oal/stream.cpp
index 5d3ff08e..6afe8e30 100644
--- a/src/audio/oal/stream.cpp
+++ b/src/audio/oal/stream.cpp
@@ -1,10 +1,8 @@
#include "common.h"
#ifdef AUDIO_OAL
-#include "stream.h"
-#include "sampman.h"
-#if defined _MSC_VER && !defined RE3_NO_AUTOLINK
+#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK
#ifdef AUDIO_OAL_USE_SNDFILE
#pragma comment( lib, "libsndfile-1.lib" )
#endif
@@ -22,6 +20,29 @@
#include <opusfile.h>
#endif
+#include <queue>
+#include <utility>
+
+#ifdef MULTITHREADED_AUDIO
+#include <iostream>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include "MusicManager.h"
+#include "stream.h"
+
+std::thread gAudioThread;
+std::mutex gAudioThreadQueueMutex;
+std::condition_variable gAudioThreadCv;
+bool gAudioThreadTerm = false;
+std::queue<CStream*> gStreamsToProcess; // values are not unique, we will handle that ourself
+std::queue<std::pair<IDecoder*, void*>> gStreamsToClose;
+#else
+#include "stream.h"
+#endif
+
+#include "sampman.h"
+
#ifndef _WIN32
#include "crossplatform.h"
#endif
@@ -39,6 +60,10 @@ class CSortStereoBuffer
{
uint16* PcmBuf;
size_t BufSize;
+//#ifdef MULTITHREADED_AUDIO
+// std::mutex Mutex;
+//#endif
+
public:
CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {}
~CSortStereoBuffer()
@@ -65,6 +90,9 @@ public:
void SortStereo(void* buf, size_t size)
{
+//#ifdef MULTITHREADED_AUDIO
+// std::lock_guard<std::mutex> lock(Mutex);
+//#endif
uint16* InBuf = (uint16*)buf;
uint16* OutBuf = GetBuffer(size);
@@ -279,6 +307,10 @@ public:
#undef CLOSE_ON_ERROR
}
+ void FileOpen()
+ {
+ }
+
~CWavFile()
{
Close();
@@ -289,6 +321,7 @@ public:
return m_bIsOpen;
}
+
uint32 GetSampleSize()
{
return sizeof(uint16);
@@ -405,6 +438,10 @@ public:
m_pfSound = sf_open(path, SFM_READ, &m_soundInfo);
}
+ void FileOpen()
+ {
+ }
+
~CSndFile()
{
if ( m_pfSound )
@@ -464,46 +501,59 @@ public:
#endif
#ifdef AUDIO_OAL_USE_MPG123
-// fuzzy seek eliminates stutter when playing ADF but spams errors a lot (nothing breaks though)
-#define MP3_USE_FUZZY_SEEK
class CMP3File : public IDecoder
{
+protected:
mpg123_handle *m_pMH;
bool m_bOpened;
uint32 m_nRate;
uint32 m_nChannels;
+ const char* m_pPath;
+ bool m_bFileNotOpenedYet;
public:
CMP3File(const char *path) :
m_pMH(nil),
m_bOpened(false),
m_nRate(0),
- m_nChannels(0)
+ m_nChannels(0),
+ m_pPath(path),
+ m_bFileNotOpenedYet(false)
{
m_pMH = mpg123_new(nil, nil);
if ( m_pMH )
{
-#ifdef MP3_USE_FUZZY_SEEK
- mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET, 0.0);
-#endif
- long rate = 0;
- int channels = 0;
- int encoding = 0;
-
- m_bOpened = mpg123_open(m_pMH, path) == MPG123_OK
- && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
+ mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);
- m_nRate = rate;
- m_nChannels = channels;
-
- if ( IsOpened() )
- {
- mpg123_format_none(m_pMH);
- mpg123_format(m_pMH, rate, channels, encoding);
- }
+ m_bOpened = true;
+ m_bFileNotOpenedYet = true;
+ // It's possible to move this to audioFileOpsThread(), but effect isn't noticable + probably not compatible with our current cutscene audio handling
+#if 1
+ FileOpen();
+#endif
}
}
+ void FileOpen()
+ {
+ if(!m_bFileNotOpenedYet) return;
+
+ long rate = 0;
+ int channels = 0;
+ int encoding = 0;
+ m_bOpened = mpg123_open(m_pMH, m_pPath) == MPG123_OK
+ && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
+
+ m_nRate = rate;
+ m_nChannels = channels;
+
+ if(IsOpened()) {
+ mpg123_format_none(m_pMH);
+ mpg123_format(m_pMH, rate, channels, encoding);
+ }
+ m_bFileNotOpenedYet = false;
+ }
+
~CMP3File()
{
if ( m_pMH )
@@ -526,7 +576,7 @@ public:
uint32 GetSampleCount()
{
- if ( !IsOpened() ) return 0;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
return mpg123_length(m_pMH);
}
@@ -542,19 +592,19 @@ public:
void Seek(uint32 milliseconds)
{
- if ( !IsOpened() ) return;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return;
mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET);
}
uint32 Tell()
{
- if ( !IsOpened() ) return 0;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
return samples2ms(mpg123_tell(m_pMH));
}
uint32 Decode(void *buffer)
{
- if ( !IsOpened() ) return 0;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
size_t size;
int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size);
@@ -685,6 +735,10 @@ public:
m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE];
}
+ void FileOpen()
+ {
+ }
+
~CVbFile()
{
if (m_pFile)
@@ -837,6 +891,10 @@ public:
m_bOpened = true;
}
}
+
+ void FileOpen()
+ {
+ }
~COpusFile()
{
@@ -902,11 +960,173 @@ public:
};
#endif
+
+// For multi-thread: Someone always acquire stream's mutex before entering here
+void
+CStream::BuffersShouldBeFilled()
+{
+#ifdef MULTITHREADED_AUDIO
+ if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
+ std::queue<std::pair<ALuint, ALuint>> tempQueue;
+ for(int i = 0; i < NUM_STREAMBUFFERS / 2; i++) {
+ tempQueue.push(std::pair<ALuint, ALuint>(m_alBuffers[i * 2], m_alBuffers[i * 2 + 1]));
+ }
+ m_fillBuffers.swap(tempQueue);
+
+ FlagAsToBeProcessed();
+
+ m_bActive = true; // to allow Update() to queue the filled buffers & play
+ return;
+ }
+ std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
+#endif
+ if ( FillBuffers() != 0 )
+ {
+ SetPlay(true);
+ }
+}
+
+// returns whether it's queued (not on multi-thread)
+bool
+CStream::BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>* bufs)
+{
+#ifdef MULTITHREADED_AUDIO
+ if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE)
+ m_fillBuffers.push(*bufs);
+ else
+#endif
+ {
+ ALuint alBuffers[2] = {(*bufs).first, (*bufs).second}; // left - right
+ if (FillBuffer(alBuffers)) {
+ alSourceQueueBuffers(m_pAlSources[0], 1, &alBuffers[0]);
+ alSourceQueueBuffers(m_pAlSources[1], 1, &alBuffers[1]);
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifdef MULTITHREADED_AUDIO
+void
+CStream::FlagAsToBeProcessed(bool close)
+{
+ if (!close && MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE)
+ return;
+
+ gAudioThreadQueueMutex.lock();
+ if (close)
+ gStreamsToClose.push(std::pair<IDecoder*, void*>(m_pSoundFile ? m_pSoundFile : nil, m_pBuffer ? m_pBuffer : nil));
+ else
+ gStreamsToProcess.push(this);
+
+ gAudioThreadQueueMutex.unlock();
+
+ gAudioThreadCv.notify_one();
+}
+
+void audioFileOpsThread()
+{
+ do
+ {
+ CStream *stream;
+ {
+ // Just a semaphore
+ std::unique_lock<std::mutex> queueMutex(gAudioThreadQueueMutex);
+ gAudioThreadCv.wait(queueMutex, [] { return gStreamsToProcess.size() > 0 || gStreamsToClose.size() > 0 || gAudioThreadTerm; });
+ if (gAudioThreadTerm)
+ return;
+
+ if (!gStreamsToClose.empty()) {
+ auto streamToClose = gStreamsToClose.front();
+ gStreamsToClose.pop();
+ if (streamToClose.first) { // pSoundFile
+ delete streamToClose.first;
+ }
+
+ if (streamToClose.second) { // pBuffer
+ free(streamToClose.second);
+ }
+ }
+
+ if (!gStreamsToProcess.empty()) {
+ stream = gStreamsToProcess.front();
+ gStreamsToProcess.pop();
+ } else
+ continue;
+ }
+
+ std::unique_lock<std::mutex> lock(stream->m_mutex);
+
+ std::pair<ALuint, ALuint> buffers, *lastBufAddr;
+ bool insertBufsAfterCheck = false;
+
+ do {
+ if (!stream->IsOpened()) {
+ break;
+ }
+
+ if (stream->m_bReset)
+ break;
+
+ // We gave up this idea for now
+ /*
+ stream->m_pSoundFile->FileOpen();
+
+ // Deffered allocation, do it now
+ if (stream->m_pBuffer == nil) {
+ stream->m_pBuffer = malloc(stream->m_pSoundFile->GetBufferSize());
+ ASSERT(stream->m_pBuffer != nil);
+ }
+ */
+
+ if (stream->m_bDoSeek) {
+ stream->m_bDoSeek = false;
+ int pos = stream->m_SeekPos;
+ lock.unlock();
+ stream->m_pSoundFile->Seek(pos);
+ lock.lock();
+
+ continue; // let's do the checks again, make sure we didn't miss anything while Seeking
+ }
+
+ if (insertBufsAfterCheck) {
+ stream->m_queueBuffers.push(buffers);
+ insertBufsAfterCheck = false;
+ }
+
+ if (!stream->m_fillBuffers.empty()) {
+ lastBufAddr = &stream->m_fillBuffers.front();
+ buffers = *lastBufAddr;
+ lock.unlock();
+
+ ALuint alBuffers[2] = {buffers.first, buffers.second}; // left - right
+ bool filled = stream->FillBuffer(alBuffers);
+
+ lock.lock();
+
+ // Make sure queue isn't touched after we released mutex
+ if (!stream->m_fillBuffers.empty() && lastBufAddr == &stream->m_fillBuffers.front()) {
+ stream->m_fillBuffers.pop();
+ if (filled)
+ insertBufsAfterCheck = true; // Also make sure stream's properties aren't changed. So make one more pass, and push it to m_queueBuffers only if it pass checks again.
+ }
+ } else
+ break;
+
+ } while (true);
+
+ } while(true);
+}
+#endif
+
void CStream::Initialise()
{
#ifdef AUDIO_OAL_USE_MPG123
mpg123_init();
#endif
+#ifdef MULTITHREADED_AUDIO
+ gAudioThread = std::thread(audioFileOpsThread);
+#endif
}
void CStream::Terminate()
@@ -914,14 +1134,27 @@ void CStream::Terminate()
#ifdef AUDIO_OAL_USE_MPG123
mpg123_exit();
#endif
+#ifdef MULTITHREADED_AUDIO
+ gAudioThreadQueueMutex.lock();
+ gAudioThreadTerm = true;
+ gAudioThreadQueueMutex.unlock();
+
+ gAudioThreadCv.notify_one();
+ gAudioThread.join();
+#endif
}
-CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate) :
+CStream::CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]) :
m_pAlSources(sources),
m_alBuffers(buffers),
m_pBuffer(nil),
m_bPaused(false),
m_bActive(false),
+#ifdef MULTITHREADED_AUDIO
+ m_bIExist(false),
+ m_bDoSeek(false),
+ m_SeekPos(0),
+#endif
m_pSoundFile(nil),
m_bReset(false),
m_nVolume(0),
@@ -930,6 +1163,27 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU
m_nLoopCount(1)
{
+}
+
+bool CStream::Open(const char* filename, uint32 overrideSampleRate)
+{
+ if (IsOpened()) return false;
+
+#ifdef MULTITHREADED_AUDIO
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ m_bDoSeek = false;
+ m_SeekPos = 0;
+#endif
+
+ m_bPaused = false;
+ m_bActive = false;
+ m_bReset = false;
+ m_nVolume = 0;
+ m_nPan = 0;
+ m_nPosBeforeReset = 0;
+ m_nLoopCount = 1;
+
// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/)
#if !defined(_WIN32)
char *real = casepath(filename);
@@ -964,44 +1218,67 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU
else
m_pSoundFile = nil;
- if ( IsOpened() )
+ if ( m_pSoundFile && m_pSoundFile->IsOpened() )
{
- m_pBuffer = malloc(m_pSoundFile->GetBufferSize());
- ASSERT(m_pBuffer!=nil);
-
- DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec());
- DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount());
- DEV("SampleRate: %d\n", m_pSoundFile->GetSampleRate());
- DEV("Channels: %d\n", m_pSoundFile->GetChannels());
- DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples());
- DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate())));
- DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60);
-
- return;
+ uint32 bufSize = m_pSoundFile->GetBufferSize();
+ if(bufSize != 0) { // Otherwise it's deferred
+ m_pBuffer = malloc(bufSize);
+ ASSERT(m_pBuffer != nil);
+
+ DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec());
+ DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount());
+ DEV("SampleRate: %d\n", m_pSoundFile->GetSampleRate());
+ DEV("Channels: %d\n", m_pSoundFile->GetChannels());
+ DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples());
+ DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate())));
+ DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60);
+ }
+#ifdef MULTITHREADED_AUDIO
+ m_bIExist = true;
+#endif
+ return true;
}
+ return false;
}
CStream::~CStream()
{
- Delete();
+ assert(!IsOpened());
}
-void CStream::Delete()
+void CStream::Close()
{
+ if(!IsOpened()) return;
+
+#ifdef MULTITHREADED_AUDIO
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ Stop();
+ ClearBuffers();
+ m_bIExist = false;
+ std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
+ tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, mutex is acquired
+ }
+
+ FlagAsToBeProcessed(true);
+#else
+
Stop();
ClearBuffers();
-
+
if ( m_pSoundFile )
{
delete m_pSoundFile;
m_pSoundFile = nil;
}
-
+
if ( m_pBuffer )
{
free(m_pBuffer);
m_pBuffer = nil;
}
+#endif
}
bool CStream::HasSource()
@@ -1009,9 +1286,14 @@ bool CStream::HasSource()
return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE);
}
+// m_bIExist only written in main thread, thus mutex is not needed on main thread
bool CStream::IsOpened()
{
+#ifdef MULTITHREADED_AUDIO
+ return m_bIExist;
+#else
return m_pSoundFile && m_pSoundFile->IsOpened();
+#endif
}
bool CStream::IsPlaying()
@@ -1025,6 +1307,14 @@ bool CStream::IsPlaying()
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
if (sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING)
return true;
+
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ // Streams are designed in such a way that m_fillBuffers and m_queueBuffers will be *always* filled if audio is playing, and mutex is acquired
+ if (!m_fillBuffers.empty() || !m_queueBuffers.emptyNts())
+ return true;
+#endif
}
return false;
@@ -1099,8 +1389,24 @@ void CStream::SetPan(uint8 nPan)
void CStream::SetPosMS(uint32 nPos)
{
if ( !IsOpened() ) return;
- m_pSoundFile->Seek(nPos);
+
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
+ tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired
+
+ if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
+ m_bDoSeek = true;
+ m_SeekPos = nPos;
+ } else
+#endif
+ {
+ m_pSoundFile->Seek(nPos);
+ }
ClearBuffers();
+
+ // adding to gStreamsToProcess not needed, someone always calls Start() / BuffersShouldBeFilled() after SetPosMS
}
uint32 CStream::GetPosMS()
@@ -1108,10 +1414,16 @@ uint32 CStream::GetPosMS()
if ( !HasSource() ) return 0;
if ( !IsOpened() ) return 0;
+ // Deferred init causes division by zero
+ if (m_pSoundFile->GetChannels() == 0)
+ return 0;
+
ALint offset;
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset);
+ //std::lock_guard<std::mutex> lock(m_mutex);
+
return m_pSoundFile->Tell()
- m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels()
+ m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
@@ -1125,6 +1437,7 @@ uint32 CStream::GetLengthMS()
bool CStream::FillBuffer(ALuint *alBuffer)
{
+#ifndef MULTITHREADED_AUDIO
if ( !HasSource() )
return false;
if ( !IsOpened() )
@@ -1133,7 +1446,8 @@ bool CStream::FillBuffer(ALuint *alBuffer)
return false;
if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) )
return false;
-
+#endif
+
uint32 size = m_pSoundFile->Decode(m_pBuffer);
if( size == 0 )
return false;
@@ -1149,6 +1463,26 @@ bool CStream::FillBuffer(ALuint *alBuffer)
return true;
}
+#ifdef MULTITHREADED_AUDIO
+bool CStream::QueueBuffers()
+{
+ bool buffersQueued = false;
+ std::pair<ALuint, ALuint> buffers;
+ while (m_queueBuffers.peekPop(&buffers)) // beware: m_queueBuffers is tsQueue
+ {
+ ALuint leftBuf = buffers.first;
+ ALuint rightBuf = buffers.second;
+
+ alSourceQueueBuffers(m_pAlSources[0], 1, &leftBuf);
+ alSourceQueueBuffers(m_pAlSources[1], 1, &rightBuf);
+
+ buffersQueued = true;
+ }
+ return buffersQueued;
+}
+#endif
+
+// Only used in single-threaded audio or cutscene audio
int32 CStream::FillBuffers()
{
int32 i = 0;
@@ -1178,17 +1512,33 @@ void CStream::ClearBuffers()
alSourceUnqueueBuffers(m_pAlSources[1], 1, &value);
}
-bool CStream::Setup(bool imSureQueueIsEmpty)
+bool CStream::Setup(bool imSureQueueIsEmpty, bool lock)
{
if ( IsOpened() )
{
- alSourcei(m_pAlSources[0], AL_LOOPING, AL_FALSE);
- alSourcei(m_pAlSources[1], AL_LOOPING, AL_FALSE);
+#ifdef MULTITHREADED_AUDIO
+ if (lock)
+ m_mutex.lock();
+#endif
+
if (!imSureQueueIsEmpty) {
- SetPlay(false);
+ Stop();
ClearBuffers();
}
+#ifdef MULTITHREADED_AUDIO
+ if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) {
+ m_pSoundFile->Seek(0);
+ } else {
+ m_bDoSeek = true;
+ m_SeekPos = 0;
+ }
+
+ if (lock)
+ m_mutex.unlock();
+#else
m_pSoundFile->Seek(0);
+#endif
+
//SetPosition(0.0f, 0.0f, 0.0f);
SetPitch(1.0f);
//SetPan(m_nPan);
@@ -1241,8 +1591,12 @@ void CStream::SetPlay(bool state)
void CStream::Start()
{
if ( !HasSource() ) return;
- if ( FillBuffers() != 0 )
- SetPlay(true);
+
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::mutex> lock(m_mutex);
+ tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired
+#endif
+ BuffersShouldBeFilled();
}
void CStream::Stop()
@@ -1264,9 +1618,23 @@ void CStream::Update()
if ( !m_bPaused )
{
- ALint totalBuffers[2] = { 0, 0 };
- ALint buffersProcessed[2] = { 0, 0 };
+ bool buffersQueuedAndStarted = false;
+ bool buffersQueuedButNotStarted = false;
+#ifdef MULTITHREADED_AUDIO
+ // Put it in here because we need totalBuffers after queueing to decide when to loop audio
+ if (m_bActive)
+ {
+ buffersQueuedAndStarted = QueueBuffers();
+ if(buffersQueuedAndStarted) {
+ SetPlay(true);
+ }
+ }
+#endif
+
+ ALint totalBuffers[2] = {0, 0};
+ ALint buffersProcessed[2] = {0, 0};
+
// Relying a lot on left buffer states in here
do
@@ -1278,44 +1646,66 @@ void CStream::Update()
alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &totalBuffers[1]);
alGetSourcei(m_pAlSources[1], AL_BUFFERS_PROCESSED, &buffersProcessed[1]);
} while (buffersProcessed[0] != buffersProcessed[1]);
-
+
assert(buffersProcessed[0] == buffersProcessed[1]);
// Correcting OpenAL concepts here:
// AL_BUFFERS_QUEUED = Number of *all* buffers in queue, including processed, processing and pending
// AL_BUFFERS_PROCESSED = Index of the buffer being processing right now. Buffers coming after that(have greater index) are pending buffers.
// which means: totalBuffers[0] - buffersProcessed[0] = pending buffers
-
- bool buffersRefilled = false;
-
+
// We should wait queue to be cleared to loop track, because position calculation relies on queue.
if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0)
{
- Setup(true);
- buffersRefilled = FillBuffers() != 0;
- if (m_nLoopCount != 0)
- m_nLoopCount--;
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ if (m_fillBuffers.empty() && m_queueBuffers.emptyNts()) // we already acquired stream mutex, which is enough for second thread. thus Nts variant
+#endif
+ {
+ Setup(true, false);
+ BuffersShouldBeFilled(); // will also call SetPlay(true)
+ if (m_nLoopCount != 0)
+ m_nLoopCount--;
+ }
}
else
{
- while( buffersProcessed[0]-- )
+ static std::queue<std::pair<ALuint, ALuint>> tempFillBuffer;
+
+ while ( buffersProcessed[0]-- )
{
ALuint buffer[2];
alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]);
alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]);
-
- if (m_bActive && FillBuffer(buffer))
+
+ if (m_bActive)
{
- buffersRefilled = true;
- alSourceQueueBuffers(m_pAlSources[0], 1, &buffer[0]);
- alSourceQueueBuffers(m_pAlSources[1], 1, &buffer[1]);
+ tempFillBuffer.push(std::pair<ALuint, ALuint>(buffer[0], buffer[1]));
}
}
+
+ if (m_bActive && buffersProcessed[1])
+ {
+#ifdef MULTITHREADED_AUDIO
+ m_mutex.lock();
+#endif
+ while (!tempFillBuffer.empty()) {
+ auto elem = tempFillBuffer.front();
+ tempFillBuffer.pop();
+ buffersQueuedButNotStarted = BufferShouldBeFilledAndQueued(&elem);
+ }
+#ifdef MULTITHREADED_AUDIO
+ m_mutex.unlock();
+ FlagAsToBeProcessed();
+#endif
+
+ }
}
- // Two reasons: 1-Source may be starved to audio and stopped itself, 2- We're already waiting it to starve and die for looping track!
- if (m_bActive && (buffersRefilled || (totalBuffers[1] - buffersProcessed[1] != 0)))
+ // Source may be starved to audio and stopped itself
+ if (m_bActive && !buffersQueuedAndStarted && (buffersQueuedButNotStarted || (totalBuffers[1] - buffersProcessed[1] != 0)))
SetPlay(true);
}
}
@@ -1324,28 +1714,45 @@ void CStream::ProviderInit()
{
if ( m_bReset )
{
- if ( Setup(true) )
+ if ( Setup(true, false) ) // lock not needed, thread can't process streams with m_bReset set
{
SetPan(m_nPan);
SetVolume(m_nVolume);
SetLoopCount(m_nLoopCount);
SetPosMS(m_nPosBeforeReset);
- if (m_bActive)
- FillBuffers();
- SetPlay(m_bActive);
- if ( m_bPaused )
+#ifdef MULTITHREADED_AUDIO
+ std::unique_lock<std::mutex> lock(m_mutex);
+#endif
+ if(m_bActive)
+ BuffersShouldBeFilled();
+
+ if (m_bPaused)
Pause();
+
+ m_bReset = false;
+
+ } else {
+#ifdef MULTITHREADED_AUDIO
+ std::unique_lock<std::mutex> lock(m_mutex);
+#endif
+ m_bReset = false;
}
-
- m_bReset = false;
}
}
void CStream::ProviderTerm()
{
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ // unlike Close() we will reuse this stream, so clearing queues are important.
+ std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
+ tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // stream mutex is already acquired, thus Nts variant
+#endif
m_bReset = true;
m_nPosBeforeReset = GetPosMS();
-
+
+ Stop();
ClearBuffers();
}
diff --git a/src/audio/oal/stream.h b/src/audio/oal/stream.h
index 9a2a2fbe..f0456925 100644
--- a/src/audio/oal/stream.h
+++ b/src/audio/oal/stream.h
@@ -11,6 +11,7 @@ public:
virtual ~IDecoder() { }
virtual bool IsOpened() = 0;
+ virtual void FileOpen() = 0;
virtual uint32 GetSampleSize() = 0;
virtual uint32 GetSampleCount() = 0;
@@ -48,12 +49,70 @@ public:
uint32 GetLength()
{
+ FileOpen(); // abort deferred init, we need length now - game has to cache audio file sizes
return float(GetSampleCount()) * 1000.0f / float(GetSampleRate());
}
virtual uint32 Decode(void *buffer) = 0;
};
+#ifdef MULTITHREADED_AUDIO
+template <typename T> class tsQueue
+{
+public:
+ tsQueue() : count(0) { }
+
+ void push(const T &value)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_queue.push(value);
+ count++;
+ }
+
+ bool peekPop(T *retVal)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (count == 0)
+ return false;
+
+ *retVal = m_queue.front();
+ m_queue.pop();
+ count--;
+ return true;
+ }
+
+ void swapNts(tsQueue<T> &replaceWith)
+ {
+ m_queue.swap(replaceWith.m_queue);
+ replaceWith.count = count;
+ }
+
+ /*
+ void swapTs(tsQueue<T> &replaceWith)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ std::lock_guard<std::mutex> lock2(replaceWith.m_mutex);
+ swapNts(replaceWith);
+ }
+ */
+
+ bool emptyNts()
+ {
+ return count == 0;
+ }
+ /*
+ bool emptyTs()
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ return emptyNts();
+ }
+ */
+
+ std::queue<T> m_queue;
+ int count;
+ mutable std::mutex m_mutex;
+};
+#endif
class CStream
{
char m_aFilename[128];
@@ -63,6 +122,17 @@ class CStream
bool m_bPaused;
bool m_bActive;
+public:
+#ifdef MULTITHREADED_AUDIO
+ std::mutex m_mutex;
+ std::queue<std::pair<ALuint, ALuint>> m_fillBuffers; // left and right buffer
+ tsQueue<std::pair<ALuint, ALuint>> m_queueBuffers;
+// std::condition_variable m_closeCv;
+ bool m_bDoSeek;
+ uint32 m_SeekPos;
+ bool m_bIExist;
+#endif
+
void *m_pBuffer;
bool m_bReset;
@@ -72,7 +142,14 @@ class CStream
int32 m_nLoopCount;
IDecoder *m_pSoundFile;
-
+
+ void BuffersShouldBeFilled(); // all
+ bool BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>*); // two (left-right)
+#ifdef MULTITHREADED_AUDIO
+ void FlagAsToBeProcessed(bool close = false);
+ bool QueueBuffers();
+#endif
+
bool HasSource();
void SetPosition(int i, float x, float y, float z);
void SetPitch(float pitch);
@@ -81,15 +158,17 @@ class CStream
void SetPlay(bool state);
bool FillBuffer(ALuint *alBuffer);
- int32 FillBuffers();
+ int32 FillBuffers();
void ClearBuffers();
-public:
+//public:
static void Initialise();
static void Terminate();
- CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate = 32000);
+ CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]);
~CStream();
void Delete();
+ bool Open(const char *filename, uint32 overrideSampleRate = 32000);
+ void Close();
bool IsOpened();
bool IsPlaying();
@@ -100,12 +179,11 @@ public:
uint32 GetPosMS();
uint32 GetLengthMS();
- bool Setup(bool imSureQueueIsEmpty = false);
+ bool Setup(bool imSureQueueIsEmpty = false, bool lock = true);
void Start();
void Stop();
void Update(void);
void SetLoopCount(int32);
-
void ProviderInit();
void ProviderTerm();
diff --git a/src/audio/sampman.h b/src/audio/sampman.h
index d1ad9a26..dc95622b 100644
--- a/src/audio/sampman.h
+++ b/src/audio/sampman.h
@@ -259,8 +259,8 @@ static char StreamedNameTable[][25] = {
"AUDIO\\door_2.OPUS", "AUDIO\\door_3.OPUS", "AUDIO\\door_4.OPUS", "AUDIO\\door_5.OPUS", "AUDIO\\door_6.OPUS", "AUDIO\\t3_a.OPUS",
"AUDIO\\t3_b.OPUS", "AUDIO\\t3_c.OPUS", "AUDIO\\k1_b.OPUS", "AUDIO\\cat1.OPUS"};
#else
-#if defined(PS2_AUDIO_PATHS)
-static char StreamedNameTable[][25]=
+#ifdef PS2_AUDIO_PATHS
+static char PS2StreamedNameTable[][25]=
{
"AUDIO\\MUSIC\\HEAD.VB",
"AUDIO\\MUSIC\\CLASS.VB",
@@ -357,7 +357,110 @@ static char StreamedNameTable[][25]=
"AUDIO\\PHONE\\MT_PH4.VB",
"AUDIO\\MUSIC\\MISCOM.VB",
"AUDIO\\MUSIC\\END.VB",
-#else
+ "AUDIO\\lib_a1.WAV",
+ "AUDIO\\lib_a2.WAV",
+ "AUDIO\\lib_a.WAV",
+ "AUDIO\\lib_b.WAV",
+ "AUDIO\\lib_c.WAV",
+ "AUDIO\\lib_d.WAV",
+ "AUDIO\\l2_a.WAV",
+ "AUDIO\\j4t_1.WAV",
+ "AUDIO\\j4t_2.WAV",
+ "AUDIO\\j4t_3.WAV",
+ "AUDIO\\j4t_4.WAV",
+ "AUDIO\\j4_a.WAV",
+ "AUDIO\\j4_b.WAV",
+ "AUDIO\\j4_c.WAV",
+ "AUDIO\\j4_d.WAV",
+ "AUDIO\\j4_e.WAV",
+ "AUDIO\\j4_f.WAV",
+ "AUDIO\\j6_1.WAV",
+ "AUDIO\\j6_a.WAV",
+ "AUDIO\\j6_b.WAV",
+ "AUDIO\\j6_c.WAV",
+ "AUDIO\\j6_d.WAV",
+ "AUDIO\\t4_a.WAV",
+ "AUDIO\\s1_a.WAV",
+ "AUDIO\\s1_a1.WAV",
+ "AUDIO\\s1_b.WAV",
+ "AUDIO\\s1_c.WAV",
+ "AUDIO\\s1_c1.WAV",
+ "AUDIO\\s1_d.WAV",
+ "AUDIO\\s1_e.WAV",
+ "AUDIO\\s1_f.WAV",
+ "AUDIO\\s1_g.WAV",
+ "AUDIO\\s1_h.WAV",
+ "AUDIO\\s1_i.WAV",
+ "AUDIO\\s1_j.WAV",
+ "AUDIO\\s1_k.WAV",
+ "AUDIO\\s1_l.WAV",
+ "AUDIO\\s3_a.WAV",
+ "AUDIO\\s3_b.WAV",
+ "AUDIO\\el3_a.WAV",
+ "AUDIO\\mf1_a.WAV",
+ "AUDIO\\mf2_a.WAV",
+ "AUDIO\\mf3_a.WAV",
+ "AUDIO\\mf3_b.WAV",
+ "AUDIO\\mf3_b1.WAV",
+ "AUDIO\\mf3_c.WAV",
+ "AUDIO\\mf4_a.WAV",
+ "AUDIO\\mf4_b.WAV",
+ "AUDIO\\mf4_c.WAV",
+ "AUDIO\\a1_a.WAV",
+ "AUDIO\\a3_a.WAV",
+ "AUDIO\\a5_a.WAV",
+ "AUDIO\\a4_a.WAV",
+ "AUDIO\\a4_b.WAV",
+ "AUDIO\\a4_c.WAV",
+ "AUDIO\\a4_d.WAV",
+ "AUDIO\\k1_a.WAV",
+ "AUDIO\\k3_a.WAV",
+ "AUDIO\\r1_a.WAV",
+ "AUDIO\\r2_a.WAV",
+ "AUDIO\\r2_b.WAV",
+ "AUDIO\\r2_c.WAV",
+ "AUDIO\\r2_d.WAV",
+ "AUDIO\\r2_e.WAV",
+ "AUDIO\\r2_f.WAV",
+ "AUDIO\\r2_g.WAV",
+ "AUDIO\\r2_h.WAV",
+ "AUDIO\\r5_a.WAV",
+ "AUDIO\\r6_a.WAV",
+ "AUDIO\\r6_a1.WAV",
+ "AUDIO\\r6_b.WAV",
+ "AUDIO\\lo2_a.WAV",
+ "AUDIO\\lo6_a.WAV",
+ "AUDIO\\yd2_a.WAV",
+ "AUDIO\\yd2_b.WAV",
+ "AUDIO\\yd2_c.WAV",
+ "AUDIO\\yd2_c1.WAV",
+ "AUDIO\\yd2_d.WAV",
+ "AUDIO\\yd2_e.WAV",
+ "AUDIO\\yd2_f.WAV",
+ "AUDIO\\yd2_g.WAV",
+ "AUDIO\\yd2_h.WAV",
+ "AUDIO\\yd2_ass.WAV",
+ "AUDIO\\yd2_ok.WAV",
+ "AUDIO\\h5_a.WAV",
+ "AUDIO\\h5_b.WAV",
+ "AUDIO\\h5_c.WAV",
+ "AUDIO\\ammu_a.WAV",
+ "AUDIO\\ammu_b.WAV",
+ "AUDIO\\ammu_c.WAV",
+ "AUDIO\\door_1.WAV",
+ "AUDIO\\door_2.WAV",
+ "AUDIO\\door_3.WAV",
+ "AUDIO\\door_4.WAV",
+ "AUDIO\\door_5.WAV",
+ "AUDIO\\door_6.WAV",
+ "AUDIO\\t3_a.WAV",
+ "AUDIO\\t3_b.WAV",
+ "AUDIO\\t3_c.WAV",
+ "AUDIO\\k1_b.WAV",
+ "AUDIO\\cat1.WAV"
+};
+#endif
+
static char StreamedNameTable[][25] =
{
"AUDIO\\HEAD.WAV",
@@ -455,7 +558,6 @@ static char StreamedNameTable[][25] =
"AUDIO\\MT_PH4.MP3",
"AUDIO\\MISCOM.WAV",
"AUDIO\\END.MP3",
-#endif
"AUDIO\\lib_a1.WAV",
"AUDIO\\lib_a2.WAV",
"AUDIO\\lib_a.WAV",
diff --git a/src/audio/sampman_miles.cpp b/src/audio/sampman_miles.cpp
index e820864c..d529513d 100644
--- a/src/audio/sampman_miles.cpp
+++ b/src/audio/sampman_miles.cpp
@@ -992,11 +992,20 @@ cSampleManager::Initialise(void)
if ( GetDriveType(m_szCDRomRootPath) == DRIVE_CDROM )
{
+ FILE *f;
+#ifdef PS2_AUDIO_PATHS
strcpy(filepath, m_szCDRomRootPath);
- strcat(filepath, StreamedNameTable[0]);
-
- FILE *f = fopen(filepath, "rb");
+ strcat(filepath, PS2StreamedNameTable[0]);
+ f = fopen(filepath, "rb");
+
+ if ( !f )
+#endif
+ {
+ strcpy(filepath, m_szCDRomRootPath);
+ strcat(filepath, StreamedNameTable[0]);
+ f = fopen(filepath, "rb");
+ }
if ( f )
{
fclose(f);
@@ -1005,11 +1014,20 @@ cSampleManager::Initialise(void)
for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
{
+#ifdef PS2_AUDIO_PATHS
strcpy(filepath, m_szCDRomRootPath);
- strcat(filepath, StreamedNameTable[i]);
-
+ strcat(filepath, PS2StreamedNameTable[i]);
+
mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
-
+ if ( !mp3Stream[0] )
+#endif
+ {
+ strcpy(filepath, m_szCDRomRootPath);
+ strcat(filepath, StreamedNameTable[i]);
+
+ mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
+ }
+
if ( mp3Stream[0] )
{
AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL);
@@ -1078,7 +1096,14 @@ cSampleManager::Initialise(void)
strcpy(_aHDDPath, m_szCDRomRootPath);
rootpath[0] = '\0';
- FILE *f = fopen(StreamedNameTable[0], "rb");
+ FILE *f;
+
+#ifdef PS2_AUDIO_PATHS
+ f = fopen(PS2StreamedNameTable[0], "rb");
+ if (!f)
+#endif
+
+ f = fopen(StreamedNameTable[0], "rb");
if ( f )
{
@@ -1086,11 +1111,20 @@ cSampleManager::Initialise(void)
for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
{
+#ifdef PS2_AUDIO_PATHS
strcpy(filepath, rootpath);
- strcat(filepath, StreamedNameTable[i]);
-
+ strcat(filepath, PS2StreamedNameTable[i]);
+
mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
-
+ if ( !mp3Stream[0] )
+#endif
+ {
+ strcpy(filepath, rootpath);
+ strcat(filepath, StreamedNameTable[i]);
+
+ mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
+ }
+
if ( mp3Stream[0] )
{
AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL);
@@ -1299,9 +1333,11 @@ cSampleManager::CheckForAnAudioFileOnCD(void)
{
#if GTA_VERSION < GTA3_PC_STEAM && !defined(NO_CDCHECK)
char filepath[MAX_PATH];
+ FILE *f;
+#ifdef PS2_AUDIO_PATHS
#if GTA_VERSION >= GTA3_PC_11
- if (_bUseHDDAudio)
+ if(_bUseHDDAudio)
strcpy(filepath, _aHDDPath);
else
strcpy(filepath, m_szCDRomRootPath);
@@ -1309,10 +1345,25 @@ cSampleManager::CheckForAnAudioFileOnCD(void)
strcpy(filepath, m_szCDRomRootPath);
#endif // #if GTA_VERSION >= GTA3_PC_11
- strcat(filepath, StreamedNameTable[AudioManager.GetRandomNumber(1) % TOTAL_STREAMED_SOUNDS]);
-
- FILE *f = fopen(filepath, "rb");
+ strcat(filepath, PS2StreamedNameTable[AudioManager.GetRandomNumber(1) % TOTAL_STREAMED_SOUNDS]);
+
+ f = fopen(filepath, "rb");
+ if ( !f )
+#endif // PS2_AUDIO_PATHS
+ {
+#if GTA_VERSION >= GTA3_PC_11
+ if (_bUseHDDAudio)
+ strcpy(filepath, _aHDDPath);
+ else
+ strcpy(filepath, m_szCDRomRootPath);
+#else
+ strcpy(filepath, m_szCDRomRootPath);
+#endif // #if GTA_VERSION >= GTA3_PC_11
+
+ strcat(filepath, StreamedNameTable[AudioManager.GetRandomNumber(1) % TOTAL_STREAMED_SOUNDS]);
+ f = fopen(filepath, "rb");
+ }
if ( f )
{
fclose(f);
@@ -2007,11 +2058,19 @@ cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
}
char filepath[MAX_PATH];
-
+#ifdef PS2_AUDIO_PATHS
strcpy(filepath, m_szCDRomRootPath);
- strcat(filepath, StreamedNameTable[nFile]);
-
+ strcat(filepath, PS2StreamedNameTable[nFile]);
+
mp3Stream[nStream] = AIL_open_stream(DIG, filepath, 0);
+ if ( !mp3Stream[nStream] )
+#endif
+ {
+ strcpy(filepath, m_szCDRomRootPath);
+ strcat(filepath, StreamedNameTable[nFile]);
+
+ mp3Stream[nStream] = AIL_open_stream(DIG, filepath, 0);
+ }
if ( mp3Stream[nStream] )
{
@@ -2073,10 +2132,19 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
// Try to continue from previous song, if already started
if(!_GetMP3PosFromStreamPos(&position, &e) && !e) {
nFile = 0;
+#ifdef PS2_AUDIO_PATHS
strcpy(filename, m_szCDRomRootPath);
- strcat(filename, StreamedNameTable[nFile]);
-
+ strcat(filename, PS2StreamedNameTable[nFile]);
+
mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
+ if ( !mp3Stream[nStream] )
+#endif
+ {
+ strcpy(filename, m_szCDRomRootPath);
+ strcat(filename, StreamedNameTable[nFile]);
+
+ mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
+ }
if ( mp3Stream[nStream] )
{
AIL_set_stream_loop_count(mp3Stream[nStream], 1);
@@ -2120,10 +2188,19 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
nFile = 0;
_bIsMp3Active = 0;
+#ifdef PS2_AUDIO_PATHS
strcpy(filename, m_szCDRomRootPath);
- strcat(filename, StreamedNameTable[nFile]);
-
+ strcat(filename, PS2StreamedNameTable[nFile]);
+
mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
+ if ( !mp3Stream[nStream] )
+#endif
+ {
+ strcpy(filename, m_szCDRomRootPath);
+ strcat(filename, StreamedNameTable[nFile]);
+
+ mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
+ }
if ( mp3Stream[nStream] )
{
AIL_set_stream_loop_count(mp3Stream[nStream], 1);
@@ -2161,10 +2238,19 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
position = 0;
nFile = 0;
}
+#ifdef PS2_AUDIO_PATHS
strcpy(filename, m_szCDRomRootPath);
- strcat(filename, StreamedNameTable[nFile]);
-
+ strcat(filename, PS2StreamedNameTable[nFile]);
+
mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
+ if ( !mp3Stream[nStream] )
+#endif
+ {
+ strcpy(filename, m_szCDRomRootPath);
+ strcat(filename, StreamedNameTable[nFile]);
+
+ mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
+ }
if ( mp3Stream[nStream] )
{
AIL_set_stream_loop_count(mp3Stream[nStream], 1);
diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp
index 7fb84965..2d9f9e86 100644
--- a/src/audio/sampman_oal.cpp
+++ b/src/audio/sampman_oal.cpp
@@ -34,6 +34,13 @@
#include "oal/oal_utils.h"
#include "oal/aldlist.h"
#include "oal/channel.h"
+
+#include <utility>
+#ifdef MULTITHREADED_AUDIO
+#include <mutex>
+#include <queue>
+#include <condition_variable>
+#endif
#include "oal/stream.h"
#include "AudioManager.h"
@@ -515,14 +522,11 @@ _FindMP3s(void)
continue;
}
}
- aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]);
-
- if (aStream[0] && aStream[0]->IsOpened())
+ if (aStream[0] && aStream[0]->Open(filepath))
{
total_ms = aStream[0]->GetLengthMS();
- delete aStream[0];
- aStream[0] = NULL;
-
+ aStream[0]->Close();
+
OutputDebugString(fd.cFileName);
_pMP3List = new tMP3Entry;
@@ -573,14 +577,13 @@ _FindMP3s(void)
else
bShortcut = FALSE;
- aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]);
-
- if (aStream[0] && aStream[0]->IsOpened())
+ if (aStream[0] && aStream[0]->Open(filepath))
{
total_ms = aStream[0]->GetLengthMS();
- delete aStream[0];
- aStream[0] = NULL;
+ aStream[0]->Close();
+ OutputDebugString(fd.cFileName);
+
pList->pNext = new tMP3Entry;
tMP3Entry *e = pList->pNext;
@@ -732,6 +735,10 @@ cSampleManager::Initialise(void)
return TRUE;
EFXInit();
+
+ for(int i = 0; i < MAX_STREAMS; i++)
+ aStream[i] = new CStream(ALStreamSources[i], ALStreamBuffers[i]);
+
CStream::Initialise();
{
@@ -885,14 +892,16 @@ cSampleManager::Initialise(void)
debug("Cannot load audio cache\n");
#endif
- for(int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++) {
- aStream[0] = new CStream(StreamedNameTable[i], ALStreamSources[0], ALStreamBuffers[0], IsThisTrackAt16KHz(i) ? 16000 : 32000);
-
- if(aStream[0] && aStream[0]->IsOpened()) {
+ for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
+ {
+ if(aStream[0] && (
+#ifdef PS2_AUDIO_PATHS
+ aStream[0]->Open(PS2StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000) ||
+#endif
+ aStream[0]->Open(StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000)))
+ {
uint32 tatalms = aStream[0]->GetLengthMS();
- delete aStream[0];
- aStream[0] = NULL;
-
+ aStream[0]->Close();
nStreamLength[i] = tatalms;
} else
USERERROR("Can't open '%s'\n", StreamedNameTable[i]);
@@ -934,12 +943,13 @@ cSampleManager::Initialise(void)
{
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
- aStream[i] = NULL;
+ aStream[i]->Close();
+
nStreamVolume[i] = 100;
nStreamPan[i] = 63;
}
}
-
+
{
_bSampmanInitialised = TRUE;
@@ -1021,15 +1031,8 @@ void
cSampleManager::Terminate(void)
{
for (int32 i = 0; i < MAX_STREAMS; i++)
- {
- CStream *stream = aStream[i];
- if (stream)
- {
- delete stream;
- aStream[i] = NULL;
- }
- }
-
+ aStream[i]->Close();
+
for ( int32 i = 0; i < NUM_CHANNELS; i++ )
aChannel[i].Term();
@@ -1079,6 +1082,9 @@ cSampleManager::Terminate(void)
CStream::Terminate();
+ for(int32 i = 0; i < MAX_STREAMS; i++)
+ delete aStream[i];
+
if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 )
{
free((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]);
@@ -1599,28 +1605,21 @@ cSampleManager::StopChannel(uint32 nChannel)
void
cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
{
- char filename[MAX_PATH];
-
ASSERT( nStream < MAX_STREAMS );
if ( nFile < TOTAL_STREAMED_SOUNDS )
{
- if ( aStream[nStream] )
- {
- delete aStream[nStream];
- aStream[nStream] = NULL;
- }
-
- strcpy(filename, StreamedNameTable[nFile]);
-
- CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
- ASSERT(stream != NULL);
-
- aStream[nStream] = stream;
+ CStream *stream = aStream[nStream];
+
+ stream->Close();
+
+#ifdef PS2_AUDIO_PATHS
+ if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
+#endif
+ stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
if ( !stream->Setup() )
{
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
}
}
@@ -1632,7 +1631,7 @@ cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
stream->SetPause(nPauseFlag != FALSE);
}
@@ -1645,12 +1644,9 @@ cSampleManager::StartPreloadedStreamedFile(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
- if ( stream->IsOpened() )
- {
- stream->Start();
- }
+ stream->Start();
}
}
@@ -1664,11 +1660,8 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
if ( nFile >= TOTAL_STREAMED_SOUNDS )
return FALSE;
- if ( aStream[nStream] )
- {
- delete aStream[nStream];
- aStream[nStream] = NULL;
- }
+ aStream[nStream]->Close();
+
if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER )
{
do
@@ -1683,13 +1676,12 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
// Try to continue from previous song, if already started
if(!_GetMP3PosFromStreamPos(&position, &e) && !e) {
nFile = 0;
- strcpy(filename, StreamedNameTable[nFile]);
-
- CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
-
- aStream[nStream] = stream;
-
- if (stream->Setup()) {
+ CStream *stream = aStream[nStream];
+#ifdef PS2_AUDIO_PATHS
+ if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
+#endif
+ stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
+ if ( stream->Setup() ) {
if (position != 0)
stream->SetPosMS(position);
@@ -1697,19 +1689,18 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
return TRUE;
} else {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
return FALSE;
} else {
- if ( e->pLinkPath != NULL )
- aStream[nStream] = new CStream(e->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
+ if (e->pLinkPath != NULL)
+ aStream[nStream]->Open(e->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
else {
strcpy(filename, _mp3DirectoryPath);
strcat(filename, e->aFilename);
-
- aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+
+ aStream[nStream]->Open(filename);
}
if (aStream[nStream]->Setup()) {
@@ -1721,8 +1712,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
_bIsMp3Active = TRUE;
return TRUE;
} else {
- delete aStream[nStream];
- aStream[nStream] = NULL;
+ aStream[nStream]->Close();
}
// fall through, start playing from another song
}
@@ -1739,11 +1729,11 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
nFile = 0;
_bIsMp3Active = 0;
- strcpy(filename, StreamedNameTable[nFile]);
-
- CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
-
- aStream[nStream] = stream;
+ CStream *stream = aStream[nStream];
+#ifdef PS2_AUDIO_PATHS
+ if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
+#endif
+ stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
if (stream->Setup()) {
if (position != 0)
@@ -1753,19 +1743,17 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
return TRUE;
} else {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
return FALSE;
}
}
- if(mp3->pLinkPath != NULL)
- aStream[nStream] = new CStream(mp3->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
+ if (mp3->pLinkPath != NULL)
+ aStream[nStream]->Open(mp3->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
else {
strcpy(filename, _mp3DirectoryPath);
strcat(filename, mp3->aFilename);
-
- aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
}
if (aStream[nStream]->Setup()) {
@@ -1775,8 +1763,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
#endif
return TRUE;
} else {
- delete aStream[nStream];
- aStream[nStream] = NULL;
+ aStream[nStream]->Close();
}
}
@@ -1786,11 +1773,11 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
position = 0;
nFile = 0;
}
- strcpy(filename, StreamedNameTable[nFile]);
-
- CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
-
- aStream[nStream] = stream;
+ CStream *stream = aStream[nStream];
+#ifdef PS2_AUDIO_PATHS
+ if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
+#endif
+ stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
if ( stream->Setup() ) {
if (position != 0)
@@ -1800,8 +1787,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
return TRUE;
} else {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
return FALSE;
}
@@ -1813,14 +1799,10 @@ cSampleManager::StopStreamedFile(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
- {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
- if ( nStream == 0 )
- _bIsMp3Active = FALSE;
- }
+ if ( nStream == 0 )
+ _bIsMp3Active = FALSE;
}
int32
@@ -1830,7 +1812,7 @@ cSampleManager::GetStreamedFilePosition(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
if ( _bIsMp3Active )
{
@@ -1868,7 +1850,7 @@ cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffect
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
if ( nEffectFlag )
stream->SetVolume(m_nEffectsFadeVolume*nVolume*m_nEffectsVolume >> 14);
@@ -1894,7 +1876,7 @@ cSampleManager::IsStreamPlaying(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
if ( stream->IsPlaying() )
return TRUE;
@@ -1910,7 +1892,7 @@ cSampleManager::Service(void)
{
CStream *stream = aStream[i];
- if ( stream )
+ if ( stream->IsOpened() )
stream->Update();
}
int refCount = CChannel::channelsThatNeedService;
diff --git a/src/control/AutoPilot.cpp b/src/control/AutoPilot.cpp
index 22a73179..5af4071a 100644
--- a/src/control/AutoPilot.cpp
+++ b/src/control/AutoPilot.cpp
@@ -50,41 +50,41 @@ void CAutoPilot::RemoveOnePathNode()
#ifdef COMPATIBLE_SAVES
void CAutoPilot::Save(uint8*& buf)
{
- WriteSaveBuf<int32>(buf, m_nCurrentRouteNode);
- WriteSaveBuf<int32>(buf, m_nNextRouteNode);
- WriteSaveBuf<int32>(buf, m_nPrevRouteNode);
- WriteSaveBuf<int32>(buf, m_nTimeEnteredCurve);
- WriteSaveBuf<int32>(buf, m_nTimeToSpendOnCurrentCurve);
- WriteSaveBuf<uint32>(buf, m_nCurrentPathNodeInfo);
- WriteSaveBuf<uint32>(buf, m_nNextPathNodeInfo);
- WriteSaveBuf<uint32>(buf, m_nPreviousPathNodeInfo);
- WriteSaveBuf<uint32>(buf, m_nAntiReverseTimer);
- WriteSaveBuf<uint32>(buf, m_nTimeToStartMission);
- WriteSaveBuf<int8>(buf, m_nPreviousDirection);
- WriteSaveBuf<int8>(buf, m_nCurrentDirection);
- WriteSaveBuf<int8>(buf, m_nNextDirection);
- WriteSaveBuf<int8>(buf, m_nCurrentLane);
- WriteSaveBuf<int8>(buf, m_nNextLane);
- WriteSaveBuf<uint8>(buf, m_nDrivingStyle);
- WriteSaveBuf<uint8>(buf, m_nCarMission);
- WriteSaveBuf<uint8>(buf, m_nTempAction);
- WriteSaveBuf<uint32>(buf, m_nTimeTempAction);
- WriteSaveBuf<float>(buf, m_fMaxTrafficSpeed);
- WriteSaveBuf<uint8>(buf, m_nCruiseSpeed);
+ WriteSaveBuf(buf, m_nCurrentRouteNode);
+ WriteSaveBuf(buf, m_nNextRouteNode);
+ WriteSaveBuf(buf, m_nPrevRouteNode);
+ WriteSaveBuf(buf, m_nTimeEnteredCurve);
+ WriteSaveBuf(buf, m_nTimeToSpendOnCurrentCurve);
+ WriteSaveBuf(buf, m_nCurrentPathNodeInfo);
+ WriteSaveBuf(buf, m_nNextPathNodeInfo);
+ WriteSaveBuf(buf, m_nPreviousPathNodeInfo);
+ WriteSaveBuf(buf, m_nAntiReverseTimer);
+ WriteSaveBuf(buf, m_nTimeToStartMission);
+ WriteSaveBuf(buf, m_nPreviousDirection);
+ WriteSaveBuf(buf, m_nCurrentDirection);
+ WriteSaveBuf(buf, m_nNextDirection);
+ WriteSaveBuf(buf, m_nCurrentLane);
+ WriteSaveBuf(buf, m_nNextLane);
+ WriteSaveBuf(buf, m_nDrivingStyle);
+ WriteSaveBuf(buf, m_nCarMission);
+ WriteSaveBuf(buf, m_nTempAction);
+ WriteSaveBuf(buf, m_nTimeTempAction);
+ WriteSaveBuf(buf, m_fMaxTrafficSpeed);
+ WriteSaveBuf(buf, m_nCruiseSpeed);
uint8 flags = 0;
if (m_bSlowedDownBecauseOfCars) flags |= BIT(0);
if (m_bSlowedDownBecauseOfPeds) flags |= BIT(1);
if (m_bStayInCurrentLevel) flags |= BIT(2);
if (m_bStayInFastLane) flags |= BIT(3);
if (m_bIgnorePathfinding) flags |= BIT(4);
- WriteSaveBuf<uint8>(buf, flags);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<float>(buf, m_vecDestinationCoors.x);
- WriteSaveBuf<float>(buf, m_vecDestinationCoors.y);
- WriteSaveBuf<float>(buf, m_vecDestinationCoors.z);
- SkipSaveBuf(buf, 32);
- WriteSaveBuf<int16>(buf, m_nPathFindNodesCount);
- SkipSaveBuf(buf, 6);
+ WriteSaveBuf(buf, flags);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_vecDestinationCoors.x);
+ WriteSaveBuf(buf, m_vecDestinationCoors.y);
+ WriteSaveBuf(buf, m_vecDestinationCoors.z);
+ ZeroSaveBuf(buf, 32);
+ WriteSaveBuf(buf, m_nPathFindNodesCount);
+ ZeroSaveBuf(buf, 6);
}
void CAutoPilot::Load(uint8*& buf)
diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp
index 0516e214..35580053 100644
--- a/src/control/CarCtrl.cpp
+++ b/src/control/CarCtrl.cpp
@@ -77,7 +77,7 @@ int32 CCarCtrl::NumRandomCars;
int32 CCarCtrl::NumParkedCars;
int32 CCarCtrl::NumPermanentCars;
int8 CCarCtrl::CountDownToCarsAtStart;
-int32 CCarCtrl::MaxNumberOfCarsInUse = 12;
+int32 CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS;
uint32 CCarCtrl::LastTimeLawEnforcerCreated;
uint32 CCarCtrl::LastTimeFireTruckCreated;
uint32 CCarCtrl::LastTimeAmbulanceCreated;
diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp
index 3410c881..91971ae7 100644
--- a/src/control/Garages.cpp
+++ b/src/control/Garages.cpp
@@ -26,13 +26,6 @@
#include "World.h"
#include "SaveBuf.h"
-#define CRUSHER_GARAGE_X1 (1135.5f)
-#define CRUSHER_GARAGE_Y1 (57.0f)
-#define CRUSHER_GARAGE_Z1 (-1.0f)
-#define CRUSHER_GARAGE_X2 (1149.5f)
-#define CRUSHER_GARAGE_Y2 (63.7f)
-#define CRUSHER_GARAGE_Z2 (3.5f)
-
#define ROTATED_DOOR_OPEN_SPEED (0.015f)
#define ROTATED_DOOR_CLOSE_SPEED (0.02f)
#define DEFAULT_DOOR_OPEN_SPEED (0.035f)
@@ -1883,11 +1876,12 @@ void CStoredCar::StoreCar(CVehicle* pVehicle)
m_nRadioStation = pVehicle->m_nRadioStation;
m_nVariationA = pVehicle->m_aExtras[0];
m_nVariationB = pVehicle->m_aExtras[1];
- m_bBulletproof = pVehicle->bBulletProof;
- m_bFireproof = pVehicle->bFireProof;
- m_bExplosionproof = pVehicle->bExplosionProof;
- m_bCollisionproof = pVehicle->bCollisionProof;
- m_bMeleeproof = pVehicle->bMeleeProof;
+ m_nFlags = 0;
+ if (pVehicle->bBulletProof) m_nFlags |= FLAG_BULLETPROOF;
+ if (pVehicle->bFireProof) m_nFlags |= FLAG_FIREPROOF;
+ if (pVehicle->bExplosionProof) m_nFlags |= FLAG_EXPLOSIONPROOF;
+ if (pVehicle->bCollisionProof) m_nFlags |= FLAG_COLLISIONPROOF;
+ if (pVehicle->bMeleeProof) m_nFlags |= FLAG_MELEEPROOF;
if (pVehicle->IsCar())
m_nCarBombType = ((CAutomobile*)pVehicle)->m_bombType;
}
@@ -1936,11 +1930,11 @@ CVehicle* CStoredCar::RestoreCar()
}
pVehicle->bHasBeenOwnedByPlayer = true;
pVehicle->m_nDoorLock = CARLOCK_UNLOCKED;
- pVehicle->bBulletProof = m_bBulletproof;
- pVehicle->bFireProof = m_bFireproof;
- pVehicle->bExplosionProof = m_bExplosionproof;
- pVehicle->bCollisionProof = m_bCollisionproof;
- pVehicle->bMeleeProof = m_bMeleeproof;
+ if (m_nFlags & FLAG_BULLETPROOF) pVehicle->bBulletProof = true;
+ if (m_nFlags & FLAG_FIREPROOF) pVehicle->bFireProof = true;
+ if (m_nFlags & FLAG_EXPLOSIONPROOF) pVehicle->bExplosionProof = true;
+ if (m_nFlags & FLAG_COLLISIONPROOF) pVehicle->bCollisionProof = true;
+ if (m_nFlags & FLAG_MELEEPROOF) pVehicle->bMeleeProof = true;
return pVehicle;
}
@@ -2327,8 +2321,47 @@ void CGarages::Save(uint8 * buf, uint32 * size)
WriteSaveBuf(buf, aCarsInSafeHouse2[i]);
WriteSaveBuf(buf, aCarsInSafeHouse3[i]);
}
- for (int i = 0; i < NUM_GARAGES; i++)
+ for (int i = 0; i < NUM_GARAGES; i++) {
+#ifdef COMPATIBLE_SAVES
+ WriteSaveBuf(buf, aGarages[i].m_eGarageType);
+ WriteSaveBuf(buf, aGarages[i].m_eGarageState);
+ WriteSaveBuf(buf, aGarages[i].field_2);
+ WriteSaveBuf(buf, aGarages[i].m_bClosingWithoutTargetCar);
+ WriteSaveBuf(buf, aGarages[i].m_bDeactivated);
+ WriteSaveBuf(buf, aGarages[i].m_bResprayHappened);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, aGarages[i].m_nTargetModelIndex);
+ ZeroSaveBuf(buf, 4 + 4);
+ WriteSaveBuf(buf, aGarages[i].m_bDoor1PoolIndex);
+ WriteSaveBuf(buf, aGarages[i].m_bDoor2PoolIndex);
+ WriteSaveBuf(buf, aGarages[i].m_bDoor1IsDummy);
+ WriteSaveBuf(buf, aGarages[i].m_bDoor2IsDummy);
+ WriteSaveBuf(buf, aGarages[i].m_bRecreateDoorOnNextRefresh);
+ WriteSaveBuf(buf, aGarages[i].m_bRotatedDoor);
+ WriteSaveBuf(buf, aGarages[i].m_bCameraFollowsPlayer);
+ ZeroSaveBuf(buf, 1);
+ WriteSaveBuf(buf, aGarages[i].m_fX1);
+ WriteSaveBuf(buf, aGarages[i].m_fX2);
+ WriteSaveBuf(buf, aGarages[i].m_fY1);
+ WriteSaveBuf(buf, aGarages[i].m_fY2);
+ WriteSaveBuf(buf, aGarages[i].m_fZ1);
+ WriteSaveBuf(buf, aGarages[i].m_fZ2);
+ WriteSaveBuf(buf, aGarages[i].m_fDoorPos);
+ WriteSaveBuf(buf, aGarages[i].m_fDoorHeight);
+ WriteSaveBuf(buf, aGarages[i].m_fDoor1X);
+ WriteSaveBuf(buf, aGarages[i].m_fDoor1Y);
+ WriteSaveBuf(buf, aGarages[i].m_fDoor2X);
+ WriteSaveBuf(buf, aGarages[i].m_fDoor2Y);
+ WriteSaveBuf(buf, aGarages[i].m_fDoor1Z);
+ WriteSaveBuf(buf, aGarages[i].m_fDoor2Z);
+ WriteSaveBuf(buf, aGarages[i].m_nTimeToStartAction);
+ WriteSaveBuf(buf, aGarages[i].m_bCollectedCarsState);
+ ZeroSaveBuf(buf, 3 + 4 + 4);
+ ZeroSaveBuf(buf, sizeof(aGarages[i].m_sStoredCar));
+#else
WriteSaveBuf(buf, aGarages[i]);
+#endif
+ }
#ifdef FIX_GARAGE_SIZE
VALIDATESAVEBUF(*size);
#endif
@@ -2339,11 +2372,7 @@ const CStoredCar &CStoredCar::operator=(const CStoredCar & other)
m_nModelIndex = other.m_nModelIndex;
m_vecPos = other.m_vecPos;
m_vecAngle = other.m_vecAngle;
- m_bBulletproof = other.m_bBulletproof;
- m_bFireproof = other.m_bFireproof;
- m_bExplosionproof = other.m_bExplosionproof;
- m_bCollisionproof = other.m_bCollisionproof;
- m_bMeleeproof = other.m_bMeleeproof;
+ m_nFlags = other.m_nFlags;
m_nPrimaryColor = other.m_nPrimaryColor;
m_nSecondaryColor = other.m_nSecondaryColor;
m_nRadioStation = other.m_nRadioStation;
@@ -2357,7 +2386,7 @@ void CGarages::Load(uint8* buf, uint32 size)
{
#ifdef FIX_GARAGE_SIZE
INITSAVEBUF
- assert(size == (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage));
+ assert(size == (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage)));
#else
assert(size == 5484);
#endif
@@ -2380,7 +2409,45 @@ void CGarages::Load(uint8* buf, uint32 size)
ReadSaveBuf(&aCarsInSafeHouse3[i], buf);
}
for (int i = 0; i < NUM_GARAGES; i++) {
+#ifdef COMPATIBLE_SAVES
+ ReadSaveBuf(&aGarages[i].m_eGarageType, buf);
+ ReadSaveBuf(&aGarages[i].m_eGarageState, buf);
+ ReadSaveBuf(&aGarages[i].field_2, buf);
+ ReadSaveBuf(&aGarages[i].m_bClosingWithoutTargetCar, buf);
+ ReadSaveBuf(&aGarages[i].m_bDeactivated, buf);
+ ReadSaveBuf(&aGarages[i].m_bResprayHappened, buf);
+ SkipSaveBuf(buf, 2);
+ ReadSaveBuf(&aGarages[i].m_nTargetModelIndex, buf);
+ SkipSaveBuf(buf, 4 + 4);
+ ReadSaveBuf(&aGarages[i].m_bDoor1PoolIndex, buf);
+ ReadSaveBuf(&aGarages[i].m_bDoor2PoolIndex, buf);
+ ReadSaveBuf(&aGarages[i].m_bDoor1IsDummy, buf);
+ ReadSaveBuf(&aGarages[i].m_bDoor2IsDummy, buf);
+ ReadSaveBuf(&aGarages[i].m_bRecreateDoorOnNextRefresh, buf);
+ ReadSaveBuf(&aGarages[i].m_bRotatedDoor, buf);
+ ReadSaveBuf(&aGarages[i].m_bCameraFollowsPlayer, buf);
+ SkipSaveBuf(buf, 1);
+ ReadSaveBuf(&aGarages[i].m_fX1, buf);
+ ReadSaveBuf(&aGarages[i].m_fX2, buf);
+ ReadSaveBuf(&aGarages[i].m_fY1, buf);
+ ReadSaveBuf(&aGarages[i].m_fY2, buf);
+ ReadSaveBuf(&aGarages[i].m_fZ1, buf);
+ ReadSaveBuf(&aGarages[i].m_fZ2, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoorPos, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoorHeight, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoor1X, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoor1Y, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoor2X, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoor2Y, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoor1Z, buf);
+ ReadSaveBuf(&aGarages[i].m_fDoor2Z, buf);
+ ReadSaveBuf(&aGarages[i].m_nTimeToStartAction, buf);
+ ReadSaveBuf(&aGarages[i].m_bCollectedCarsState, buf);
+ SkipSaveBuf(buf, 3 + 4 + 4);
+ SkipSaveBuf(buf, sizeof(aGarages[i].m_sStoredCar));
+#else
ReadSaveBuf(&aGarages[i], buf);
+#endif
aGarages[i].m_pDoor1 = nil;
aGarages[i].m_pDoor2 = nil;
aGarages[i].m_pTarget = nil;
diff --git a/src/control/Garages.h b/src/control/Garages.h
index a7dfa462..8a9fd1b6 100644
--- a/src/control/Garages.h
+++ b/src/control/Garages.h
@@ -51,14 +51,17 @@ enum
class CStoredCar
{
+ enum {
+ FLAG_BULLETPROOF = 0x1,
+ FLAG_FIREPROOF = 0x2,
+ FLAG_EXPLOSIONPROOF = 0x4,
+ FLAG_COLLISIONPROOF = 0x8,
+ FLAG_MELEEPROOF = 0x10,
+ };
int32 m_nModelIndex;
CVector m_vecPos;
CVector m_vecAngle;
- int32 m_bBulletproof : 1;
- int32 m_bFireproof : 1;
- int32 m_bExplosionproof : 1;
- int32 m_bCollisionproof : 1;
- int32 m_bMeleeproof : 1;
+ int32 m_nFlags;
int8 m_nPrimaryColor;
int8 m_nSecondaryColor;
int8 m_nRadioStation;
@@ -78,6 +81,13 @@ VALIDATE_SIZE(CStoredCar, 0x28);
#define SWITCH_GARAGE_DISTANCE_CLOSE 40.0f
+#define CRUSHER_GARAGE_X1 (1135.5f)
+#define CRUSHER_GARAGE_Y1 (57.0f)
+#define CRUSHER_GARAGE_Z1 (-1.0f)
+#define CRUSHER_GARAGE_X2 (1149.5f)
+#define CRUSHER_GARAGE_Y2 (63.7f)
+#define CRUSHER_GARAGE_Z2 (3.5f)
+
class CGarage
{
public:
@@ -87,7 +97,7 @@ public:
bool m_bClosingWithoutTargetCar;
bool m_bDeactivated;
bool m_bResprayHappened;
- int m_nTargetModelIndex;
+ int32 m_nTargetModelIndex;
CEntity *m_pDoor1;
CEntity *m_pDoor2;
uint8 m_bDoor1PoolIndex;
diff --git a/src/control/Phones.cpp b/src/control/Phones.cpp
index f9cb1421..7632cfa3 100644
--- a/src/control/Phones.cpp
+++ b/src/control/Phones.cpp
@@ -18,6 +18,12 @@
#include "Replay.h"
#endif
+#ifdef COMPATIBLE_SAVES
+#define PHONEINFO_SAVE_SIZE 0xA30
+#else
+#define PHONEINFO_SAVE_SIZE sizeof(CPhoneInfo)
+#endif
+
CPhoneInfo gPhoneInfo;
bool CPhoneInfo::bDisplayingPhoneMessage; // is phone picked up
@@ -209,6 +215,22 @@ CPhoneInfo::IsMessageBeingDisplayed(int phoneId)
return pPhoneDisplayingMessages == &m_aPhones[phoneId];
}
+#ifdef COMPATIBLE_SAVES
+static inline void
+LoadPhone(CPhone &phone, uint8 *&buf)
+{
+ ReadSaveBuf(&phone.m_vecPos, buf);
+ SkipSaveBuf(buf, 6 * 4);
+ ReadSaveBuf<uint32>(&phone.m_repeatedMessagePickupStart, buf);
+ uint32 tmp;
+ ReadSaveBuf(&tmp, buf);
+ phone.m_pEntity = (CEntity*)(uintptr)tmp;
+ ReadSaveBuf<PhoneState>(&phone.m_nState, buf);
+ ReadSaveBuf<bool>(&phone.m_visibleToCam, buf);
+ SkipSaveBuf(buf, 3);
+}
+#endif
+
void
CPhoneInfo::Load(uint8 *buf, uint32 size)
{
@@ -226,7 +248,12 @@ INITSAVEBUF
// We can do it without touching saves. We'll only load script phones, others are already loaded in Initialise
for (int i = 0; i < 50; i++) {
CPhone phoneToLoad;
+#ifdef COMPATIBLE_SAVES
+ phoneToLoad.m_apMessages[0]=phoneToLoad.m_apMessages[1]=phoneToLoad.m_apMessages[2]=phoneToLoad.m_apMessages[3]=phoneToLoad.m_apMessages[4]=phoneToLoad.m_apMessages[5] = nil;
+ LoadPhone(phoneToLoad, buf);
+#else
ReadSaveBuf(&phoneToLoad, buf);
+#endif
if (ignoreOtherPhones)
continue;
@@ -252,7 +279,11 @@ INITSAVEBUF
m_nScriptPhonesMax = scriptPhonesMax;
for (int i = 0; i < NUMPHONES; i++) {
+#ifdef COMPATIBLE_SAVES
+ LoadPhone(m_aPhones[i], buf);
+#else
ReadSaveBuf(&m_aPhones[i], buf);
+#endif
// It's saved as building pool index in save file, convert it to true entity
if (m_aPhones[i].m_pEntity) {
m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1);
@@ -376,7 +407,7 @@ CPhoneInfo::Initialise(void)
void
CPhoneInfo::Save(uint8 *buf, uint32 *size)
{
- *size = sizeof(CPhoneInfo);
+ *size = PHONEINFO_SAVE_SIZE;
INITSAVEBUF
WriteSaveBuf(buf, m_nMax);
WriteSaveBuf(buf, m_nScriptPhonesMax);
@@ -385,12 +416,24 @@ INITSAVEBUF
#else
for (int phoneId = 0; phoneId < NUMPHONES; phoneId++) {
#endif
+#ifdef COMPATIBLE_SAVES
+ WriteSaveBuf(buf, m_aPhones[phoneId].m_vecPos);
+ ZeroSaveBuf(buf, 6 * 4);
+ WriteSaveBuf(buf, m_aPhones[phoneId].m_repeatedMessagePickupStart);
+ // Convert entity pointer to building pool index while saving
+ int32 tmp = m_aPhones[phoneId].m_pEntity ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)m_aPhones[phoneId].m_pEntity) + 1 : 0;
+ WriteSaveBuf(buf, tmp);
+ WriteSaveBuf(buf, m_aPhones[phoneId].m_nState);
+ WriteSaveBuf(buf, m_aPhones[phoneId].m_visibleToCam);
+ ZeroSaveBuf(buf, 3);
+#else
CPhone* phone = WriteSaveBuf(buf, m_aPhones[phoneId]);
// Convert entity pointer to building pool index while saving
if (phone->m_pEntity) {
phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)phone->m_pEntity) + 1);
}
+#endif
}
VALIDATESAVEBUF(*size)
}
diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp
index 10175fba..8d3472ea 100644
--- a/src/control/Pickups.cpp
+++ b/src/control/Pickups.cpp
@@ -32,6 +32,12 @@
#include "WaterLevel.h"
#include "World.h"
+#ifdef COMPATIBLE_SAVES
+#define PICKUPS_SAVE_SIZE 0x24C0
+#else
+#define PICKUPS_SAVE_SIZE sizeof(aPickUps)
+#endif
+
CPickup CPickups::aPickUps[NUMPICKUPS];
int16 CPickups::NumMessages;
int32 CPickups::aPickUpsCollected[NUMCOLLECTEDPICKUPS];
@@ -1000,10 +1006,23 @@ CPickups::Load(uint8 *buf, uint32 size)
INITSAVEBUF
for (int32 i = 0; i < NUMPICKUPS; i++) {
+#ifdef COMPATIBLE_SAVES
+ ReadSaveBuf(&aPickUps[i].m_eType, buf);
+ ReadSaveBuf(&aPickUps[i].m_bRemoved, buf);
+ ReadSaveBuf(&aPickUps[i].m_nQuantity, buf);
+ int32 tmp;
+ ReadSaveBuf(&tmp, buf);
+ aPickUps[i].m_pObject = aPickUps[i].m_eType != PICKUP_NONE && tmp != 0 ? CPools::GetObjectPool()->GetSlot(tmp - 1) : nil;
+ ReadSaveBuf(&aPickUps[i].m_nTimer, buf);
+ ReadSaveBuf(&aPickUps[i].m_eModelIndex, buf);
+ ReadSaveBuf(&aPickUps[i].m_nIndex, buf);
+ ReadSaveBuf(&aPickUps[i].m_vecPos, buf);
+#else
ReadSaveBuf(&aPickUps[i], buf);
if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_pObject != nil)
aPickUps[i].m_pObject = CPools::GetObjectPool()->GetSlot((uintptr)aPickUps[i].m_pObject - 1);
+#endif
}
ReadSaveBuf(&CollectedPickUpIndex, buf);
@@ -1019,14 +1038,26 @@ VALIDATESAVEBUF(size)
void
CPickups::Save(uint8 *buf, uint32 *size)
{
- *size = sizeof(aPickUps) + sizeof(uint16) + sizeof(uint16) + sizeof(aPickUpsCollected);
+ *size = PICKUPS_SAVE_SIZE + sizeof(uint16) + sizeof(uint16) + sizeof(aPickUpsCollected);
INITSAVEBUF
for (int32 i = 0; i < NUMPICKUPS; i++) {
+#ifdef COMPATIBLE_SAVES
+ WriteSaveBuf(buf, aPickUps[i].m_eType);
+ WriteSaveBuf(buf, aPickUps[i].m_bRemoved);
+ WriteSaveBuf(buf, aPickUps[i].m_nQuantity);
+ int32 tmp = aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_pObject != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aPickUps[i].m_pObject) + 1 : 0;
+ WriteSaveBuf(buf, tmp);
+ WriteSaveBuf(buf, aPickUps[i].m_nTimer);
+ WriteSaveBuf(buf, aPickUps[i].m_eModelIndex);
+ WriteSaveBuf(buf, aPickUps[i].m_nIndex);
+ WriteSaveBuf(buf, aPickUps[i].m_vecPos);
+#else
CPickup *buf_pickup = WriteSaveBuf(buf, aPickUps[i]);
if (buf_pickup->m_eType != PICKUP_NONE && buf_pickup->m_pObject != nil)
buf_pickup->m_pObject = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(buf_pickup->m_pObject) + 1);
+#endif
}
WriteSaveBuf(buf, CollectedPickUpIndex);
diff --git a/src/control/Script5.cpp b/src/control/Script5.cpp
index a9aec18e..953a1f50 100644
--- a/src/control/Script5.cpp
+++ b/src/control/Script5.cpp
@@ -2089,33 +2089,33 @@ VALIDATESAVEBUF(size)
void CRunningScript::Save(uint8*& buf)
{
#ifdef COMPATIBLE_SAVES
- SkipSaveBuf(buf, 8);
+ ZeroSaveBuf(buf, 8);
for (int i = 0; i < 8; i++)
- WriteSaveBuf<char>(buf, m_abScriptName[i]);
- WriteSaveBuf<uint32>(buf, m_nIp);
+ WriteSaveBuf(buf, m_abScriptName[i]);
+ WriteSaveBuf(buf, m_nIp);
#ifdef CHECK_STRUCT_SIZES
static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6");
#endif
for (int i = 0; i < MAX_STACK_DEPTH; i++)
- WriteSaveBuf<uint32>(buf, m_anStack[i]);
- WriteSaveBuf<uint16>(buf, m_nStackPointer);
- SkipSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_anStack[i]);
+ WriteSaveBuf(buf, m_nStackPointer);
+ ZeroSaveBuf(buf, 2);
#ifdef CHECK_STRUCT_SIZES
static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18");
#endif
for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++)
- WriteSaveBuf<int32>(buf, m_anLocalVariables[i]);
- WriteSaveBuf<bool>(buf, m_bCondResult);
- WriteSaveBuf<bool>(buf, m_bIsMissionScript);
- WriteSaveBuf<bool>(buf, m_bSkipWakeTime);
- SkipSaveBuf(buf, 1);
- WriteSaveBuf<uint32>(buf, m_nWakeTime);
- WriteSaveBuf<uint16>(buf, m_nAndOrState);
- WriteSaveBuf<bool>(buf, m_bNotFlag);
- WriteSaveBuf<bool>(buf, m_bDeatharrestEnabled);
- WriteSaveBuf<bool>(buf, m_bDeatharrestExecuted);
- WriteSaveBuf<bool>(buf, m_bMissionFlag);
- SkipSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_anLocalVariables[i]);
+ WriteSaveBuf(buf, m_bCondResult);
+ WriteSaveBuf(buf, m_bIsMissionScript);
+ WriteSaveBuf(buf, m_bSkipWakeTime);
+ ZeroSaveBuf(buf, 1);
+ WriteSaveBuf(buf, m_nWakeTime);
+ WriteSaveBuf(buf, m_nAndOrState);
+ WriteSaveBuf(buf, m_bNotFlag);
+ WriteSaveBuf(buf, m_bDeatharrestEnabled);
+ WriteSaveBuf(buf, m_bDeatharrestExecuted);
+ WriteSaveBuf(buf, m_bMissionFlag);
+ ZeroSaveBuf(buf, 2);
#else
WriteSaveBuf(buf, *this);
#endif
diff --git a/src/core/Fire.cpp b/src/core/Fire.cpp
index 984b21bb..8b184622 100644
--- a/src/core/Fire.cpp
+++ b/src/core/Fire.cpp
@@ -396,7 +396,7 @@ CFireManager::StartScriptFire(const CVector &pos, CEntity *target, float strengt
if (target) {
if (target->IsPed()) {
ped->m_pFire = fire;
- if (target != (CVehicle *)FindPlayerPed()) {
+ if (target != FindPlayerPed()) {
CVector2D pos = target->GetPosition();
ped->SetFlee(pos, 10000);
ped->SetMoveAnim();
diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp
index 7ff80697..ecb893b4 100644
--- a/src/core/Frontend.cpp
+++ b/src/core/Frontend.cpp
@@ -336,6 +336,7 @@ const char* MenuFilenames[][2] = {
CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE), MENU_Y(SMALLESTTEXT_Y_SCALE)); \
CFont::SetFontStyle(FONT_LOCALE(FONT_BANK));
+// value must be between 0.0-1.0
#define ProcessSlider(value, increaseAction, decreaseAction, hoverStartX, hoverEndX) \
do { \
lastActiveBarX = DisplaySlider(MENU_X_RIGHT_ALIGNED(MENUSLIDER_X + columnWidth), MENU_Y(bitAboveNextItemY), MENU_Y(smallestSliderBar), MENU_Y(usableLineHeight), MENU_X(MENUSLIDER_UNK), value); \
@@ -489,7 +490,7 @@ CMenuManager::ThingsToDoBeforeGoingBack()
option.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_FOCUSLOSS);
if (option.m_Action == MENUACTION_CFO_SELECT && option.m_CFOSelect->onlyApplyOnEnter && option.m_CFOSelect->lastSavedValue != option.m_CFOSelect->displayedValue)
- option.m_CFOSelect->displayedValue = *option.m_CFO->value = option.m_CFOSelect->lastSavedValue;
+ option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue;
if (aScreens[m_nCurrScreen].returnPrevPageFunc) {
aScreens[m_nCurrScreen].returnPrevPageFunc();
@@ -898,29 +899,29 @@ CMenuManager::CheckSliderMovement(int value)
{
switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) {
case MENUACTION_BRIGHTNESS:
- m_PrefsBrightness += value * (512/16);
+ m_PrefsBrightness += value * (512/MENUSLIDER_LOGICAL_BARS);
m_PrefsBrightness = Clamp(m_PrefsBrightness, 0, 511);
break;
case MENUACTION_DRAWDIST:
if(value > 0)
- m_PrefsLOD += ((1.8f - 0.8f) / 16.0f);
+ m_PrefsLOD += ((1.8f - 0.8f) / MENUSLIDER_LOGICAL_BARS);
else
- m_PrefsLOD -= ((1.8f - 0.8f) / 16.0f);
+ m_PrefsLOD -= ((1.8f - 0.8f) / MENUSLIDER_LOGICAL_BARS);
m_PrefsLOD = Clamp(m_PrefsLOD, 0.8f, 1.8f);
CRenderer::ms_lodDistScale = m_PrefsLOD;
break;
case MENUACTION_MUSICVOLUME:
- m_PrefsMusicVolume += value * (128/16);
+ m_PrefsMusicVolume += value * (128/MENUSLIDER_LOGICAL_BARS);
m_PrefsMusicVolume = Clamp(m_PrefsMusicVolume, 0, 127);
DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume);
break;
case MENUACTION_SFXVOLUME:
- m_PrefsSfxVolume += value * (128/16);
+ m_PrefsSfxVolume += value * (128/MENUSLIDER_LOGICAL_BARS);
m_PrefsSfxVolume = Clamp(m_PrefsSfxVolume, 0, 127);
DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume);
break;
case MENUACTION_MOUSESENS:
- TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // ???
+ TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps
TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f/3200.0f, 1.0f/200.0f);
#ifdef FIX_BUGS
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f;
@@ -928,6 +929,20 @@ CMenuManager::CheckSliderMovement(int value)
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl;
#endif
break;
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case MENUACTION_CFO_SLIDER:
+ {
+ CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption];
+ float oldValue = *(float*)option.m_CFOSlider->value;
+ *(float*)option.m_CFOSlider->value += value * ((option.m_CFOSlider->max - option.m_CFOSlider->min) / MENUSLIDER_LOGICAL_BARS);
+ *(float*)option.m_CFOSlider->value = Clamp(*(float*)option.m_CFOSlider->value, option.m_CFOSlider->min, option.m_CFOSlider->max);
+
+ if (*(float*)option.m_CFOSlider->value != oldValue && option.m_CFOSlider->changeFunc)
+ option.m_CFOSlider->changeFunc(oldValue, *(float*)option.m_CFOSlider->value);
+
+ break;
+ }
+#endif
default:
return;
}
@@ -1001,10 +1016,10 @@ CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostR
int lastActiveBarX = 0;
float curBarX = 0.0f;
float spacing = SCREEN_SCALE_X(10.0f);
- for (int i = 0; i < 16; i++) {
- curBarX = i * rectSize/16.0f + x;
+ for (int i = 0; i < MENUSLIDER_BARS; i++) {
+ curBarX = i * rectSize/MENUSLIDER_BARS + x;
- if (i / 16.0f + 1 / 32.0f < progress) {
+ if (i / (float)MENUSLIDER_BARS + 1 / (MENUSLIDER_BARS * 2.f) < progress) {
color = CRGBA(SLIDERON_COLOR.r, SLIDERON_COLOR.g, SLIDERON_COLOR.b, FadeIn(255));
lastActiveBarX = curBarX;
} else
@@ -1012,7 +1027,7 @@ CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostR
maxBarHeight = Max(mostLeftBarSize, mostRightBarSize);
- float curBarFreeSpace = ((16 - i) * mostLeftBarSize + i * mostRightBarSize) / 16.0f;
+ float curBarFreeSpace = ((MENUSLIDER_BARS - i) * mostLeftBarSize + i * mostRightBarSize) / (float)MENUSLIDER_BARS;
float left = curBarX;
float top = y + maxBarHeight - curBarFreeSpace;
float right = spacing + curBarX;
@@ -1595,10 +1610,10 @@ CMenuManager::Draw()
// If that was previously selected option, restore it to default value.
// if (m_nCurrOption != lastSelectedOpt && lastSelectedOpt == i)
- option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *option.m_CFO->value;
+ option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *(int8*)option.m_CFO->value;
} else {
- if (option.m_CFOSelect->displayedValue != *option.m_CFO->value)
+ if (option.m_CFOSelect->displayedValue != *(int8*)option.m_CFO->value)
SetHelperText(1); // Enter to apply
else if (m_nHelperTextMsgId == 1)
ResetHelperText(); // Applied
@@ -1606,8 +1621,8 @@ CMenuManager::Draw()
}
// To whom manipulate option.m_CFO->value of select options externally (like RestoreDef functions)
- if (*option.m_CFO->value != option.m_CFOSelect->lastSavedValue)
- option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *option.m_CFO->value;
+ if (*(int8*)option.m_CFO->value != option.m_CFOSelect->lastSavedValue)
+ option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *(int8*)option.m_CFO->value;
if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0)
option.m_CFOSelect->displayedValue = 0;
@@ -1799,6 +1814,12 @@ CMenuManager::Draw()
case MENUACTION_MOUSESENS:
ProcessSlider(TheCamera.m_fMouseAccelHorzntl * 200.0f, HOVEROPTION_INCREASE_MOUSESENS, HOVEROPTION_DECREASE_MOUSESENS, MENU_X_LEFT_ALIGNED(200.0f), SCREEN_WIDTH);
break;
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case MENUACTION_CFO_SLIDER:
+ CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[i];
+ ProcessSlider((*(float*)option.m_CFOSlider->value - option.m_CFOSlider->min) / (option.m_CFOSlider->max - option.m_CFOSlider->min), HOVEROPTION_INCREASE_CFO_SLIDER, HOVEROPTION_DECREASE_CFO_SLIDER, MENU_X_LEFT_ALIGNED(170.0f), SCREEN_WIDTH);
+ break;
+#endif
}
// Needed after the bug fix in Font.cpp
@@ -4477,7 +4498,7 @@ CMenuManager::ProcessButtonPresses(void)
#ifndef TIDY_UP_PBP
switch (m_nHoverOption) {
case HOVEROPTION_INCREASE_BRIGHTNESS:
- m_PrefsBrightness = m_PrefsBrightness + 32;
+ m_PrefsBrightness = m_PrefsBrightness + (512 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsBrightness < 0) {
m_PrefsBrightness = 0;
}
@@ -4487,7 +4508,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_DECREASE_BRIGHTNESS:
- m_PrefsBrightness = m_PrefsBrightness - 32;
+ m_PrefsBrightness = m_PrefsBrightness - (512 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsBrightness < 0) {
m_PrefsBrightness = 0;
}
@@ -4497,25 +4518,25 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_INCREASE_DRAWDIST:
- m_PrefsLOD = m_PrefsLOD + (1.0f / 16);
+ m_PrefsLOD = m_PrefsLOD + (1.0f / MENUSLIDER_LOGICAL_BARS);
m_PrefsLOD = min(1.8f, m_PrefsLOD);
CRenderer::ms_lodDistScale = m_PrefsLOD;
SaveSettings();
break;
case HOVEROPTION_DECREASE_DRAWDIST:
- m_PrefsLOD = m_PrefsLOD - (1.0f / 16);
+ m_PrefsLOD = m_PrefsLOD - (1.0f / MENUSLIDER_LOGICAL_BARS);
m_PrefsLOD = max(0.8f, m_PrefsLOD);
CRenderer::ms_lodDistScale = m_PrefsLOD;
SaveSettings();
break;
case HOVEROPTION_INCREASE_MUSICVOLUME:
- m_PrefsMusicVolume = m_PrefsMusicVolume + 8;
+ m_PrefsMusicVolume = m_PrefsMusicVolume + (128 / MENUSLIDER_LOGICAL_BARS);
m_PrefsMusicVolume = Clamp(m_PrefsMusicVolume, 0, 127);
DMAudio.SetMusicMasterVolume(uchar)(m_PrefsMusicVolume);
SaveSettings();
break;
case HOVEROPTION_DECREASE_MUSICVOLUME:
- m_PrefsMusicVolume = m_PrefsMusicVolume - 8;
+ m_PrefsMusicVolume = m_PrefsMusicVolume - (128 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsMusicVolume < 0) {
m_PrefsMusicVolume = 0;
}
@@ -4526,7 +4547,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_INCREASE_SFXVOLUME:
- m_PrefsSFXVolume = m_PrefsSFXVolume + 8;
+ m_PrefsSFXVolume = m_PrefsSFXVolume + (128 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsSFXVolume < 0) {
m_PrefsSFXVolume = 0;
}
@@ -4537,7 +4558,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_DECREASE_SFXVOLUME:
- m_PrefsSFXVolume = m_PrefsSFXVolume - 8;
+ m_PrefsSFXVolume = m_PrefsSFXVolume - (128 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsSFXVolume < 0) {
m_PrefsSFXVolume = 0;
}
@@ -4548,7 +4569,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_INCREASE_MOUSESENS:
- TheCamera.m_fMouseAccelHorzntl += (1.0f / 3000);
+ TheCamera.m_fMouseAccelHorzntl += 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps
TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f / 3200, 1.0f / 200);
#ifdef FIX_BUGS
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f;
@@ -4558,7 +4579,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_DECREASE_MOUSESENS:
- TheCamera.m_fMouseAccelHorzntl -= (1.0f / 3000);
+ TheCamera.m_fMouseAccelHorzntl -= 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps
TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f / 3200, 1.0f / 200);
#ifdef FIX_BUGS
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f;
@@ -4575,6 +4596,9 @@ CMenuManager::ProcessButtonPresses(void)
case HOVEROPTION_INCREASE_MUSICVOLUME:
case HOVEROPTION_INCREASE_SFXVOLUME:
case HOVEROPTION_INCREASE_MOUSESENS:
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case HOVEROPTION_INCREASE_CFO_SLIDER:
+#endif
CheckSliderMovement(1);
break;
case HOVEROPTION_DECREASE_BRIGHTNESS:
@@ -4582,6 +4606,9 @@ CMenuManager::ProcessButtonPresses(void)
case HOVEROPTION_DECREASE_MUSICVOLUME:
case HOVEROPTION_DECREASE_SFXVOLUME:
case HOVEROPTION_DECREASE_MOUSESENS:
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case HOVEROPTION_DECREASE_CFO_SLIDER:
+#endif
CheckSliderMovement(-1);
break;
}
@@ -4612,7 +4639,11 @@ CMenuManager::ProcessButtonPresses(void)
|| CPad::GetPad(0)->GetAnaloguePadLeftJustUp() || CPad::GetPad(0)->GetAnaloguePadRightJustUp()
|| CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustDown()) {
int option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action;
- if (option == MENUACTION_BRIGHTNESS || option == MENUACTION_DRAWDIST)
+ if (option == MENUACTION_BRIGHTNESS || option == MENUACTION_DRAWDIST
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ || option == MENUACTION_CFO_SLIDER
+#endif
+ )
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0);
else if (option == MENUACTION_SFXVOLUME)
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_AUDIO_TEST, 0);
@@ -4775,7 +4806,12 @@ CMenuManager::ProcessButtonPresses(void)
} else if (option != MENUACTION_CHANGEMENU && option != MENUACTION_BRIGHTNESS && option != MENUACTION_DRAWDIST
&& option != MENUACTION_MUSICVOLUME && option != MENUACTION_SFXVOLUME
&& option != MENUACTION_CHECKSAVE && option != MENUACTION_UNK24
- && option != MENUACTION_MOUSESENS && option != MENUACTION_SCREENRES) {
+ && option != MENUACTION_MOUSESENS && option != MENUACTION_SCREENRES
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ && option != MENUACTION_CFO_SLIDER
+#endif
+ )
+ {
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0);
}
@@ -5166,9 +5202,9 @@ CMenuManager::ProcessButtonPresses(void)
if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0)
option.m_CFOSelect->displayedValue = 0;
}
- int8 oldValue = *option.m_CFO->value;
+ int8 oldValue = *(int8*)option.m_CFO->value;
- *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
+ *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
// Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO
// if (option.m_CFOSelect->save)
@@ -5412,9 +5448,9 @@ CMenuManager::ProcessButtonPresses(void)
option.m_CFOSelect->displayedValue = option.m_CFOSelect->numRightTexts - 1;
}
if (!option.m_CFOSelect->onlyApplyOnEnter) {
- int8 oldValue = *option.m_CFO->value;
+ int8 oldValue = *(int8*)option.m_CFO->value;
- *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
+ *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
// Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO
// if (option.m_CFOSelect->save)
diff --git a/src/core/Frontend.h b/src/core/Frontend.h
index 5c3523ab..32e5ef9d 100644
--- a/src/core/Frontend.h
+++ b/src/core/Frontend.h
@@ -25,6 +25,9 @@
#define MENUSLIDER_X 256.0f
#define MENUSLIDER_UNK 256.0f
+#define MENUSLIDER_BARS 16
+#define MENUSLIDER_LOGICAL_BARS MENUSLIDER_BARS
+
#define BIGTEXT_X_SCALE 0.75f // For FONT_HEADING
#define BIGTEXT_Y_SCALE 0.9f
#define MEDIUMTEXT_X_SCALE 0.55f // For FONT_HEADING
@@ -256,6 +259,7 @@ enum eMenuScreen
enum eMenuAction
{
#ifdef CUSTOM_FRONTEND_OPTIONS
+ MENUACTION_CFO_SLIDER = -3,
MENUACTION_CFO_SELECT = -2,
MENUACTION_CFO_DYNAMIC = -1,
#endif
@@ -424,6 +428,10 @@ enum eCheckHover
HOVEROPTION_DECREASE_SFXVOLUME,
HOVEROPTION_INCREASE_MOUSESENS,
HOVEROPTION_DECREASE_MOUSESENS,
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ HOVEROPTION_INCREASE_CFO_SLIDER,
+ HOVEROPTION_DECREASE_CFO_SLIDER,
+#endif
HOVEROPTION_NOT_HOVERING,
};
@@ -493,7 +501,7 @@ struct CCustomScreenLayout {
struct CCFO
{
- int8 *value;
+ void *value;
const char *saveCat;
const char *save;
};
@@ -524,6 +532,24 @@ struct CCFOSelect : CCFO
}
};
+// Value is float in here
+struct CCFOSlider : CCFO
+{
+ ChangeFuncFloat changeFunc;
+ float min;
+ float max;
+
+ CCFOSlider() {};
+ CCFOSlider(float* value, const char* saveCat, const char* save, float min, float max, ChangeFuncFloat changeFunc = nil){
+ this->value = value;
+ this->saveCat = saveCat;
+ this->save = save;
+ this->changeFunc = changeFunc;
+ this->min = min;
+ this->max = max;
+ }
+};
+
struct CCFODynamic : CCFO
{
DrawFunc drawFunc;
@@ -555,6 +581,7 @@ struct CMenuScreenCustom
CCFO *m_CFO; // for initializing
CCFOSelect *m_CFOSelect;
CCFODynamic *m_CFODynamic;
+ CCFOSlider *m_CFOSlider;
};
int32 m_SaveSlot; // eSaveSlot
int32 m_TargetMenu; // eMenuScreen
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index f6156a4c..b3dd1eda 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -409,7 +409,11 @@ bool CGame::Initialise(const char* datFile)
#endif
#ifndef GTA_PS2
- CIniFile::LoadIniFile();
+#ifdef PED_CAR_DENSITY_SLIDERS
+ // Load density values from gta3.ini only if our re3.ini have them 1.f
+ if (CIniFile::PedNumberMultiplier == 1.f && CIniFile::CarNumberMultiplier == 1.f)
+#endif
+ CIniFile::LoadIniFile();
#endif
currLevel = LEVEL_INDUSTRIAL;
diff --git a/src/core/IniFile.cpp b/src/core/IniFile.cpp
index df01b440..524632fe 100644
--- a/src/core/IniFile.cpp
+++ b/src/core/IniFile.cpp
@@ -23,6 +23,6 @@ void CIniFile::LoadIniFile()
CarNumberMultiplier = Min(3.0f, Max(0.5f, CarNumberMultiplier));
CFileMgr::CloseFile(f);
}
- CPopulation::MaxNumberOfPedsInUse = 25.0f * PedNumberMultiplier;
- CCarCtrl::MaxNumberOfCarsInUse = 12.0f * CarNumberMultiplier;
+ CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * PedNumberMultiplier;
+ CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CarNumberMultiplier;
} \ No newline at end of file
diff --git a/src/core/IniFile.h b/src/core/IniFile.h
index 1e30c4de..30dc8c21 100644
--- a/src/core/IniFile.h
+++ b/src/core/IniFile.h
@@ -1,5 +1,8 @@
#pragma once
+#define DEFAULT_MAX_NUMBER_OF_PEDS 25.0f
+#define DEFAULT_MAX_NUMBER_OF_CARS 12.0f
+
class CIniFile
{
public:
diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp
index 6e23f76a..033ed9b9 100644
--- a/src/core/MenuScreensCustom.cpp
+++ b/src/core/MenuScreensCustom.cpp
@@ -26,6 +26,9 @@
#include "ModelInfo.h"
#include "Pad.h"
#include "ControllerConfig.h"
+#include "IniFile.h"
+#include "CarCtrl.h"
+#include "Population.h"
// Menu screens array is at the bottom of the file.
@@ -63,6 +66,15 @@
#define DUALPASS_SELECTOR
#endif
+#ifdef PED_CAR_DENSITY_SLIDERS
+ // 0.2f - 3.4f makes it possible to have 1.0f somewhere inbetween
+ #define DENSITY_SLIDERS \
+ MENUACTION_CFO_SLIDER, "FEM_PED", { new CCFOSlider(&CIniFile::PedNumberMultiplier, "Display", "PedDensity", 0.2f, 3.4f, PedDensityChange) }, \
+ MENUACTION_CFO_SLIDER, "FEM_CAR", { new CCFOSlider(&CIniFile::CarNumberMultiplier, "Display", "CarDensity", 0.2f, 3.4f, CarDensityChange) },
+#else
+ #define DENSITY_SLIDERS
+#endif
+
#ifdef NO_ISLAND_LOADING
#define ISLAND_LOADING_SELECTOR MENUACTION_CFO_SELECT, "FEM_ISL", { new CCFOSelect((int8*)&CMenuManager::m_PrefsIslandLoading, "Graphics", "IslandLoading", islandLoadingOpts, ARRAY_SIZE(islandLoadingOpts), true, IslandLoadingAfterChange) },
#else
@@ -145,6 +157,9 @@ void RestoreDefDisplay(int8 action) {
#ifdef FREE_CAM
TheCamera.bFreeCam = false;
#endif
+ #ifdef PED_CAR_DENSITY_SLIDERS
+ CIniFile::LoadIniFile();
+ #endif
#ifdef GRAPHICS_MENU_OPTIONS // otherwise Frontend will handle those
CMenuManager::m_PrefsBrightness = 256;
CMenuManager::m_PrefsLOD = 1.2f;
@@ -195,6 +210,16 @@ void IslandLoadingAfterChange(int8 before, int8 after) {
}
#endif
+#ifdef PED_CAR_DENSITY_SLIDERS
+void PedDensityChange(float before, float after) {
+ CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * after;
+}
+
+void CarDensityChange(float before, float after) {
+ CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * after;
+}
+#endif
+
#ifndef MULTISAMPLING
void GraphicsGoBack() {
}
@@ -423,6 +448,7 @@ CMenuScreenCustom aScreens[MENUPAGES] = {
{ "FET_DIS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil,
MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
+ DENSITY_SLIDERS
MENUACTION_FRAMESYNC, "FEM_VSC", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
MENUACTION_FRAMELIMIT, "FEM_FRM", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
#ifndef EXTENDED_COLOURFILTER
@@ -447,6 +473,7 @@ CMenuScreenCustom aScreens[MENUPAGES] = {
{ "FET_DIS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil,
MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
+ DENSITY_SLIDERS
CUTSCENE_BORDERS_TOGGLE
FREE_CAM_TOGGLE
MENUACTION_SUBTITLES, "FED_SUB", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
diff --git a/src/core/Pools.cpp b/src/core/Pools.cpp
index 5cffe9e4..b0248664 100644
--- a/src/core/Pools.cpp
+++ b/src/core/Pools.cpp
@@ -281,9 +281,9 @@ INITSAVEBUF
#else
if ((pVehicle->IsCar() || pVehicle->IsBoat()) && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif
- WriteSaveBuf<uint32>(buf, pVehicle->m_vehType);
- WriteSaveBuf<int16>(buf, pVehicle->GetModelIndex());
- WriteSaveBuf<int32>(buf, GetVehicleRef(pVehicle));
+ WriteSaveBuf(buf, pVehicle->m_vehType);
+ WriteSaveBuf(buf, pVehicle->GetModelIndex());
+ WriteSaveBuf(buf, GetVehicleRef(pVehicle));
pVehicle->Save(buf);
}
#else
@@ -292,7 +292,7 @@ INITSAVEBUF
#else
if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif
- WriteSaveBuf(buf, (uint32)pVehicle->m_vehType);
+ WriteSaveBuf(buf, pVehicle->m_vehType);
WriteSaveBuf(buf, pVehicle->GetModelIndex());
WriteSaveBuf(buf, GetVehicleRef(pVehicle));
memcpy(buf, pVehicle, sizeof(CAutomobile));
@@ -303,7 +303,7 @@ INITSAVEBUF
#else
if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif
- WriteSaveBuf(buf, (uint32)pVehicle->m_vehType);
+ WriteSaveBuf(buf, pVehicle->m_vehType);
WriteSaveBuf(buf, pVehicle->GetModelIndex());
WriteSaveBuf(buf, GetVehicleRef(pVehicle));
memcpy(buf, pVehicle, sizeof(CBoat));
diff --git a/src/core/Zones.cpp b/src/core/Zones.cpp
index 107b1db8..82fbc047 100644
--- a/src/core/Zones.cpp
+++ b/src/core/Zones.cpp
@@ -10,6 +10,14 @@
#include "Timer.h"
#include "SaveBuf.h"
+#ifdef COMPATIBLE_SAVES
+#define ZONEARRAY_SAVE_SIZE 0xAF0
+#define MAPZONEARRAY_SAVE_SIZE 0x578
+#else
+#define ZONEARRAY_SAVE_SIZE sizeof(ZoneArray)
+#define MAPZONEARRAY_SAVE_SIZE sizeof(MapZoneArray)
+#endif
+
eLevelName CTheZones::m_CurrLevel;
CZone *CTheZones::m_pPlayersZone;
int16 CTheZones::FindIndex;
@@ -633,6 +641,28 @@ CTheZones::InitialiseAudioZoneArray(void)
}
}
+#ifdef COMPATIBLE_SAVES
+static inline void
+SaveOneZone(CZone &zone, uint8 *&buffer)
+{
+ memcpy(buffer, zone.name, sizeof(zone.name));
+ SkipSaveBuf(buffer, sizeof(zone.name));
+ WriteSaveBuf(buffer, zone.minx);
+ WriteSaveBuf(buffer, zone.miny);
+ WriteSaveBuf(buffer, zone.minz);
+ WriteSaveBuf(buffer, zone.maxx);
+ WriteSaveBuf(buffer, zone.maxy);
+ WriteSaveBuf(buffer, zone.maxz);
+ WriteSaveBuf(buffer, zone.type);
+ WriteSaveBuf(buffer, zone.level);
+ WriteSaveBuf(buffer, zone.zoneinfoDay);
+ WriteSaveBuf(buffer, zone.zoneinfoNight);
+ WriteSaveBuf(buffer, (int32)CTheZones::GetIndexForZonePointer(zone.child));
+ WriteSaveBuf(buffer, (int32)CTheZones::GetIndexForZonePointer(zone.parent));
+ WriteSaveBuf(buffer, (int32)CTheZones::GetIndexForZonePointer(zone.next));
+}
+#endif
+
void
CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
{
@@ -643,9 +673,9 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
+ sizeof(int32) // GetIndexForZonePointer
+ sizeof(m_CurrLevel) + sizeof(FindIndex)
+ sizeof(int16) // padding
- + sizeof(ZoneArray) + sizeof(ZoneInfoArray)
+ + ZONEARRAY_SAVE_SIZE + sizeof(ZoneInfoArray)
+ sizeof(TotalNumberOfZones) + sizeof(TotalNumberOfZoneInfos)
- + sizeof(MapZoneArray) + sizeof(AudioZoneArray)
+ + MAPZONEARRAY_SAVE_SIZE + sizeof(AudioZoneArray)
+ sizeof(TotalNumberOfMapZones) + sizeof(NumberOfAudioZones);
WriteSaveHeader(buffer, 'Z', 'N', 'S', '\0', *size - SAVE_HEADER_SIZE);
@@ -656,10 +686,14 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
WriteSaveBuf(buffer, (int16)0); // padding
for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){
+#ifdef COMPATIBLE_SAVES
+ SaveOneZone(ZoneArray[i], buffer);
+#else
CZone *zone = WriteSaveBuf(buffer, ZoneArray[i]);
zone->child = (CZone*)GetIndexForZonePointer(ZoneArray[i].child);
zone->parent = (CZone*)GetIndexForZonePointer(ZoneArray[i].parent);
zone->next = (CZone*)GetIndexForZonePointer(ZoneArray[i].next);
+#endif
}
for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
@@ -669,7 +703,9 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
WriteSaveBuf(buffer, TotalNumberOfZoneInfos);
for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) {
+#ifndef COMPATIBLE_SAVES
CZone* zone = WriteSaveBuf(buffer, MapZoneArray[i]);
+#endif
/*
The call of GetIndexForZonePointer is wrong, as it is
@@ -679,9 +715,13 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
assert(MapZoneArray[i].child == nil);
assert(MapZoneArray[i].parent == nil);
assert(MapZoneArray[i].next == nil);
+#ifndef COMPATIBLE_SAVES
zone->child = (CZone*)GetIndexForZonePointer(MapZoneArray[i].child);
zone->parent = (CZone*)GetIndexForZonePointer(MapZoneArray[i].parent);
zone->next = (CZone*)GetIndexForZonePointer(MapZoneArray[i].next);
+#else
+ SaveOneZone(MapZoneArray[i], buffer);
+#endif
}
for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++)
@@ -693,6 +733,32 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
VALIDATESAVEBUF(*size)
}
+#ifdef COMPATIBLE_SAVES
+static inline void
+LoadOneZone(CZone &zone, uint8 *&buffer)
+{
+ memcpy(zone.name, buffer, sizeof(zone.name));
+ SkipSaveBuf(buffer, sizeof(zone.name));
+ ReadSaveBuf(&zone.minx, buffer);
+ ReadSaveBuf(&zone.miny, buffer);
+ ReadSaveBuf(&zone.minz, buffer);
+ ReadSaveBuf(&zone.maxx, buffer);
+ ReadSaveBuf(&zone.maxy, buffer);
+ ReadSaveBuf(&zone.maxz, buffer);
+ ReadSaveBuf(&zone.type, buffer);
+ ReadSaveBuf(&zone.level, buffer);
+ ReadSaveBuf(&zone.zoneinfoDay, buffer);
+ ReadSaveBuf(&zone.zoneinfoNight, buffer);
+ int32 tmp;
+ ReadSaveBuf(&tmp, buffer);
+ zone.child = CTheZones::GetPointerForZoneIndex(tmp);
+ ReadSaveBuf(&tmp, buffer);
+ zone.parent = CTheZones::GetPointerForZoneIndex(tmp);
+ ReadSaveBuf(&tmp, buffer);
+ zone.next = CTheZones::GetPointerForZoneIndex(tmp);
+}
+#endif
+
void
CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
{
@@ -708,11 +774,15 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
SkipSaveBuf(buffer, 2);
for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){
+#ifdef COMPATIBLE_SAVES
+ LoadOneZone(ZoneArray[i], buffer);
+#else
ReadSaveBuf(&ZoneArray[i], buffer);
ZoneArray[i].child = GetPointerForZoneIndex((uintptr)ZoneArray[i].child);
ZoneArray[i].parent = GetPointerForZoneIndex((uintptr)ZoneArray[i].parent);
ZoneArray[i].next = GetPointerForZoneIndex((uintptr)ZoneArray[i].next);
+#endif
}
for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
@@ -722,6 +792,9 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
ReadSaveBuf(&TotalNumberOfZoneInfos, buffer);
for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++){
+#ifdef COMPATIBLE_SAVES
+ LoadOneZone(MapZoneArray[i], buffer);
+#else
ReadSaveBuf(&MapZoneArray[i], buffer);
/*
@@ -732,6 +805,7 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
MapZoneArray[i].child = GetPointerForZoneIndex((uintptr)MapZoneArray[i].child);
MapZoneArray[i].parent = GetPointerForZoneIndex((uintptr)MapZoneArray[i].parent);
MapZoneArray[i].next = GetPointerForZoneIndex((uintptr)MapZoneArray[i].next);
+#endif
assert(MapZoneArray[i].child == nil);
assert(MapZoneArray[i].parent == nil);
assert(MapZoneArray[i].next == nil);
diff --git a/src/core/config.h b/src/core/config.h
index c9339b74..8f5cfb3d 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -1,7 +1,9 @@
#pragma once
-// disables (most) stuff that wasn't in original gta3.exe - check section at the bottom of this file
-//#define VANILLA_DEFINES
+// disables (most) stuff that wasn't in original gta3.exe
+#ifdef __MWERKS__
+#define VANILLA_DEFINES
+#endif
enum Config {
NUMPLAYERS = 1, // 4 on PS2
@@ -11,7 +13,7 @@ enum Config {
MAX_CDCHANNELS = 5,
MODELINFOSIZE = 5500, // 3150 on PS2
-#if defined __MWERKS__ || defined VANILLA_DEFINES
+#ifdef VANILLA_DEFINES
TXDSTORESIZE = 850,
#else
TXDSTORESIZE = 1024, // for Xbox map
@@ -146,8 +148,30 @@ enum Config {
//#define GTA_PS2
//#define GTA_XBOX
-// This enables things from the PS2 version on PC
-#define GTA_PS2_STUFF
+// Version defines
+#define GTA3_PS2_140 300
+#define GTA3_PS2_160 301
+#define GTA3_PC_10 310
+#define GTA3_PC_11 311
+#define GTA3_PC_STEAM 312
+// TODO? maybe something for xbox or android?
+
+#define GTA_VERSION GTA3_PC_11
+
+#if defined GTA_PS2
+# define GTA_PS2_STUFF
+# define RANDOMSPLASH
+# define USE_CUSTOM_ALLOCATOR
+# define VU_COLLISION
+# define ANIM_COMPRESSION
+# define PS2_MENU
+#elif defined GTA_PC
+# define PC_PLAYER_CONTROLS // mouse player/cam mode
+# define GTA_REPLAY
+# define GTA_SCENE_EDIT
+# define PC_MENU
+#elif defined GTA_XBOX
+#endif
// This is enabled for all released games.
// any debug stuff that isn't left in any game is not in FINAL
@@ -166,19 +190,29 @@ enum Config {
#define FINAL
#endif
-// Version defines
-#define GTA3_PS2_140 300
-#define GTA3_PS2_160 301
-#define GTA3_PC_10 310
-#define GTA3_PC_11 311
-#define GTA3_PC_STEAM 312
-// TODO? maybe something for xbox or android?
+// these are placed here to work with VANILLA_DEFINES for compatibility
+#define NO_CDCHECK // skip audio CD check
+#define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch)
-#define GTA_VERSION GTA3_PC_11
+#ifdef VANILLA_DEFINES
+#if !defined(_WIN32) || defined(__LP64__) || defined(_WIN64)
+#error Vanilla can only be built for win-x86
+#endif
+
+#define FINAL
+#define MASTER
+//#define USE_MY_DOCUMENTS
+#define THIS_IS_STUPID
+#define PC_PARTICLE
+#define DONT_FIX_REPLAY_BUGS
+#define USE_TXD_CDIMAGE // generate and load textures from txd.img
+//#define USE_TEXTURE_POOL // not possible because R* used custom RW33
+#else
+// This enables things from the PS2 version on PC
+#define GTA_PS2_STUFF
// quality of life fixes that should also be in FINAL
#define NASTY_GAME // nasty game for all languages
-#define NO_CDCHECK
// those infamous texts
#define DRAW_GAME_VERSION_TEXT
@@ -194,22 +228,10 @@ enum Config {
//#define COMPRESSED_COL_VECTORS // use compressed vectors for collision vertices
//#define ANIM_COMPRESSION // only keep most recently used anims uncompressed
-#if defined GTA_PS2
-# define GTA_PS2_STUFF
-# define RANDOMSPLASH
-# define USE_CUSTOM_ALLOCATOR
-# define VU_COLLISION
-# define ANIM_COMPRESSION
-#elif defined GTA_PC
-# ifdef GTA_PS2_STUFF
-# define USE_PS2_RAND
-# define RANDOMSPLASH // use random splash as on PS2
-# define PS2_MATFX
-# endif
-# define PC_PLAYER_CONTROLS // mouse player/cam mode
-# define GTA_REPLAY
-# define GTA_SCENE_EDIT
-#elif defined GTA_XBOX
+#if defined GTA_PC && defined GTA_PS2_STUFF
+# define USE_PS2_RAND
+# define RANDOMSPLASH // use random splash as on PS2
+# define PS2_MATFX
#endif
#ifdef VU_COLLISION
@@ -237,7 +259,8 @@ enum Config {
#define FIX_BUGS // fixes bugs that we've came across during reversing. You can undefine this only on release builds.
#define MORE_LANGUAGES // Add more translations to the game
-#define COMPATIBLE_SAVES // this allows changing structs while keeping saves compatible
+#define COMPATIBLE_SAVES // this allows changing structs while keeping saves compatible, and keeps saves compatible between platforms
+#define FIX_INCOMPATIBLE_SAVES // try to fix incompatible saves, requires COMPATIBLE_SAVES
#define LOAD_INI_SETTINGS // as the name suggests. fundamental for CUSTOM_FRONTEND_OPTIONS
#define NO_MOVIES // add option to disable intro videos
@@ -248,7 +271,7 @@ enum Config {
#define ASCII_STRCMP // use faster ascii str comparisons
-#if !defined _WIN32 || defined __MWERKS__ || defined __MINGW32__ || defined VANILLA_DEFINES
+#if !defined _WIN32 || defined __MINGW32__
#undef ASCII_STRCMP
#endif
@@ -340,6 +363,7 @@ enum Config {
# define CUTSCENE_BORDERS_SWITCH
# define MULTISAMPLING // adds MSAA option
# define INVERT_LOOK_FOR_PAD // add bInvertLook4Pad from VC
+# define PED_CAR_DENSITY_SLIDERS
# endif
#endif
@@ -396,10 +420,11 @@ enum Config {
#define RADIO_SCROLL_TO_PREV_STATION
#define AUDIO_CACHE
#define PS2_AUDIO_CHANNELS // increases the maximum number of audio channels to PS2 value of 44 (PC has 28 originally)
-//#define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds)
+#define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds)
//#define AUDIO_OAL_USE_SNDFILE // use libsndfile to decode WAVs instead of our internal decoder
#define AUDIO_OAL_USE_MPG123 // use mpg123 to support mp3 files
#define PAUSE_RADIO_IN_FRONTEND // pause radio when game is paused
+#define MULTITHREADED_AUDIO // for streams. requires C++11 or later
#ifdef AUDIO_OPUS
#define AUDIO_OAL_USE_OPUS // enable support of opus files
@@ -436,97 +461,4 @@ enum Config {
#undef PEDS_REPORT_CRIMES_ON_PHONE
#endif
-// -------
-
-#if defined __MWERKS__ || defined VANILLA_DEFINES
-#define FINAL
-#undef CHATTYSPLASH
-#undef TIMEBARS
-//#define USE_MY_DOCUMENTS
-
-#define MASTER
-#undef VALIDATE_SAVE_SIZE
-#undef NO_MOVIES
-#undef DEBUGMENU
-
-//#undef NASTY_GAME
-//#undef NO_CDCHECK
-
-#undef DRAW_GAME_VERSION_TEXT
-#undef DRAW_MENU_VERSION_TEXT
-
-#undef GTA_PS2_STUFF
-#undef USE_PS2_RAND
-#undef RANDOMSPLASH
-#undef PS2_MATFX
-
-#undef FIX_BUGS
-#define THIS_IS_STUPID
-#undef MORE_LANGUAGES
-#undef COMPATIBLE_SAVES
-#undef LOAD_INI_SETTINGS
-
-#undef ASPECT_RATIO_SCALE
-#undef PROPER_SCALING
-//#undef DEFAULT_NATIVE_RESOLUTION
-#undef PS2_ALPHA_TEST
-#undef IMPROVED_VIDEOMODE
-#undef DISABLE_LOADING_SCREEN
-#undef DISABLE_VSYNC_ON_TEXTURE_CONVERSION
-#undef ANISOTROPIC_FILTERING
-//#define USE_TEXTURE_POOL // not possible because R* used custom RW33
-
-#undef EXTENDED_COLOURFILTER
-#undef EXTENDED_PIPELINES
-#undef SCREEN_DROPLETS
-#undef NEW_RENDERER
-
-#undef FIX_SPRITES
-
-#define PC_PARTICLE
-
-#undef XINPUT
-#undef DETECT_PAD_INPUT_SWITCH
-#undef KANGAROO_CHEAT
-#undef ALLCARSHELI_CHEAT
-#undef ALT_DODO_CHEAT
-#undef REGISTER_START_BUTTON
-#undef BIND_VEHICLE_FIREWEAPON
-#undef BUTTON_ICONS
-
-#undef HUD_ENHANCEMENTS
-#undef TRIANGULAR_BLIPS
-#undef FIX_RADAR
-#undef RADIO_OFF_TEXT
-
-#undef MENU_MAP
-#undef GAMEPAD_MENU
-#undef SCROLLABLE_STATS_PAGE
-#undef CUSTOM_FRONTEND_OPTIONS
-
-#undef GRAPHICS_MENU_OPTIONS
-#undef NO_ISLAND_LOADING
-#undef CUTSCENE_BORDERS_SWITCH
-#undef MULTISAMPLING
-#undef INVERT_LOOK_FOR_PAD
-
-#undef USE_DEBUG_SCRIPT_LOADER
-#undef USE_MEASUREMENTS_IN_METERS
-#undef USE_PRECISE_MEASUREMENT_CONVERTION
-#undef MISSION_REPLAY
-#undef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT
-#undef USE_BASIC_SCRIPT_DEBUG_OUTPUT
-
-#define DONT_FIX_REPLAY_BUGS
-
-#undef EXPLODING_AIRTRAIN
-#undef CAMERA_PICKUP
-#undef PED_SKIN
-#undef ANIMATE_PED_COL_MODEL
-#undef CANCELLABLE_CAR_ENTER
-#undef IMPROVED_CAMERA
-#undef FREE_CAM
-#undef RADIO_SCROLL_TO_PREV_STATION
-#undef BIG_IMG
-#undef PS2_AUDIO_CHANNELS
-#endif
+#endif // VANILLA_DEFINES
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index fe0347d9..b7d89363 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -41,6 +41,9 @@
#include "Camera.h"
#include "MBlur.h"
#include "ControllerConfig.h"
+#include "CarCtrl.h"
+#include "Population.h"
+#include "IniFile.h"
#ifdef DETECT_JOYSTICK_MENU
#include "crossplatform.h"
@@ -179,16 +182,29 @@ CustomFrontendOptionsPopulate(void)
#endif
#ifdef LOAD_INI_SETTINGS
-#include "ini_parser.hpp"
+#define MINI_CASE_SENSITIVE
+#include "ini.h"
+
+mINI::INIFile ini("re3.ini");
+mINI::INIStructure cfg;
-linb::ini cfg;
bool ReadIniIfExists(const char *cat, const char *key, uint32 *out)
{
- std::string strval = cfg.get(cat, key, "\xBA");
- const char *value = strval.c_str();
- char *endPtr;
- if (value && value[0] != '\xBA') {
- *out = strtoul(value, &endPtr, 0);
+ mINI::INIMap<std::string> section = cfg.get(cat);
+ if (section.has(key)) {
+ char *endPtr;
+ *out = strtoul(section.get(key).c_str(), &endPtr, 0);
+ return true;
+ }
+ return false;
+}
+
+bool ReadIniIfExists(const char *cat, const char *key, uint8 *out)
+{
+ mINI::INIMap<std::string> section = cfg.get(cat);
+ if (section.has(key)) {
+ char *endPtr;
+ *out = strtoul(section.get(key).c_str(), &endPtr, 0);
return true;
}
return false;
@@ -196,11 +212,10 @@ bool ReadIniIfExists(const char *cat, const char *key, uint32 *out)
bool ReadIniIfExists(const char *cat, const char *key, bool *out)
{
- std::string strval = cfg.get(cat, key, "\xBA");
- const char *value = strval.c_str();
- char *endPtr;
- if (value && value[0] != '\xBA') {
- *out = strtoul(value, &endPtr, 0);
+ mINI::INIMap<std::string> section = cfg.get(cat);
+ if (section.has(key)) {
+ char *endPtr;
+ *out = strtoul(section.get(key).c_str(), &endPtr, 0);
return true;
}
return false;
@@ -208,11 +223,10 @@ bool ReadIniIfExists(const char *cat, const char *key, bool *out)
bool ReadIniIfExists(const char *cat, const char *key, int32 *out)
{
- std::string strval = cfg.get(cat, key, "\xBA");
- const char *value = strval.c_str();
- char *endPtr;
- if (value && value[0] != '\xBA') {
- *out = strtol(value, &endPtr, 0);
+ mINI::INIMap<std::string> section = cfg.get(cat);
+ if (section.has(key)) {
+ char *endPtr;
+ *out = strtol(section.get(key).c_str(), &endPtr, 0);
return true;
}
return false;
@@ -220,11 +234,10 @@ bool ReadIniIfExists(const char *cat, const char *key, int32 *out)
bool ReadIniIfExists(const char *cat, const char *key, int8 *out)
{
- std::string strval = cfg.get(cat, key, "\xBA");
- const char *value = strval.c_str();
- char *endPtr;
- if (value && value[0] != '\xBA') {
- *out = strtol(value, &endPtr, 0);
+ mINI::INIMap<std::string> section = cfg.get(cat);
+ if (section.has(key)) {
+ char *endPtr;
+ *out = strtol(section.get(key).c_str(), &endPtr, 0);
return true;
}
return false;
@@ -232,10 +245,10 @@ bool ReadIniIfExists(const char *cat, const char *key, int8 *out)
bool ReadIniIfExists(const char *cat, const char *key, float *out)
{
- std::string strval = cfg.get(cat, key, "\xBA");
- const char *value = strval.c_str();
- if (value && value[0] != '\xBA') {
- *out = atof(value);
+ mINI::INIMap<std::string> section = cfg.get(cat);
+ if (section.has(key)) {
+ char *endPtr;
+ *out = strtof(section.get(key).c_str(), &endPtr);
return true;
}
return false;
@@ -243,10 +256,10 @@ bool ReadIniIfExists(const char *cat, const char *key, float *out)
bool ReadIniIfExists(const char *cat, const char *key, char *out, int size)
{
- std::string strval = cfg.get(cat, key, "\xBA");
- const char *value = strval.c_str();
- if (value && value[0] != '\xBA') {
- strncpy(out, value, size);
+ mINI::INIMap<std::string> section = cfg.get(cat);
+ if (section.has(key)) {
+ strncpy(out, section.get(key).c_str(), size - 1);
+ out[size - 1] = '\0';
return true;
}
return false;
@@ -254,42 +267,42 @@ bool ReadIniIfExists(const char *cat, const char *key, char *out, int size)
void StoreIni(const char *cat, const char *key, uint32 val)
{
- char temp[10];
+ char temp[11];
sprintf(temp, "%u", val);
- cfg.set(cat, key, temp);
+ cfg[cat][key] = temp;
}
void StoreIni(const char *cat, const char *key, uint8 val)
{
- char temp[10];
- sprintf(temp, "%u", (uint32)val);
- cfg.set(cat, key, temp);
+ char temp[11];
+ sprintf(temp, "%u", val);
+ cfg[cat][key] = temp;
}
void StoreIni(const char *cat, const char *key, int32 val)
{
- char temp[10];
+ char temp[11];
sprintf(temp, "%d", val);
- cfg.set(cat, key, temp);
+ cfg[cat][key] = temp;
}
void StoreIni(const char *cat, const char *key, int8 val)
{
- char temp[10];
- sprintf(temp, "%d", (int32)val);
- cfg.set(cat, key, temp);
+ char temp[11];
+ sprintf(temp, "%d", val);
+ cfg[cat][key] = temp;
}
void StoreIni(const char *cat, const char *key, float val)
{
- char temp[10];
+ char temp[50];
sprintf(temp, "%f", val);
- cfg.set(cat, key, temp);
+ cfg[cat][key] = temp;
}
void StoreIni(const char *cat, const char *key, char *val, int size)
{
- cfg.set(cat, key, val);
+ cfg[cat][key] = val;
}
const char *iniControllerActions[] = { "PED_FIREWEAPON", "PED_CYCLE_WEAPON_RIGHT", "PED_CYCLE_WEAPON_LEFT", "GO_FORWARD", "GO_BACK", "GO_LEFT", "GO_RIGHT", "PED_SNIPER_ZOOM_IN",
@@ -351,7 +364,7 @@ void LoadINIControllerSettings()
#endif
// force to default GTA behaviour (never overwrite bindings on joy change/initialization) if user init'ed/set bindings before we introduced that
if (!ReadIniIfExists("Controller", "PadButtonsInited", &ControlsManager.ms_padButtonsInited)) {
- ControlsManager.ms_padButtonsInited = cfg.category_size("Bindings") != 0 ? 16 : 0;
+ ControlsManager.ms_padButtonsInited = cfg.get("Bindings").size() != 0 ? 16 : 0;
}
for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) {
@@ -453,12 +466,13 @@ void SaveINIControllerSettings()
#endif
#endif
StoreIni("Controller", "PadButtonsInited", ControlsManager.ms_padButtonsInited);
- cfg.write_file("re3.ini");
+
+ ini.write(cfg);
}
bool LoadINISettings()
{
- if (!cfg.load_file("re3.ini"))
+ if (!ini.read(cfg))
return false;
#ifdef IMPROVED_VIDEOMODE
@@ -524,7 +538,7 @@ bool LoadINISettings()
#endif
#ifdef CUSTOM_FRONTEND_OPTIONS
- bool migrate = cfg.category_size("FrontendOptions") != 0;
+ bool migrate = cfg.get("FrontendOptions").size() != 0;
for (int i = 0; i < MENUPAGES; i++) {
for (int j = 0; j < NUM_MENUROWS; j++) {
CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j];
@@ -533,22 +547,29 @@ bool LoadINISettings()
// CFO check
if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) {
- // CFO only supports saving uint8 right now
-
// Migrate from old .ini to new .ini
- if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, option.m_CFO->value))
- cfg.remove("FrontendOptions", option.m_CFO->save);
+ // Old values can only be int8, new ones can contain float if it is slider
+ if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, (int8*)option.m_CFO->value))
+ cfg["FrontendOptions"].remove(option.m_CFO->save);
+ else if (option.m_Action == MENUACTION_CFO_SLIDER)
+ ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (float*)option.m_CFO->value);
else
- ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, option.m_CFO->value);
+ ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (int8*)option.m_CFO->value);
if (option.m_Action == MENUACTION_CFO_SELECT) {
- option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *option.m_CFO->value;
+ option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value;
}
}
}
}
#endif
+ // Fetched in above block, but needs evaluation
+#ifdef PED_CAR_DENSITY_SLIDERS
+ CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * CIniFile::PedNumberMultiplier;
+ CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CIniFile::CarNumberMultiplier;
+#endif
+
return true;
}
@@ -623,14 +644,16 @@ void SaveINISettings()
break;
if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) {
- // Beware: CFO only supports saving uint8 right now
- StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *option.m_CFO->value);
+ if (option.m_Action == MENUACTION_CFO_SLIDER)
+ StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(float*)option.m_CFO->value);
+ else
+ StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(int8*)option.m_CFO->value);
}
}
}
#endif
- cfg.write_file("re3.ini");
+ ini.write(cfg);
}
#endif
diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp
index a7f4bd45..c38f12c7 100644
--- a/src/entities/Entity.cpp
+++ b/src/entities/Entity.cpp
@@ -732,7 +732,7 @@ CEntity::SaveEntityFlags(uint8*& buf)
if (bZoneCulled) tmp |= BIT(30);
if (bZoneCulled2) tmp |= BIT(31);
- WriteSaveBuf<uint32>(buf, tmp);
+ WriteSaveBuf(buf, tmp);
tmp = 0;
@@ -748,7 +748,7 @@ CEntity::SaveEntityFlags(uint8*& buf)
if (bDistanceFade) tmp |= BIT(8);
if (m_flagE2) tmp |= BIT(9);
- WriteSaveBuf<uint32>(buf, tmp);
+ WriteSaveBuf(buf, tmp);
}
void
diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp
index 182ff46b..591fb501 100644
--- a/src/entities/Physical.cpp
+++ b/src/entities/Physical.cpp
@@ -480,6 +480,10 @@ CPhysical::ApplySpringDampening(float damping, CVector &springDir, CVector &poin
{
float speedA = DotProduct(speed, springDir);
float speedB = DotProduct(GetSpeed(point), springDir);
+#ifdef FIX_BUGS
+ if (speedB == 0.0f)
+ return true;
+#endif
float step = Min(CTimer::GetTimeStep(), 3.0f);
float impulse = -damping * (speedA + speedB)/2.0f * m_fMass * step * 0.53f;
diff --git a/src/extras/frontendoption.h b/src/extras/frontendoption.h
index 8b64335a..a571170f 100644
--- a/src/extras/frontendoption.h
+++ b/src/extras/frontendoption.h
@@ -40,6 +40,8 @@ typedef void (*ReturnPrevPageFunc)();
typedef void (*ChangeFunc)(int8 before, int8 after); // called after updating the value.
// only called on enter if onlyApplyOnEnter set, otherwise called on every value change
+typedef void (*ChangeFuncFloat)(float before, float after); // called after updating the value.
+
// for dynamic options
typedef wchar* (*DrawFunc)(bool* disabled, bool userHovering); // you must return a pointer for right text.
// you can also set *disabled if you want to gray it out.
diff --git a/src/extras/ini.h b/src/extras/ini.h
new file mode 100644
index 00000000..44dd3d57
--- /dev/null
+++ b/src/extras/ini.h
@@ -0,0 +1,761 @@
+/*
+ * The MIT License (MIT)
+ * Copyright (c) 2018 Danijel Durakovic
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// /mINI/ v0.9.10
+// An INI file reader and writer for the modern age.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// A tiny utility library for manipulating INI files with a straightforward
+// API and a minimal footprint. It conforms to the (somewhat) standard INI
+// format - sections and keys are case insensitive and all leading and
+// trailing whitespace is ignored. Comments are lines that begin with a
+// semicolon. Trailing comments are allowed on section lines.
+//
+// Files are read on demand, upon which data is kept in memory and the file
+// is closed. This utility supports lazy writing, which only writes changes
+// and updates to a file and preserves custom formatting and comments. A lazy
+// write invoked by a write() call will read the output file, find what
+// changes have been made and update the file accordingly. If you only need to
+// generate files, use generate() instead. Section and key order is preserved
+// on read, write and insert.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// /* BASIC USAGE EXAMPLE: */
+//
+// /* read from file */
+// mINI::INIFile file("myfile.ini");
+// mINI::INIStructure ini;
+// file.read(ini);
+//
+// /* read value; gets a reference to actual value in the structure.
+// if key or section don't exist, a new empty value will be created */
+// std::string& value = ini["section"]["key"];
+//
+// /* read value safely; gets a copy of value in the structure.
+// does not alter the structure */
+// std::string value = ini.get("section").get("key");
+//
+// /* set or update values */
+// ini["section"]["key"] = "value";
+//
+// /* set multiple values */
+// ini["section2"].set({
+// {"key1", "value1"},
+// {"key2", "value2"}
+// });
+//
+// /* write updates back to file, preserving comments and formatting */
+// file.write(ini);
+//
+// /* or generate a file (overwrites the original) */
+// file.generate(ini);
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// Long live the INI file!!!
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MINI_INI_H_
+#define MINI_INI_H_
+
+#include <string>
+#include <sstream>
+#include <algorithm>
+#include <utility>
+#include <unordered_map>
+#include <vector>
+#include <memory>
+#include <fstream>
+#include <sys/stat.h>
+#include <cctype>
+
+namespace mINI
+{
+ namespace INIStringUtil
+ {
+ const char* const whitespaceDelimiters = " \t\n\r\f\v";
+ inline void trim(std::string& str)
+ {
+ str.erase(str.find_last_not_of(whitespaceDelimiters) + 1);
+ str.erase(0, str.find_first_not_of(whitespaceDelimiters));
+ }
+#ifndef MINI_CASE_SENSITIVE
+ inline void toLower(std::string& str)
+ {
+ std::transform(str.begin(), str.end(), str.begin(), [](const char c) {
+ return static_cast<const char>(std::tolower(c));
+ });
+ }
+#endif
+ inline void replace(std::string& str, std::string const& a, std::string const& b)
+ {
+ if (!a.empty())
+ {
+ std::size_t pos = 0;
+ while ((pos = str.find(a, pos)) != std::string::npos)
+ {
+ str.replace(pos, a.size(), b);
+ pos += b.size();
+ }
+ }
+ }
+#ifdef _WIN32
+ const char* const endl = "\r\n";
+#else
+ const char* const endl = "\n";
+#endif
+ };
+
+ template<typename T>
+ class INIMap
+ {
+ private:
+ using T_DataIndexMap = std::unordered_map<std::string, std::size_t>;
+ using T_DataItem = std::pair<std::string, T>;
+ using T_DataContainer = std::vector<T_DataItem>;
+ using T_MultiArgs = typename std::vector<std::pair<std::string, T>>;
+
+ T_DataIndexMap dataIndexMap;
+ T_DataContainer data;
+
+ inline std::size_t setEmpty(std::string& key)
+ {
+ std::size_t index = data.size();
+ dataIndexMap[key] = index;
+ data.emplace_back(key, T());
+ return index;
+ }
+
+ public:
+ using const_iterator = typename T_DataContainer::const_iterator;
+
+ INIMap() { }
+
+ INIMap(INIMap const& other)
+ {
+ std::size_t data_size = other.data.size();
+ for (std::size_t i = 0; i < data_size; ++i)
+ {
+ auto const& key = other.data[i].first;
+ auto const& obj = other.data[i].second;
+ data.emplace_back(key, obj);
+ }
+ dataIndexMap = T_DataIndexMap(other.dataIndexMap);
+ }
+
+ T& operator[](std::string key)
+ {
+ INIStringUtil::trim(key);
+#ifndef MINI_CASE_SENSITIVE
+ INIStringUtil::toLower(key);
+#endif
+ auto it = dataIndexMap.find(key);
+ bool hasIt = (it != dataIndexMap.end());
+ std::size_t index = (hasIt) ? it->second : setEmpty(key);
+ return data[index].second;
+ }
+ T get(std::string key) const
+ {
+ INIStringUtil::trim(key);
+#ifndef MINI_CASE_SENSITIVE
+ INIStringUtil::toLower(key);
+#endif
+ auto it = dataIndexMap.find(key);
+ if (it == dataIndexMap.end())
+ {
+ return T();
+ }
+ return T(data[it->second].second);
+ }
+ bool has(std::string key) const
+ {
+ INIStringUtil::trim(key);
+#ifndef MINI_CASE_SENSITIVE
+ INIStringUtil::toLower(key);
+#endif
+ return (dataIndexMap.count(key) == 1);
+ }
+ void set(std::string key, T obj)
+ {
+ INIStringUtil::trim(key);
+#ifndef MINI_CASE_SENSITIVE
+ INIStringUtil::toLower(key);
+#endif
+ auto it = dataIndexMap.find(key);
+ if (it != dataIndexMap.end())
+ {
+ data[it->second].second = obj;
+ }
+ else
+ {
+ dataIndexMap[key] = data.size();
+ data.emplace_back(key, obj);
+ }
+ }
+ void set(T_MultiArgs const& multiArgs)
+ {
+ for (auto const& it : multiArgs)
+ {
+ auto const& key = it.first;
+ auto const& obj = it.second;
+ set(key, obj);
+ }
+ }
+ bool remove(std::string key)
+ {
+ INIStringUtil::trim(key);
+#ifndef MINI_CASE_SENSITIVE
+ INIStringUtil::toLower(key);
+#endif
+ auto it = dataIndexMap.find(key);
+ if (it != dataIndexMap.end())
+ {
+ std::size_t index = it->second;
+ data.erase(data.begin() + index);
+ dataIndexMap.erase(it);
+ for (auto& it2 : dataIndexMap)
+ {
+ auto& vi = it2.second;
+ if (vi > index)
+ {
+ vi--;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ void clear()
+ {
+ data.clear();
+ dataIndexMap.clear();
+ }
+ std::size_t size() const
+ {
+ return data.size();
+ }
+ const_iterator begin() const { return data.begin(); }
+ const_iterator end() const { return data.end(); }
+ };
+
+ using INIStructure = INIMap<INIMap<std::string>>;
+
+ namespace INIParser
+ {
+ using T_ParseValues = std::pair<std::string, std::string>;
+
+ enum class PDataType : char
+ {
+ PDATA_NONE,
+ PDATA_COMMENT,
+ PDATA_SECTION,
+ PDATA_KEYVALUE,
+ PDATA_UNKNOWN
+ };
+
+ inline PDataType parseLine(std::string line, T_ParseValues& parseData)
+ {
+ parseData.first.clear();
+ parseData.second.clear();
+ INIStringUtil::trim(line);
+ if (line.empty())
+ {
+ return PDataType::PDATA_NONE;
+ }
+ char firstCharacter = line[0];
+ if (firstCharacter == ';')
+ {
+ return PDataType::PDATA_COMMENT;
+ }
+ if (firstCharacter == '[')
+ {
+ auto commentAt = line.find_first_of(';');
+ if (commentAt != std::string::npos)
+ {
+ line = line.substr(0, commentAt);
+ }
+ auto closingBracketAt = line.find_last_of(']');
+ if (closingBracketAt != std::string::npos)
+ {
+ auto section = line.substr(1, closingBracketAt - 1);
+ INIStringUtil::trim(section);
+ parseData.first = section;
+ return PDataType::PDATA_SECTION;
+ }
+ }
+ auto lineNorm = line;
+ INIStringUtil::replace(lineNorm, "\\=", " ");
+ auto equalsAt = lineNorm.find_first_of('=');
+ if (equalsAt != std::string::npos)
+ {
+ auto key = line.substr(0, equalsAt);
+ INIStringUtil::trim(key);
+ INIStringUtil::replace(key, "\\=", "=");
+ auto value = line.substr(equalsAt + 1);
+ INIStringUtil::trim(value);
+ parseData.first = key;
+ parseData.second = value;
+ return PDataType::PDATA_KEYVALUE;
+ }
+ return PDataType::PDATA_UNKNOWN;
+ }
+ };
+
+ class INIReader
+ {
+ public:
+ using T_LineData = std::vector<std::string>;
+ using T_LineDataPtr = std::shared_ptr<T_LineData>;
+
+ private:
+ std::ifstream fileReadStream;
+ T_LineDataPtr lineData;
+
+ T_LineData readFile()
+ {
+ std::string fileContents;
+ fileReadStream.seekg(0, std::ios::end);
+ fileContents.resize(fileReadStream.tellg());
+ fileReadStream.seekg(0, std::ios::beg);
+ std::size_t fileSize = fileContents.size();
+ fileReadStream.read(&fileContents[0], fileSize);
+ fileReadStream.close();
+ T_LineData output;
+ if (fileSize == 0)
+ {
+ return output;
+ }
+ std::string buffer;
+ buffer.reserve(50);
+ for (std::size_t i = 0; i < fileSize; ++i)
+ {
+ char& c = fileContents[i];
+ if (c == '\n')
+ {
+ output.emplace_back(buffer);
+ buffer.clear();
+ continue;
+ }
+ if (c != '\0' && c != '\r')
+ {
+ buffer += c;
+ }
+ }
+ output.emplace_back(buffer);
+ return output;
+ }
+
+ public:
+ INIReader(std::string const& filename, bool keepLineData = false)
+ {
+ fileReadStream.open(filename, std::ios::in | std::ios::binary);
+ if (keepLineData)
+ {
+ lineData = std::make_shared<T_LineData>();
+ }
+ }
+ ~INIReader() { }
+
+ bool operator>>(INIStructure& data)
+ {
+ if (!fileReadStream.is_open())
+ {
+ return false;
+ }
+ T_LineData fileLines = readFile();
+ std::string section;
+ bool inSection = false;
+ INIParser::T_ParseValues parseData;
+ for (auto const& line : fileLines)
+ {
+ auto parseResult = INIParser::parseLine(line, parseData);
+ if (parseResult == INIParser::PDataType::PDATA_SECTION)
+ {
+ inSection = true;
+ data[section = parseData.first];
+ }
+ else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE)
+ {
+ auto const& key = parseData.first;
+ auto const& value = parseData.second;
+ data[section][key] = value;
+ }
+ if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN)
+ {
+ if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection)
+ {
+ continue;
+ }
+ lineData->emplace_back(line);
+ }
+ }
+ return true;
+ }
+ T_LineDataPtr getLines()
+ {
+ return lineData;
+ }
+ };
+
+ class INIGenerator
+ {
+ private:
+ std::ofstream fileWriteStream;
+
+ public:
+ bool prettyPrint = false;
+
+ INIGenerator(std::string const& filename)
+ {
+ fileWriteStream.open(filename, std::ios::out | std::ios::binary);
+ }
+ ~INIGenerator() { }
+
+ bool operator<<(INIStructure const& data)
+ {
+ if (!fileWriteStream.is_open())
+ {
+ return false;
+ }
+ if (!data.size())
+ {
+ return true;
+ }
+ auto it = data.begin();
+ for (;;)
+ {
+ auto const& section = it->first;
+ auto const& collection = it->second;
+ fileWriteStream
+ << "["
+ << section
+ << "]";
+ if (collection.size())
+ {
+ fileWriteStream << INIStringUtil::endl;
+ auto it2 = collection.begin();
+ for (;;)
+ {
+ auto key = it2->first;
+ INIStringUtil::replace(key, "=", "\\=");
+ auto value = it2->second;
+ INIStringUtil::trim(value);
+ fileWriteStream
+ << key
+ << ((prettyPrint) ? " = " : "=")
+ << value;
+ if (++it2 == collection.end())
+ {
+ break;
+ }
+ fileWriteStream << INIStringUtil::endl;
+ }
+ }
+ if (++it == data.end())
+ {
+ break;
+ }
+ fileWriteStream << INIStringUtil::endl;
+ if (prettyPrint)
+ {
+ fileWriteStream << INIStringUtil::endl;
+ }
+ }
+ return true;
+ }
+ };
+
+ class INIWriter
+ {
+ private:
+ using T_LineData = std::vector<std::string>;
+ using T_LineDataPtr = std::shared_ptr<T_LineData>;
+
+ std::string filename;
+
+ T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original)
+ {
+ T_LineData output;
+ INIParser::T_ParseValues parseData;
+ std::string sectionCurrent;
+ bool parsingSection = false;
+ bool continueToNextSection = false;
+ bool discardNextEmpty = false;
+ bool writeNewKeys = false;
+ std::size_t lastKeyLine = 0;
+ for (auto line = lineData->begin(); line != lineData->end(); ++line)
+ {
+ if (!writeNewKeys)
+ {
+ auto parseResult = INIParser::parseLine(*line, parseData);
+ if (parseResult == INIParser::PDataType::PDATA_SECTION)
+ {
+ if (parsingSection)
+ {
+ writeNewKeys = true;
+ parsingSection = false;
+ --line;
+ continue;
+ }
+ sectionCurrent = parseData.first;
+ if (data.has(sectionCurrent))
+ {
+ parsingSection = true;
+ continueToNextSection = false;
+ discardNextEmpty = false;
+ output.emplace_back(*line);
+ lastKeyLine = output.size();
+ }
+ else
+ {
+ continueToNextSection = true;
+ discardNextEmpty = true;
+ continue;
+ }
+ }
+ else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE)
+ {
+ if (continueToNextSection)
+ {
+ continue;
+ }
+ if (data.has(sectionCurrent))
+ {
+ auto& collection = data[sectionCurrent];
+ auto const& key = parseData.first;
+ auto const& value = parseData.second;
+ if (collection.has(key))
+ {
+ auto outputValue = collection[key];
+ if (value == outputValue)
+ {
+ output.emplace_back(*line);
+ }
+ else
+ {
+ INIStringUtil::trim(outputValue);
+ auto lineNorm = *line;
+ INIStringUtil::replace(lineNorm, "\\=", " ");
+ auto equalsAt = lineNorm.find_first_of('=');
+ auto valueAt = lineNorm.find_first_not_of(
+ INIStringUtil::whitespaceDelimiters,
+ equalsAt + 1
+ );
+ std::string outputLine = line->substr(0, valueAt);
+ if (prettyPrint && equalsAt + 1 == valueAt)
+ {
+ outputLine += " ";
+ }
+ outputLine += outputValue;
+ output.emplace_back(outputLine);
+ }
+ lastKeyLine = output.size();
+ }
+ }
+ }
+ else
+ {
+ if (discardNextEmpty && line->empty())
+ {
+ discardNextEmpty = false;
+ }
+ else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN)
+ {
+ output.emplace_back(*line);
+ }
+ }
+ }
+ if (writeNewKeys || std::next(line) == lineData->end())
+ {
+ T_LineData linesToAdd;
+ if (data.has(sectionCurrent) && original.has(sectionCurrent))
+ {
+ auto const& collection = data[sectionCurrent];
+ auto const& collectionOriginal = original[sectionCurrent];
+ for (auto const& it : collection)
+ {
+ auto key = it.first;
+ if (collectionOriginal.has(key))
+ {
+ continue;
+ }
+ auto value = it.second;
+ INIStringUtil::replace(key, "=", "\\=");
+ INIStringUtil::trim(value);
+ linesToAdd.emplace_back(
+ key + ((prettyPrint) ? " = " : "=") + value
+ );
+ }
+ }
+ if (!linesToAdd.empty())
+ {
+ output.insert(
+ output.begin() + lastKeyLine,
+ linesToAdd.begin(),
+ linesToAdd.end()
+ );
+ }
+ if (writeNewKeys)
+ {
+ writeNewKeys = false;
+ --line;
+ }
+ }
+ }
+ for (auto const& it : data)
+ {
+ auto const& section = it.first;
+ if (original.has(section))
+ {
+ continue;
+ }
+ if (prettyPrint && output.size() > 0 && !output.back().empty())
+ {
+ output.emplace_back();
+ }
+ output.emplace_back("[" + section + "]");
+ auto const& collection = it.second;
+ for (auto const& it2 : collection)
+ {
+ auto key = it2.first;
+ auto value = it2.second;
+ INIStringUtil::replace(key, "=", "\\=");
+ INIStringUtil::trim(value);
+ output.emplace_back(
+ key + ((prettyPrint) ? " = " : "=") + value
+ );
+ }
+ }
+ return output;
+ }
+
+ public:
+ bool prettyPrint = false;
+
+ INIWriter(std::string const& filename)
+ : filename(filename)
+ {
+ }
+ ~INIWriter() { }
+
+ bool operator<<(INIStructure& data)
+ {
+ struct stat buf;
+ bool fileExists = (stat(filename.c_str(), &buf) == 0);
+ if (!fileExists)
+ {
+ INIGenerator generator(filename);
+ generator.prettyPrint = prettyPrint;
+ return generator << data;
+ }
+ INIStructure originalData;
+ T_LineDataPtr lineData;
+ bool readSuccess = false;
+ {
+ INIReader reader(filename, true);
+ if ((readSuccess = reader >> originalData))
+ {
+ lineData = reader.getLines();
+ }
+ }
+ if (!readSuccess)
+ {
+ return false;
+ }
+ T_LineData output = getLazyOutput(lineData, data, originalData);
+ std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary);
+ if (fileWriteStream.is_open())
+ {
+ if (output.size())
+ {
+ auto line = output.begin();
+ for (;;)
+ {
+ fileWriteStream << *line;
+ if (++line == output.end())
+ {
+ break;
+ }
+ fileWriteStream << INIStringUtil::endl;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+
+ class INIFile
+ {
+ private:
+ std::string filename;
+
+ public:
+ INIFile(std::string const& filename)
+ : filename(filename)
+ { }
+
+ ~INIFile() { }
+
+ bool read(INIStructure& data) const
+ {
+ if (data.size())
+ {
+ data.clear();
+ }
+ if (filename.empty())
+ {
+ return false;
+ }
+ INIReader reader(filename);
+ return reader >> data;
+ }
+ bool generate(INIStructure const& data, bool pretty = false) const
+ {
+ if (filename.empty())
+ {
+ return false;
+ }
+ INIGenerator generator(filename);
+ generator.prettyPrint = pretty;
+ return generator << data;
+ }
+ bool write(INIStructure& data, bool pretty = false) const
+ {
+ if (filename.empty())
+ {
+ return false;
+ }
+ INIWriter writer(filename);
+ writer.prettyPrint = pretty;
+ return writer << data;
+ }
+ };
+}
+
+#endif // MINI_INI_H_
diff --git a/src/extras/ini_parser.hpp b/src/extras/ini_parser.hpp
deleted file mode 100644
index 7bea024c..00000000
--- a/src/extras/ini_parser.hpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (c) 2013-2015 Denilson das Mercês Amorim <dma_2012@hotmail.com>
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- *
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- *
- * 3. This notice may not be removed or altered from any source
- * distribution.
- *
- */
-#ifndef LINB_INI_PARSER_HPP
-#define LINB_INI_PARSER_HPP
-
-/*
- * STL-like INI Container
- */
-
-#include <string> // for std::string
-#include <map> // for std::map
-#include <cstdio> // for std::FILE
-#include <algorithm> // for std::find_if
-#include <functional> // for std::function
-
-namespace linb
-{
- template<
- class CharT = char, /* Not compatible with other type here, since we're using C streams */
- class StringType = std::basic_string<CharT>,
- class KeyContainer = std::map<StringType, StringType>,
- class SectionContainer = std::map<StringType, KeyContainer>
- > class basic_ini
- {
- public:
- typedef CharT char_type;
- typedef StringType string_type;
- typedef KeyContainer key_container;
- typedef SectionContainer section_container;
-
- // Typedef container values types
- typedef typename section_container::value_type value_type;
- typedef typename section_container::key_type key_type;
- typedef typename section_container::mapped_type mapped_type;
-
- // Typedef common types
- typedef typename section_container::size_type size_type;
- typedef typename section_container::difference_type difference_type;
-
- // Typedef iterators
- typedef typename section_container::iterator iterator;
- typedef typename section_container::const_iterator const_iterator;
- typedef typename section_container::reverse_iterator reverse_iterator;
- typedef typename section_container::const_reverse_iterator const_reverse_iterator;
-
- // typedef References and pointers
- typedef typename section_container::reference reference;
- typedef typename section_container::const_reference const_reference;
- typedef typename section_container::pointer pointer;
- typedef typename section_container::const_pointer const_pointer;
-
- private:
- section_container data;
-
- public:
-
- basic_ini()
- { }
-
- basic_ini(const char_type* filename)
- { this->read_file(filename); }
-
- /* Iterator methods */
- iterator begin()
- { return data.begin(); }
- const_iterator begin() const
- { return data.begin(); }
- iterator end()
- { return data.end(); }
- const_iterator end() const
- { return data.end(); }
- const_iterator cbegin() const
- { return data.cbegin(); }
- const_iterator cend() const
- { return data.cend(); }
-
- /* Reverse iterator methods */
- reverse_iterator rbegin()
- { return data.rbegin(); }
- const_reverse_iterator rbegin() const
- { return data.rbegin(); }
- reverse_iterator rend()
- { return data.rend(); }
- const_reverse_iterator rend() const
- { return data.rend(); }
- const_reverse_iterator crbegin() const
- { return data.crbegin(); }
- const_reverse_iterator crend() const
- { return data.crend(); }
-
- /* Acessing index methods */
- mapped_type& operator[](const string_type& sect)
- { return data[sect]; }
- mapped_type& operator[](string_type&& sect)
- { return data[std::forward<string_type>(sect)]; }
- mapped_type& at( const string_type& sect)
- { return data.at(sect); }
- const mapped_type& at(const string_type& sect) const
- { return data.at(sect); }
-
- /* Capacity information */
- bool empty() const
- { return data.empty(); }
- size_type size() const
- { return data.size(); }
- size_type max_size() const
- { return data.max_size(); }
-
- /* Modifiers */
- void clear()
- { return data.clear(); }
-
- /* Lookup */
- size_type count(const string_type& sect)
- { return data.count(sect); }
- iterator find(const string_type& sect)
- { return data.find(sect); }
-
- /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */
- string_type get(const string_type& sect, const key_type& key, const string_type& default_value)
- {
- auto it = this->find(sect);
- if(it != this->end())
- {
- auto itv = it->second.find(key);
- if(itv != it->second.end())
- return itv->second;
- }
- return default_value;
- }
-
- /* Sets the value of a value in the ini */
- void set(const string_type& sect, const key_type& key, const string_type& value)
- {
- (*this)[sect][key] = value; // no emplace since overwrite!
- }
-
- /* Too lazy to continue this container... If you need more methods, just add it */
-
- // re3
- void remove(const string_type& sect, const key_type& key)
- {
- auto it = this->find(sect);
- if(it != this->end())
- {
- it->second.erase(key);
- }
- }
-
- int category_size(const string_type& sect)
- {
- auto it = this->find(sect);
- if(it != this->end())
- {
- return it->second.size();
- }
- return 0;
- }
-
-#if 1
- bool read_file(const char_type* filename)
- {
- /* Using C stream in a STL-like container, funny?
- */
- if(FILE* f = fopen(filename, "r"))
- {
- key_container* keys = nullptr;
- char_type buf[2048];
- string_type line;
- string_type key;
- string_type value;
- string_type null_string;
- size_type pos;
-
- // Trims an string
- auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type&
- {
- if(s.size())
- {
- // Ignore UTF-8 BOM
- while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF))
- s.erase(s.begin(), s.begin() + 3);
-
- if(trimLeft)
- s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function<int(int)>(::isspace))));
- if(trimRight)
- s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function<int(int)>(::isspace))).base(), s.end());
- }
- return s;
- };
-
- // Start parsing
- while(fgets(buf, sizeof(buf), f))
- {
- // What a thing, reading into a char buffer and then putting in the string...
- line = buf;
-
- // Find comment and remove anything after it from the line
- if((pos = line.find_first_of(';')) != line.npos)
- line.erase(pos);
-
- // Trim the string, and if it gets empty, skip this line
- if(trim(line, true, true).empty())
- continue;
-
- // Find section name
- if(line.front() == '[' && line.back() == ']')
- {
- pos = line.length() - 1; //line.find_first_of(']');
- if(pos != line.npos)
- {
- trim(key.assign(line, 1, pos-1), true, true);
- keys = &data[std::move(key)]; // Create section
- }
- else
- keys = nullptr;
- }
- else
- {
- // Find key and value positions
- pos = line.find_first_of('=');
- if(pos == line.npos)
- {
- // There's only the key
- key = line; // No need for trim, line is already trimmed
- value.clear();
- }
- else
- {
- // There's the key and the value
- trim(key.assign(line, 0, pos), false, true); // trim the right
- trim(value.assign(line, pos + 1, line.npos), true, false); // trim the left
- }
-
- // Put the key/value into the current keys object, or into the section "" if no section has been found
- #if __cplusplus >= 201103L || _MSC_VER >= 1800
- (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value));
- #else
- (keys ? *keys : data[null_string])[key] = value;
- key.clear(); value.clear();
- #endif
- }
- }
-
- fclose(f);
- return true;
- }
- return false;
- }
-
- /*
- * Dumps the content of this container into an ini file
- */
- bool write_file(const char_type* filename)
- {
- if(FILE* f = fopen(filename, "w"))
- {
- bool first = true;
- for(auto& sec : this->data)
- {
- fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str());
- first = false;
- for(auto& kv : sec.second)
- {
- if(kv.second.empty())
- fprintf(f, "%s\n", kv.first.c_str());
- else
- fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str());
- }
- }
- fclose(f);
- return true;
- }
- return false;
- }
-
-
- /*
- */
- bool load_file(const char_type* filename)
- {
- return read_file(filename);
- }
-
- bool load_file(const StringType& filename)
- {
- return load_file(filename.c_str());
- }
-
- bool write_file(const StringType& filename)
- {
- return write_file(filename.c_str());
- }
-#endif
-
-
-
- };
-
-
- /* Use default basic_ini
- *
- * Limitations:
- * * Not unicode aware
- * * Case sensitive
- * * Sections must have unique keys
- */
- typedef basic_ini<> ini;
-}
-
-#endif
-
diff --git a/src/objects/ParticleObject.cpp b/src/objects/ParticleObject.cpp
index 211a568c..5d480ecc 100644
--- a/src/objects/ParticleObject.cpp
+++ b/src/objects/ParticleObject.cpp
@@ -10,6 +10,12 @@
#include "DMAudio.h"
#include "screendroplets.h"
+#ifdef COMPATIBLE_SAVES
+#define PARTICLE_OBJECT_SIZEOF 0x88
+#else
+#define PARTICLE_OBJECT_SIZEOF sizeof(CParticleObject)
+#endif
+
CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS];
@@ -1111,6 +1117,49 @@ CParticleObject::UpdateFar(void)
}
}
+#ifdef COMPATIBLE_SAVES
+static inline void
+SaveOneParticle(CParticleObject *p, uint8 *&buffer)
+{
+#define SkipBuf(buf, num) buf += num
+#define ZeroBuf(buf, num) memset(buf, 0, num); SkipBuf(buf, num)
+#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipBuf(buf, sizeof(data))
+ // CPlaceable
+ {
+ ZeroBuf(buffer, 4);
+ CopyToBuf(buffer, p->GetMatrix().f);
+ ZeroBuf(buffer, 4);
+ CopyToBuf(buffer, p->GetMatrix().m_hasRwMatrix);
+ ZeroBuf(buffer, 3);
+ }
+
+ // CParticleObject
+ {
+ ZeroBuf(buffer, 4);
+ ZeroBuf(buffer, 4);
+ ZeroBuf(buffer, 4);
+ CopyToBuf(buffer, p->m_nRemoveTimer);
+ CopyToBuf(buffer, p->m_Type);
+ CopyToBuf(buffer, p->m_ParticleType);
+ CopyToBuf(buffer, p->m_nNumEffectCycles);
+ CopyToBuf(buffer, p->m_nSkipFrames);
+ CopyToBuf(buffer, p->m_nFrameCounter);
+ CopyToBuf(buffer, p->m_nState);
+ ZeroBuf(buffer, 2);
+ CopyToBuf(buffer, p->m_vecTarget);
+ CopyToBuf(buffer, p->m_fRandVal);
+ CopyToBuf(buffer, p->m_fSize);
+ CopyToBuf(buffer, p->m_Color);
+ CopyToBuf(buffer, p->m_bRemove);
+ CopyToBuf(buffer, p->m_nCreationChance);
+ ZeroBuf(buffer, 2);
+ }
+#undef SkipBuf
+#undef ZeroBuf
+#undef CopyToBuf
+}
+#endif
+
bool
CParticleObject::SaveParticle(uint8 *buffer, uint32 *length)
{
@@ -1128,27 +1177,35 @@ CParticleObject::SaveParticle(uint8 *buffer, uint32 *length)
*(int32 *)buffer = numObjects;
buffer += sizeof(int32);
- int32 objectsLength = sizeof(CParticleObject) * (numObjects + 1);
+ int32 objectsLength = PARTICLE_OBJECT_SIZEOF * (numObjects + 1);
int32 dataLength = objectsLength + sizeof(int32);
for ( CParticleObject *p = pCloseListHead; p != NULL; p = p->m_pNext )
{
-#if 0 // todo better
+#ifdef COMPATIBLE_SAVES
+ SaveOneParticle(p, buffer);
+#else
+#ifdef THIS_IS_STUPID
*(CParticleObject*)buffer = *p;
#else
memcpy(buffer, p, sizeof(CParticleObject));
#endif
buffer += sizeof(CParticleObject);
+#endif
}
for ( CParticleObject *p = pFarListHead; p != NULL; p = p->m_pNext )
{
-#if 0 // todo better
+#ifdef COMPATIBLE_SAVES
+ SaveOneParticle(p, buffer);
+#else
+#ifdef THIS_IS_STUPID
*(CParticleObject*)buffer = *p;
#else
memcpy(buffer, p, sizeof(CParticleObject));
#endif
buffer += sizeof(CParticleObject);
+#endif
}
*length = dataLength;
@@ -1166,7 +1223,7 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
int32 numObjects = *(int32 *)buffer;
buffer += sizeof(int32);
- if ( length != sizeof(CParticleObject) * (numObjects + 1) + sizeof(int32) )
+ if ( length != PARTICLE_OBJECT_SIZEOF * (numObjects + 1) + sizeof(int32) )
return false;
if ( numObjects == 0 )
@@ -1177,14 +1234,17 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
while ( i < numObjects )
{
CParticleObject *dst = pUnusedListHead;
+#ifndef COMPATIBLE_SAVES
CParticleObject *src = (CParticleObject *)buffer;
buffer += sizeof(CParticleObject);
+#endif
if ( dst == NULL )
return false;
MoveToList(&pUnusedListHead, &pCloseListHead, dst);
+#ifndef COMPATIBLE_SAVES
dst->m_nState = POBJECTSTATE_UPDATE_CLOSE;
dst->m_Type = src->m_Type;
dst->m_ParticleType = src->m_ParticleType;
@@ -1200,6 +1260,47 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
dst->m_nNumEffectCycles = src->m_nNumEffectCycles;
dst->m_nSkipFrames = src->m_nSkipFrames;
dst->m_nCreationChance = src->m_nCreationChance;
+#else
+ dst->m_nState = POBJECTSTATE_UPDATE_CLOSE;
+ dst->m_pParticle = NULL;
+
+#define SkipBuf(buf, num) buf += num
+#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipBuf(buf, sizeof(data))
+ // CPlaceable
+ {
+ SkipBuf(buffer, 4);
+ CMatrix matrix;
+ CopyFromBuf(buffer, matrix.f);
+ SkipBuf(buffer, 4);
+ CopyFromBuf(buffer, matrix.m_hasRwMatrix);
+ SkipBuf(buffer, 3);
+ dst->SetPosition(matrix.GetPosition());
+ }
+
+ // CParticleObject
+ {
+ SkipBuf(buffer, 4);
+ SkipBuf(buffer, 4);
+ SkipBuf(buffer, 4);
+ CopyFromBuf(buffer, dst->m_nRemoveTimer);
+ CopyFromBuf(buffer, dst->m_Type);
+ CopyFromBuf(buffer, dst->m_ParticleType);
+ CopyFromBuf(buffer, dst->m_nNumEffectCycles);
+ CopyFromBuf(buffer, dst->m_nSkipFrames);
+ CopyFromBuf(buffer, dst->m_nFrameCounter);
+ SkipBuf(buffer, 2);
+ SkipBuf(buffer, 2);
+ CopyFromBuf(buffer, dst->m_vecTarget);
+ CopyFromBuf(buffer, dst->m_fRandVal);
+ CopyFromBuf(buffer, dst->m_fSize);
+ CopyFromBuf(buffer, dst->m_Color);
+ CopyFromBuf(buffer, dst->m_bRemove);
+ CopyFromBuf(buffer, dst->m_nCreationChance);
+ SkipBuf(buffer, 2);
+ }
+#undef CopyFromBuf
+#undef SkipBuf
+#endif
i++;
}
diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp
index 4d80cac2..9be58d11 100644
--- a/src/peds/Ped.cpp
+++ b/src/peds/Ped.cpp
@@ -326,6 +326,7 @@ CPed::~CPed(void)
nearPed->m_nearPeds[k] = nearPed->m_nearPeds[k + 1];
nearPed->m_nearPeds[k + 1] = nil;
}
+ nearPed->m_nearPeds[ARRAY_SIZE(m_nearPeds) - 1] = nil;
nearPed->m_numNearPeds--;
} else
j++;
@@ -8496,21 +8497,21 @@ CPed::renderLimb(int node)
void
CPed::Save(uint8*& buf)
{
- SkipSaveBuf(buf, 52);
+ ZeroSaveBuf(buf, 52);
CopyToBuf(buf, GetPosition().x);
CopyToBuf(buf, GetPosition().y);
CopyToBuf(buf, GetPosition().z);
- SkipSaveBuf(buf, 288);
+ ZeroSaveBuf(buf, 288);
CopyToBuf(buf, CharCreatedBy);
- SkipSaveBuf(buf, 351);
+ ZeroSaveBuf(buf, 351);
CopyToBuf(buf, m_fHealth);
CopyToBuf(buf, m_fArmour);
- SkipSaveBuf(buf, 148);
+ ZeroSaveBuf(buf, 148);
for (int i = 0; i < 13; i++) // has to be hardcoded
m_weapons[i].Save(buf);
- SkipSaveBuf(buf, 5);
+ ZeroSaveBuf(buf, 5);
CopyToBuf(buf, m_maxWeaponTypeAllowed);
- SkipSaveBuf(buf, 162);
+ ZeroSaveBuf(buf, 162);
}
void
diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp
index 93a403bd..6d6fc714 100644
--- a/src/peds/PlayerPed.cpp
+++ b/src/peds/PlayerPed.cpp
@@ -1492,14 +1492,14 @@ void
CPlayerPed::Save(uint8*& buf)
{
CPed::Save(buf);
- SkipSaveBuf(buf, 16);
+ ZeroSaveBuf(buf, 16);
CopyToBuf(buf, m_fMaxStamina);
- SkipSaveBuf(buf, 28);
+ ZeroSaveBuf(buf, 28);
CopyToBuf(buf, m_nTargettableObjects[0]);
CopyToBuf(buf, m_nTargettableObjects[1]);
CopyToBuf(buf, m_nTargettableObjects[2]);
CopyToBuf(buf, m_nTargettableObjects[3]);
- SkipSaveBuf(buf, 116);
+ ZeroSaveBuf(buf, 116);
}
void
diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp
index ace6d37c..1d2a5798 100644
--- a/src/peds/Population.cpp
+++ b/src/peds/Population.cpp
@@ -55,7 +55,7 @@ bool CPopulation::ms_bGivePedsWeapons;
int32 CPopulation::m_AllRandomPedsThisType = -1;
float CPopulation::PedDensityMultiplier = 1.0f;
uint32 CPopulation::ms_nTotalMissionPeds;
-int32 CPopulation::MaxNumberOfPedsInUse = 25;
+int32 CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS;
uint32 CPopulation::ms_nNumCivMale;
uint32 CPopulation::ms_nNumCivFemale;
uint32 CPopulation::ms_nNumCop;
@@ -1122,12 +1122,6 @@ CPopulation::ManagePopulation(void)
}
float dist = (ped->GetPosition() - playerPos).Magnitude2D();
-#ifdef SQUEEZE_PERFORMANCE
- if (dist > 50.f)
- ped->bUsesCollision = false;
- else
- ped->bUsesCollision = true;
-#endif
bool pedIsFarAway = false;
if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist
diff --git a/src/save/GenericGameStorage.cpp b/src/save/GenericGameStorage.cpp
index 23a8fd6a..f51f8233 100644
--- a/src/save/GenericGameStorage.cpp
+++ b/src/save/GenericGameStorage.cpp
@@ -600,6 +600,552 @@ align4bytes(int32 size)
return (size + 3) & 0xFFFFFFFC;
}
+#ifdef FIX_INCOMPATIBLE_SAVES
+#define LoadSaveDataBlockNoCheck(buf, file, size) \
+do { \
+ CFileMgr::Read(file, (const char *)&size, sizeof(size)); \
+ size = align4bytes(size); \
+ CFileMgr::Read(file, (const char *)work_buff, size); \
+ buf = work_buff; \
+} while(0)
+
+#define WriteSavaDataBlockNoFunc(buf, file, size) \
+do { \
+ if (!PcSaveHelper.PcClassSaveRoutine(file, buf, size)) \
+ goto fail; \
+ totalSize += size; \
+} while(0)
+
+#define FixSaveDataBlock(fix_func, file, size) \
+do { \
+ ReadDataFromBufferPointer(buf, size); \
+ memset(work_buff2, 0, sizeof(work_buff2)); \
+ buf2 = work_buff2; \
+ reserved = 0; \
+ MakeSpaceForSizeInBufferPointer(presize, buf2, postsize); \
+ fix_func(save_type, buf, buf2, &size); \
+ CopySizeAndPreparePointer(presize, buf2, postsize, reserved, size); \
+ if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff2, buf2 - work_buff2)) \
+ goto fail; \
+ totalSize += buf2 - work_buff2; \
+} while(0)
+
+#define ReadDataFromBufferPointerWithSize(buf, to, size) memcpy(&to, buf, size); buf += align4bytes(size)
+
+#define ReadBuf(buf, to) memcpy(&to, buf, sizeof(to)); buf += sizeof(to)
+#define WriteBuf(buf, from) memcpy(buf, &from, sizeof(from)); buf += sizeof(from)
+#define CopyBuf(from, to, size) memcpy(to, from, size); to += (size); from += (size)
+#define CopyPtr(from, to) memcpy(to, from, 4); to += 4; from += 8
+#define SkipBuf(buf, size) buf += (size)
+#define SkipBoth(from, to, size) to += (size); from += (size)
+#define SkipPtr(from, to) to += 4; from += 8
+
+// unfortunately we need a 2nd buffer of the same size to store the fixed output ...
+static uint8 work_buff2[sizeof(work_buff)];
+
+enum
+{
+ SAVE_TYPE_NONE = 0,
+ SAVE_TYPE_32_BIT = 1,
+ SAVE_TYPE_64_BIT = 2,
+ SAVE_TYPE_MSVC = 4,
+ SAVE_TYPE_GCC = 8,
+};
+
+uint8
+GetSaveType(char *savename)
+{
+ uint8 save_type = SAVE_TYPE_NONE;
+ int file = CFileMgr::OpenFile(savename, "rb");
+
+ uint32 size;
+ CFileMgr::Read(file, (const char *)&size, sizeof(size));
+
+ uint8 *buf = work_buff;
+ CFileMgr::Read(file, (const char *)work_buff, size); // simple vars + scripts
+
+ LoadSaveDataBlockNoCheck(buf, file, size); // ped pool
+
+ LoadSaveDataBlockNoCheck(buf, file, size); // garages
+ ReadDataFromBufferPointer(buf, size);
+
+ // store for later after we know how much data we need to skip
+ ReadDataFromBufferPointerWithSize(buf, work_buff2, size);
+
+ LoadSaveDataBlockNoCheck(buf, file, size); // vehicle pool
+ LoadSaveDataBlockNoCheck(buf, file, size); // object pool
+ LoadSaveDataBlockNoCheck(buf, file, size); // paths
+
+ LoadSaveDataBlockNoCheck(buf, file, size); // cranes
+
+ CFileMgr::CloseFile(file);
+
+ ReadDataFromBufferPointer(buf, size);
+
+ if (size == 1032)
+ save_type |= SAVE_TYPE_32_BIT;
+ else if (size == 1160)
+ save_type |= SAVE_TYPE_64_BIT;
+ else
+ assert(0); // this should never happen
+
+ buf = work_buff2;
+
+ buf += 760; // skip everything before the first garage
+ buf += save_type & SAVE_TYPE_32_BIT ? 28 : 40; // skip first garage up to m_fX1
+
+ // now the values we want to verify
+ float fX1, fX2, fY1, fY2, fZ1, fZ2;
+
+ ReadBuf(buf, fX1);
+ ReadBuf(buf, fX2);
+ ReadBuf(buf, fY1);
+ ReadBuf(buf, fY2);
+ ReadBuf(buf, fZ1);
+ ReadBuf(buf, fZ2);
+
+ if (fX1 == CRUSHER_GARAGE_X1 && fX2 == CRUSHER_GARAGE_X2 &&
+ fY1 == CRUSHER_GARAGE_Y1 && fY2 == CRUSHER_GARAGE_Y2 &&
+ fZ1 == CRUSHER_GARAGE_Z1 && fZ2 == CRUSHER_GARAGE_Z2)
+ save_type |= SAVE_TYPE_MSVC;
+ else
+ save_type |= SAVE_TYPE_GCC;
+
+ return save_type;
+}
+
+static void
+FixGarages(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
+{
+ // hardcoded: 5484
+ // x86 msvc: 5240
+ // x86 gcc: 5040
+ // amd64 msvc: 5880
+ // amd64 gcc: 5808
+
+ uint8 *buf_start = buf;
+ uint8 *buf2_start = buf2;
+ uint32 read;
+ uint32 written = 5240;
+
+ if (save_type & SAVE_TYPE_32_BIT && save_type & SAVE_TYPE_GCC)
+ read = 5040;
+ else if (save_type & SAVE_TYPE_64_BIT && save_type & SAVE_TYPE_GCC)
+ read = 5808;
+ else
+ read = 5880;
+
+ uint32 ptrsize = save_type & SAVE_TYPE_32_BIT ? 4 : 8;
+
+ CopyBuf(buf, buf2, 4 * 6);
+ CopyBuf(buf, buf2, 4 * TOTAL_COLLECTCARS_GARAGES);
+ CopyBuf(buf, buf2, 4);
+
+ if (save_type & SAVE_TYPE_GCC)
+ {
+ for (int32 i = 0; i < NUM_GARAGE_STORED_CARS; i++)
+ {
+#define FixStoredCar(buf, buf2) \
+do { \
+ CopyBuf(buf, buf2, 4 + sizeof(CVector) + sizeof(CVector)); \
+ uint8 nFlags8; \
+ ReadBuf(buf, nFlags8); \
+ int32 nFlags32 = nFlags8; \
+ WriteBuf(buf2, nFlags32); \
+ CopyBuf(buf, buf2, 1 * 6); \
+ SkipBuf(buf, 1); \
+ SkipBuf(buf2, 2); \
+} while(0)
+
+ FixStoredCar(buf, buf2);
+ FixStoredCar(buf, buf2);
+ FixStoredCar(buf, buf2);
+
+#undef FixStoredCar
+ }
+ }
+ else
+ {
+ CopyBuf(buf, buf2, sizeof(CStoredCar) * NUM_GARAGE_STORED_CARS);
+ CopyBuf(buf, buf2, sizeof(CStoredCar) * NUM_GARAGE_STORED_CARS);
+ CopyBuf(buf, buf2, sizeof(CStoredCar) * NUM_GARAGE_STORED_CARS);
+ }
+
+ for (int32 i = 0; i < NUM_GARAGES; i++)
+ {
+ // skip the last 5 garages in 64bit builds without FIX_GARAGE_SIZE since they weren't actually saved and are unused
+ if (save_type & SAVE_TYPE_64_BIT && *size == 5484 && i >= NUM_GARAGES - 5)
+ {
+ SkipBuf(buf, 160); // sizeof(CGarage) on x64
+ SkipBuf(buf2, 140); // sizeof(CGarage) on x86
+ }
+ else
+ {
+ CopyBuf(buf, buf2, 1 * 6);
+ SkipBoth(buf, buf2, 2);
+ CopyBuf(buf, buf2, 4);
+ SkipBuf(buf, ptrsize - 4); // write 4 bytes padding if 8 byte pointer, if not, write 0
+ SkipBuf(buf, ptrsize * 2);
+ SkipBuf(buf2, 4 * 2);
+ CopyBuf(buf, buf2, 1 * 7);
+ SkipBoth(buf, buf2, 1);
+ CopyBuf(buf, buf2, 4 * 15 + 1);
+ SkipBoth(buf, buf2, 3);
+ SkipBuf(buf, ptrsize * 2);
+ SkipBuf(buf2, 4 * 2);
+
+ if (save_type & SAVE_TYPE_GCC)
+ SkipBuf(buf, save_type & SAVE_TYPE_64_BIT ? 36 + 4 : 36); // sizeof(CStoredCar) on gcc 64/32 before fix
+ else
+ SkipBuf(buf, sizeof(CStoredCar));
+
+ SkipBuf(buf2, sizeof(CStoredCar));
+ }
+ }
+
+ *size = 0;
+
+ assert(buf - buf_start == read);
+ assert(buf2 - buf2_start == written);
+
+#ifdef FIX_GARAGE_SIZE
+ *size = (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CGarages::CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage));
+#else
+ *size = 5484;
+#endif
+}
+
+static void
+FixCranes(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
+{
+ uint8 *buf_start = buf;
+ uint8 *buf2_start = buf2;
+ uint32 read = 2 * sizeof(uint32) + 0x480; // sizeof(aCranes)
+ uint32 written = 2 * sizeof(uint32) + 0x400; // see CRANES_SAVE_SIZE
+
+ CopyBuf(buf, buf2, 4 + 4);
+
+ for (int32 i = 0; i < NUM_CRANES; i++)
+ {
+ CopyPtr(buf, buf2);
+ CopyPtr(buf, buf2);
+ CopyBuf(buf, buf2, 15 * 4 + sizeof(CVector) * 3 + sizeof(CVector2D));
+ CopyPtr(buf, buf2);
+ CopyBuf(buf, buf2, 4 + 7 * 1);
+ SkipBuf(buf, 5);
+ SkipBuf(buf2, 1);
+ }
+
+ *size = 0;
+
+ assert(buf - buf_start == read);
+ assert(buf2 - buf2_start == written);
+
+ *size = written;
+}
+
+static void
+FixPickups(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
+{
+ uint8 *buf_start = buf;
+ uint8 *buf2_start = buf2;
+ uint32 read = 0x3480 + sizeof(uint16) + sizeof(uint16) + sizeof(int32) * NUMCOLLECTEDPICKUPS; // sizeof(aPickUps)
+ uint32 written = 0x24C0 + sizeof(uint16) + sizeof(uint16) + sizeof(int32) * NUMCOLLECTEDPICKUPS; // see PICKUPS_SAVE_SIZE
+
+ for (int32 i = 0; i < NUMPICKUPS; i++)
+ {
+ CopyBuf(buf, buf2, 1 + 1 + 2);
+ SkipBuf(buf, 4);
+ CopyPtr(buf, buf2);
+ CopyBuf(buf, buf2, 4 + 2 + 2 + sizeof(CVector));
+ SkipBuf(buf, 4);
+ }
+
+ CopyBuf(buf, buf2, 2);
+ SkipBoth(buf, buf2, 2);
+
+ CopyBuf(buf, buf2, NUMCOLLECTEDPICKUPS * 4);
+
+ *size = 0;
+
+ assert(buf - buf_start == read);
+ assert(buf2 - buf2_start == written);
+
+ *size = written;
+}
+
+static void
+FixPhoneInfo(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
+{
+ uint8 *buf_start = buf;
+ uint8 *buf2_start = buf2;
+ uint32 read = 0x1138; // sizeof(CPhoneInfo)
+ uint32 written = 0xA30; // see PHONEINFO_SAVE_SIZE
+
+ CopyBuf(buf, buf2, 4 + 4);
+
+ for (int32 i = 0; i < NUMPHONES; i++)
+ {
+ CopyBuf(buf, buf2, sizeof(CVector));
+ SkipBuf(buf, 4);
+ SkipPtr(buf, buf2);
+ SkipPtr(buf, buf2);
+ SkipPtr(buf, buf2);
+ SkipPtr(buf, buf2);
+ SkipPtr(buf, buf2);
+ SkipPtr(buf, buf2);
+ CopyBuf(buf, buf2, 4);
+ SkipBuf(buf, 4);
+ CopyPtr(buf, buf2);
+ CopyBuf(buf, buf2, 4 + 1);
+ SkipBoth(buf, buf2, 3);
+ }
+
+ *size = 0;
+
+ assert(buf - buf_start == read);
+ assert(buf2 - buf2_start == written);
+
+ *size = written;
+}
+
+static void
+FixZones(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
+{
+ uint8 *buf_start = buf;
+ uint8 *buf2_start = buf2;
+ uint32 read = 11300; // see SaveAllZones
+ uint32 written = 10100; // see SaveAllZones
+
+ CopyBuf(buf, buf2, 1 * 4);
+
+ SkipBuf(buf, 4);
+ uint32 hdr_size = 10100 - (1 * 4 + 4); // see SaveAllZones
+ WriteBuf(buf2, hdr_size);
+
+ CopyBuf(buf, buf2, 4 * 2 + 2);
+ SkipBoth(buf, buf2, 2);
+
+#define FixOneZone(buf, buf2) \
+do { \
+ CopyBuf(buf, buf2, 8 + 8 * 4 + 2 * 2); \
+ SkipBuf(buf, 4); \
+ CopyPtr(buf, buf2); \
+ CopyPtr(buf, buf2); \
+ CopyPtr(buf, buf2); \
+} while(0)
+
+ for (int32 i = 0; i < NUMZONES; i++)
+ FixOneZone(buf, buf2);
+
+ CopyBuf(buf, buf2, sizeof(CZoneInfo) * NUMZONES * 2);
+ CopyBuf(buf, buf2, 2 + 2);
+
+ for (int32 i = 0; i < NUMMAPZONES; i++)
+ FixOneZone(buf, buf2);
+
+ CopyBuf(buf, buf2, 2 * NUMAUDIOZONES);
+ CopyBuf(buf, buf2, 2 + 2);
+
+#undef FixOneZone
+
+ *size = 0;
+
+ assert(buf - buf_start == read);
+ assert(buf2 - buf2_start == written);
+
+ *size = written;
+}
+
+static void
+FixParticles(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
+{
+ uint8 *buf_start = buf;
+ uint8 *buf2_start = buf2;
+
+ int32 numObjects;
+ ReadBuf(buf, numObjects);
+ WriteBuf(buf2, numObjects);
+
+ uint32 read = 0xA0 * (numObjects + 1) + 4; // sizeof(CParticleObject)
+ uint32 written = 0x88 * (numObjects + 1) + 4; // see PARTICLE_OBJECT_SIZEOF
+
+ for (int32 i = 0; i < numObjects; i++)
+ {
+ // CPlaceable
+ SkipPtr(buf, buf2);
+ CopyBuf(buf, buf2, 4 * 4 * 4);
+ SkipPtr(buf, buf2);
+ CopyBuf(buf, buf2, 1);
+ SkipBuf(buf, 7);
+ SkipBuf(buf2, 3);
+
+ // CParticleObject
+ SkipPtr(buf, buf2);
+ SkipPtr(buf, buf2);
+ SkipPtr(buf, buf2);
+ CopyBuf(buf, buf2, 4 * 3 + 2 * 1 + 2 * 2);
+ SkipBoth(buf, buf2, 2);
+ CopyBuf(buf, buf2, sizeof(CVector) + 2 * 4 + sizeof(CRGBA) + 2 * 1);
+ SkipBoth(buf, buf2, 2);
+ }
+
+ SkipBuf(buf, 0xA0); // sizeof(CParticleObject)
+ SkipBuf(buf2, 0x88); // see PARTICLE_OBJECT_SIZEOF
+
+ *size = 0;
+
+ assert(buf - buf_start == read);
+ assert(buf2 - buf2_start == written);
+
+ *size = written;
+}
+
+bool
+FixSave(int32 slot, uint8 save_type)
+{
+ if (save_type & SAVE_TYPE_32_BIT && save_type & SAVE_TYPE_MSVC)
+ return true;
+
+ bool success = false;
+
+ uint8 *buf, *presize, *postsize, *buf2;
+ uint32 size;
+ uint32 reserved;
+
+ uint32 totalSize;
+
+ char savename[MAX_PATH];
+ char savename_bak[MAX_PATH];
+
+ sprintf(savename, "%s%i%s", DefaultPCSaveFileName, slot + 1, ".b");
+ sprintf(savename_bak, "%s%i%s.%lld.bak", DefaultPCSaveFileName, slot + 1, ".b", time(nil));
+
+ assert(caserename(savename, savename_bak) == 0);
+
+ int file_in = CFileMgr::OpenFile(savename_bak, "rb");
+ int file_out = CFileMgr::OpenFileForWriting(savename);
+
+ CheckSum = 0;
+ totalSize = 0;
+
+ CFileMgr::Read(file_in, (const char *)&size, sizeof(size));
+
+ buf = work_buff;
+ CFileMgr::Read(file_in, (const char *)work_buff, size); // simple vars + scripts
+
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // ped pool
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // garages
+ FixSaveDataBlock(FixGarages, file_out, size); // garages need to be fixed in either case
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // vehicle pool
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // object pool
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // paths
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // cranes
+ if (save_type & SAVE_TYPE_64_BIT)
+ FixSaveDataBlock(FixCranes, file_out, size);
+ else
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // pickups
+ if (save_type & SAVE_TYPE_64_BIT)
+ FixSaveDataBlock(FixPickups, file_out, size);
+ else
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // phoneinfo
+ if (save_type & SAVE_TYPE_64_BIT)
+ FixSaveDataBlock(FixPhoneInfo, file_out, size);
+ else
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // restart
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // radar blips
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // zones
+ if (save_type & SAVE_TYPE_64_BIT)
+ FixSaveDataBlock(FixZones, file_out, size);
+ else
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // gang data
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // car generators
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // particles
+ if (save_type & SAVE_TYPE_64_BIT)
+ FixSaveDataBlock(FixParticles, file_out, size);
+ else
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // audio script objects
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // player info
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // stats
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // streaming
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ LoadSaveDataBlockNoCheck(buf, file_in, size); // ped type
+ WriteSavaDataBlockNoFunc(buf, file_out, size);
+
+ memset(work_buff, 0, sizeof(work_buff));
+
+ for (int i = 0; i < 4; i++) {
+ size = align4bytes(SIZE_OF_ONE_GAME_IN_BYTES - totalSize - 4);
+ if (size > sizeof(work_buff))
+ size = sizeof(work_buff);
+ if (size > 4) {
+ if (!PcSaveHelper.PcClassSaveRoutine(file_out, work_buff, size))
+ goto fail;
+ totalSize += size;
+ }
+ }
+
+ if (!CFileMgr::Write(file_out, (const char *)&CheckSum, sizeof(CheckSum)))
+ goto fail;
+
+ success = true;
+
+fail:;
+ CFileMgr::CloseFile(file_in);
+ CFileMgr::CloseFile(file_out);
+
+ return success;
+}
+
+#undef LoadSaveDataBlockNoCheck
+#undef WriteSavaDataBlockNoFunc
+#undef FixSaveDataBlock
+#undef ReadDataFromBufferPointerWithSize
+#undef ReadBuf
+#undef WriteBuf
+#undef CopyBuf
+#undef CopyPtr
+#undef SkipBuf
+#undef SkipBoth
+#undef SkipPtr
+#endif
+
#ifdef MISSION_REPLAY
void DisplaySaveResult(int unk, char* name)
diff --git a/src/save/GenericGameStorage.h b/src/save/GenericGameStorage.h
index 069ba7cd..b291ddf9 100644
--- a/src/save/GenericGameStorage.h
+++ b/src/save/GenericGameStorage.h
@@ -22,6 +22,11 @@ bool CheckDataNotCorrupt(int32 slot, char *name);
bool RestoreForStartLoad();
int align4bytes(int32 size);
+#ifdef FIX_INCOMPATIBLE_SAVES
+uint8 GetSaveType(char *savename);
+bool FixSave(int32 slot, uint8 save_type);
+#endif
+
extern class CDate CompileDateAndTime;
extern char DefaultPCSaveFileName[260];
diff --git a/src/save/PCSave.cpp b/src/save/PCSave.cpp
index a9df00af..0c228a6d 100644
--- a/src/save/PCSave.cpp
+++ b/src/save/PCSave.cpp
@@ -122,6 +122,13 @@ C_PcSave::PopulateSlotInfo()
}
if (Slots[i + 1] == SLOT_OK) {
if (CheckDataNotCorrupt(i, savename)) {
+#ifdef FIX_INCOMPATIBLE_SAVES
+ if (!FixSave(i, GetSaveType(savename))) {
+ CMessages::InsertNumberInString(TheText.Get("FEC_SLC"), i + 1, -1, -1, -1, -1, -1, SlotFileName[i]);
+ Slots[i + 1] = SLOT_CORRUPTED;
+ continue;
+ }
+#endif
SYSTEMTIME st;
memcpy(&st, &header.SaveDateTime, sizeof(SYSTEMTIME));
const char *month;
diff --git a/src/save/PCSave.h b/src/save/PCSave.h
index 4a2d9a66..83471b5d 100644
--- a/src/save/PCSave.h
+++ b/src/save/PCSave.h
@@ -33,7 +33,7 @@ public:
void PopulateSlotInfo();
bool DeleteSlot(int32 slot);
bool SaveSlot(int32 slot);
- bool PcClassSaveRoutine(int32 a2, uint8 *data, uint32 size);
+ bool PcClassSaveRoutine(int32 file, uint8 *data, uint32 size);
static void SetSaveDirectory(const char *path);
};
diff --git a/src/save/SaveBuf.h b/src/save/SaveBuf.h
index 98fe888b..aad2e1a8 100644
--- a/src/save/SaveBuf.h
+++ b/src/save/SaveBuf.h
@@ -36,6 +36,15 @@ WriteSaveBuf(uint8 *&buf, const T &value)
return p;
}
+#ifdef COMPATIBLE_SAVES
+inline void
+ZeroSaveBuf(uint8 *&buf, uint32 length)
+{
+ memset(buf, 0, length);
+ SkipSaveBuf(buf, length);
+}
+#endif
+
#define SAVE_HEADER_SIZE (4 * sizeof(char) + sizeof(uint32))
#define WriteSaveHeader(buf, a, b, c, d, size) \
diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp
index 1d49ebd2..577983b6 100644
--- a/src/skel/crossplatform.cpp
+++ b/src/skel/crossplatform.cpp
@@ -155,6 +155,29 @@ FILE* _fcaseopen(char const* filename, char const* mode)
return result;
}
+int _caserename(const char *old_filename, const char *new_filename)
+{
+ int result;
+ char *real_old = casepath(old_filename);
+ char *real_new = casepath(new_filename);
+
+ // hack so we don't even try to rename it to new_filename if it already exists
+ if (!real_new) {
+ free(real_old);
+ return -1;
+ }
+
+ if (!real_old)
+ result = rename(old_filename, real_new);
+ else
+ result = rename(real_old, real_new);
+
+ free(real_old);
+ free(real_new);
+
+ return result;
+}
+
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
// Returned string should freed manually (if exists)
char* casepath(char const* path, bool checkPathFirst)
diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h
index 2dd9c162..aa90ce5a 100644
--- a/src/skel/crossplatform.h
+++ b/src/skel/crossplatform.h
@@ -29,6 +29,7 @@ enum eWinVersion
#endif
extern DWORD _dwOperatingSystemVersion;
#define fcaseopen fopen
+#define caserename rename
#else
char *strupr(char *str);
char *strlwr(char *str);
@@ -51,6 +52,8 @@ extern long _dwOperatingSystemVersion;
char *casepath(char const *path, bool checkPathFirst = true);
FILE *_fcaseopen(char const *filename, char const *mode);
#define fcaseopen _fcaseopen
+int _caserename(const char *old_filename, const char *new_filename);
+#define caserename _caserename
#endif
#ifdef RW_GL3
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
index 3de3e12b..7d942dcd 100644
--- a/src/vehicles/Automobile.cpp
+++ b/src/vehicles/Automobile.cpp
@@ -4717,8 +4717,8 @@ void
CAutomobile::Save(uint8*& buf)
{
CVehicle::Save(buf);
- WriteSaveBuf<CDamageManager>(buf, Damage);
- SkipSaveBuf(buf, 800 - sizeof(CDamageManager));
+ WriteSaveBuf(buf, Damage);
+ ZeroSaveBuf(buf, 800 - sizeof(CDamageManager));
}
void
diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp
index 88444e95..65cdd8c6 100644
--- a/src/vehicles/Boat.cpp
+++ b/src/vehicles/Boat.cpp
@@ -940,7 +940,7 @@ void
CBoat::Save(uint8*& buf)
{
CVehicle::Save(buf);
- SkipSaveBuf(buf, 1156 - 648);
+ ZeroSaveBuf(buf, 1156 - 648);
}
void
diff --git a/src/vehicles/Cranes.cpp b/src/vehicles/Cranes.cpp
index 0f1b8b4c..db9d2e00 100644
--- a/src/vehicles/Cranes.cpp
+++ b/src/vehicles/Cranes.cpp
@@ -37,6 +37,12 @@
#define MIN_VALID_POSITION (-10000.0f)
#define DEFAULT_OFFSET (20.0f)
+#ifdef COMPATIBLE_SAVES
+#define CRANES_SAVE_SIZE 0x400
+#else
+#define CRANES_SAVE_SIZE sizeof(aCranes)
+#endif
+
uint32 TimerForCamInterpolation;
uint32 CCranes::CarsCollectedMilitaryCrane;
@@ -634,10 +640,46 @@ void CCranes::Save(uint8* buf, uint32* size)
{
INITSAVEBUF
- *size = 2 * sizeof(uint32) + sizeof(aCranes);
+ *size = 2 * sizeof(uint32) + CRANES_SAVE_SIZE;
WriteSaveBuf(buf, NumCranes);
WriteSaveBuf(buf, CarsCollectedMilitaryCrane);
for (int i = 0; i < NUM_CRANES; i++) {
+#ifdef COMPATIBLE_SAVES
+ int32 tmp = aCranes[i].m_pCraneEntity != nil ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pCraneEntity) + 1 : 0;
+ WriteSaveBuf(buf, tmp);
+ tmp = aCranes[i].m_pHook != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pHook) + 1 : 0;
+ WriteSaveBuf(buf, tmp);
+ WriteSaveBuf(buf, aCranes[i].m_nAudioEntity);
+ WriteSaveBuf(buf, aCranes[i].m_fPickupX1);
+ WriteSaveBuf(buf, aCranes[i].m_fPickupX2);
+ WriteSaveBuf(buf, aCranes[i].m_fPickupY1);
+ WriteSaveBuf(buf, aCranes[i].m_fPickupY2);
+ WriteSaveBuf(buf, aCranes[i].m_vecDropoffTarget);
+ WriteSaveBuf(buf, aCranes[i].m_fDropoffHeading);
+ WriteSaveBuf(buf, aCranes[i].m_fPickupAngle);
+ WriteSaveBuf(buf, aCranes[i].m_fDropoffAngle);
+ WriteSaveBuf(buf, aCranes[i].m_fPickupDistance);
+ WriteSaveBuf(buf, aCranes[i].m_fDropoffDistance);
+ WriteSaveBuf(buf, aCranes[i].m_fPickupHeight);
+ WriteSaveBuf(buf, aCranes[i].m_fDropoffHeight);
+ WriteSaveBuf(buf, aCranes[i].m_fHookAngle);
+ WriteSaveBuf(buf, aCranes[i].m_fHookOffset);
+ WriteSaveBuf(buf, aCranes[i].m_fHookHeight);
+ WriteSaveBuf(buf, aCranes[i].m_vecHookInitPos);
+ WriteSaveBuf(buf, aCranes[i].m_vecHookCurPos);
+ WriteSaveBuf(buf, aCranes[i].m_vecHookVelocity);
+ tmp = aCranes[i].m_pVehiclePickedUp != nil ? CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pVehiclePickedUp) + 1 : 0;
+ WriteSaveBuf(buf, tmp);
+ WriteSaveBuf(buf, aCranes[i].m_nTimeForNextCheck);
+ WriteSaveBuf(buf, aCranes[i].m_nCraneStatus);
+ WriteSaveBuf(buf, aCranes[i].m_nCraneState);
+ WriteSaveBuf(buf, aCranes[i].m_nVehiclesCollected);
+ WriteSaveBuf(buf, aCranes[i].m_bIsCrusher);
+ WriteSaveBuf(buf, aCranes[i].m_bIsMilitaryCrane);
+ WriteSaveBuf(buf, aCranes[i].m_bWasMilitaryCrane);
+ WriteSaveBuf(buf, aCranes[i].m_bIsTop);
+ ZeroSaveBuf(buf, 1);
+#else
CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]);
if (pCrane->m_pCraneEntity != nil)
pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pCrane->m_pCraneEntity) + 1);
@@ -645,6 +687,7 @@ void CCranes::Save(uint8* buf, uint32* size)
pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pCrane->m_pHook) + 1);
if (pCrane->m_pVehiclePickedUp != nil)
pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pCrane->m_pVehiclePickedUp) + 1);
+#endif
}
VALIDATESAVEBUF(*size);
@@ -656,8 +699,46 @@ void CCranes::Load(uint8* buf, uint32 size)
ReadSaveBuf(&NumCranes, buf);
ReadSaveBuf(&CarsCollectedMilitaryCrane, buf);
- for (int i = 0; i < NUM_CRANES; i++)
+ for (int i = 0; i < NUM_CRANES; i++) {
+#ifdef COMPATIBLE_SAVES
+ int32 tmp;
+ ReadSaveBuf(&tmp, buf);
+ aCranes[i].m_pCraneEntity = tmp != 0 ? CPools::GetBuildingPool()->GetSlot(tmp - 1) : nil;
+ ReadSaveBuf(&tmp, buf);
+ aCranes[i].m_pHook = tmp != 0 ? CPools::GetObjectPool()->GetSlot(tmp - 1) : nil;
+ ReadSaveBuf(&aCranes[i].m_nAudioEntity, buf);
+ ReadSaveBuf(&aCranes[i].m_fPickupX1, buf);
+ ReadSaveBuf(&aCranes[i].m_fPickupX2, buf);
+ ReadSaveBuf(&aCranes[i].m_fPickupY1, buf);
+ ReadSaveBuf(&aCranes[i].m_fPickupY2, buf);
+ ReadSaveBuf(&aCranes[i].m_vecDropoffTarget, buf);
+ ReadSaveBuf(&aCranes[i].m_fDropoffHeading, buf);
+ ReadSaveBuf(&aCranes[i].m_fPickupAngle, buf);
+ ReadSaveBuf(&aCranes[i].m_fDropoffAngle, buf);
+ ReadSaveBuf(&aCranes[i].m_fPickupDistance, buf);
+ ReadSaveBuf(&aCranes[i].m_fDropoffDistance, buf);
+ ReadSaveBuf(&aCranes[i].m_fPickupHeight, buf);
+ ReadSaveBuf(&aCranes[i].m_fDropoffHeight, buf);
+ ReadSaveBuf(&aCranes[i].m_fHookAngle, buf);
+ ReadSaveBuf(&aCranes[i].m_fHookOffset, buf);
+ ReadSaveBuf(&aCranes[i].m_fHookHeight, buf);
+ ReadSaveBuf(&aCranes[i].m_vecHookInitPos, buf);
+ ReadSaveBuf(&aCranes[i].m_vecHookCurPos, buf);
+ ReadSaveBuf(&aCranes[i].m_vecHookVelocity, buf);
+ ReadSaveBuf(&tmp, buf);
+ aCranes[i].m_pVehiclePickedUp = tmp != 0 ? CPools::GetVehiclePool()->GetSlot(tmp - 1) : nil;
+ ReadSaveBuf(&aCranes[i].m_nTimeForNextCheck, buf);
+ ReadSaveBuf(&aCranes[i].m_nCraneStatus, buf);
+ ReadSaveBuf(&aCranes[i].m_nCraneState, buf);
+ ReadSaveBuf(&aCranes[i].m_nVehiclesCollected, buf);
+ ReadSaveBuf(&aCranes[i].m_bIsCrusher, buf);
+ ReadSaveBuf(&aCranes[i].m_bIsMilitaryCrane, buf);
+ ReadSaveBuf(&aCranes[i].m_bWasMilitaryCrane, buf);
+ ReadSaveBuf(&aCranes[i].m_bIsTop, buf);
+ SkipSaveBuf(buf, 1);
+#else
ReadSaveBuf(&aCranes[i], buf);
+ }
for (int i = 0; i < NUM_CRANES; i++) {
CCrane *pCrane = &aCranes[i];
if (pCrane->m_pCraneEntity != nil)
@@ -666,6 +747,7 @@ void CCranes::Load(uint8* buf, uint32 size)
pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uintptr)pCrane->m_pHook - 1);
if (pCrane->m_pVehiclePickedUp != nil)
pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1);
+#endif
}
for (int i = 0; i < NUM_CRANES; i++) {
aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[i]);
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 3d3ba8f2..4259f9d8 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -1262,42 +1262,42 @@ DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle)
void
CVehicle::Save(uint8*& buf)
{
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetRight().x);
- WriteSaveBuf<float>(buf, GetRight().y);
- WriteSaveBuf<float>(buf, GetRight().z);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetForward().x);
- WriteSaveBuf<float>(buf, GetForward().y);
- WriteSaveBuf<float>(buf, GetForward().z);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetUp().x);
- WriteSaveBuf<float>(buf, GetUp().y);
- WriteSaveBuf<float>(buf, GetUp().z);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetPosition().x);
- WriteSaveBuf<float>(buf, GetPosition().y);
- WriteSaveBuf<float>(buf, GetPosition().z);
- SkipSaveBuf(buf, 16);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetRight().x);
+ WriteSaveBuf(buf, GetRight().y);
+ WriteSaveBuf(buf, GetRight().z);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetForward().x);
+ WriteSaveBuf(buf, GetForward().y);
+ WriteSaveBuf(buf, GetForward().z);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetUp().x);
+ WriteSaveBuf(buf, GetUp().y);
+ WriteSaveBuf(buf, GetUp().z);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetPosition().x);
+ WriteSaveBuf(buf, GetPosition().y);
+ WriteSaveBuf(buf, GetPosition().z);
+ ZeroSaveBuf(buf, 16);
SaveEntityFlags(buf);
- SkipSaveBuf(buf, 212);
+ ZeroSaveBuf(buf, 212);
AutoPilot.Save(buf);
- WriteSaveBuf<int8>(buf, m_currentColour1);
- WriteSaveBuf<int8>(buf, m_currentColour2);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<int16>(buf, m_nAlarmState);
- SkipSaveBuf(buf, 43);
- WriteSaveBuf<uint8>(buf, m_nNumMaxPassengers);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<float>(buf, field_1D0[0]);
- WriteSaveBuf<float>(buf, field_1D0[1]);
- WriteSaveBuf<float>(buf, field_1D0[2]);
- WriteSaveBuf<float>(buf, field_1D0[3]);
- SkipSaveBuf(buf, 8);
- WriteSaveBuf<float>(buf, m_fSteerAngle);
- WriteSaveBuf<float>(buf, m_fGasPedal);
- WriteSaveBuf<float>(buf, m_fBrakePedal);
- WriteSaveBuf<uint8>(buf, VehicleCreatedBy);
+ WriteSaveBuf(buf, m_currentColour1);
+ WriteSaveBuf(buf, m_currentColour2);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_nAlarmState);
+ ZeroSaveBuf(buf, 43);
+ WriteSaveBuf(buf, m_nNumMaxPassengers);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, field_1D0[0]);
+ WriteSaveBuf(buf, field_1D0[1]);
+ WriteSaveBuf(buf, field_1D0[2]);
+ WriteSaveBuf(buf, field_1D0[3]);
+ ZeroSaveBuf(buf, 8);
+ WriteSaveBuf(buf, m_fSteerAngle);
+ WriteSaveBuf(buf, m_fGasPedal);
+ WriteSaveBuf(buf, m_fBrakePedal);
+ WriteSaveBuf(buf, VehicleCreatedBy);
uint8 flags = 0;
if (bIsLawEnforcer) flags |= BIT(0);
if (bIsLocked) flags |= BIT(3);
@@ -1305,19 +1305,19 @@ CVehicle::Save(uint8*& buf)
if (bIsHandbrakeOn) flags |= BIT(5);
if (bLightsOn) flags |= BIT(6);
if (bFreebies) flags |= BIT(7);
- WriteSaveBuf<uint8>(buf, flags);
- SkipSaveBuf(buf, 10);
- WriteSaveBuf<float>(buf, m_fHealth);
- WriteSaveBuf<uint8>(buf, m_nCurrentGear);
- SkipSaveBuf(buf, 3);
- WriteSaveBuf<float>(buf, m_fChangeGearTime);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<uint32>(buf, m_nTimeOfDeath);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<int16>(buf, m_nBombTimer);
- SkipSaveBuf(buf, 12);
- WriteSaveBuf<int8>(buf, m_nDoorLock);
- SkipSaveBuf(buf, 99);
+ WriteSaveBuf(buf, flags);
+ ZeroSaveBuf(buf, 10);
+ WriteSaveBuf(buf, m_fHealth);
+ WriteSaveBuf(buf, m_nCurrentGear);
+ ZeroSaveBuf(buf, 3);
+ WriteSaveBuf(buf, m_fChangeGearTime);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, m_nTimeOfDeath);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_nBombTimer);
+ ZeroSaveBuf(buf, 12);
+ WriteSaveBuf(buf, m_nDoorLock);
+ ZeroSaveBuf(buf, 96);
}
void
@@ -1379,8 +1379,7 @@ CVehicle::Load(uint8*& buf)
SkipSaveBuf(buf, 2);
ReadSaveBuf(&m_nBombTimer, buf);
SkipSaveBuf(buf, 12);
- ReadSaveBuf(&flags, buf);
- m_nDoorLock = (eCarLock)flags;
- SkipSaveBuf(buf, 99);
+ ReadSaveBuf(&m_nDoorLock, buf);
+ SkipSaveBuf(buf, 96);
}
#endif
diff --git a/src/weapons/Weapon.cpp b/src/weapons/Weapon.cpp
index 43a85db8..6f0e9094 100644
--- a/src/weapons/Weapon.cpp
+++ b/src/weapons/Weapon.cpp
@@ -2337,7 +2337,7 @@ CWeapon::Save(uint8*& buf)
CopyToBuf(buf, m_nAmmoTotal);
CopyToBuf(buf, m_nTimer);
CopyToBuf(buf, m_bAddRotOffset);
- SkipSaveBuf(buf, 3);
+ ZeroSaveBuf(buf, 3);
}
void
diff --git a/utils/gxt/american.txt b/utils/gxt/american.txt
index cf71c958..d8f79f05 100644
--- a/utils/gxt/american.txt
+++ b/utils/gxt/american.txt
@@ -8094,6 +8094,12 @@ VIBRATION
[FET_AGS]
GAMEPAD SETTINGS
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
{ end of file }
[DUMMY]
diff --git a/utils/gxt/french.txt b/utils/gxt/french.txt
index 09a1f5c2..bd4acf96 100644
--- a/utils/gxt/french.txt
+++ b/utils/gxt/french.txt
@@ -8362,6 +8362,12 @@ VIBRATIONS
[FET_AGS]
GAMEPAD SETTINGS
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
{ end of file }
[DUMMY]
diff --git a/utils/gxt/german.txt b/utils/gxt/german.txt
index 4f7ee052..5f5c53c4 100644
--- a/utils/gxt/german.txt
+++ b/utils/gxt/german.txt
@@ -8179,6 +8179,12 @@ Vibration :
[FET_AGS]
KONTROLLEREINSTELLUNGEN
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
[DUMMY]
THIS LABEL NEEDS TO BE HERE !!!
AS THE LAST LABEL DOES NOT GET COMPILED
diff --git a/utils/gxt/italian.txt b/utils/gxt/italian.txt
index 54c9fee8..803b7fbf 100644
--- a/utils/gxt/italian.txt
+++ b/utils/gxt/italian.txt
@@ -8191,6 +8191,12 @@ VIBRAZIONE
[FET_AGS]
GAMEPAD SETTINGS
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
[DUMMY]
THIS LABEL NEEDS TO BE HERE !!!
AS THE LAST LABEL DOES NOT GET COMPILED \ No newline at end of file
diff --git a/utils/gxt/polish.txt b/utils/gxt/polish.txt
index 39727554..33716291 100755
--- a/utils/gxt/polish.txt
+++ b/utils/gxt/polish.txt
@@ -8100,6 +8100,12 @@ WIBRACJA
[FET_AGS]
GAMEPAD SETTINGS
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
{ end of file }
[DUMMY]
diff --git a/utils/gxt/russian.txt b/utils/gxt/russian.txt
index 3a723aa2..221e59f5 100644
--- a/utils/gxt/russian.txt
+++ b/utils/gxt/russian.txt
@@ -8102,6 +8102,12 @@ DUALSHOCK 4
[FET_AGS]
НАСТРОЙКИ ГЕЙМПАДА
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
{ end of file }
[DUMMY]
diff --git a/utils/gxt/spanish.txt b/utils/gxt/spanish.txt
index b2e418dd..5f108d3b 100644
--- a/utils/gxt/spanish.txt
+++ b/utils/gxt/spanish.txt
@@ -8161,6 +8161,12 @@ VIBRACIÓN
[FET_AGS]
AJUSTES DE MANDO
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
{ end of file }
[DUMMY]