Introduce Mix_LoadMUSType_RW() The new function allows to override SDL_mixer's auto-detection of the music format. Mix_LoadMUS_RW() is now a simple wrapper for Mix_LoadMUSType_RW(). Of course, auto-detection is still possible. The code that does the auto-detecting has been split into the new detect_music_type() static. This can get rid of some code duplication in Mix_LoadMUS() as well (though this patch does not change Mix_LoadMUS() to use this new function.) diff -r 3a4c352a9a00 SDL_mixer.h --- a/SDL_mixer.h Thu Jul 14 12:33:48 2011 -0700 +++ b/SDL_mixer.h Mon Jul 18 14:04:02 2011 +0300 @@ -155,6 +155,9 @@ Matt Campbell (matt@campbellhome.dhs.org) April 2000 */ extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUS_RW(SDL_RWops *rw); +/* Load a music file from an SDL_RWop object assuming a specific format */ +extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUSType_RW(SDL_RWops *rw, Mix_MusicType type); + /* Load a wave file of the mixer format from a memory buffer */ extern DECLSPEC Mix_Chunk * SDLCALL Mix_QuickLoad_WAV(Uint8 *mem); diff -r 3a4c352a9a00 music.c --- a/music.c Thu Jul 14 12:33:48 2011 -0700 +++ b/music.c Mon Jul 18 14:04:02 2011 +0300 @@ -1385,79 +1385,140 @@ Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) { - Uint8 magic[5]; /*Apparently there is no way to check if the file is really a MOD,*/ - /* or there are too many formats supported by MikMod or MikMod does */ - /* this check by itself. If someone implements other formats (e.g. MP3) */ - /* the check can be uncommented */ + return Mix_LoadMUSType_RW(rw, MUS_NONE); +} + +/* MUS_MOD can't be auto-detected. If no other format was detected, MOD is + * assumed and MUS_MOD will be returned, meaning that the format might not + * actually be MOD-based. + * + * Returns MUS_NONE in case of errors. */ +static Mix_MusicType detect_music_type(SDL_RWops *rw) +{ + Uint8 magic[5]; Uint8 moremagic[9]; + + int start = SDL_RWtell(rw); + if (SDL_RWread(rw, magic, 1, 4) != 4 || SDL_RWread(rw, moremagic, 1, 8) != 8 ) { + Mix_SetError("Couldn't read from RWops"); + return MUS_NONE; + } + SDL_RWseek(rw, start, RW_SEEK_SET); + magic[4]='\0'; + moremagic[8] = '\0'; + + /* WAVE files have the magic four bytes "RIFF" + AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */ + if (((strcmp((char *)magic, "RIFF") == 0) && (strcmp((char *)(moremagic+4), "WAVE") == 0)) || + (strcmp((char *)magic, "FORM") == 0)) { + return MUS_WAV; + } + + /* Ogg Vorbis files have the magic four bytes "OggS" */ + if (strcmp((char *)magic, "OggS") == 0) { + return MUS_OGG; + } + + /* FLAC files have the magic four bytes "fLaC" */ + if (strcmp((char *)magic, "fLaC") == 0) { + return MUS_FLAC; + } + + /* MP3 files either have a 0xFF byte followed by a byte for which & 0xF0 + * yields 0xF0, or by the three bytes "ID3" when the file has ID3 metadata */ + if ((magic[0] == 0xFF && (magic[1] & 0xF0) == 0xF0) || (strncmp((char *)magic, "ID3", 3) == 0)) { + return MUS_MP3; + } + + /* MIDI files have the magic four bytes "MThd" */ + if (strcmp((char *)magic, "MThd") == 0) { + return MUS_MID; + } + + /* Assume MOD format. + * + * Apparently there is no way to check if the file is really a MOD, + * or there are too many formats supported by MikMod/ModPlug, or + * MikMod/ModPlug does this check by itself. */ + return MUS_MOD; +} + +Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *rw, Mix_MusicType want) +{ Mix_Music *music; - int start; if (!rw) { Mix_SetError("RWops pointer is NULL"); return NULL; } - /* Figure out what kind of file this is */ - start = SDL_RWtell(rw); - if ( SDL_RWread(rw,magic,1,4) != 4 || - SDL_RWread(rw,moremagic,1,8) != 8 ) { - Mix_SetError("Couldn't read from RWops"); - return NULL; + /* If the caller wants auto-detection, figure out what kind of file + * this is. */ + if (want == MUS_NONE) { + if ((want = detect_music_type(rw)) == MUS_NONE) { + /* Don't call Mix_SetError() here since detect_music_type() + * does that. */ + return NULL; + } } - SDL_RWseek(rw, start, RW_SEEK_SET); - magic[4]='\0'; - moremagic[8] = '\0'; /* Allocate memory for the music structure */ - music=(Mix_Music *)malloc(sizeof(Mix_Music)); - if (music==NULL ) { + music = (Mix_Music *)malloc(sizeof(Mix_Music)); + if (music == NULL ) { Mix_SetError("Out of memory"); return(NULL); } music->error = 0; + switch (want) { #ifdef WAV_MUSIC - /* WAVE files have the magic four bytes "RIFF" - AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" - */ - if ( ((strcmp((char *)magic, "RIFF") == 0) && (strcmp((char *)(moremagic+4), "WAVE") == 0)) || - (strcmp((char *)magic, "FORM") == 0) ) { - music->type = MUS_WAV; - music->data.wave = WAVStream_LoadSong_RW(rw, (char *)magic); - if ( music->data.wave == NULL ) { + case MUS_WAV: + /* The WAVE loader needs the first 4 bytes of the header */ + { + Uint8 magic[5]; + int start = SDL_RWtell(rw); + if (SDL_RWread(rw, magic, 1, 4) != 4) { + Mix_SetError("Couldn't read from RWops"); + return MUS_NONE; + } + SDL_RWseek(rw, start, RW_SEEK_SET); + magic[4] = '\0'; + music->type = MUS_WAV; + music->data.wave = WAVStream_LoadSong_RW(rw, (char *)magic); + } + if (music->data.wave == NULL) { music->error = 1; } + break; +#endif - } else -#endif #ifdef OGG_MUSIC - /* Ogg Vorbis files have the magic four bytes "OggS" */ - if ( strcmp((char *)magic, "OggS") == 0 ) { + case MUS_OGG: music->type = MUS_OGG; music->data.ogg = OGG_new_RW(rw); - if ( music->data.ogg == NULL ) { + if (music->data.ogg == NULL) { music->error = 1; } - } else + break; #endif + #ifdef FLAC_MUSIC - /* FLAC files have the magic four bytes "fLaC" */ - if ( strcmp((char *)magic, "fLaC") == 0 ) { + case MUS_FLAC: music->type = MUS_FLAC; music->data.flac = FLAC_new_RW(rw); - if ( music->data.flac == NULL ) { + if (music->data.flac == NULL) { music->error = 1; } - } else + break; #endif + #ifdef MP3_MUSIC - if ( ( magic[0] == 0xFF && (magic[1] & 0xF0) == 0xF0) || ( strncmp((char *)magic, "ID3", 3) == 0 ) ) { - if ( Mix_Init(MIX_INIT_MP3) ) { + case MUS_MP3: + if (Mix_Init(MIX_INIT_MP3)) { SMPEG_Info info; music->type = MUS_MP3; music->data.mp3 = smpeg.SMPEG_new_rwops(rw, &info, 0); - if ( !info.has_audio ) { + if (!info.has_audio) { Mix_SetError("MPEG file does not have any audio stream."); music->error = 1; } else { @@ -1466,45 +1527,44 @@ } else { music->error = 1; } - } else -#endif -#ifdef MP3_MAD_MUSIC - if ( ( magic[0] == 0xFF && (magic[1] & 0xF0) == 0xF0) || ( strncmp((char *)magic, "ID3", 3) == 0 ) ) { + break; +#elif defined(MP3_MAD_MUSIC) + case MUS_MP3: music->type = MUS_MP3_MAD; music->data.mp3_mad = mad_openFileRW(rw, &used_mixer); if (music->data.mp3_mad == 0) { Mix_SetError("Could not initialize MPEG stream."); music->error = 1; } - } else + break; #endif + #ifdef MID_MUSIC - /* MIDI files have the magic four bytes "MThd" */ - if ( strcmp((char *)magic, "MThd") == 0 ) { + case MUS_MID: music->type = MUS_MID; #ifdef USE_NATIVE_MIDI - if ( native_midi_ok ) { + if (native_midi_ok) { music->data.nativemidi = native_midi_loadsong_RW(rw); - if ( music->data.nativemidi == NULL ) { - Mix_SetError("%s", native_midi_error()); - music->error = 1; + if (music->data.nativemidi == NULL) { + Mix_SetError("%s", native_midi_error()); + music->error = 1; } - goto skip; + break; } #endif #ifdef USE_FLUIDSYNTH_MIDI - if ( fluidsynth_ok ) { + if (fluidsynth_ok) { music->data.fluidsynthmidi = fluidsynth_loadsong_RW(rw); - if ( music->data.fluidsynthmidi == NULL ) { + if (music->data.fluidsynthmidi == NULL) { music->error = 1; } - goto skip; + break; } #endif #ifdef USE_TIMIDITY_MIDI - if ( timidity_ok ) { + if (timidity_ok) { music->data.midi = Timidity_LoadSong_RW(rw); - if ( music->data.midi == NULL ) { + if (music->data.midi == NULL) { Mix_SetError("%s", Timidity_Error()); music->error = 1; } @@ -1513,40 +1573,41 @@ music->error = 1; } #endif - } else + break; #endif + #if defined(MODPLUG_MUSIC) || defined(MOD_MUSIC) - if (1) { + case MUS_MOD: music->error = 1; #ifdef MODPLUG_MUSIC - if ( music->error ) { + if (music->error) { music->type = MUS_MODPLUG; music->data.modplug = modplug_new_RW(rw); - if ( music->data.modplug ) { + if (music->data.modplug) { music->error = 0; } } #endif #ifdef MOD_MUSIC - if ( music->error ) { + if (music->error) { music->type = MUS_MOD; music->data.module = MOD_new_RW(rw); - if ( music->data.module ) { + if (music->data.module) { music->error = 0; } } #endif - } else -#endif /* MODPLUG_MUSIC || MOD_MUSIC */ - { + break; +#endif + + default: Mix_SetError("Unrecognized music format"); - music->error=1; - } + music->error = 1; + } /* switch (want) */ -skip: if (music->error) { free(music); - music=NULL; + music = NULL; } return(music); }