diff -Nur dosbox-0.74-3-vanilla/src/dosbox.cpp dosbox-0.74-3/src/dosbox.cpp
--- dosbox-0.74-3-vanilla/src/dosbox.cpp 2022-02-19 12:13:10.176703695 +0000
+++ dosbox-0.74-3/src/dosbox.cpp 2023-03-26 12:29:21.977752653 +0100
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2002-2010 The DOSBox Team
+ * Modified 2019, 2023 by PluMGMK to include Rayman soundtrack code.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -114,6 +115,10 @@
void INT10_Init(Section*);
+/* PluM's soundtrack thingy */
+void RAYMAN_Init(Section*);
+bool HandleRaymanSoundtrack();
+
static LoopHandler * loop;
bool SDLNetInited;
@@ -142,6 +147,7 @@
#endif
} else {
GFX_Events();
+ HandleRaymanSoundtrack();
if (ticksRemain>0) {
TIMER_AddTick();
ticksRemain--;
@@ -724,6 +730,19 @@
Pstring = Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
Pmulti_remain->Set_help("see serial1");
+ // PluM's Rayman addition
+ secprop=control->AddSection_prop("rayman",&RAYMAN_Init,false);//done
+ const char* rayvers[] = { "auto", "1.00", "1.10", "1.12.0", "1.12.1", "1.12_Unprotected", "1.12.2", "1.20", "1.21", "1.21_Chinese",0};
+ Pstring = secprop->Add_string("gameversion",Property::Changeable::OnlyAtStart,"auto");
+ Pstring->Set_values(rayvers);
+ Pstring->Set_help(
+ "Rayman version you plan to run: auto (default),\n"
+ "1.00, 1.10, 1.12.0, 1.12.1, 1.12.2, 1.20, 1.21,\n"
+ "1.12_Unprotected or 1.21_Chinese.\n"
+ "auto can detect 1.12.0, 1.12_Unprotected, 1.20 or 1.21.\n");
+ Pstring = secprop->Add_path("musicfile",Property::Changeable::OnlyAtStart,"Music.dat");
+ Pstring->Set_help("Path (relative or absolute) to Music.dat file containing full Rayman soundtrack");
+
/* All the DOS Related stuff, which will eventually start up in the shell */
secprop=control->AddSection_prop("dos",&DOS_Init,false);//done
diff -Nur dosbox-0.74-3-vanilla/src/misc/Makefile.am dosbox-0.74-3/src/misc/Makefile.am
--- dosbox-0.74-3-vanilla/src/misc/Makefile.am 2022-02-19 12:13:10.209703826 +0000
+++ dosbox-0.74-3/src/misc/Makefile.am 2023-03-26 12:29:21.977752653 +0100
@@ -1,4 +1,4 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
noinst_LIBRARIES = libmisc.a
-libmisc_a_SOURCES = cross.cpp messages.cpp programs.cpp setup.cpp support.cpp
+libmisc_a_SOURCES = cross.cpp messages.cpp programs.cpp setup.cpp support.cpp rayman_soundtrack.cpp
diff -Nur dosbox-0.74-3-vanilla/src/misc/Makefile.in dosbox-0.74-3/src/misc/Makefile.in
--- dosbox-0.74-3-vanilla/src/misc/Makefile.in 2022-02-19 12:13:10.209703826 +0000
+++ dosbox-0.74-3/src/misc/Makefile.in 2023-03-26 12:29:21.978752660 +0100
@@ -109,7 +109,7 @@
libmisc_a_AR = $(AR) $(ARFLAGS)
libmisc_a_LIBADD =
am_libmisc_a_OBJECTS = cross.$(OBJEXT) messages.$(OBJEXT) \
- programs.$(OBJEXT) setup.$(OBJEXT) support.$(OBJEXT)
+ programs.$(OBJEXT) setup.$(OBJEXT) support.$(OBJEXT) rayman_soundtrack.$(OBJEXT)
libmisc_a_OBJECTS = $(am_libmisc_a_OBJECTS)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -277,7 +277,7 @@
top_srcdir = @top_srcdir@
AM_CPPFLAGS = -I$(top_srcdir)/include
noinst_LIBRARIES = libmisc.a
-libmisc_a_SOURCES = cross.cpp messages.cpp programs.cpp setup.cpp support.cpp
+libmisc_a_SOURCES = cross.cpp messages.cpp programs.cpp setup.cpp support.cpp rayman_soundtrack.cpp
all: all-am
.SUFFIXES:
@@ -331,6 +331,7 @@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/programs.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setup.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/support.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rayman_soundtrack.Po@am__quote@
.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff -Nur dosbox-0.74-3-vanilla/src/misc/rayman_soundtrack.cpp dosbox-0.74-3/src/misc/rayman_soundtrack.cpp
--- dosbox-0.74-3-vanilla/src/misc/rayman_soundtrack.cpp 1970-01-01 01:00:00.000000000 +0100
+++ dosbox-0.74-3/src/misc/rayman_soundtrack.cpp 2023-03-26 13:28:56.413519224 +0100
@@ -0,0 +1,1219 @@
+/*
+ * This Rayman soundtrack implementation code Copyright (C) 2019, 2023 PluMGMK
+ * Based on DOSBox, Copyright (C) 2002-2010 The DOSBox Team
+ * Incorporating LGPL code from SDL - Simple DirectMedia Layer,
+ * Copyright (C) 1997-2012 Sam Lantinga
+ * Most logic based on TPLS, created by Snagglebee and included in
+ * MIT-licensed Rayman Control Panel, Copyright (c) 2019 RayCarrot
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "dosbox.h"
+#include "mem.h"
+#include "mixer.h"
+#include "SDL.h"
+#include "SDL_thread.h"
+#include "SDL_sound.h"
+#include "setup.h" // For Section_prop
+#include "control.h" // For control->cmdline
+#include <cstring>
+#include <stdlib.h>
+
+// Rayman Versions
+#define RAY_AUTOVER 0
+#define RAY_1_00 1
+#define RAY_1_10 2
+#define RAY_1_12_0 3
+#define RAY_1_12_1 4
+#define RAY_1_12_2 5
+#define RAY_1_20 6
+#define RAY_1_21 7
+#define RAY_1_21_CN 8
+#define RAY_1_12_U 9 // The "UNPROTECTED" version
+
+static unsigned char gRayVer = 0;
+// Offsets associated with the current Rayman version...
+static PhysPt gRayWorldBase;
+static PhysPt gRayLevelOffset;
+static PhysPt gRayInLevelOffset;
+static PhysPt gRayMusOnOffset;
+static PhysPt gRayOptionsOnOffset;
+static PhysPt gRayOptionsOffOffset;
+static PhysPt gRayBossEventOffset;
+static PhysPt gRayXOffset;
+static PhysPt gRayYOffset;
+// Info on the actual game state
+static char gRayWorld[8];
+static char gRayLevel[8];
+static Bit8u gRayInLevel;
+static Bit8u gRayMusOn;
+static Bit8u gRayOptionsOn;
+static Bit8u gRayOptionsOff;
+static Bit8u gRayBossEvent;
+static Bit16u gRayX;
+static Bit16u gRayY;
+
+// Express world and level as integers to make things a bit easier...
+inline bool RaySanityCheck(char *name) { return ((name[0] == 'R') && (name[1] == 'A') && (name[2] == 'Y')); }
+inline int RayWorldNumber() { return RaySanityCheck(gRayWorld) ? std::atoi(gRayWorld+3) : 0; }
+inline int RayLevelNumber() { return RaySanityCheck(gRayLevel) ? std::atoi(gRayLevel+3) : 0; }
+
+// Location of Music.dat file
+static std::string gRayMusPath;
+// Offsets and lengths of the Ogg files for the current track, within the Music.dat file
+static int gRaySoundtrackOffsets[2];
+static int gRaySoundtrackLengths[2];
+// Other info about the currently-playing soundtrack(s)
+static unsigned char gRayNumCurSoundtracks = 0;
+static unsigned char gRayCurSoundtrackIdx = 0;
+static bool gRayPosDep = false;
+static bool gRaySoundtrackPaused = false;
+static int gRayFadeAfterSamples = 0;
+static int gRayFadeDurationSamples = 0;
+
+// Low-level sound stuff...
+#define RAY_MAX_STRACKS 2
+static MixerChannel *gRayChannel = NULL;
+static Sound_Sample *gRaySamples[RAY_MAX_STRACKS];
+static SDL_mutex *gRayMutex = NULL;
+
+// Additional MIDI soundtracks.
+static int gRayAddStrackOffset = 0;
+static int gRayAddStrackLength = 0;
+static MixerChannel *gRayAddChannel = NULL;
+static Sound_Sample *gRayAddSample = NULL;
+static SDL_mutex *gRayAddMutex = NULL;
+
+// Custom RWops functions for reading specific sections of Music.dat
+typedef struct RayWrapRWops {
+ SDL_RWops *underlying;
+ int offset;
+ int length;
+} RayWrapRWops;
+
+#define RAY_TROFFSET(ctx) ((RayWrapRWops*)(ctx)->hidden.unknown.data1)->offset
+#define RAY_TRLENGTH(ctx) ((RayWrapRWops*)(ctx)->hidden.unknown.data1)->length
+#define RAY_UNDERLYING(ctx) ((RayWrapRWops*)(ctx)->hidden.unknown.data1)->underlying
+
+static Uint32 gRayRWtype = ('P'<< 3)+('L'<< 2)+('U'<< 1)+'M';
+
+static int SDLCALL RaymanSeek(SDL_RWops *context, int offset, int whence) {
+ // Offset definitions...
+ int startpos = RAY_TROFFSET(context);
+ int endpos = startpos + RAY_TRLENGTH(context);
+ int newpos;
+ switch (whence) {
+ case RW_SEEK_SET:
+ // Relative to the start of our music "file".
+ newpos = startpos + offset;
+ break;
+ case RW_SEEK_CUR:
+ // No need to change anything.
+ newpos = SDL_RWtell(RAY_UNDERLYING(context)) + offset;
+ break;
+ case RW_SEEK_END:
+ // Relative to the end of our music "file".
+ newpos = endpos + offset;
+ break;
+ default:
+ SDL_SetError("Unknown value for 'whence'");
+ return(-1);
+ }
+ if ( (newpos >= startpos) && (newpos <= endpos) && // Sanity check first...
+ SDL_RWseek(RAY_UNDERLYING(context), newpos, RW_SEEK_SET) >= 0 ) {
+ return(SDL_RWtell(RAY_UNDERLYING(context)) - startpos);
+ } else {
+ SDL_Error(SDL_EFSEEK);
+ return(-1);
+ }
+}
+static int SDLCALL RaymanRead(SDL_RWops *context, void *ptr, int size, int maxnum)
+{
+ // Offset definitions...
+ int endpos = RAY_TROFFSET(context) + RAY_TRLENGTH(context);
+ int curpos = SDL_RWtell(RAY_UNDERLYING(context));
+ if((curpos + maxnum) > endpos)
+ // Clamp within the extent of our music "file".
+ maxnum = endpos - curpos;
+
+ size_t nread;
+
+ nread = SDL_RWread(RAY_UNDERLYING(context), ptr, size, maxnum);
+ return(nread);
+}
+static int SDLCALL RaymanWrite(SDL_RWops *context, const void *ptr, int size, int maxnum)
+{
+ LOG_MSG("Something's trying to write to Music.dat - https://xkcd.com/2200/");
+ // Make the caller happy anyway...
+ return(maxnum);
+}
+static int SDLCALL RaymanClose(SDL_RWops *context)
+{
+ if ( context ) {
+ if(context->type != gRayRWtype) {
+ SDL_SetError("Wrong kind of SDL_RWops for RaymanClose()");
+ return 0;
+ }
+
+ SDL_FreeRW(RAY_UNDERLYING(context));
+ SDL_free(context->hidden.unknown.data1);
+ SDL_FreeRW(context);
+ }
+ return(0);
+}
+
+// Stuff for hijacking the CD player
+static float gRayDesiredVol[2] = {1, 1};
+void GagCDAudio() {
+ MixerChannel *CDchan = MIXER_FindChannel("CDAUDIO");
+
+ if (!CDchan)
+ return;
+
+ // Make sure it's not already gagged.
+ if ((CDchan->volmain[0] == 0) && (CDchan->volmain[1] == 0))
+ return;
+
+ // Save the volume.
+ gRayDesiredVol[0] = CDchan->volmain[0];
+ gRayDesiredVol[1] = CDchan->volmain[1];
+
+ // Mute the channel.
+ CDchan->SetVolume(0,0);
+}
+
+void UngagCDAudio() {
+ MixerChannel *CDchan = MIXER_FindChannel("CDAUDIO");
+
+ if (!CDchan)
+ return;
+
+ // Make sure it's actually gagged first.
+ if((CDchan->volmain[0] != 0) || (CDchan->volmain[1] != 0))
+ return;
+
+ // Restore the saved volume.
+ CDchan->SetVolume(gRayDesiredVol[0], gRayDesiredVol[1]);
+}
+
+// Main logic
+#define CUR_SAMPLE gRaySamples[gRayCurSoundtrackIdx-1]
+void RayAdvanceSoundtrack() {
+ if(gRayCurSoundtrackIdx < gRayNumCurSoundtracks)
+ // Advance to the next soundtrack.
+ gRayCurSoundtrackIdx++;
+
+ // Move it to the start so we're ready to play it...
+ if (CUR_SAMPLE)
+ Sound_Seek(CUR_SAMPLE, 0);
+}
+
+void RayStopAddStrack() {
+ LOG_MSG("Stopping additional Rayman soundtrack");
+
+ // Stop our audio.
+ gRayAddChannel->Enable(false);
+
+ SDL_mutexP(gRayAddMutex);
+ Sound_FreeSample(gRayAddSample);
+ gRayAddSample = NULL;
+ SDL_mutexV(gRayAddMutex);
+}
+
+void RayStopSoundtrack() {
+ LOG_MSG("Stopping custom Rayman soundtrack");
+
+ // We're not paused, we're stopped!
+ gRaySoundtrackPaused = false;
+ gRayCurSoundtrackIdx = 0;
+
+ // Stop our audio and restore native CD audio.
+ gRayChannel->Enable(false);
+ UngagCDAudio();
+
+ // Iterate over all the non-null samples and delete them.
+ SDL_mutexP(gRayMutex);
+ for (int i=0; gRaySamples[i] && (i < RAY_MAX_STRACKS); i++) {
+ Sound_FreeSample(gRaySamples[i]);
+ gRaySamples[i] = NULL;
+ }
+ SDL_mutexV(gRayMutex);
+
+ // Also stop any additional soundtrack if necessary.
+ if(gRayAddSample)
+ RayStopAddStrack();
+}
+
+void RaySoundtrackCallBack(Bitu len)
+{
+ // Handle fading first
+ if(gRayFadeAfterSamples)
+ gRayFadeAfterSamples -= len;
+ else if (gRayFadeDurationSamples) {
+ float FadeStep = len / (1.0 * gRayFadeDurationSamples);
+ float VolSteps[2] = {FadeStep * gRayDesiredVol[0],
+ FadeStep * gRayDesiredVol[1]};
+ float NewVols[2] = {gRayChannel->volmain[0] - VolSteps[0],
+ gRayChannel->volmain[1] - VolSteps[0]};
+ if(NewVols[0] <= 0 || NewVols[1] <= 0) {
+ // Fade complete!
+ RayStopSoundtrack();
+ gRayFadeDurationSamples = 0;
+ return;
+ } else
+ gRayChannel->SetVolume(NewVols[0], NewVols[1]);
+ }
+
+ if(gRayFadeAfterSamples < 0)
+ gRayFadeAfterSamples = 0;
+
+ len *= 4; // 16 bit, stereo
+ if (!len) return;
+ if (!gRayCurSoundtrackIdx || gRaySoundtrackPaused) {
+ gRayChannel->AddSilence();
+ return;
+ }
+
+ SDL_mutexP(gRayMutex);
+ if (CUR_SAMPLE)
+ {
+ Sound_SetBufferSize(CUR_SAMPLE, len);
+ int bytes = Sound_Decode(CUR_SAMPLE);
+ bool success = (bytes == len);
+#if defined(WORDS_BIGENDIAN)
+ gRayChannel->AddSamples_s16_nonnative((success?len:bytes)/4,(Bit16s *)(CUR_SAMPLE->buffer));
+#else
+ gRayChannel->AddSamples_s16((success?len:bytes)/4,(Bit16s *)(CUR_SAMPLE->buffer));
+#endif
+ if(!success) {
+ RayAdvanceSoundtrack();
+ gRayChannel->AddSilence();
+ }
+ } else {
+ // Rudimentary error handling...
+ RayAdvanceSoundtrack();
+ gRayChannel->AddSilence();
+ }
+ SDL_mutexV(gRayMutex);
+}
+
+void RayAddStrackCallBack(Bitu len)
+{
+ // Handle fading first - piggy-back on main soundtrack
+ if (gRayFadeDurationSamples) {
+ gRayAddChannel->SetVolume(gRayChannel->volmain[0], gRayChannel->volmain[1]);
+ }
+
+ len *= 4; // 16 bit, stereo
+ if (!len) return;
+ if (!gRayAddStrackLength || gRaySoundtrackPaused) {
+ gRayAddChannel->AddSilence();
+ return;
+ }
+
+ SDL_mutexP(gRayAddMutex);
+ if (gRayAddSample)
+ {
+ Sound_SetBufferSize(gRayAddSample, len);
+ int bytes = Sound_Decode(gRayAddSample);
+ bool success = (bytes == len);
+#if defined(WORDS_BIGENDIAN)
+ gRayAddChannel->AddSamples_s16_nonnative((success?len:bytes)/4,(Bit16s *)(gRayAddSample->buffer));
+#else
+ gRayAddChannel->AddSamples_s16((success?len:bytes)/4,(Bit16s *)(gRayAddSample->buffer));
+#endif
+ if(!success) {
+ Sound_Seek(gRayAddSample, 0);
+ gRayAddChannel->AddSilence();
+ }
+ } else {
+ // Rudimentary error handling...
+ gRayAddChannel->AddSilence();
+ }
+ SDL_mutexV(gRayAddMutex);
+}
+
+static Sound_Sample *Sound_SampleFromRayMusic(int offset, int length) {
+ // Need a custom RWops to read only a specific part of Music.dat
+ // This approach involves multiple pointers to the same file when we've multiple active soundtracks...
+ SDL_RWops *underlying = SDL_RWFromFile(gRayMusPath.c_str(), "rb");
+ if(!underlying) {
+ LOG_MSG("Unable to open Music.dat (%s) - aborting", SDL_GetError());
+ return NULL;
+ }
+ // First seek to the beginning of our "file".
+ SDL_RWseek(underlying, offset, RW_SEEK_SET);
+
+ // Now create *another* RWops to wrap this one...
+ SDL_RWops *myrwops = SDL_AllocRW();
+ myrwops->type = gRayRWtype;
+
+ // Use the "unknown" part to define the offset and length of the current track.
+ myrwops->hidden.unknown.data1 = SDL_malloc(sizeof(RayWrapRWops));
+ RAY_TROFFSET(myrwops) = offset;
+ RAY_TRLENGTH(myrwops) = length;
+ RAY_UNDERLYING(myrwops) = underlying;
+
+ // Now we have to define custom read and seek functions...
+ myrwops->seek = RaymanSeek;
+ myrwops->read = RaymanRead;
+ myrwops->close = RaymanClose;
+ myrwops->write = RaymanWrite; // Shouldn't be needed...
+
+ Sound_AudioInfo desired = {AUDIO_S16, 2, 44100};
+ return Sound_NewSample(myrwops, "ogg", &desired, 2352);
+}
+
+void RayActivateAddStrack() {
+ LOG_MSG("Activating additional Rayman soundtrack");
+
+ Sound_Sample *mysample = Sound_SampleFromRayMusic(gRayAddStrackOffset, gRayAddStrackLength);
+ if(!mysample)
+ return;
+
+ SDL_mutexP(gRayAddMutex);
+ gRayAddSample = mysample;
+ SDL_mutexV(gRayAddMutex);
+
+ // Enable our own audio and bring it up to the volume the CD audio had...
+ gRayAddChannel->Enable(true);
+ gRayAddChannel->SetVolume(gRayDesiredVol[0], gRayDesiredVol[1]);
+}
+
+void RayActivateSoundtrack() {
+ LOG_MSG("Activating custom Rayman soundtrack");
+
+ // Cut short any fades.
+ if(gRayFadeAfterSamples || gRayFadeDurationSamples) {
+ gRayFadeAfterSamples = gRayFadeDurationSamples = 0;
+ RayStopSoundtrack();
+ }
+
+ // Mute native CD audio...
+ GagCDAudio();
+
+ // If I'm already active, just interpret this as an unpause command...
+ if(gRayCurSoundtrackIdx) {
+ gRaySoundtrackPaused = false;
+ return;
+ }
+
+ for (int j=0; j<gRayNumCurSoundtracks; j++) {
+ Sound_Sample *mysample = Sound_SampleFromRayMusic(gRaySoundtrackOffsets[j], gRaySoundtrackLengths[j]);
+ if(!mysample) {
+ UngagCDAudio();
+ return;
+ }
+
+ SDL_mutexP(gRayMutex);
+ gRaySamples[j] = mysample;
+ SDL_mutexV(gRayMutex);
+ }
+
+ // Start with the first soundtrack
+ gRayCurSoundtrackIdx = 1;
+
+ // Enable our own audio and bring it up to the volume the CD audio had...
+ gRayChannel->Enable(true);
+ gRayChannel->SetVolume(gRayDesiredVol[0], gRayDesiredVol[1]);
+
+ // Also activate any additional soundtrack if necessary.
+ if(gRayAddStrackLength)
+ RayActivateAddStrack();
+}
+
+void RayPauseSoundtrack() {
+ LOG_MSG("Pausing custom Rayman soundtrack");
+ gRaySoundtrackPaused = true;
+ UngagCDAudio();
+}
+
+void RayFadeOutSoundtrack() {
+ LOG_MSG("Fading custom Rayman soundtrack");
+ gRayFadeAfterSamples = 44; // ~1 ms with 44100 Hz audio
+ gRayFadeDurationSamples = 44100; // 1 s
+ gRayPosDep = false;
+}
+
+void RayChooseSoundtrack() {
+ // Default for most levels:
+ gRayPosDep = false;
+ gRayAddStrackOffset = gRayAddStrackLength = 0;
+
+ switch (RayWorldNumber()) {
+ case 1:
+ switch (RayLevelNumber()) {
+ case 1:
+ case 5:
+ case 12:
+ gRaySoundtrackOffsets[0] = 31720237;
+ gRaySoundtrackOffsets[1] = 29542498;
+ gRaySoundtrackLengths[0] = 1222633;
+ gRaySoundtrackLengths[1] = 2177739;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 2:
+ gRayAddStrackOffset = 16214875;
+ gRayAddStrackLength = 1661628;
+ // Fallthrough since level 13 has same base soundtrack
+ case 13:
+ gRaySoundtrackOffsets[0] = 22786573;
+ gRaySoundtrackOffsets[1] = 20390262;
+ gRaySoundtrackLengths[0] = 656206;
+ gRaySoundtrackLengths[1] = 2396311;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 4:
+ case 10:
+ case 11:
+ gRaySoundtrackOffsets[0] = 28460009;
+ gRaySoundtrackOffsets[1] = 26510592;
+ gRaySoundtrackLengths[0] = 1082489;
+ gRaySoundtrackLengths[1] = 1949417;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 6:
+ gRayPosDep = true;
+ if(gRayY < 830) {
+ gRaySoundtrackOffsets[0] = 22786573;
+ gRaySoundtrackOffsets[1] = 20390262;
+ gRaySoundtrackLengths[0] = 656206;
+ gRaySoundtrackLengths[1] = 2396311;
+ } else if (gRayY > 830) {
+ gRaySoundtrackOffsets[0] = 25130666;
+ gRaySoundtrackOffsets[1] = 23442779;
+ gRaySoundtrackLengths[0] = 1379926;
+ gRaySoundtrackLengths[1] = 1687887;
+ }
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 7:
+ gRayPosDep = true;
+ if(gRayX < 4850 || gRayX > 9250) {
+ gRaySoundtrackOffsets[0] = 35162640;
+ gRaySoundtrackOffsets[1] = 32942870;
+ gRaySoundtrackLengths[0] = 469006;
+ gRaySoundtrackLengths[1] = 2219770;
+ gRayNumCurSoundtracks = 2;
+ } else if (gRayX > 4850) {
+ gRaySoundtrackOffsets[0] = 35631646;
+ gRaySoundtrackLengths[0] = 942193;
+ gRayNumCurSoundtracks = 1;
+ }
+ break;
+ case 9:
+ gRayPosDep = true;
+ if(gRayY < 2650) {
+ gRaySoundtrackOffsets[0] = 45334864;
+ gRaySoundtrackOffsets[1] = 43185671;
+ gRaySoundtrackLengths[0] = 993058;
+ gRaySoundtrackLengths[1] = 2149193;
+ } else if (gRayY > 2650) {
+ gRaySoundtrackOffsets[0] = 22786573;
+ gRaySoundtrackOffsets[1] = 20390262;
+ gRaySoundtrackLengths[0] = 656206;
+ gRaySoundtrackLengths[1] = 2396311;
+ }
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 14:
+ gRaySoundtrackOffsets[0] = 87789323;
+ gRaySoundtrackOffsets[1] = 85872167;
+ gRaySoundtrackLengths[0] = 560786;
+ gRaySoundtrackLengths[1] = 1917156;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 15:
+ gRaySoundtrackOffsets[0] = 35162640;
+ gRaySoundtrackOffsets[1] = 32942870;
+ gRaySoundtrackLengths[0] = 469006;
+ gRaySoundtrackLengths[1] = 2219770;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 16:
+ gRaySoundtrackOffsets[0] = 37576545;
+ gRaySoundtrackOffsets[1] = 36573839;
+ gRaySoundtrackLengths[0] = 1210122;
+ gRaySoundtrackLengths[1] = 1002706;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 3:
+ case 8:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ default:
+ // Betilla/Magician level, so the normal game music is OK.
+ gRayNumCurSoundtracks = 0;
+ }
+ break;
+ case 2:
+ switch (RayLevelNumber()) {
+ case 1:
+ case 8:
+ gRaySoundtrackOffsets[0] = 63268113;
+ gRaySoundtrackOffsets[1] = 60977646;
+ gRaySoundtrackLengths[0] = 495197;
+ gRaySoundtrackLengths[1] = 2290467;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 2:
+ case 5:
+ gRaySoundtrackOffsets[0] = 54715946;
+ gRaySoundtrackOffsets[1] = 52302652;
+ gRaySoundtrackLengths[0] = 972212;
+ gRaySoundtrackLengths[1] = 2413294;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 3:
+ gRaySoundtrackOffsets[0] = 48840974;
+ gRaySoundtrackOffsets[1] = 46327922;
+ gRaySoundtrackLengths[0] = 931093;
+ gRaySoundtrackLengths[1] = 2513052;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 4:
+ gRayAddStrackOffset = 13763444;
+ gRayAddStrackLength = 634589;
+ gRaySoundtrackOffsets[0] = 72360982;
+ gRaySoundtrackOffsets[1] = 71234026;
+ gRaySoundtrackLengths[0] = 368425;
+ gRaySoundtrackLengths[1] = 1126956;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 6:
+ gRaySoundtrackOffsets[0] = 68284466;
+ gRaySoundtrackOffsets[1] = 66895587;
+ gRaySoundtrackLengths[0] = 999993;
+ gRaySoundtrackLengths[1] = 1388879;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 7:
+ case 9:
+ gRaySoundtrackOffsets[0] = 70729506;
+ gRaySoundtrackOffsets[1] = 69284459;
+ gRaySoundtrackLengths[0] = 504520;
+ gRaySoundtrackLengths[1] = 1445047;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 10:
+ case 14:
+ gRaySoundtrackOffsets[0] = 51971460;
+ gRaySoundtrackOffsets[1] = 49772067;
+ gRaySoundtrackLengths[0] = 331192;
+ gRaySoundtrackLengths[1] = 2199393;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 12:
+ gRaySoundtrackOffsets[0] = 57329229;
+ gRaySoundtrackOffsets[1] = 55688158;
+ gRaySoundtrackLengths[0] = 878792;
+ gRaySoundtrackLengths[1] = 1641071;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 13:
+ gRaySoundtrackOffsets[0] = 65467614;
+ gRaySoundtrackOffsets[1] = 63763310;
+ gRaySoundtrackLengths[0] = 1427973;
+ gRaySoundtrackLengths[1] = 1704304;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 15:
+ case 16:
+ gRaySoundtrackOffsets[0] = 60084178;
+ gRaySoundtrackOffsets[1] = 58208021;
+ gRaySoundtrackLengths[0] = 893468;
+ gRaySoundtrackLengths[1] = 1876157;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 11:
+ case 17:
+ case 18:
+ default:
+ // Betilla/Magician level, so the normal game music is OK.
+ gRayNumCurSoundtracks = 0;
+ }
+ break;
+ case 3:
+ switch (RayLevelNumber()) {
+ case 1:
+ case 5:
+ gRayAddStrackOffset = 17876503;
+ gRayAddStrackLength = 1569546;
+ // Fallthrough since level 6 has same base soundtrack
+ case 6:
+ gRaySoundtrackOffsets[0] = 82013734;
+ gRaySoundtrackOffsets[1] = 80248120;
+ gRaySoundtrackLengths[0] = 319362;
+ gRaySoundtrackLengths[1] = 1765614;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 2:
+ gRayPosDep = true;
+ if((1930 < gRayX && gRayX < 4525) || (5670 < gRayX && gRayX < 6670)) {
+ gRayAddStrackOffset = 19446049;
+ gRayAddStrackLength = 184365;
+ }
+ gRaySoundtrackOffsets[0] = 82013734;
+ gRaySoundtrackOffsets[1] = 80248120;
+ gRaySoundtrackLengths[0] = 319362;
+ gRaySoundtrackLengths[1] = 1765614;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 3:
+ gRaySoundtrackOffsets[0] = 75280276;
+ gRaySoundtrackOffsets[1] = 72729407;
+ gRaySoundtrackLengths[0] = 681754;
+ gRaySoundtrackLengths[1] = 2550869;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 4:
+ gRaySoundtrackOffsets[0] = 84951434;
+ gRaySoundtrackOffsets[1] = 82333096;
+ gRaySoundtrackLengths[0] = 920733;
+ gRaySoundtrackLengths[1] = 2618338;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 7:
+ gRaySoundtrackOffsets[0] = 87789323;
+ gRaySoundtrackOffsets[1] = 85872167;
+ gRaySoundtrackLengths[0] = 560786;
+ gRaySoundtrackLengths[1] = 1917156;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 8:
+ gRaySoundtrackOffsets[0] = 41987417;
+ gRaySoundtrackOffsets[1] = 38786667;
+ gRaySoundtrackLengths[0] = 1198254;
+ gRaySoundtrackLengths[1] = 3200750;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 9:
+ gRayAddStrackOffset = 17876503;
+ gRayAddStrackLength = 1569546;
+ gRaySoundtrackOffsets[0] = 89538037;
+ gRaySoundtrackOffsets[1] = 88350109;
+ gRaySoundtrackLengths[0] = 283812;
+ gRaySoundtrackLengths[1] = 1187928;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 10:
+ gRaySoundtrackOffsets[0] = 78535486;
+ gRaySoundtrackOffsets[1] = 77089354;
+ gRaySoundtrackLengths[0] = 1712634;
+ gRaySoundtrackLengths[1] = 1446132;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 11:
+ case 12:
+ case 13:
+ default:
+ // Betilla/Magician level, so the normal game music is OK.
+ gRayNumCurSoundtracks = 0;
+ }
+ break;
+ case 4:
+ switch (RayLevelNumber()) {
+ case 1:
+ gRayAddStrackOffset = 19630414;
+ gRayAddStrackLength = 759848;
+ gRaySoundtrackOffsets[0] = 94340731;
+ gRaySoundtrackOffsets[1] = 92766271;
+ gRaySoundtrackLengths[0] = 275891;
+ gRaySoundtrackLengths[1] = 1574460;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 2:
+ case 5:
+ gRaySoundtrackOffsets[0] = 99668617;
+ gRaySoundtrackOffsets[1] = 97779195;
+ gRaySoundtrackLengths[0] = 768697;
+ gRaySoundtrackLengths[1] = 1889422;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 3:
+ case 7:
+ gRaySoundtrackOffsets[0] = 91952950;
+ gRaySoundtrackOffsets[1] = 89821849;
+ gRaySoundtrackLengths[0] = 813321;
+ gRaySoundtrackLengths[1] = 2131101;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 4:
+ gRaySoundtrackOffsets[0] = 102810820;
+ gRaySoundtrackOffsets[1] = 100437314;
+ gRaySoundtrackLengths[0] = 311316;
+ gRaySoundtrackLengths[1] = 2373506;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 6:
+ case 8:
+ gRaySoundtrackOffsets[0] = 97313075;
+ gRaySoundtrackOffsets[1] = 94616622;
+ gRaySoundtrackLengths[0] = 466120;
+ gRaySoundtrackLengths[1] = 2696453;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 9:
+ gRayAddStrackOffset = 19630414;
+ gRayAddStrackLength = 759848;
+ gRaySoundtrackOffsets[0] = 72360982;
+ gRaySoundtrackOffsets[1] = 71234026;
+ gRaySoundtrackLengths[0] = 368425;
+ gRaySoundtrackLengths[1] = 1126956;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 10:
+ gRaySoundtrackOffsets[0] = 87789323;
+ gRaySoundtrackOffsets[1] = 85872167;
+ gRaySoundtrackLengths[0] = 560786;
+ gRaySoundtrackLengths[1] = 1917156;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 11:
+ gRaySoundtrackOffsets[0] = 105417855;
+ gRaySoundtrackOffsets[1] = 103122136;
+ gRaySoundtrackLengths[0] = 1364821;
+ gRaySoundtrackLengths[1] = 2295719;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 12:
+ case 13:
+ default:
+ // Magician level, so the normal game music is OK.
+ gRayNumCurSoundtracks = 0;
+ }
+ break;
+ case 5:
+ switch (RayLevelNumber()) {
+ case 1:
+ gRaySoundtrackOffsets[0] = 112108237;
+ gRaySoundtrackOffsets[1] = 109625356;
+ gRaySoundtrackLengths[0] = 315572;
+ gRaySoundtrackLengths[1] = 2482881;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 2:
+ case 5:
+ case 7:
+ gRaySoundtrackOffsets[0] = 2072694;
+ gRaySoundtrackOffsets[1] = 224859;
+ gRaySoundtrackLengths[0] = 533234;
+ gRaySoundtrackLengths[1] = 1847835;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 3:
+ case 8:
+ gRaySoundtrackOffsets[0] = 117543817;
+ gRaySoundtrackOffsets[1] = 115202576;
+ gRaySoundtrackLengths[0] = 522661;
+ gRaySoundtrackLengths[1] = 2341241;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 6:
+ gRayAddStrackOffset = 14398033;
+ gRayAddStrackLength = 1816842;
+ // Fallthrough since level 4 has same base soundtrack
+ case 4:
+ gRaySoundtrackOffsets[0] = 0; // (!)
+ gRaySoundtrackOffsets[1] = 118066478;
+ gRaySoundtrackLengths[0] = 224859;
+ gRaySoundtrackLengths[1] = 2257449;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 9:
+ gRaySoundtrackOffsets[0] = 108802337;
+ gRaySoundtrackOffsets[1] = 106782676;
+ gRaySoundtrackLengths[0] = 823019;
+ gRaySoundtrackLengths[1] = 2019661;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 10:
+ case 11:
+ gRaySoundtrackOffsets[0] = 114567259;
+ gRaySoundtrackOffsets[1] = 112423809;
+ gRaySoundtrackLengths[0] = 635317;
+ gRaySoundtrackLengths[1] = 2143450;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 12:
+ case 13:
+ default:
+ // Magician level, so the normal game music is OK.
+ gRayNumCurSoundtracks = 0;
+ }
+ break;
+ case 6:
+ switch (RayLevelNumber()) {
+ case 1:
+ gRaySoundtrackOffsets[0] = 8434183;
+ gRaySoundtrackOffsets[1] = 6531785;
+ gRaySoundtrackLengths[0] = 840804;
+ gRaySoundtrackLengths[1] = 1902398;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 2:
+ gRayAddStrackOffset = 14398033;
+ gRayAddStrackLength = 1816842;
+ gRaySoundtrackOffsets[0] = 6207028;
+ gRaySoundtrackOffsets[1] = 4923023;
+ gRaySoundtrackLengths[0] = 324757;
+ gRaySoundtrackLengths[1] = 1284005;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 3:
+ gRaySoundtrackOffsets[0] = 4804876;
+ gRaySoundtrackOffsets[1] = 2605928;
+ gRaySoundtrackLengths[0] = 118147;
+ gRaySoundtrackLengths[1] = 2198948;
+ gRayNumCurSoundtracks = 2;
+ break;
+ case 4:
+ gRaySoundtrackOffsets[0] = 11669736;
+ gRaySoundtrackOffsets[1] = 9274987;
+ gRaySoundtrackLengths[0] = 454217;
+ gRaySoundtrackLengths[1] = 2394749;
+ gRayNumCurSoundtracks = 2;
+ break;
+ default:
+ LOG_MSG("https://xkcd.com/2200/");
+ gRayNumCurSoundtracks = 0;
+ }
+ break;
+ default:
+ LOG_MSG("https://xkcd.com/2200/");
+ gRayNumCurSoundtracks = 0;
+ }
+}
+
+void SetRayVer(unsigned char curRayVer) {
+ // Is this version detection a change in state?
+ //
+ // Don't do anything if it becomes zero though,
+ // since that doesn't *necessarily* mean that
+ // the game has stopped...
+ if ((curRayVer != gRayVer) && curRayVer) {
+ gRayVer = curRayVer;
+ switch(gRayVer) {
+ case RAY_1_00:
+ LOG_MSG("Using Rayman 1.00");
+ gRayWorldBase = 0x16D310;
+ gRayLevelOffset = 0x00020;
+ gRayInLevelOffset = 0x02228;
+ gRayMusOnOffset = 0x02234;
+ gRayOptionsOnOffset = 0x174FB;
+ gRayOptionsOffOffset = 0x174FD;
+ gRayBossEventOffset = 0x02257;
+ gRayXOffset = 0x00E5C;
+ gRayYOffset = 0x00E60;
+ break;
+ case RAY_1_10:
+ LOG_MSG("Using Rayman 1.10");
+ gRayWorldBase = 0x16D7B4;
+ gRayLevelOffset = 0x0001C;
+ gRayInLevelOffset = 0x02278;
+ gRayMusOnOffset = 0x02232;
+ gRayOptionsOnOffset = 0x174E7;
+ gRayOptionsOffOffset = 0x174E9;
+ gRayBossEventOffset = 0x02256;
+ gRayXOffset = 0x00E54;
+ gRayYOffset = 0x00E58;
+ break;
+ case RAY_1_12_0:
+ LOG_MSG("Detected Rayman 1.12.0 - starting monitoring");
+ gRayWorldBase = 0x16D804;
+ gRayLevelOffset = 0x0001C;
+ gRayInLevelOffset = 0x02278;
+ gRayMusOnOffset = 0x02232;
+ gRayOptionsOnOffset = 0x174E7;
+ gRayOptionsOffOffset = 0x174E9;
+ gRayBossEventOffset = 0x02256;
+ gRayXOffset = 0x00E54;
+ gRayYOffset = 0x00E58;
+ break;
+ case RAY_1_12_1:
+ LOG_MSG("Using Rayman 1.12.1");
+ gRayWorldBase = 0x16D814;
+ gRayLevelOffset = 0x0001C;
+ gRayInLevelOffset = 0x02278;
+ gRayMusOnOffset = 0x02232;
+ gRayOptionsOnOffset = 0x174E7;
+ gRayOptionsOffOffset = 0x174E9;
+ gRayBossEventOffset = 0x02256;
+ gRayXOffset = 0x00E54;
+ gRayYOffset = 0x00E58;
+ break;
+ case RAY_1_12_2:
+ LOG_MSG("Using Rayman 1.12.2");
+ gRayWorldBase = 0x16D5B4;
+ gRayLevelOffset = 0x0001C;
+ gRayInLevelOffset = 0x02278;
+ gRayMusOnOffset = 0x02232;
+ gRayOptionsOnOffset = 0x174E7;
+ gRayOptionsOffOffset = 0x174E9;
+ gRayBossEventOffset = 0x02256;
+ gRayXOffset = 0x00E54;
+ gRayYOffset = 0x00E58;
+ break;
+ case RAY_1_12_U:
+ LOG_MSG("Detected Rayman 1.12 UNPROTECTED - starting monitoring");
+ //gRayWorldBase = 0x16E4B4;
+ gRayWorldBase = 0x16D5B4;
+ gRayLevelOffset = 0x0001C;
+ gRayInLevelOffset = 0x02278;
+ gRayMusOnOffset = 0x02232;
+ gRayOptionsOnOffset = 0x174E7;
+ gRayOptionsOffOffset = 0x174EA;
+ gRayBossEventOffset = 0x02256;
+ gRayXOffset = 0x00E54;
+ gRayYOffset = 0x00E58;
+ break;
+ case RAY_1_20:
+ LOG_MSG("Detected Rayman 1.20 - starting monitoring");
+ gRayWorldBase = 0x16E868;
+ gRayLevelOffset = 0x00034;
+ gRayInLevelOffset = 0x022C0;
+ gRayMusOnOffset = 0x02278;
+ gRayOptionsOnOffset = 0x17523;
+ gRayOptionsOffOffset = 0x17525;
+ gRayBossEventOffset = 0x022A0;
+ gRayXOffset = 0x00EA0;
+ gRayYOffset = 0x00EA4;
+ break;
+ case RAY_1_21:
+ LOG_MSG("Detected Rayman 1.21 - starting monitoring");
+ gRayWorldBase = 0x16E7D8;
+ gRayLevelOffset = 0x00034;
+ gRayInLevelOffset = 0x022C0;
+ gRayMusOnOffset = 0x02278;
+ gRayOptionsOnOffset = 0x17523;
+ gRayOptionsOffOffset = 0x17525;
+ gRayBossEventOffset = 0x022A0;
+ gRayXOffset = 0x00EA0;
+ gRayYOffset = 0x00EA4;
+ break;
+ case RAY_1_21_CN:
+ LOG_MSG("Using Rayman 1.21 (Chinese)");
+ gRayWorldBase = 0x16E9F0;
+ gRayLevelOffset = 0x00034;
+ gRayInLevelOffset = 0x022C0;
+ gRayMusOnOffset = 0x02278;
+ gRayOptionsOnOffset = 0x1752B;
+ gRayOptionsOffOffset = 0x1752D;
+ gRayBossEventOffset = 0x022A0;
+ gRayXOffset = 0x00EA0;
+ gRayYOffset = 0x00EA4;
+ break;
+ default:
+ LOG_MSG("Rayman seems to have stopped - stopping monitoring");
+ }
+ }
+}
+
+static void RAYMAN_Stop(Section *sec) {
+ SDL_DestroyMutex(gRayMutex);
+ SDL_DestroyMutex(gRayAddMutex);
+}
+
+// Function run when DOSBox starts...
+void RAYMAN_Init(Section* sec) {
+ sec->AddDestroyFunction(&RAYMAN_Stop);
+
+ Section_prop * section=static_cast<Section_prop *>(sec);
+ /* Read out config section */
+ std::string rayver;
+ // Prefer command-line specification.
+ if (!control->cmdline->FindString("-rayversion", rayver))
+ // Fall back to what's specified in the config file.
+ rayver=section->Get_string("gameversion");
+ if (rayver == "1.00") SetRayVer(RAY_1_00);
+ else if (rayver == "1.10") SetRayVer(RAY_1_10);
+ else if (rayver == "1.12.0") SetRayVer(RAY_1_12_0);
+ else if (rayver == "1.12.1") SetRayVer(RAY_1_12_1);
+ else if (rayver == "1.12.2") SetRayVer(RAY_1_12_2);
+ else if (rayver == "1.12_Unprotected") SetRayVer(RAY_1_12_U);
+ else if (rayver == "1.20") SetRayVer(RAY_1_20);
+ else if (rayver == "1.21") SetRayVer(RAY_1_21);
+ else if (rayver == "1.21_Chinese") SetRayVer(RAY_1_21_CN);
+ // Default is auto, which is 0.
+
+ // Prefer command-line specification.
+ if (!control->cmdline->FindString("-raymusicdat", gRayMusPath))
+ // Fall back to what's specified in the config file.
+ gRayMusPath=section->Get_path("musicfile")->realpath;
+
+ /* Sanity check */
+ SDL_RWops *myrwops = SDL_RWFromFile(gRayMusPath.c_str(), "rb");
+ if(!myrwops) {
+ LOG_MSG("%s not accessible - Rayman soundtrack will be unavailable", gRayMusPath.c_str());
+ gRayMusPath = "";
+ return;
+ }
+ SDL_FreeRW(myrwops);
+
+ /* Initialize the internal stuff */
+ gRayMutex = SDL_CreateMutex();
+ gRayAddMutex = SDL_CreateMutex();
+ gRayChannel = MIXER_AddChannel(&RaySoundtrackCallBack, 44100, "PLUMRAY");
+ gRayAddChannel = MIXER_AddChannel(&RayAddStrackCallBack, 44100, "MIDIRAY");
+}
+
+// Looping function
+bool HandleRaymanSoundtrack() {
+ // If we've no Music.dat path, we can do nothing.
+ if(gRayMusPath=="")
+ return false;
+
+ if (!gRayVer)
+ // Detect Rayman version
+ if(mem_readd(0x16D7BC) == 320)
+ SetRayVer(RAY_1_12_0);
+ else if (mem_readd(/*0x16E46C*/ 0x16D56C) == 320)
+ SetRayVer(RAY_1_12_U);
+ else if (mem_readd(0x16E87C) == 320)
+ SetRayVer(RAY_1_20);
+ else if (mem_readd(0x16E7EC) == 320)
+ SetRayVer(RAY_1_21);
+
+ // Return immediately if Rayman isn't running.
+ if (!gRayVer)
+ return false;
+
+ // Get world name
+ char curWorld[8];
+ MEM_StrCopy(gRayWorldBase, curWorld, 8);
+ if(!RaySanityCheck(curWorld))
+ // Rayman's probably not running...
+ return false;
+ // Truncate
+ for(int i=0;i<8;i++)
+ if (curWorld[i] == '.')
+ curWorld[i] = 0;
+ // Is this a new world?
+ if(strncmp(curWorld,gRayWorld,8)) {
+ LOG_MSG("Entered new world: %s", curWorld);
+ strncpy(gRayWorld,curWorld,8);
+ // Clear the level buffer to make sure new soundtrack gets chosen below!
+ memset(gRayLevel,0,8);
+ }
+
+ // Get level name
+ char curLevel[8];
+ MEM_StrCopy(gRayWorldBase + gRayLevelOffset, curLevel, 8);
+ // Truncate
+ for(int i=0;i<8;i++)
+ if (curLevel[i] == '.')
+ curLevel[i] = 0;
+ // Is this a new level?
+ if(strncmp(curLevel,gRayLevel,8)) {
+ LOG_MSG("Entered new level: %s", curLevel);
+ strncpy(gRayLevel,curLevel,8);
+ // New level => new soundtrack
+ RayChooseSoundtrack();
+ }
+
+ // Other game state info
+ Bit8u RayInLevel = mem_readb(gRayWorldBase + gRayInLevelOffset);
+ Bit8u RayMusOn = mem_readb(gRayWorldBase + gRayMusOnOffset);
+ Bit8u RayOptionsOn = mem_readb(gRayWorldBase + gRayOptionsOnOffset);
+ Bit8u RayOptionsOff = mem_readb(gRayWorldBase + gRayOptionsOffOffset);
+ Bit8u RayBossEvent = mem_readb(gRayWorldBase + gRayBossEventOffset);
+
+ // Has any of it changed?
+ if(RayInLevel != gRayInLevel) {
+ if(RayInLevel) {
+ LOG_MSG("Now in level");
+ if (RayMusOn && RayOptionsOff && !RayOptionsOn && gRayNumCurSoundtracks)
+ RayActivateSoundtrack();
+ else
+ RayFadeOutSoundtrack();
+ } else {
+ LOG_MSG("No longer in level");
+ RayFadeOutSoundtrack();
+ }
+ gRayInLevel = RayInLevel;
+ }
+ if(RayMusOn != gRayMusOn) {
+ if(RayMusOn) {
+ LOG_MSG("Music now on");
+ if (RayInLevel && RayOptionsOff && !RayOptionsOn && gRayNumCurSoundtracks)
+ RayActivateSoundtrack();
+ } else {
+ LOG_MSG("Music no longer on");
+ if (RayInLevel && !RayOptionsOff && RayOptionsOn && gRayNumCurSoundtracks)
+ RayStopSoundtrack();
+ }
+ gRayMusOn = RayMusOn;
+ }
+ if(RayOptionsOn != gRayOptionsOn) {
+ if(RayOptionsOn) {
+ LOG_MSG("Options now on");
+ RayPauseSoundtrack();
+ } else
+ LOG_MSG("Options no longer on");
+ gRayOptionsOn = RayOptionsOn;
+ }
+ if(RayOptionsOff != gRayOptionsOff) {
+ if(RayOptionsOff) {
+ LOG_MSG("Options now off");
+ // LOG_MSG("We have %i soundtracks", gRayNumCurSoundtracks);
+ if (RayInLevel && RayMusOn && !RayOptionsOn && gRayNumCurSoundtracks)
+ RayActivateSoundtrack();
+ } else
+ LOG_MSG("Options no longer off");
+ gRayOptionsOff = RayOptionsOff;
+ }
+ if(RayBossEvent != gRayBossEvent) {
+ if(RayBossEvent) {
+ LOG_MSG("Boss event now");
+ RayFadeOutSoundtrack();
+ } else
+ LOG_MSG("Boss event over");
+ gRayBossEvent = RayBossEvent;
+ }
+
+ // Where is Rayman?
+ Bit16u RayX = mem_readw(gRayWorldBase + gRayXOffset);
+ Bit16u RayY = mem_readw(gRayWorldBase + gRayYOffset);
+ // Has he moved?
+ if ((RayX != gRayX) || (RayY != gRayY)) {
+ // No point in having log messages for this...
+ gRayX = RayX;
+ gRayY = RayY;
+ if (gRayMusOn && gRayNumCurSoundtracks && gRayPosDep) {
+ int curSoundtrack = gRaySoundtrackOffsets[0];
+ int curAddStrack = gRayAddStrackOffset;
+ RayChooseSoundtrack();
+ if (gRaySoundtrackOffsets[0] != curSoundtrack) {
+ LOG_MSG("Restarting custom Rayman soundtrack as Rayman has moved to a different part of the map");
+ RayStopSoundtrack();
+ RayActivateSoundtrack();
+ }
+ if (gRayAddStrackOffset != curAddStrack) {
+ RayStopAddStrack();
+ RayActivateAddStrack();
+ }
+ }
+ }
+
+ return true;
+}