summaryrefslogtreecommitdiffstats
path: root/src/audio/sampman_oal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio/sampman_oal.cpp')
-rw-r--r--src/audio/sampman_oal.cpp206
1 files changed, 128 insertions, 78 deletions
diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp
index b8a3ed3d..e20da0b2 100644
--- a/src/audio/sampman_oal.cpp
+++ b/src/audio/sampman_oal.cpp
@@ -66,14 +66,14 @@ int usingEAX3=0;
ALCdevice *ALDevice = NULL;
ALCcontext *ALContext = NULL;
unsigned int _maxSamples;
-float _fPrevEaxRatioDestination;
+float _fPrevEaxRatioDestination;
bool _effectsSupported = false;
bool _usingEFX;
float _fEffectsLevel;
ALuint ALEffect = AL_EFFECT_NULL;
ALuint ALEffectSlot = AL_EFFECTSLOT_NULL;
struct
-{
+{
const char *id;
char name[256];
int sources;
@@ -92,7 +92,7 @@ OggOpusFile *fpSampleDataHandle;
#else
FILE *fpSampleDataHandle;
#endif
-bool8 bSampleBankLoaded [MAX_SFX_BANKS];
+bool8 bSampleBankLoaded [MAX_SFX_BANKS];
int32 nSampleBankDiscStartOffset [MAX_SFX_BANKS];
int32 nSampleBankSize [MAX_SFX_BANKS];
uintptr nSampleBankMemoryStartAddress[MAX_SFX_BANKS];
@@ -126,6 +126,7 @@ char _mp3DirectoryPath[MAX_PATH];
CStream *aStream[MAX_STREAMS];
uint8 nStreamPan [MAX_STREAMS];
uint8 nStreamVolume[MAX_STREAMS];
+bool8 nStreamLoopedFlag[MAX_STREAMS];
uint32 _CurMP3Index;
int32 _CurMP3Pos;
bool8 _bIsMp3Active;
@@ -157,7 +158,7 @@ static void
add_providers()
{
SampleManager.SetNum3DProvidersAvailable(0);
-
+
static ALDeviceList DeviceList;
ALDeviceList *pDeviceList = &DeviceList;
@@ -170,7 +171,7 @@ add_providers()
int i = pDeviceList->GetDefaultDevice();
{
if ( n < MAXPROVIDERS )
- {
+ {
providers[n].id = pDeviceList->GetDeviceName(i);
strcpy(providers[n].name, "OPENAL SOFT");
providers[n].sources = pDeviceList->GetMaxNumSources(i);
@@ -183,10 +184,10 @@ add_providers()
|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX3)
|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX4)
|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX5) )
- {
+ {
providers[n - 1].bSupportsFx = true;
if ( n < MAXPROVIDERS )
- {
+ {
providers[n].id = pDeviceList->GetDeviceName(i);
strcpy(providers[n].name, "OPENAL SOFT EAX");
providers[n].sources = pDeviceList->GetMaxNumSources(i);
@@ -196,7 +197,7 @@ add_providers()
}
if ( n < MAXPROVIDERS )
- {
+ {
providers[n].id = pDeviceList->GetDeviceName(i);
strcpy(providers[n].name, "OPENAL SOFT EAX3");
providers[n].sources = pDeviceList->GetMaxNumSources(i);
@@ -210,7 +211,7 @@ add_providers()
for(int j=n;j<MAXPROVIDERS;j++)
SampleManager.Set3DProviderName(j, NULL);
-
+
// devices are gone now
//defaultProvider = pDeviceList->GetDefaultDevice();
//if ( defaultProvider > MAXPROVIDERS )
@@ -233,7 +234,7 @@ release_existing()
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
}
}
-
+
DEV("release_existing()\n");
}
@@ -284,7 +285,7 @@ set_new_provider(int index)
}
//SampleManager.SetSpeakerConfig(speaker_type);
-
+
if ( IsFXSupported() )
{
for ( int32 i = 0; i < MAXCHANNELS; i++ )
@@ -300,7 +301,7 @@ set_new_provider(int index)
static bool8
IsThisTrackAt16KHz(uint32 track)
{
- return track == STREAMED_SOUND_RADIO_CHAT;
+ return track == STREAMED_SOUND_RADIO_KCHAT || track == STREAMED_SOUND_RADIO_VCPR || track == STREAMED_SOUND_RADIO_POLICE;
}
cSampleManager::cSampleManager(void)
@@ -365,6 +366,31 @@ int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider)
return curprovider;
}
+int8
+cSampleManager::AutoDetect3DProviders()
+{
+ if (!AudioManager.IsAudioInitialised())
+ return -1;
+
+ if (defaultProvider >= 0 && defaultProvider < m_nNumberOfProviders) {
+ if (set_new_provider(defaultProvider))
+ return defaultProvider;
+ }
+
+ for (uint32 i = 0; i < GetNum3DProvidersAvailable(); i++)
+ {
+ char* providername = Get3DProviderName(i);
+
+ if (!strcasecmp(providername, "OPENAL SOFT")) {
+ SetCurrent3DProvider(i);
+ if (GetCurrent3DProviderIndex() == i)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
static bool8
_ResolveLink(char const *path, char *out)
{
@@ -840,7 +866,7 @@ cSampleManager::Initialise(void)
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
- {
+ {
_effectsSupported = providers[index].bSupportsFx;
alGenAuxiliaryEffectSlots(1, &ALEffectSlot);
alGenEffects(1, &ALEffect);
@@ -849,14 +875,14 @@ cSampleManager::Initialise(void)
alGenSources(MAX_STREAMS*2, ALStreamSources[0]);
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
- alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
+ alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
alSourcei(ALStreamSources[i][0], AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(ALStreamSources[i][0], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSourcef(ALStreamSources[i][0], AL_GAIN, 1.0f);
alSourcei(ALStreamSources[i][1], AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(ALStreamSources[i][1], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSourcef(ALStreamSources[i][1], AL_GAIN, 1.0f);
- }
+ }
CChannel::InitChannels();
@@ -875,7 +901,7 @@ cSampleManager::Initialise(void)
aChannel[i].SetReverbMix(ALEffectSlot, 0.0f);
}
}
-
+
{
for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
nStreamLength[i] = 0;
@@ -894,14 +920,15 @@ cSampleManager::Initialise(void)
for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
{
- if(aStream[0] && (
+ 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)))
+ aStream[0]->Open(StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000)) )
{
uint32 tatalms = aStream[0]->GetLengthMS();
aStream[0]->Close();
+
nStreamLength[i] = tatalms;
} else
USERERROR("Can't open '%s'\n", StreamedNameTable[i]);
@@ -1035,7 +1062,7 @@ cSampleManager::Terminate(void)
for ( int32 i = 0; i < NUM_CHANNELS; i++ )
aChannel[i].Term();
-
+
if ( IsFXSupported() )
{
if ( alIsEffect(ALEffect) )
@@ -1053,7 +1080,7 @@ cSampleManager::Terminate(void)
ALEffectSlot = AL_EFFECTSLOT_NULL;
}
}
-
+
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
alDeleteBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
@@ -1077,7 +1104,7 @@ cSampleManager::Terminate(void)
_fPrevEaxRatioDestination = 0.0f;
_usingEFX = false;
_fEffectsLevel = 0.0f;
-
+
_DeleteMP3Entries();
CStream::Terminate();
@@ -1140,6 +1167,12 @@ cSampleManager::SetMusicMasterVolume(uint8 nVolume)
}
void
+cSampleManager::SetMP3BoostVolume(uint8 nVolume)
+{
+ m_nMP3BoostVolume = nVolume;
+}
+
+void
cSampleManager::SetEffectsFadeVolume(uint8 nVolume)
{
m_nEffectsFadeVolume = nVolume;
@@ -1153,7 +1186,7 @@ cSampleManager::SetMusicFadeVolume(uint8 nVolume)
}
void
-cSampleManager::SetMonoMode(uint8 nMode)
+cSampleManager::SetMonoMode(bool8 nMode)
{
m_nMonoMode = nMode;
}
@@ -1275,14 +1308,6 @@ cSampleManager::LoadPedComment(uint32 nComment)
break;
}
-
- case MUSICMODE_FRONTEND:
- {
- if ( MusicManager.GetNextTrack() == STREAMED_SOUND_GAME_COMPLETED )
- return FALSE;
-
- break;
- }
}
}
@@ -1291,7 +1316,7 @@ cSampleManager::LoadPedComment(uint32 nComment)
int samplesSize = m_aSamples[nComment].nSize / 2;
op_pcm_seek(fpSampleDataHandle, m_aSamples[nComment].nOffset / 2);
while (samplesSize > 0) {
- int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE * nCurrentPedSlot + samplesRead),
+ int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[SAMPLEBANK_PED] + PED_BLOCKSIZE * nCurrentPedSlot + samplesRead),
samplesSize, NULL);
if (size <= 0) {
return FALSE;
@@ -1363,29 +1388,25 @@ bool8 cSampleManager::UpdateReverb(void)
if ( AudioManager.m_FrameCounter & 15 )
return FALSE;
+ float fRatio = 0.0f;
+
#ifdef AUDIO_REFLECTIONS
- float y = AudioManager.m_afReflectionsDistances[REFLECTION_TOP] + AudioManager.m_afReflectionsDistances[REFLECTION_BOTTOM];
- float x = AudioManager.m_afReflectionsDistances[REFLECTION_LEFT] + AudioManager.m_afReflectionsDistances[REFLECTION_RIGHT];
- float z = AudioManager.m_afReflectionsDistances[REFLECTION_UP];
-#else
- float x = 0.0f;
- float y = 0.0f;
- float z = 0.0f;
+#define MIN_DIST 0.5f
+#define CALCULATE_RATIO(value, maxDist, maxRatio) (value > MIN_DIST && value < maxDist ? value / maxDist * maxRatio : 0)
+
+ fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_NORTH], 10.0f, 1/2.f);
+ fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_SOUTH], 10.0f, 1/2.f);
+ fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_WEST], 10.0f, 1/2.f);
+ fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_EAST], 10.0f, 1/2.f);
+
+ fRatio += CALCULATE_RATIO((AudioManager.m_afReflectionsDistances[REFLECTION_NORTH] + AudioManager.m_afReflectionsDistances[REFLECTION_SOUTH]) / 2.f, 4.0f, 1/3.f);
+ fRatio += CALCULATE_RATIO((AudioManager.m_afReflectionsDistances[REFLECTION_WEST] + AudioManager.m_afReflectionsDistances[REFLECTION_EAST]) / 2.f, 4.0f, 1/3.f);
+
+#undef CALCULATE_RATIO
+#undef MIN_DIST
#endif
- float normy = norm(y, 5.0f, 40.0f);
- float normx = norm(x, 5.0f, 40.0f);
- float normz = norm(z, 5.0f, 40.0f);
-
- #define ZR(v, a, b) (((v)==0)?(a):(b))
- #define CALCRATIO(x,y,z,min,max,val) (ZR(y, ZR(x, ZR(z, min, max), min), ZR(x, ZR(z, min, max), ZR(z, min, val))))
-
- float fRatio = CALCRATIO(normx, normy, normz, 0.3f, 0.5f, (normy+normx+normz)/3.0f);
-
- #undef CALCRATIO
- #undef ZR
-
- fRatio = Clamp(fRatio, usingEAX3==1 ? 0.0f : 0.30f, 1.0f);
+ fRatio = Clamp(fRatio, 0.0f, 0.6f);
if ( fRatio == _fPrevEaxRatioDestination )
return FALSE;
@@ -1396,6 +1417,7 @@ bool8 cSampleManager::UpdateReverb(void)
if ( usingEAX3 )
#endif
{
+ fRatio = Min(fRatio * 1.67f, 1.0f);
if ( EAX3ListenerInterpolate(&StartEAX3, &FinishEAX3, fRatio, &EAX3Params, false) )
{
EAX_SetAll(&EAX3Params);
@@ -1410,16 +1432,17 @@ bool8 cSampleManager::UpdateReverb(void)
}
*/
- _fEffectsLevel = 1.0f - fRatio * 0.5f;
+ _fEffectsLevel = fRatio * 0.75f;
}
}
else
{
if ( _usingEFX )
- _fEffectsLevel = (1.0f - fRatio) * 0.4f;
+ _fEffectsLevel = fRatio * 0.8f;
else
- _fEffectsLevel = (1.0f - fRatio) * 0.7f;
+ _fEffectsLevel = fRatio * 0.22f;
}
+ _fEffectsLevel = Min(_fEffectsLevel, 1.0f);
_fPrevEaxRatioDestination = fRatio;
@@ -1496,12 +1519,11 @@ cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume)
nChannelVolume[nChannel] = vol;
- // reduce channel volume when JB.MP3 or S4_BDBD.MP3 playing
- if ( MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE
- && MusicManager.GetNextTrack() != STREAMED_SOUND_NEWS_INTRO
- && MusicManager.GetNextTrack() != STREAMED_SOUND_CUTSCENE_SAL4_BDBD )
- {
- nChannelVolume[nChannel] = vol / 4;
+ if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) {
+ if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE)
+ nChannelVolume[nChannel] = 0;
+ else
+ nChannelVolume[nChannel] >>= 2;
}
// no idea, does this one looks like a bug or it's SetChannelVolume ?
@@ -1536,14 +1558,14 @@ cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume)
nChannelVolume[nChannel] = vol;
- // reduce the volume for JB.MP3 and S4_BDBD.MP3
- if ( MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE
- && MusicManager.GetNextTrack() != STREAMED_SOUND_NEWS_INTRO
- && MusicManager.GetNextTrack() != STREAMED_SOUND_CUTSCENE_SAL4_BDBD )
- {
- nChannelVolume[nChannel] = vol / 4;
+ // increase the volume for JB.MP3 and S4_BDBD.MP3
+ if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) {
+ if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE)
+ nChannelVolume[nChannel] = 0;
+ else
+ nChannelVolume[nChannel] >>= 2;
}
-
+
aChannel[nChannel].SetVolume(m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14);
}
}
@@ -1609,8 +1631,8 @@ cSampleManager::StopChannel(uint32 nChannel)
}
void
-cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
-{
+cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream)
+{
ASSERT( nStream < MAX_STREAMS );
if ( nFile < TOTAL_STREAMED_SOUNDS )
@@ -1618,7 +1640,6 @@ cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
CStream *stream = aStream[nStream];
stream->Close();
-
#ifdef PS2_AUDIO_PATHS
if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
#endif
@@ -1657,12 +1678,12 @@ cSampleManager::StartPreloadedStreamedFile(uint8 nStream)
}
bool8
-cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
+cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream)
{
uint32 i = 0;
uint32 position = nPos;
char filename[MAX_PATH];
-
+
if ( nFile >= TOTAL_STREAMED_SOUNDS )
return FALSE;
@@ -1672,7 +1693,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
do
{
- // Switched to MP3 player just now
+ // Just switched to MP3 player
if ( !_bIsMp3Active && i == 0 )
{
if ( nPos > nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] )
@@ -1682,12 +1703,15 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
// Try to continue from previous song, if already started
if(!_GetMP3PosFromStreamPos(&position, &e) && !e) {
nFile = 0;
+
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() ) {
+ stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1);
+ nStreamLoopedFlag[nStream] = TRUE;
if (position != 0)
stream->SetPosMS(position);
@@ -1700,6 +1724,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
return FALSE;
} else {
+
if (e->pLinkPath != NULL)
aStream[nStream]->Open(e->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
else {
@@ -1708,7 +1733,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
aStream[nStream]->Open(filename);
}
-
+
if (aStream[nStream]->Setup()) {
if (position != 0)
aStream[nStream]->SetPosMS(position);
@@ -1735,6 +1760,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
nFile = 0;
_bIsMp3Active = FALSE;
+
CStream *stream = aStream[nStream];
#ifdef PS2_AUDIO_PATHS
if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
@@ -1742,6 +1768,8 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
if (stream->Setup()) {
+ stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1);
+ nStreamLoopedFlag[nStream] = TRUE;
if (position != 0)
stream->SetPosMS(position);
@@ -1759,6 +1787,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
else {
strcpy(filename, _mp3DirectoryPath);
strcat(filename, mp3->aFilename);
+
aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
}
@@ -1779,13 +1808,18 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
position = 0;
nFile = 0;
}
+ strcpy(filename, StreamedNameTable[nFile]);
+
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() ) {
+ stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1);
+ nStreamLoopedFlag[nStream] = TRUE;
if (position != 0)
stream->SetPosMS(position);
@@ -1841,15 +1875,20 @@ cSampleManager::GetStreamedFilePosition(uint8 nStream)
}
void
-cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream)
+cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffectFlag, uint8 nStream)
{
ASSERT( nStream < MAX_STREAMS );
+ float boostMult = 0.0f;
+
if ( nVolume > MAX_VOLUME )
nVolume = MAX_VOLUME;
if ( nPan > MAX_VOLUME )
nPan = MAX_VOLUME;
+
+ if ( MusicManager.GetRadioInCar() == USERTRACK && !MusicManager.CheckForMusicInterruptions() )
+ boostMult = m_nMP3BoostVolume / 64.f;
nStreamVolume[nStream] = nVolume;
nStreamPan [nStream] = nPan;
@@ -1858,10 +1897,14 @@ cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffect
if ( stream->IsOpened() )
{
- if ( nEffectFlag )
- stream->SetVolume(m_nEffectsFadeVolume*nVolume*m_nEffectsVolume >> 14);
+ if ( nEffectFlag ) {
+ if ( nStream == 1 || nStream == 2 )
+ stream->SetVolume(128*nVolume*m_nEffectsVolume >> 14);
+ else
+ stream->SetVolume(m_nEffectsFadeVolume*nVolume*m_nEffectsVolume >> 14);
+ }
else
- stream->SetVolume(m_nMusicFadeVolume*nVolume*m_nMusicVolume >> 14);
+ stream->SetVolume((m_nMusicFadeVolume*nVolume*(uint32)(m_nMusicVolume * boostMult + m_nMusicVolume)) >> 14);
stream->SetPan(nPan);
}
@@ -1958,4 +2001,11 @@ cSampleManager::InitialiseSampleBanks(void)
return TRUE;
}
+
+void
+cSampleManager::SetStreamedFileLoopFlag(bool8 nLoopFlag, uint8 nChannel)
+{
+ nStreamLoopedFlag[nChannel] = nLoopFlag;
+}
+
#endif