refactor mixer, add support for streamed music

This commit is contained in:
zsilencer 2015-04-25 08:55:12 -06:00
parent 3b416a646e
commit 71e21c2a6c
11 changed files with 367 additions and 143 deletions

View File

@ -1555,7 +1555,7 @@ void start_title_music()
&& RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TITLE_DEMO) {
if (!RCT2_GLOBAL(0x009AF600, uint8)) {
#ifdef USE_MIXER
gTitleMusicChannel = Mixer_Play_Music(musicPathId);
gTitleMusicChannel = Mixer_Play_Music(musicPathId, true);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
int result = sound_channel_load_file2(3, (char*)get_file_path(musicPathId), 0);

View File

@ -31,21 +31,64 @@ extern "C" {
Mixer gMixer;
Sample::Sample()
Source::~Source()
{
}
unsigned long Source::GetSome(unsigned long offset, const uint8** data, unsigned long length)
{
if (offset >= Length()) {
return 0;
}
unsigned long size = length;
if (offset + length > Length()) {
size = Length() - offset;
}
return Read(offset, data, size);
}
unsigned long Source::Length()
{
return length;
}
const AudioFormat& Source::Format()
{
return format;
}
Source_Null::Source_Null()
{
length = 0;
}
unsigned long Source_Null::Read(unsigned long offset, const uint8** data, unsigned long length)
{
return 0;
}
Source_Sample::Source_Sample()
{
data = 0;
length = 0;
issdlwav = false;
}
Sample::~Sample()
Source_Sample::~Source_Sample()
{
Unload();
}
bool Sample::Load(const char* filename)
unsigned long Source_Sample::Read(unsigned long offset, const uint8** data, unsigned long length)
{
log_verbose("Sample::Load(%s)", filename);
*data = &Source_Sample::data[offset];
return length;
}
bool Source_Sample::LoadWAV(const char* filename)
{
log_verbose("Source_Sample::LoadWAV(%s)", filename);
utf8 utf8filename[512];
win1252_to_utf8(utf8filename, filename, sizeof(utf8filename));
@ -75,9 +118,9 @@ bool Sample::Load(const char* filename)
return true;
}
bool Sample::LoadCSS1(const char* filename, unsigned int offset)
bool Source_Sample::LoadCSS1(const char* filename, unsigned int offset)
{
log_verbose("Sample::LoadCSS1(%s, %d)", filename, offset);
log_verbose("Source_Sample::LoadCSS1(%s, %d)", filename, offset);
utf8 utf8filename[512];
win1252_to_utf8(utf8filename, filename, sizeof(utf8filename));
@ -107,13 +150,18 @@ bool Sample::LoadCSS1(const char* filename, unsigned int offset)
format.freq = waveformat.nSamplesPerSec;
format.format = AUDIO_S16LSB;
format.channels = waveformat.nChannels;
data = new uint8[length];
data = new (std::nothrow) uint8[length];
if (!data) {
log_verbose("Unable to allocate data");
SDL_RWclose(rw);
return false;
}
SDL_RWread(rw, data, length, 1);
SDL_RWclose(rw);
return true;
}
void Sample::Unload()
void Source_Sample::Unload()
{
if (data) {
if (issdlwav) {
@ -127,16 +175,11 @@ void Sample::Unload()
length = 0;
}
bool Sample::Loaded()
bool Source_Sample::Convert(AudioFormat format)
{
return data != 0;
}
bool Sample::Convert(AudioFormat format)
{
if(Sample::format.format != format.format || Sample::format.channels != format.channels || Sample::format.freq != format.freq){
if(Source_Sample::format.format != format.format || Source_Sample::format.channels != format.channels || Source_Sample::format.freq != format.freq){
SDL_AudioCVT cvt;
if (SDL_BuildAudioCVT(&cvt, Sample::format.format, Sample::format.channels, Sample::format.freq, format.format, format.channels, format.freq) < 0) {
if (SDL_BuildAudioCVT(&cvt, Source_Sample::format.format, Source_Sample::format.channels, Source_Sample::format.freq, format.format, format.channels, format.freq) < 0) {
return false;
}
cvt.len = length;
@ -149,69 +192,147 @@ bool Sample::Convert(AudioFormat format)
Unload();
data = cvt.buf;
length = cvt.len_cvt;
Sample::format = format;
Source_Sample::format = format;
return true;
}
return false;
}
const uint8* Sample::Data()
Source_SampleStream::Source_SampleStream()
{
return data;
length = 0;
rw = NULL;
buffer = 0;
buffersize = 0;
}
unsigned long Sample::Length()
Source_SampleStream::~Source_SampleStream()
{
return length;
Unload();
}
Stream::Stream()
unsigned long Source_SampleStream::Read(unsigned long offset, const uint8** data, unsigned long length)
{
sourcetype = SOURCE_NONE;
if (length > buffersize) {
if (buffer) {
delete[] buffer;
}
buffer = new (std::nothrow) uint8[length];
if (!buffer) {
return 0;
}
buffersize = length;
}
Sint64 currentposition = SDL_RWtell(rw);
if (currentposition == -1) {
return 0;
}
if (currentposition - databegin != offset) {
Sint64 newposition = SDL_RWseek(rw, databegin + offset, SEEK_SET);
if (newposition == -1) {
return 0;
}
currentposition = newposition;
}
*data = buffer;
int read = SDL_RWread(rw, buffer, 1, length);
if (read == -1) {
return 0;
}
return read;
}
unsigned long Stream::GetSome(unsigned long offset, const uint8** data, unsigned long length)
bool Source_SampleStream::LoadWAV(SDL_RWops* rw)
{
unsigned long size = length;
switch(sourcetype) {
case SOURCE_SAMPLE:
if (offset >= sample->Length()) {
return 0;
}
if (offset + length > sample->Length()) {
size = sample->Length() - offset;
}
*data = &sample->Data()[offset];
return size;
break;
Unload();
if (rw == NULL) {
return false;
}
Source_SampleStream::rw = rw;
Uint32 chunk_id = SDL_ReadLE32(rw);
const Uint32 RIFF = 0x46464952;
if (chunk_id != RIFF) {
log_verbose("Not a WAV file");
return false;
}
Uint32 chunk_size = SDL_ReadLE32(rw);
Uint32 chunk_format = SDL_ReadLE32(rw);
const Uint32 WAVE = 0x45564157;
if (chunk_format != WAVE) {
log_verbose("Not in WAVE format");
return false;
}
const Uint32 FMT = 0x20746D66;
Uint32 fmtchunk_size = FindChunk(rw, FMT);
if (!fmtchunk_size) {
log_verbose("Could not find FMT chunk");
return false;
}
PCMWAVEFORMAT waveformat;
SDL_RWread(rw, &waveformat, sizeof(waveformat), 1);
if (waveformat.wf.wFormatTag != WAVE_FORMAT_PCM) {
log_verbose("Not in proper format");
return false;
}
format.freq = waveformat.wf.nSamplesPerSec;
switch (waveformat.wBitsPerSample) {
case 8:
format.format = AUDIO_U8;
break;
case 16:
format.format = AUDIO_S16LSB;
break;
default:
log_verbose("Invalid bits per sample");
return false;
break;
}
format.channels = waveformat.wf.nChannels;
const Uint32 DATA = 0x61746164;
Uint32 datachunk_size = FindChunk(rw, DATA);
if (!datachunk_size) {
log_verbose("Could not find DATA chunk");
return false;
}
length = datachunk_size;
databegin = SDL_RWtell(rw);
return true;
}
Uint32 Source_SampleStream::FindChunk(SDL_RWops* rw, Uint32 wanted_id)
{
Uint32 subchunk_id = SDL_ReadLE32(rw);
Uint32 subchunk_size = SDL_ReadLE32(rw);
if (subchunk_id == wanted_id) {
return subchunk_size;
}
const Uint32 FACT = 0x74636166;
const Uint32 LIST = 0x5453494c;
const Uint32 BEXT = 0x74786562;
const Uint32 JUNK = 0x4B4E554A;
while (subchunk_id == FACT || subchunk_id == LIST || subchunk_id == BEXT || subchunk_id == JUNK) {
SDL_RWseek(rw, subchunk_size, RW_SEEK_CUR);
subchunk_id = SDL_ReadLE32(rw);
subchunk_size = SDL_ReadLE32(rw);
if (subchunk_id == wanted_id) {
return subchunk_size;
}
}
return 0;
}
unsigned long Stream::Length()
void Source_SampleStream::Unload()
{
switch(sourcetype) {
case SOURCE_SAMPLE:
return sample->Length();
break;
if (rw) {
SDL_RWclose(rw);
rw = NULL;
}
return 0;
}
void Stream::SetSource_Sample(Sample& sample)
{
sourcetype = SOURCE_SAMPLE;
Stream::sample = &sample;
}
const AudioFormat* Stream::Format()
{
switch(sourcetype) {
case SOURCE_SAMPLE:
return &sample->format;
break;
length = 0;
if (buffer) {
delete[] buffer;
buffer = 0;
}
return 0;
buffersize = 0;
}
Channel::Channel()
@ -223,7 +344,8 @@ Channel::Channel()
SetPan(0.5f);
done = true;
stopping = false;
stream = 0;
source = 0;
deletesourceondone = false;
}
Channel::~Channel()
@ -232,11 +354,14 @@ Channel::~Channel()
speex_resampler_destroy(resampler);
resampler = 0;
}
if (deletesourceondone) {
delete source;
}
}
void Channel::Play(Stream& stream, int loop = MIXER_LOOP_NONE)
void Channel::Play(Source& source, int loop = MIXER_LOOP_NONE)
{
Channel::stream = &stream;
Channel::source = &source;
Channel::loop = loop;
offset = 0;
done = false;
@ -286,14 +411,25 @@ unsigned long Channel::GetOffset()
bool Channel::SetOffset(unsigned long offset)
{
if (stream && offset < stream->Length()) {
int samplesize = stream->Format()->channels * stream->Format()->BytesPerSample();
if (source && offset < source->Length()) {
int samplesize = source->Format().channels * source->Format().BytesPerSample();
Channel::offset = (offset / samplesize) * samplesize;
return true;
}
return false;
}
Mixer::Mixer()
{
effectbuffer = 0;
for (int i = 0; i < countof(css1sources); i++) {
css1sources[i] = 0;
}
for (int i = 0; i < countof(musicsources); i++) {
musicsources[i] = 0;
}
}
void Mixer::Init(const char* device)
{
Close();
@ -310,10 +446,15 @@ void Mixer::Init(const char* device)
format.channels = have.channels;
format.freq = have.freq;
const char* filename = get_file_path(PATH_ID_CSS1);
for (int i = 0; i < SOUND_MAXID; i++) {
css1samples[i].LoadCSS1(filename, i);
css1samples[i].Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional
css1streams[i].SetSource_Sample(css1samples[i]);
for (int i = 0; i < countof(css1sources); i++) {
Source_Sample* source_sample = new Source_Sample;
if (source_sample->LoadCSS1(filename, i)) {
source_sample->Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional
css1sources[i] = source_sample;
} else {
css1sources[i] = &source_null;
delete source_sample;
}
}
effectbuffer = new uint8[(have.samples * format.BytesPerSample() * format.channels)];
SDL_PauseAudioDevice(deviceid, 0);
@ -328,7 +469,22 @@ void Mixer::Close()
}
Unlock();
SDL_CloseAudioDevice(deviceid);
delete[] effectbuffer;
for (int i = 0; i < countof(css1sources); i++) {
if (css1sources[i] && css1sources[i] != &source_null) {
delete css1sources[i];
css1sources[i] = 0;
}
}
for (int i = 0; i < countof(musicsources); i++) {
if (musicsources[i] && musicsources[i] != &source_null) {
delete musicsources[i];
musicsources[i] = 0;
}
}
if (effectbuffer) {
delete[] effectbuffer;
effectbuffer = 0;
}
}
void Mixer::Lock()
@ -341,13 +497,14 @@ void Mixer::Unlock()
SDL_UnlockAudioDevice(deviceid);
}
Channel* Mixer::Play(Stream& stream, int loop, bool deleteondone)
Channel* Mixer::Play(Source& source, int loop, bool deleteondone, bool deletesourceondone)
{
Lock();
Channel* newchannel = new (std::nothrow) Channel();
Channel* newchannel = new (std::nothrow) Channel;
if (newchannel) {
newchannel->Play(stream, loop);
newchannel->Play(source, loop);
newchannel->deleteondone = deleteondone;
newchannel->deletesourceondone = deletesourceondone;
newchannel->stopping = false;
channels.push_back(newchannel);
}
@ -364,13 +521,20 @@ void Mixer::Stop(Channel& channel)
bool Mixer::LoadMusic(int pathid)
{
if (pathid >= PATH_ID_END) {
if (pathid >= countof(musicsources)) {
return false;
}
if (!musicsamples[pathid].Loaded()) {
if (!musicsources[pathid]) {
const char* filename = get_file_path(pathid);
musicstreams[pathid].SetSource_Sample(musicsamples[pathid]);
return musicsamples[pathid].Load(filename);
Source_Sample* source_sample = new Source_Sample;
if (source_sample->LoadWAV(filename)) {
musicsources[pathid] = source_sample;
return true;
} else {
delete source_sample;
musicsources[pathid] = &source_null;
return false;
}
} else {
return true;
}
@ -394,8 +558,8 @@ void SDLCALL Mixer::Callback(void* arg, uint8* stream, int length)
void Mixer::MixChannel(Channel& channel, uint8* data, int length)
{
if (channel.stream && !channel.done) {
AudioFormat streamformat = *channel.stream->Format();
if (channel.source && channel.source->Length() > 0 && !channel.done) {
AudioFormat streamformat = channel.source->Format();
int loaded = 0;
SDL_AudioCVT cvt;
cvt.len_ratio = 1;
@ -409,9 +573,9 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
}
int samplestoread = (int)((samples - samplesloaded) * rate);
int lengthloaded = 0;
if (channel.offset < channel.stream->Length()) {
if (channel.offset < channel.source->Length()) {
bool mustconvert = false;
if (MustConvert(*channel.stream)) {
if (MustConvert(*channel.source)) {
if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, Mixer::format.format, Mixer::format.channels, Mixer::format.freq) == -1) {
break;
}
@ -420,7 +584,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
const uint8* datastream = 0;
int toread = (int)(samplestoread / cvt.len_ratio) * samplesize;
int readfromstream = (channel.stream->GetSome(channel.offset, &datastream, toread));
int readfromstream = (channel.source->GetSome(channel.offset, &datastream, toread));
if (readfromstream == 0) {
break;
}
@ -519,7 +683,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
loaded += lengthloaded;
if (channel.loop != 0 && channel.offset >= channel.stream->Length()) {
if (channel.loop != 0 && channel.offset >= channel.source->Length()) {
if (channel.loop != -1) {
channel.loop--;
}
@ -528,7 +692,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
} while(loaded < length && channel.loop != 0 && !channel.stopping);
channel.oldvolume = channel.volume;
if (channel.loop == 0 && channel.offset >= channel.stream->Length()) {
if (channel.loop == 0 && channel.offset >= channel.source->Length()) {
channel.done = true;
}
}
@ -574,13 +738,10 @@ void Mixer::EffectFadeU8(uint8* data, int length, int startvolume, int endvolume
}
}
bool Mixer::MustConvert(Stream& stream)
bool Mixer::MustConvert(Source& source)
{
const AudioFormat* streamformat = stream.Format();
if (!streamformat) {
return false;
}
if (streamformat->format != format.format || streamformat->channels != format.channels || streamformat->freq != format.freq) {
const AudioFormat sourceformat = source.Format();
if (sourceformat.format != format.format || sourceformat.channels != format.channels || sourceformat.freq != format.freq) {
return true;
}
return false;
@ -609,11 +770,11 @@ void Mixer_Init(const char* device)
void* Mixer_Play_Effect(int id, int loop, int volume, float pan, double rate, int deleteondone)
{
if (id >= SOUND_MAXID) {
if (id >= countof(gMixer.css1sources)) {
return 0;
}
gMixer.Lock();
Channel* channel = gMixer.Play(gMixer.css1streams[id], loop, deleteondone != 0);
Channel* channel = gMixer.Play(*gMixer.css1sources[id], loop, deleteondone != 0, false);
if (channel) {
channel->SetVolume(volume);
channel->SetPan(pan);
@ -664,10 +825,33 @@ int Mixer_Channel_SetOffset(void* channel, unsigned long offset)
return ((Channel*)channel)->SetOffset(offset);
}
void* Mixer_Play_Music(int pathid)
void* Mixer_Play_Music(int pathid, int streaming)
{
if (gMixer.LoadMusic(pathid)) {
return gMixer.Play(gMixer.musicstreams[pathid], MIXER_LOOP_INFINITE, false);
if (streaming) {
const char* filename = get_file_path(pathid);
utf8 utf8filename[512];
win1252_to_utf8(utf8filename, filename, sizeof(utf8filename));
SDL_RWops* rw = SDL_RWFromFile(utf8filename, "rb");
if (rw == NULL) {
return 0;
}
Source_SampleStream* source_samplestream = new Source_SampleStream;
if (source_samplestream->LoadWAV(rw)) {
Channel* channel = gMixer.Play(*source_samplestream, MIXER_LOOP_INFINITE, false, true);
if (!channel) {
delete source_samplestream;
}
return channel;
} else {
delete source_samplestream;
return 0;
}
} else {
if (gMixer.LoadMusic(pathid)) {
return gMixer.Play(*gMixer.musicsources[pathid], MIXER_LOOP_INFINITE, false, false);
}
}
return 0;
}

View File

@ -43,45 +43,73 @@ struct AudioFormat {
int channels;
};
class Sample
class Source
{
public:
Sample();
~Sample();
bool Load(const char* filename);
bool LoadCSS1(const char* filename, unsigned int offset);
void Unload();
bool Loaded();
bool Convert(AudioFormat format);
const uint8* Data();
unsigned long Length();
friend class Stream;
private:
AudioFormat format;
uint8* data;
unsigned long length;
bool issdlwav;
};
class Stream
{
public:
Stream();
virtual ~Source();
unsigned long GetSome(unsigned long offset, const uint8** data, unsigned long length);
unsigned long Length();
void SetSource_Sample(Sample& sample);
const AudioFormat* Format();
const AudioFormat& Format();
friend class Mixer;
protected:
virtual unsigned long Read(unsigned long offset, const uint8** data, unsigned long length) = 0;
AudioFormat format;
unsigned long length;
};
class Source_Null : public Source
{
public:
Source_Null();
protected:
unsigned long Read(unsigned long offset, const uint8** data, unsigned long length);
};
class Source_Sample : public Source
{
public:
Source_Sample();
~Source_Sample();
bool LoadWAV(const char* filename);
bool LoadCSS1(const char* filename, unsigned int offset);
friend class Mixer;
protected:
bool Convert(AudioFormat format);
private:
enum {
SOURCE_NONE = 0,
SOURCE_SAMPLE
} sourcetype;
Sample* sample;
void Unload();
uint8* data;
bool issdlwav;
protected:
unsigned long Read(unsigned long offset, const uint8** data, unsigned long length);
};
class Source_SampleStream : public Source
{
public:
Source_SampleStream();
~Source_SampleStream();
bool LoadWAV(SDL_RWops* rw);
private:
Uint32 FindChunk(SDL_RWops* rw, Uint32 wanted_id);
void Unload();
SDL_RWops* rw;
Uint64 databegin;
uint8* buffer;
unsigned long buffersize;
protected:
unsigned long Read(unsigned long offset, const uint8** data, unsigned long length);
};
class Channel
@ -89,7 +117,7 @@ class Channel
public:
Channel();
~Channel();
void Play(Stream& stream, int loop);
void Play(Source& source, int loop);
void SetRate(double rate);
void SetVolume(int volume);
void SetPan(float pan);
@ -108,25 +136,27 @@ private:
float pan;
bool done;
bool deleteondone;
bool deletesourceondone;
bool stopping;
int oldvolume;
SpeexResamplerState* resampler;
Stream* stream;
Source* source;
};
class Mixer
{
public:
Mixer();
void Init(const char* device);
void Close();
void Lock();
void Unlock();
Channel* Play(Stream& stream, int loop, bool deleteondone);
Channel* Play(Source& source, int loop, bool deleteondone, bool deletesourceondone);
void Stop(Channel& channel);
bool LoadMusic(int pathid);
Stream css1streams[SOUND_MAXID];
Stream musicstreams[PATH_ID_END];
Source* css1sources[SOUND_MAXID];
Source* musicsources[PATH_ID_END];
private:
static void SDLCALL Callback(void* arg, uint8* data, int length);
@ -135,14 +165,13 @@ private:
void EffectPanU8(Channel& channel, uint8* data, int length);
void EffectFadeS16(sint16* data, int length, int startvolume, int endvolume);
void EffectFadeU8(uint8* data, int length, int startvolume, int endvolume);
bool MustConvert(Stream& stream);
bool MustConvert(Source& source);
bool Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout);
SDL_AudioDeviceID deviceid;
AudioFormat format;
uint8* effectbuffer;
Sample css1samples[SOUND_MAXID];
Sample musicsamples[PATH_ID_END];
std::list<Channel*> channels;
Source_Null source_null;
};
extern "C"
@ -158,7 +187,7 @@ void Mixer_Channel_Rate(void* channel, double rate);
int Mixer_Channel_IsPlaying(void* channel);
unsigned long Mixer_Channel_GetOffset(void* channel);
int Mixer_Channel_SetOffset(void* channel, unsigned long offset);
void* Mixer_Play_Music(int pathid);
void* Mixer_Play_Music(int pathid, int streaming);
static int DStoMixerVolume(int volume) { return (int)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)volume / 2000))); };
static float DStoMixerPan(int pan) { return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2; };

View File

@ -192,6 +192,8 @@ enum {
STR_LOCATE_SUBJECT_TIP = 1027,
STR_OFF_EDGE_OF_MAP = 1028,
STR_CAN_ONLY_BUILD_THIS_ON_LAND = 1034,
STR_LOAD_GAME_DIALOG_TITLE = 1036,
STR_LOAD_LANDSCAPE_DIALOG_TITLE = 1037,
STR_CONVERT_SAVED_GAME_TO_SCENARIO_1038 = 1038,
@ -378,6 +380,7 @@ enum {
STR_SHOPS_STALLS_TIP = 1228,
STR_ROTATE_OBJECTS_90 = 1327,
STR_LEVEL_LAND_REQUIRED = 1328,
STR_LAUNCH_SPEED = 1329,
STR_LAUNCH_SPEED_TIP = 1330,

View File

@ -4577,7 +4577,7 @@ void peep_update_crowd_noise()
// Load and play crowd noise
#ifdef USE_MIXER
if (!gCrowdSoundChannel) {
gCrowdSoundChannel = Mixer_Play_Music(PATH_ID_CSS2);
gCrowdSoundChannel = Mixer_Play_Music(PATH_ID_CSS2, false);
}
if (gCrowdSoundChannel) {
Mixer_Channel_Volume(gCrowdSoundChannel, DStoMixerVolume(volume));

View File

@ -2662,7 +2662,7 @@ void ride_music_update_final()
rct_ride_music_info* ride_music_info = &RCT2_GLOBAL(0x009AF1C8, rct_ride_music_info*)[ride_music_params->tuneid];
#ifdef USE_MIXER
rct_ride_music* ride_music = &gRideMusicList[ebx];
ride_music->sound_channel = Mixer_Play_Music(ride_music_info->pathid);
ride_music->sound_channel = Mixer_Play_Music(ride_music_info->pathid, true);
if (ride_music->sound_channel) {
ride_music->volume = ride_music_params->volume;
ride_music->pan = ride_music_params->pan;
@ -2678,7 +2678,7 @@ void ride_music_update_final()
}
Mixer_Channel_SetOffset(ride_music->sound_channel, offset);
} else {
RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0;
//RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0;
}
#else
const char* filename = get_file_path(ride_music_info->pathid);

View File

@ -1416,11 +1416,11 @@ static void window_top_toolbar_scenery_tool_down(short x, short y, rct_window* w
int cost;
{
int esi = 0, eax = grid_x, ecx = grid_y, edx = grid_z;
game_do_command_p(GAME_COMMAND_PLACE_BANNER, &eax, &ebx, &ecx, &edx, &esi, &banner_id, &ebp);
int esi = 0, eax = grid_x, ecx = grid_y, edx = parameter_2, ebx = (parameter_1 & 0xFF00) | 1;
cost = game_do_command_p(GAME_COMMAND_PLACE_BANNER, &eax, &ebx, &ecx, &edx, &esi, &banner_id, &ebp);
}
if (ebx == MONEY32_UNDEFINED)return;
if (cost == MONEY32_UNDEFINED)return;
sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16));

View File

@ -48,5 +48,6 @@ void footpath_provisional_remove();
void footpath_provisional_update();
void footpath_get_coordinates_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement);
void footpath_bridge_get_info_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement);
void sub_673883(int x, int y, int z);
#endif

View File

@ -28,6 +28,7 @@
#include "../scenario.h"
#include "banner.h"
#include "climate.h"
#include "footpath.h"
#include "map.h"
#include "map_animation.h"
#include "park.h"
@ -905,7 +906,7 @@ void game_command_set_fence_colour(int* eax, int* ebx, int* ecx, int* edx, int*
return;
}
if(*ebx & GAME_COMMAND_FLAG_APPLY){
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.slope];
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.type];
map_element->properties.fence.item[1] &= 0xE0;
map_element->properties.fence.item[1] |= color1;
map_element->flags &= 0x9F;
@ -1655,7 +1656,7 @@ void game_command_remove_fence(int* eax, int* ebx, int* ecx, int* edx, int* esi,
return;
}
}
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.slope];
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.type];
if(scenery_entry->wall.var_0D != 0xFF){
rct_banner* banner = &gBanners[map_element->properties.fence.item[0]];
if(banner->type != BANNER_NULL){

View File

@ -178,7 +178,7 @@ void scenery_remove_ghost_tool_placement(){
105 | (RCT2_GLOBAL(0x00F64EC0, uint8) << 8),
y,
z,
GAME_COMMAND_44,
GAME_COMMAND_REMOVE_LARGE_SCENERY,
0,
0);
}
@ -191,7 +191,7 @@ void scenery_remove_ghost_tool_placement(){
105,
y,
z | (RCT2_GLOBAL(0x00F64EC0, uint8) << 8),
GAME_COMMAND_51,
GAME_COMMAND_REMOVE_BANNER,
0,
0);
}

View File

@ -56,6 +56,12 @@ typedef enum {
SMALL_SCENERY_FLAG18 = (1 << 17), // 0x20000
SMALL_SCENERY_FLAG19 = (1 << 18), // 0x40000
SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR = (1 << 19), // 0x80000
SMALL_SCENERY_FLAG20 = (1 << 20), // 0x100000
SMALL_SCENERY_FLAG21 = (1 << 21), // 0x200000
SMALL_SCENERY_FLAG22 = (1 << 22), // 0x400000
SMALL_SCENERY_FLAG23 = (1 << 23), // 0x800000
SMALL_SCENERY_FLAG24 = (1 << 24), // 0x1000000
SMALL_SCENERY_FLAG25 = (1 << 25), // 0x2000000
} SMALL_SCENERY_FLAGS;
typedef struct {