Code: Select all
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;
+}
I've tested it and it seems to work on Linux. I can't remember how to turn it into a release archive with Windows binaries though…
Also, I was thinking that the Dosbox patch should be overhauled a bit, since it does weird things like determining the version by searching for an arbitrary integer in memory, and parsing the current WLD and LEV filenames instead of reading the numbers directly. I would envisage making it work a lot more like my TSR, but right now I don't have all the versions at my fingertips, so that's probably a longer-term thing…