diff --git a/Frameworks/OpenMPT/OpenMPT/build/dist.mk b/Frameworks/OpenMPT/OpenMPT/build/dist.mk index 3d617da48..32cbfc597 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/dist.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/dist.mk @@ -1,4 +1,4 @@ -MPT_SVNVERSION=15412 -MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.10 -MPT_SVNDATE=2021-07-04T16:30:04.479627Z +MPT_SVNVERSION=15759 +MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12 +MPT_SVNDATE=2021-10-04T13:48:02.912129Z diff --git a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h index 26f6b0302..2d89fb7d8 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h +++ b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h @@ -1,10 +1,10 @@ #pragma once -#define OPENMPT_VERSION_SVNVERSION "15412" -#define OPENMPT_VERSION_REVISION 15412 +#define OPENMPT_VERSION_SVNVERSION "15759" +#define OPENMPT_VERSION_REVISION 15759 #define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0 -#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.10" -#define OPENMPT_VERSION_DATE "2021-07-04T16:30:04.479627Z" +#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12" +#define OPENMPT_VERSION_DATE "2021-10-04T13:48:02.912129Z" #define OPENMPT_VERSION_IS_PACKAGE 1 diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 82cb7a138..451be4395 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN // Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 29 -#define VER_MINOR 11 +#define VER_MINOR 13 #define VER_MINORMINOR 00 OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh index 8c0e2ff4c..6f67ea491 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh @@ -10,4 +10,4 @@ mkdir $FUZZING_TEMPDIR/bin cp -d ../../bin/* $FUZZING_TEMPDIR/bin/ #export AFL_PRELOAD=$AFL_DIR/libdislocator.so -LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01 +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01 diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt index 714bc017a..e723225a3 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt @@ -1,6 +1,6 @@ This folder contains the stb_vorbis library from -https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.21 -commit 8e51be04dc7dcee462e1f09e410faceab52cc6d2 (2021-07-02) +https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.22 +commit 5a0bb8b1c1b1ca3f4e2485f4114c1c8ea021b781 (2021-07-12) Modifications: * Use of alloca has been replaced with malloc, as alloca is not in C99 and diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c index 4bf24bec6..1d4deecb8 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.21 - public domain +// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -29,13 +29,14 @@ // Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// github:manxorist saga musix github:infatum +// github:manxorist Saga Musix github:infatum // Timur Gagiev Maxwell Koo Peter Waller // github:audinowho Dougall Johnson David Reid // github:Clownacy Pedro J. Estebanez Remi Verschelde -// AnthoFoxo +// AnthoFoxo github:morlat Gabriel Ravier // // Partial history: +// 1.22 - 2021-07-11 - various small fixes // 1.21 - 2021-07-02 - fix bug for files with no comments // 1.20 - 2020-07-11 - several small fixes // 1.19 - 2020-02-05 - warnings @@ -222,6 +223,12 @@ extern int stb_vorbis_decode_frame_pushdata( // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with @@ -583,7 +590,7 @@ enum STBVorbisError #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) + #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT @@ -652,6 +659,12 @@ typedef signed int int32; typedef float codetype; +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + // @NOTE // // Some arrays below are tagged "//varies", which means it's actually @@ -1057,7 +1070,7 @@ static float float32_unpack(uint32 x) uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; - return (float) ldexp((float)res, exp-788); + return (float) ldexp((float)res, (int)exp-788); } @@ -1088,6 +1101,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves @@ -1101,6 +1115,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) uint32 res; int z = len[i], y; if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have @@ -1110,12 +1125,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; - assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); @@ -2588,34 +2601,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, while (z > base) { float k00,k11; + float l00,l11; - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11 ; + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; - k00 = z[ -2] - z[-10]; - k11 = z[ -3] - z[-11]; - z[ -2] = z[ -2] + z[-10]; - z[ -3] = z[ -3] + z[-11]; - z[-10] = (k00+k11) * A2; - z[-11] = (k11-k00) * A2; - - k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k00 = z[ -4] - z[-12]; k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation - k11 = z[ -7] - z[-15]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; - z[-14] = (k00+k11) * A2; - z[-15] = (k00-k11) * A2; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; iter_54(z); iter_54(z-8); @@ -3080,6 +3092,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) @@ -3182,6 +3195,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // WINDOWING + STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; @@ -3379,7 +3393,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; @@ -3880,8 +3894,7 @@ static int start_decoder(vorb *f) unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off]*c->delta_value + c->minimum_value + last; + float val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; @@ -3964,7 +3977,7 @@ static int start_decoder(vorb *f) if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f,8)-1; + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } @@ -4522,6 +4535,7 @@ stb_vorbis *stb_vorbis_open_pushdata( *error = VORBIS_need_more_data; else *error = p.error; + vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); @@ -4579,7 +4593,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; - goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; @@ -4983,7 +4997,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) // set. whoops! break; } - previous_safe = last_page_loc+1; + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging last_page_loc = stb_vorbis_get_file_offset(f); } @@ -5094,7 +5108,10 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - if (data == NULL) return NULL; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + len; @@ -5169,11 +5186,11 @@ static void copy_samples(short *dest, float *src, int len) static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { + for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { @@ -5190,16 +5207,17 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in output[o+i] = v; } } + #undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE >> 1; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); @@ -5229,6 +5247,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d output[o2+i] = v; } } + #undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) @@ -5301,8 +5320,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short float **outputs; int len = num_shorts / channels; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; @@ -5321,8 +5338,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in { float **outputs; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index 5f82a4b6a..17e309db2 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,6 +5,46 @@ Changelog {#changelog} For fully detailed change log, please see the source repository directly. This is just a high-level summary. +### libopenmpt 0.5.12 (2021-10-04) + + * [**Sec**] Possible crash when loading malformed MDL files. (r15603) + + * [**Bug**] Fixed various undefined behaviour found with ubsan. + + * Seeking with sample sync sometimes didn't compute the correct sample + position with pingpong-looped samples. + * IT: Tremor command I11 erroneously behaved like I00 (use previous parameter) + unless IT Old Effects were enabled. + * PTM: Panning was translated wrong in some edge cases. + * IMF / PTM: Note Slide commands were sometimes slightly off. + * OKT: Better support for fine note slides. + * DBM: Echo enable effect parameter range checks were incorrect. + * XM: Sample texts in XMs made with MadTracker are now also decoded using + Windows-1252 encoding. + + * in_openmpt: Song metadata is no longer reverted when viewing file info. + +### libopenmpt 0.5.11 (2021-08-22) + + * [**Sec**] Possible crash with malformed modules when trying to access + non-existent plugin slots FX251-FX255. (r15479, r15518) + * [**Sec**] Possible read beyond sample start after swapping to a sample with + loop points set but not loop enabled. (r15499) + * [**Sec**] Fixed various possible crashes with malformed MMCMP files. + (r15504, 15528) + * [**Sec**] MED: Possible read past end of sequence name (stack-allocated, so + relatively unlikely to result in a crash). (r15477) + + * Fixed excessive memory usage with files claiming to have an extremely high + rows per beat count while also using tempo swing. Maximum rows per beat are + now limited to 65536. + * STP: Avoid creating thousands of patterns when loading malformed files even + though no more pattern data can be read. + + * mpg123: Update to v1.28.2 (2021-07-12). + * stb_vorbis: Update v1.22 commit 5a0bb8b1c1b1ca3f4e2485f4114c1c8ea021b781 + (2021-07-12). + ### libopenmpt 0.5.10 (2021-07-04) * S3M: Honor the Stereo flag not being set. This improves the sound of some diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp index d5ad75df4..65159a240 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp @@ -342,7 +342,7 @@ static int infobox( const in_char * fn, HWND hWndParent ) { } else { libopenmpt::plugin::gui_show_file_info( hWndParent, TEXT(SHORT_TITLE), StringReplace( self->cached_infotext, L"\n", L"\r\n" ) ); } - return 0; + return INFOBOX_UNCHANGED; } static void getfileinfo( const in_char * filename, in_char * title, int * length_in_ms ) { diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index ed1f4b729..a9efad396 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -19,7 +19,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 5 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 10 +#define OPENMPT_API_VERSION_PATCH 12 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk index de1bc97db..4e981f6ab 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=5 -LIBOPENMPT_VERSION_PATCH=10 +LIBOPENMPT_VERSION_PATCH=12 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=2 -LIBOPENMPT_LTVER_REVISION=10 +LIBOPENMPT_LTVER_REVISION=12 LIBOPENMPT_LTVER_AGE=2 diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index c85908d1a..38ee80728 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -504,7 +504,7 @@ static void show_info( std::ostream & log, bool verbose ) { SDL_version sdlver; std::memset( &sdlver, 0, sizeof( SDL_version ) ); SDL_GetVersion( &sdlver ); - log << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ) << "." << SDL_GetRevisionNumber(); + log << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ); const char * revision = SDL_GetRevision(); if ( revision ) { log << " (" << revision << ")"; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp index 88cf18664..c52a9b2ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp @@ -102,6 +102,7 @@ static bool MMCMP_IsDstBlockValid(const std::vector &unpackedData, uint32 if(pos >= unpackedData.size()) return false; if(len > unpackedData.size()) return false; if(len > unpackedData.size() - pos) return false; + if(!len) return false; return true; } @@ -252,7 +253,9 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C if(!psubblk) return false; if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; char *pDest = &(unpackedData[psubblk[subblk].unpk_pos]); - uint32 dwSize = psubblk[subblk].unpk_size; + uint32 dwSize = psubblk[subblk].unpk_size & ~1u; + if(!dwSize) + return false; uint32 dwPos = 0; uint32 numbits = blk.num_bits; uint32 oldval = 0; @@ -316,7 +319,9 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C dwPos = 0; if(!(subblk < blk.sub_blk)) break; if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; - dwSize = psubblk[subblk].unpk_size; + dwSize = psubblk[subblk].unpk_size & ~1u; + if(!dwSize) + return false; pDest = &(unpackedData[psubblk[subblk].unpk_pos]); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp index a38efee02..3dc06874e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp @@ -581,44 +581,46 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename) const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 program, uint32 key, uint32 *pInsNo) const { - if (m_Instruments.empty()) return nullptr; - for (uint32 iIns=0; iIns> 1) | (dlsIns.ulBank & 0x7F); - if ((bank >= 0x4000) || (insbank == bank)) + if((bank >= 0x4000) || (insbank == bank)) { - if (isDrum) + if(isDrum && (dlsIns.ulBank & F_INSTRUMENT_DRUMS)) { - if (dlsIns.ulBank & F_INSTRUMENT_DRUMS) + if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { - if ((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) + for(const auto ®ion : dlsIns.Regions) { - for (uint32 iRgn=0; iRgn= 0x80) + || (key >= region.uKeyMin && key <= region.uKeyMax)) { - if ((!key) || (key >= 0x80) - || ((key >= dlsIns.Regions[iRgn].uKeyMin) - && (key <= dlsIns.Regions[iRgn].uKeyMax))) - { - if (pInsNo) *pInsNo = iIns; - return &dlsIns; - } + if(pInsNo) + *pInsNo = iIns; + return &dlsIns; } } } - } else + } else if(!isDrum && !(dlsIns.ulBank & F_INSTRUMENT_DRUMS)) { - if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS)) + if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { - if ((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) - { - if (pInsNo) *pInsNo = iIns; - return &dlsIns; - } + if(pInsNo) + *pInsNo = iIns; + return &dlsIns; } } } } + return nullptr; } @@ -678,7 +680,7 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu { case IFFID_rgn: // Level 1 region case IFFID_rgn2: // Level 2 region - if (pDlsIns->nRegions < DLSMAXREGIONS) pDlsIns->nRegions++; + pDlsIns->Regions.push_back({}); break; } } else @@ -696,11 +698,11 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu } case IFFID_rgnh: - if (pDlsIns->nRegions < DLSMAXREGIONS) + if(!pDlsIns->Regions.empty()) { RGNHChunk rgnh; chunk.ReadStruct(rgnh); - DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + DLSREGION ®ion = pDlsIns->Regions.back(); region.uKeyMin = (uint8)rgnh.RangeKey.usLow; region.uKeyMax = (uint8)rgnh.RangeKey.usHigh; region.fuOptions = (uint8)(rgnh.usKeyGroup & DLSREGION_KEYGROUPMASK); @@ -712,11 +714,11 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu break; case IFFID_wlnk: - if (pDlsIns->nRegions < DLSMAXREGIONS) + if (!pDlsIns->Regions.empty()) { WLNKChunk wlnk; chunk.ReadStruct(wlnk); - DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + DLSREGION ®ion = pDlsIns->Regions.back(); region.nWaveLink = (uint16)wlnk.ulTableIndex; if((region.nWaveLink < Util::MaxValueOfType(region.nWaveLink)) && (region.nWaveLink >= m_nMaxWaveLink)) m_nMaxWaveLink = region.nWaveLink + 1; @@ -726,9 +728,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu break; case IFFID_wsmp: - if (pDlsIns->nRegions < DLSMAXREGIONS) + if(!pDlsIns->Regions.empty()) { - DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + DLSREGION ®ion = pDlsIns->Regions.back(); WSMPCHUNK wsmp; chunk.ReadStruct(wsmp); region.fuOptions |= DLSREGION_OVERRIDEWSMP; @@ -762,10 +764,7 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu { ART1Chunk art1; chunk.ReadStruct(art1); - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) - { - if (pDlsIns->nRegions >= DLSMAXREGIONS) break; - } else + if(!(pDlsIns->ulBank & F_INSTRUMENT_DRUMS)) { pDlsIns->nMelodicEnv = static_cast(m_Envelopes.size() + 1); } @@ -791,9 +790,7 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu case ART_DEFAULTPAN: { int32 pan = 128 + blk.lScale / (65536000/128); - if (pan < 0) pan = 0; - if (pan > 255) pan = 255; - dlsEnv.nDefPan = (uint8)pan; + dlsEnv.nDefPan = mpt::saturate_cast(pan); } break; @@ -1083,7 +1080,7 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) dlsIns.nMelodicEnv = static_cast(m_Envelopes.size()); } // Load Instrument Bags - dlsIns.nRegions = 0; + dlsIns.Regions.clear(); for(const auto nInstrNdx : instruments) { if(nInstrNdx >= numInsts) @@ -1091,13 +1088,13 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) sf2info.insts.Seek(nInstrNdx * sizeof(SFINST)); SFINST insts[2]; sf2info.insts.ReadArray(insts); - const auto startRegion = dlsIns.nRegions; - dlsIns.nRegions += insts[1].wInstBagNdx - insts[0].wInstBagNdx; + const auto startRegion = static_cast(dlsIns.Regions.size()); + const auto endRegion = startRegion + insts[1].wInstBagNdx - insts[0].wInstBagNdx; + dlsIns.Regions.resize(endRegion); //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions); - if (dlsIns.nRegions > DLSMAXREGIONS) dlsIns.nRegions = DLSMAXREGIONS; DLSREGION *pRgn = &dlsIns.Regions[startRegion]; bool hasGlobalZone = false; - for(uint32 nRgn = startRegion; nRgn < dlsIns.nRegions; nRgn++, pRgn++) + for(uint32 nRgn = startRegion; nRgn < endRegion; nRgn++, pRgn++) { uint32 ibagcnt = insts[0].wInstBagNdx + nRgn - startRegion; if(ibagcnt >= numInstBags) @@ -1164,7 +1161,8 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) case SF2_GEN_PAN: { int32 pan = static_cast(value); - pan = (((pan + 500) * 127) / 500) + 128; + pan = std::clamp(Util::muldivr(pan + 500, 256, 1000), 0, 256); + pRgn->panning = static_cast(pan); pDlsEnv->nDefPan = mpt::saturate_cast(pan); } break; @@ -1403,6 +1401,7 @@ bool CDLSBank::Open(FileReader file) { DLSINSTRUMENT *pDlsIns = &m_Instruments[nInsDef]; //Log("Instrument %d:\n", nInsDef); + pDlsIns->Regions.push_back({}); UpdateInstrumentDefinition(pDlsIns, subData); nInsDef++; } @@ -1488,14 +1487,16 @@ bool CDLSBank::Open(FileReader file) uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const { - if (nIns >= m_Instruments.size()) return 0; + if(nIns >= m_Instruments.size()) + return 0; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; - for (uint32 rgn = 0; rgn < dlsIns.nRegions; rgn++) + for(uint32 rgn = 0; rgn < static_cast(dlsIns.Regions.size()); rgn++) { - if ((nKey >= dlsIns.Regions[rgn].uKeyMin) && (nKey <= dlsIns.Regions[rgn].uKeyMax)) - { - return rgn; - } + if(nKey < dlsIns.Regions[rgn].uKeyMin || nKey > dlsIns.Regions[rgn].uKeyMax) + continue; + if(dlsIns.Regions[rgn].fuOptions & DLSREGION_ISGLOBAL) + continue; + return rgn; } return 0; } @@ -1514,7 +1515,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav return false; } const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; - if (nRgn >= dlsIns.nRegions) + if(nRgn >= dlsIns.Regions.size()) { #ifdef DLSBANK_LOG MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("invalid waveform region: nIns=%1 nRgn=%2 pSmp->nRegions=%3"))(nIns, nRgn, dlsIns.nRegions)); @@ -1535,6 +1536,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav { return false; } + mpt::IO::Offset sampleOffset = mpt::saturate_cast(m_WaveForms[nWaveLink] + m_dwWavePoolOffset); if(mpt::IO::SeekAbsolute(f, sampleOffset)) { @@ -1586,11 +1588,15 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI uint32 dwLen = 0; bool ok, hasWaveform; - if (nIns >= m_Instruments.size()) return false; + if(nIns >= m_Instruments.size()) + return false; const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns]; - if (nRgn >= pDlsIns->nRegions) return false; - if (!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) return false; - if (dwLen < 16) return false; + if(nRgn >= pDlsIns->Regions.size()) + return false; + if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) + return false; + if(dwLen < 16) + return false; ok = false; FileReader wsmpChunk; @@ -1719,22 +1725,24 @@ static uint16 ScaleEnvelope(uint32 time, float tempoScale) bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const { - SAMPLEINDEX RgnToSmp[DLSMAXREGIONS]; uint32 nRgnMin, nRgnMax, nEnv; if (nIns >= m_Instruments.size()) return false; const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns]; + std::vector RgnToSmp(pDlsIns->Regions.size()); if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { - if (nDrumRgn >= pDlsIns->nRegions) return false; + if(nDrumRgn >= pDlsIns->Regions.size()) + return false; nRgnMin = nDrumRgn; nRgnMax = nDrumRgn+1; nEnv = pDlsIns->Regions[nDrumRgn].uPercEnv; } else { - if (!pDlsIns->nRegions) return false; + if(pDlsIns->Regions.empty()) + return false; nRgnMin = 0; - nRgnMax = pDlsIns->nRegions; + nRgnMax = static_cast(pDlsIns->Regions.size()); nEnv = pDlsIns->nMelodicEnv; } if(nRgnMin == 0 && (pDlsIns->Regions[0].fuOptions & DLSREGION_ISGLOBAL)) @@ -1742,8 +1750,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui #ifdef DLSINSTR_LOG MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("DLS Instrument #%1: %2"))(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(pDlsIns->szName)))); MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Bank=0x%1 Instrument=0x%2"))(mpt::ufmt::HEX0<4>(pDlsIns->ulBank), mpt::ufmt::HEX0<4>(pDlsIns->ulInstrument))); - MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(pDlsIns->nRegions, pDlsIns->nMelodicEnv)); - for (uint32 iDbg=0; iDbgnRegions; iDbg++) + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(pDlsIns->Regions.size(), pDlsIns->nMelodicEnv)); + for (uint32 iDbg=0; iDbgRegions.size(); iDbg++) { const DLSREGION *prgn = &pDlsIns->Regions[iDbg]; MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Region %1:"))(iDbg)); @@ -1845,7 +1853,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : 32; if (nLoadedSmp >= nmaxsmp) { - nSmp = RgnToSmp[nRgn-1]; + nSmp = RgnToSmp[nRgn - 1]; } else { nextSample = sndFile.GetNextFreeSample(nInstr, nextSample + 1); @@ -1874,7 +1882,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui } else if(sndFile.GetSample(nSmp).GetNumChannels() == 1) { // Try to combine stereo samples - uint8 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, iDup); + const uint16 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, iDup); if((pan1 < 16 && pan2 >= 240) || (pan2 < 16 && pan1 >= 240)) { ModSample &sample = sndFile.GetSample(nSmp); @@ -2029,6 +2037,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui pIns->VolEnv[3] = EnvelopeNode(pIns->VolEnv[2].tick * 2u, ENVELOPE_MIN); // 1 second max. for drums } } + pIns->Sanitize(MOD_TYPE_MPT); pIns->Convert(MOD_TYPE_MPT, sndFile.GetType()); return true; } @@ -2036,9 +2045,11 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui const char *CDLSBank::GetRegionName(uint32 nIns, uint32 nRgn) const { - if (nIns >= m_Instruments.size()) return nullptr; + if(nIns >= m_Instruments.size()) + return nullptr; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; - if (nRgn >= dlsIns.nRegions) return nullptr; + if(nRgn >= dlsIns.Regions.size()) + return nullptr; if (m_nType & SOUNDBANK_TYPE_SF2) { @@ -2052,12 +2063,15 @@ const char *CDLSBank::GetRegionName(uint32 nIns, uint32 nRgn) const } -uint8 CDLSBank::GetPanning(uint32 ins, uint32 region) const +uint16 CDLSBank::GetPanning(uint32 ins, uint32 region) const { const DLSINSTRUMENT &dlsIns = m_Instruments[ins]; - if(region >= CountOf(dlsIns.Regions)) + if(region >= dlsIns.Regions.size()) return 128; const DLSREGION &rgn = dlsIns.Regions[region]; + if(rgn.panning >= 0) + return static_cast(rgn.panning); + if(dlsIns.ulBank & F_INSTRUMENT_DRUMS) { if(rgn.uPercEnv > 0 && rgn.uPercEnv <= m_Envelopes.size()) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h index bf49ad564..4a865cf47 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h @@ -23,17 +23,16 @@ OPENMPT_NAMESPACE_BEGIN #ifdef MODPLUG_TRACKER -#define DLSMAXREGIONS 128 - struct DLSREGION { uint32 ulLoopStart; uint32 ulLoopEnd; uint16 nWaveLink; uint16 uPercEnv; - uint16 usVolume; // 0..256 - uint16 fuOptions; // flags + key group - int16 sFineTune; // +128 = +1 semitone + uint16 usVolume; // 0..256 + uint16 fuOptions; // flags + key group + int16 sFineTune; // +128 = +1 semitone + int16 panning = -1; // -1= unset (DLS), otherwise 0...256 uint8 uKeyMin; uint8 uKeyMax; uint8 uUnityNote; @@ -43,11 +42,11 @@ struct DLSREGION struct DLSENVELOPE { // Volume Envelope - uint16 wVolAttack; // Attack Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] - uint16 wVolDecay; // Decay Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] - uint16 wVolRelease; // Release Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] - uint8 nVolSustainLevel; // Sustain Level: 0-128, 128=100% - uint8 nDefPan; // Default Pan + uint16 wVolAttack; // Attack Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] + uint16 wVolDecay; // Decay Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] + uint16 wVolRelease; // Release Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] + uint8 nVolSustainLevel; // Sustain Level: 0-128, 128=100% + uint8 nDefPan; // Default Pan }; // Special Bank bits @@ -55,12 +54,12 @@ struct DLSENVELOPE struct DLSINSTRUMENT { - uint32 ulBank, ulInstrument; - uint32 nRegions, nMelodicEnv; - DLSREGION Regions[DLSMAXREGIONS]; + uint32 ulBank = 0, ulInstrument = 0; + uint32 nMelodicEnv = 0; + std::vector Regions; char szName[32]; // SF2 stuff (DO NOT USE! -> used internally by the SF2 loader) - uint16 wPresetBagNdx, wPresetBagNum; + uint16 wPresetBagNdx = 0, wPresetBagNum = 0; }; struct DLSSAMPLEEX @@ -130,7 +129,7 @@ public: bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose = 0) const; bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const; const char *GetRegionName(uint32 nIns, uint32 nRgn) const; - uint8 GetPanning(uint32 ins, uint32 region) const; + uint16 GetPanning(uint32 ins, uint32 region) const; // Internal Loader Functions protected: diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp index 9bb79a80c..d10bdcb38 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp @@ -248,7 +248,7 @@ struct MixLoopState { if (nPosDest < nLoopStart) { - nSmpCount = DistanceToBufferLength(SamplePosition(chn.nLoopStart, 0), nPos, nInv); + nSmpCount = DistanceToBufferLength(SamplePosition(nLoopStart, 0), nPos, nInv); } } else { @@ -605,7 +605,7 @@ void CSoundFile::ProcessPlugins(uint32 nCount) if (!plugin.IsOutputToMaster()) { PLUGINDEX nOutput = plugin.GetOutputPlugin(); - if(nOutput > plug && nOutput != PLUGINDEX_INVALID + if(nOutput > plug && nOutput < MAX_MIXPLUGINS && m_MixPlugins[nOutput].pMixPlugin != nullptr) { IMixPlugin *outPlugin = m_MixPlugins[nOutput].pMixPlugin; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp index c985ef303..38ac5e315 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp @@ -596,7 +596,7 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, case MagicBE("R..."): { // Resampling has been written as various sizes including uint16 and uint32 in the past - uint32 tmp = file.ReadTruncatedIntLE(fsize); + uint32 tmp = file.ReadSizedIntLE(fsize); if(Resampling::IsKnownMode(tmp)) input->resampling = static_cast(tmp); result = true; @@ -604,27 +604,27 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, case MagicBE("PTTL"): { // Integer part of pitch/tempo lock - uint16 tmp = file.ReadTruncatedIntLE(fsize); + uint16 tmp = file.ReadSizedIntLE(fsize); input->pitchToTempoLock.Set(tmp, input->pitchToTempoLock.GetFract()); result = true; } break; case MagicLE("PTTF"): { // Fractional part of pitch/tempo lock - uint16 tmp = file.ReadTruncatedIntLE(fsize); + uint16 tmp = file.ReadSizedIntLE(fsize); input->pitchToTempoLock.Set(input->pitchToTempoLock.GetInt(), tmp); result = true; } break; case MagicBE("VE.."): - input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); + input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE(fsize))); result = true; break; case MagicBE("PE.."): - input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); + input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE(fsize))); result = true; break; case MagicBE("PiE."): - input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); + input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE(fsize))); result = true; break; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp index 64fe59efe..0b77b35fe 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp @@ -1126,6 +1126,8 @@ uintptr_t DMFUnpack(FileReader &file, uint8 *psample, uint32 maxlen) try { tree.DMFNewNode(); + if(tree.nodes[0].left < 0 || tree.nodes[0].right < 0) + return tree.file.GetPosition(); for(uint32 i = 0; i < maxlen; i++) { int actnode = 0; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp index a9e8a00e1..c9b237e5f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp @@ -2112,7 +2112,9 @@ void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin) if(!memcmp(code, "DWRT", 4)) { - plugin.fDryRatio = dataChunk.ReadFloatLE(); + plugin.fDryRatio = std::clamp(dataChunk.ReadFloatLE(), 0.0f, 1.0f); + if(!std::isnormal(plugin.fDryRatio)) + plugin.fDryRatio = 0.0f; } else if(!memcmp(code, "PROG", 4)) { plugin.defaultProgram = dataChunk.ReadUint32LE(); @@ -2448,8 +2450,10 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel // Validate read values. Limit(m_nDefaultTempo, GetModSpecifications().GetTempoMin(), GetModSpecifications().GetTempoMax()); - if(m_nTempoMode >= tempoModeMax) m_nTempoMode = tempoModeClassic; - if(m_nMixLevels >= mixLevelsMax) m_nMixLevels = mixLevelsOriginal; + if(m_nTempoMode >= tempoModeMax) + m_nTempoMode = tempoModeClassic; + if(m_nMixLevels >= mixLevelsMax) + m_nMixLevels = mixLevelsOriginal; //m_dwCreatedWithVersion //m_dwLastSavedWithVersion //m_nSamplePreAmp @@ -2458,7 +2462,8 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME); //m_nRestartPos //m_ModFlags - if(!m_tempoSwing.empty()) m_tempoSwing.resize(m_nDefaultRowsPerBeat); + LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT); + LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp index 699a929ca..026ebb618 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp @@ -679,7 +679,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) } for(CHANNELINDEX chn = 0; chn < numChans; chn++) { - if(chunk.ReadUint16LE() > 0 && chn >= m_nChannels) + if(chunk.ReadUint16LE() > 0 && chn >= m_nChannels && chn < 32) m_nChannels = chn + 1; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp index e7c4ea4f5..7910d455d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp @@ -1139,7 +1139,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) } } if(playSeq.name[0]) - order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, playSeq.name)); + order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, mpt::String::ReadAutoBuf(playSeq.name))); // Play commands (jump / stop) if(playSeq.commandTableOffset > 0 && file.Seek(playSeq.commandTableOffset)) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp index 7c7b4ef05..616527222 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp @@ -697,14 +697,26 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) uint16 finishedTracks = 0; PATTERNINDEX emptyPattern = PATTERNINDEX_INVALID; - ORDERINDEX lastOrd = 0; - ROWINDEX lastRow = 0; + ORDERINDEX lastOrd = 0, loopEndOrd = ORDERINDEX_INVALID; + ROWINDEX lastRow = 0, loopEndRow = ROWINDEX_INVALID; ROWINDEX restartRow = ROWINDEX_INVALID; int8 masterTranspose = 0; bool isXG = false; bool isEMIDI = false; + bool isEMIDILoop = false; const bool isType2 = (fileHeader.format == 2); + const auto ModPositionFromTick = [&](const tick_t tick, const tick_t offset = 0) + { + tick_t modTicks = Util::muldivr_unsigned(tick, quantize * ticksPerRow, ppqn * 4u) - offset; + + ORDERINDEX ord = static_cast((modTicks / ticksPerRow) / patternLen); + ROWINDEX row = (modTicks / ticksPerRow) % patternLen; + uint8 delay = static_cast(modTicks % ticksPerRow); + + return std::make_tuple(ord, row, delay); + }; + while(finishedTracks < numTracks) { uint16 t = 0; @@ -721,11 +733,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) } FileReader &track = tracks[t].track; - tick_t modTicks = Util::muldivr_unsigned(tick, quantize * ticksPerRow, ppqn * 4u); - - ORDERINDEX ord = static_cast((modTicks / ticksPerRow) / patternLen); - ROWINDEX row = (modTicks / ticksPerRow) % patternLen; - uint8 delay = static_cast(modTicks % ticksPerRow); + const auto [ord, row, delay] = ModPositionFromTick(tick); if(ord >= Order().GetLength()) { @@ -800,6 +808,9 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { Order().SetRestartPos(ord); restartRow = row; + } else if(!mpt::CompareNoCaseAscii(s, "loopEnd")) + { + std::tie(loopEndOrd, loopEndRow, std::ignore) = ModPositionFromTick(tick, 1); } } break; @@ -1003,13 +1014,31 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) case 111: // Non-standard MIDI loop point. May conflict with Apogee EMIDI CCs (110/111), which is why we also check if CC 110 is ever used. - if(data2 == 0) + if(data2 == 0 && !isEMIDI) { Order().SetRestartPos(ord); restartRow = row; } break; + case 118: + // EMIDI Global Loop Start + isEMIDI = true; + isEMIDILoop = false; + Order().SetRestartPos(ord); + restartRow = row; + break; + + case 119: + // EMIDI Global Loop End + if(data2 == 0x7F) + { + isEMIDILoop = true; + isEMIDI = true; + std::tie(loopEndOrd, loopEndRow, std::ignore) = ModPositionFromTick(tick, 1); + } + break; + case MIDIEvents::MIDICC_AllControllersOff: midiChnStatus[midiCh].ResetAllControllers(); break; @@ -1210,18 +1239,29 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) } } + if(isEMIDILoop) + isEMIDI = false; + if(isEMIDI) { Order().SetRestartPos(0); } - if(Order().IsValidPat(lastOrd)) + if(loopEndOrd == ORDERINDEX_INVALID) + loopEndOrd = lastOrd; + if(loopEndRow == ROWINDEX_INVALID) + loopEndRow = lastRow; + + if(Order().IsValidPat(loopEndOrd)) { - PATTERNINDEX lastPat = Order()[lastOrd]; - Patterns[lastPat].Resize(lastRow + 1); + PATTERNINDEX lastPat = Order()[loopEndOrd]; + if(loopEndOrd == lastOrd) + Patterns[lastPat].Resize(loopEndRow + 1); if(restartRow != ROWINDEX_INVALID && !isEMIDI) { - Patterns[lastPat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, mpt::saturate_cast(restartRow)).Row(lastRow)); + Patterns[lastPat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, mpt::saturate_cast(restartRow)).Row(loopEndRow)); + if(ORDERINDEX restartPos = Order().GetRestartPos(); loopEndOrd != lastOrd || restartPos <= std::numeric_limits::max()) + Patterns[lastPat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast(restartPos)).Row(loopEndRow)); } } Order.SetSequence(0); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp index 00366012c..2d8eb03b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp @@ -590,7 +590,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) { m_nTempoMode = tempoModeModern; double d = chunk.ReadDoubleLE(); - if(d != 0.0) + if(d > 0.00000001) { m_nDefaultTempo = TEMPO(44100.0 * 60.0 / (m_nDefaultSpeed * m_nDefaultRowsPerBeat * d)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp index 3544a89bb..3abafc65a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp @@ -389,6 +389,12 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) sampleChunks.push_back(chunk); } break; + + default: + // Non-ASCII chunk ID? + if(iffHead.signature & 0x80808080) + return false; + break; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp index 22c3bfddd..5b8f1beec 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp @@ -267,13 +267,9 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) switch(m.command) { case CMD_PANNING8: - // My observations of this weird command... - // 800...887 pan from hard left to hard right (whereas the low nibble seems to be ignored) - // 888...8FF do the same (so 888...88F is hard left, and 890 is identical to 810) - if(m.param >= 0x88) m.param &= 0x7F; - else if(m.param > 0x80) m.param = 0x80; - m.param &= 0x7F; - m.param = (m.param * 0xFF) / 0x7F; + // Don't be surprised about the strange formula, this is directly translated from original disassembly... + m.command = CMD_S3MCMDEX; + m.param = 0x80 | ((std::max(m.param >> 3, 1u) - 1u) & 0x0F); break; case CMD_GLOBALVOLUME: m.param = std::min(m.param, uint8(0x40)) * 2u; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp index 2beb381b3..3bd9822d0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp @@ -374,13 +374,13 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) patternLength = file.ReadUint16BE(); channels = file.ReadUint16BE(); + if(channels > MAX_BASECHANNELS) + return false; m_nChannels = std::max(m_nChannels, channels); file.Skip(channels * patternLength * 4u); } file.Seek(patOffset); - if(m_nChannels > MAX_BASECHANNELS) - return false; } struct ChannelMemory @@ -406,6 +406,9 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) channels = file.ReadUint16BE(); } + if(!file.CanRead(channels * patternLength * 4u)) + break; + if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength)) { file.Skip(channels * patternLength * 4u); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp index 1c0b10846..aa37ea5b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp @@ -603,6 +603,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) FlagSet madeWith(verUnknown); mpt::ustring madeWithTracker; + bool isMadTracker = false; if(!memcmp(fileHeader.trackerName, "FastTracker v2.00 ", 20) && fileHeader.size == 276) { @@ -645,6 +646,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kFT2PortaNoNote); // Fix arpeggios in kragle_-_happy_day.xm m_playBehaviour.reset(kFT2Arpeggio); + isMadTracker = true; } else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14)) { m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); @@ -1021,20 +1023,18 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) Order().Replace(0xFF, Order.GetInvalidPatIndex()); } + m_modFormat.formatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + m_modFormat.charset = (m_dwLastSavedWithVersion || isMadTracker) ? mpt::Charset::Windows1252 : mpt::Charset::CP437; if(isOXM) { + m_modFormat.originalFormatName = std::move(m_modFormat.formatName); m_modFormat.formatName = U_("OggMod FastTracker 2"); m_modFormat.type = U_("oxm"); - m_modFormat.originalFormatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); m_modFormat.originalType = U_("xm"); - m_modFormat.madeWithTracker = std::move(madeWithTracker); - m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; } else { - m_modFormat.formatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); m_modFormat.type = U_("xm"); - m_modFormat.madeWithTracker = std::move(madeWithTracker); - m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; } return true; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp index 60d826606..305e8c92e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN // Read song message from a mapped file. // [in] data: pointer to the data in memory that is going to be read -// [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). +// [in] length: number of characters that should be read. Trailing null characters are automatically removed. // [in] lineEnding: line ending formatting of the text in memory. // [out] returns true on success. bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEnding) @@ -36,18 +36,19 @@ bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEndi // Simple line-ending detection algorithm. VERY simple. if(lineEnding == leAutodetect) { - char cprev = 0; size_t nCR = 0, nLF = 0, nCRLF = 0; // find CRs, LFs and CRLFs for(size_t i = 0; i < length; i++) { char c = str[i]; - if(c == '\r') nCR++; - else if(c == '\n') nLF++; + if(c == '\r') + nCR++; + else if(c == '\n') + nLF++; - if(i && cprev == '\r' && c == '\n') nCRLF++; - cprev = c; + if(i && str[i - 1] == '\r' && c == '\n') + nCRLF++; } // evaluate findings if(nCR == nLF && nCR == nCRLF) @@ -64,16 +65,15 @@ bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEndi // calculate the final amount of characters to be allocated. for(size_t i = 0; i < length; i++) { - char c = str[i]; - - if(c != '\n' || lineEnding != leCRLF) - finalLength++; + finalLength++; + if(str[i] == '\r' && lineEnding == leCRLF) + i++; // skip the LF } - resize(finalLength); + clear(); + reserve(finalLength); - size_t cpos = 0; - for(size_t i = 0; i < length; i++, cpos++) + for(size_t i = 0; i < length; i++) { char c = str[i]; @@ -81,24 +81,25 @@ bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEndi { case '\r': if(lineEnding != leLF) - at(cpos) = InternalLineEnding; + c = InternalLineEnding; else - at(cpos) = ' '; - if(lineEnding == leCRLF) i++; // skip the LF + c = ' '; + if(lineEnding == leCRLF) + i++; // skip the LF break; case '\n': if(lineEnding != leCR && lineEnding != leCRLF) - at(cpos) = InternalLineEnding; + c = InternalLineEnding; else - at(cpos) = ' '; + c = ' '; break; case '\0': - at(cpos) = ' '; + c = ' '; break; default: - at(cpos) = c; break; } + push_back(c); } return true; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp index ba087f258..653fc3193 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp @@ -106,7 +106,7 @@ int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int { // Linear approximation between the points; // f(x + d) ~ f(x) + f'(x) * d, where f'(x) = (y2 - y1) / (x2 - x1) - value += ((position - x1) * (at(pt).value * ENV_PRECISION / rangeIn - value)) / (x2 - x1); + value += Util::muldiv(position - x1, (at(pt).value * ENV_PRECISION / rangeIn - value), x2 - x1); } } @@ -299,6 +299,9 @@ void ModInstrument::Sanitize(MODTYPE modType) if(!Resampling::IsKnownMode(resampling)) resampling = SRCMODE_DEFAULT; + + if(nMixPlug > MAX_MIXPLUGINS) + nMixPlug = 0; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp index da140cdde..7e6470ee0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp @@ -30,6 +30,12 @@ static bool SFZStartsWith(const std::string_view &l, const char(&r)[N]) return l.substr(0, N - 1) == r; } +template +static bool SFZEndsWith(const std::string_view &l, const char (&r)[N]) +{ + return l.size() >= (N - 1) && l.substr(l.size() - (N - 1), N - 1) == r; +} + static bool SFZIsNumeric(const std::string_view &str) { return std::find_if(str.begin(), str.end(), [](char c) { return c < '0' || c > '9'; }) == str.end(); @@ -243,7 +249,7 @@ struct SFZEnvelope auto &env = eg.points; if(attack > 0 || delay > 0) { - env.push_back({0, startLevel}); + env.push_back({0.0, startLevel}); if(delay > 0) env.push_back({delay, env.back().second}); env.push_back({attack, 100.0}); @@ -255,7 +261,7 @@ struct SFZEnvelope env.push_back({hold, env.back().second}); } if(env.empty()) - env.push_back({0, 100.0}); + env.push_back({0.0, 100.0}); if(env.back().second != sustainLevel) env.push_back({decay, sustainLevel}); if(sustainLevel != 0) @@ -685,7 +691,8 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) break; } const std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd)); - if(key == "sample" || key == "default_path" || SFZStartsWith(key, "label_cc") || SFZStartsWith(key, "label_key") || SFZStartsWith(key, "region_label")) + // Currently defined *_label opcodes are global_label, group_label, master_label, region_label, sw_label + if(key == "sample" || key == "default_path" || SFZStartsWith(key, "label_cc") || SFZStartsWith(key, "label_key") || SFZEndsWith(key, "_label")) { // Sample / CC name may contain spaces... charsRead = s.find_first_of("=\t<", valueStart); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp index 6a131c438..9ef0b8961 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp @@ -56,6 +56,8 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const restrictedSampleDataView = file.GetPinnedRawDataView(CalculateEncodedSize(sample.nLength)); sourceBuf = restrictedSampleDataView.data(); fileSize = restrictedSampleDataView.size(); + if(sourceBuf == nullptr) + return 0; } else { MPT_ASSERT_NOTREACHED(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h index 26b9ab96d..a8e6d729f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h @@ -43,6 +43,7 @@ using SmpLength = uint32; inline constexpr SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in frames. Sample size in bytes can be more than this (= 256 MB). inline constexpr ROWINDEX MAX_PATTERN_ROWS = 1024; +inline constexpr ROWINDEX MAX_ROWS_PER_BEAT = 65536; inline constexpr ORDERINDEX MAX_ORDERS = ORDERINDEX_MAX + 1; inline constexpr PATTERNINDEX MAX_PATTERNS = 4000; inline constexpr SAMPLEINDEX MAX_SAMPLES = 4000; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index 7d6d8d480..ab40c0165 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -113,7 +113,8 @@ public: return; } - const SmpLength sampleEnd = chn.dwFlags[CHN_LOOP] ? chn.nLoopEnd : chn.nLength; + const SamplePosition loopStart(chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0u, 0); + const SamplePosition sampleEnd(chn.dwFlags[CHN_LOOP] ? chn.nLoopEnd : chn.nLength, 0); const SmpLength loopLength = chn.nLoopEnd - chn.nLoopStart; const bool itEnvMode = sndFile.m_playBehaviour[kITEnvelopePositionHandling]; const bool updatePitchEnv = (chn.PitchEnv.flags & (ENV_ENABLED | ENV_FILTER)) == ENV_ENABLED; @@ -176,53 +177,51 @@ public: chn.position += inc; - if(chn.position.GetUInt() >= sampleEnd) + if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative())) { - if(chn.dwFlags[CHN_LOOP]) - { - // We exceeded the sample loop, go back to loop start. - if(chn.dwFlags[CHN_PINGPONGLOOP]) - { - if(chn.position < SamplePosition(chn.nLoopStart, 0)) - { - chn.position = SamplePosition(chn.nLoopStart + chn.nLoopStart, 0) - chn.position; - chn.dwFlags.flip(CHN_PINGPONGFLAG); - inc.Negate(); - } - SmpLength posInt = chn.position.GetUInt() - chn.nLoopStart; - SmpLength pingpongLength = loopLength * 2; - if(sndFile.m_playBehaviour[kITPingPongMode]) pingpongLength--; - posInt %= pingpongLength; - bool forward = (posInt < loopLength); - if(forward) - chn.position.SetInt(chn.nLoopStart + posInt); - else - chn.position.SetInt(chn.nLoopEnd - (posInt - loopLength)); - if(forward == chn.dwFlags[CHN_PINGPONGFLAG]) - { - chn.dwFlags.flip(CHN_PINGPONGFLAG); - inc.Negate(); - } - } else - { - SmpLength posInt = chn.position.GetUInt(); - if(posInt >= chn.nLoopEnd + loopLength) - { - const SmpLength overshoot = posInt - chn.nLoopEnd; - posInt -= (overshoot / loopLength) * loopLength; - } - while(posInt >= chn.nLoopEnd) - { - posInt -= loopLength; - } - chn.position.SetInt(posInt); - } - } else + if(!chn.dwFlags[CHN_LOOP]) { // Past sample end. stopNote = true; break; } + // We exceeded the sample loop, go back to loop start. + if(chn.dwFlags[CHN_PINGPONGLOOP]) + { + if(chn.position < loopStart) + { + chn.position = SamplePosition(chn.nLoopStart + chn.nLoopStart, 0) - chn.position; + chn.dwFlags.flip(CHN_PINGPONGFLAG); + inc.Negate(); + } + SmpLength posInt = chn.position.GetUInt() - chn.nLoopStart; + SmpLength pingpongLength = loopLength * 2; + if(sndFile.m_playBehaviour[kITPingPongMode]) pingpongLength--; + posInt %= pingpongLength; + bool forward = (posInt < loopLength); + if(forward) + chn.position.SetInt(chn.nLoopStart + posInt); + else + chn.position.SetInt(chn.nLoopEnd - (posInt - loopLength)); + if(forward == chn.dwFlags[CHN_PINGPONGFLAG]) + { + chn.dwFlags.flip(CHN_PINGPONGFLAG); + inc.Negate(); + } + } else + { + SmpLength posInt = chn.position.GetUInt(); + if(posInt >= chn.nLoopEnd + loopLength) + { + const SmpLength overshoot = posInt - chn.nLoopEnd; + posInt -= (overshoot / loopLength) * loopLength; + } + while(posInt >= chn.nLoopEnd) + { + posInt -= loopLength; + } + chn.position.SetInt(posInt); + } } } @@ -1794,7 +1793,7 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE } } - // IT compatibility tentative fix: Clear channel note memory. + // IT compatibility tentative fix: Clear channel note memory (TRANCE_N.IT by A3F). if(m_playBehaviour[kITClearOldNoteAfterCut]) { chn.nNote = chn.nNewNote = NOTE_NONE; @@ -2758,7 +2757,6 @@ bool CSoundFile::ProcessEffects() // Now it's time for some FT2 crap... if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) { - // XM: Key-Off + Sample == Note Cut (BUT: Only if no instr number or volume effect is present!) // Test case: NoteOffVolume.xm if(note == NOTE_KEYOFF @@ -3347,8 +3345,11 @@ bool CSoundFile::ProcessEffects() if(param && !m_SongFlags[SONG_ITOLDEFFECTS]) { // Old effects have different length interpretation (+1 for both on and off) - if(param & 0xF0) param -= 0x10; - if(param & 0x0F) param -= 0x01; + if(param & 0xF0) + param -= 0x10; + if(param & 0x0F) + param -= 0x01; + chn.nTremorParam = static_cast(param); } chn.nTremorCount |= 0x80; // set on/off flag } else if(m_playBehaviour[kFT2Tremor]) @@ -3358,7 +3359,8 @@ bool CSoundFile::ProcessEffects() } chn.nCommand = CMD_TREMOR; - if (param) chn.nTremorParam = static_cast(param); + if(param) + chn.nTremorParam = static_cast(param); break; @@ -3579,13 +3581,13 @@ bool CSoundFile::ProcessEffects() case CMD_DBMECHO: if(m_PlayState.m_nTickCount == 0) { - uint32 chns = (param >> 4), enable = (param & 0x0F); - if(chns > 1 || enable > 2) + uint32 echoType = (param >> 4), enable = (param & 0x0F); + if(echoType > 2 || enable > 1) { break; } CHANNELINDEX firstChn = nChn, lastChn = nChn; - if(chns == 1) + if(echoType == 1) { firstChn = 0; lastChn = m_nChannels - 1; @@ -4115,8 +4117,10 @@ void CSoundFile::ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM para } } -// Implemented for IMF compatibility, can't actually save this in any formats + +// Implemented for IMF / PTM / OKT compatibility, can't actually save this in any formats // Slide up / down every x ticks by y semitones +// Oktalyzer: Slide down on first tick only, or on every tick void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const { uint8 x, y; @@ -4129,23 +4133,26 @@ void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool ret if (y) chn.nNoteSlideStep = y; chn.nNoteSlideCounter = chn.nNoteSlideSpeed; - } else - { - if (--chn.nNoteSlideCounter == 0) - { - chn.nNoteSlideCounter = chn.nNoteSlideSpeed; - // update it - const int32 delta = (slideUp ? 1 : -1) * chn.nNoteSlideStep; - if(chn.HasCustomTuning()) - chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount(); - else - chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod), 8363, 0); + } - if(retrig) - { - chn.position.Set(0); - } - } + bool doTrigger = false; + if(GetType() == MOD_TYPE_OKT) + doTrigger = (chn.nNoteSlideSpeed == 0x10) || m_SongFlags[SONG_FIRSTTICK]; + else + doTrigger = !m_SongFlags[SONG_FIRSTTICK] && (--chn.nNoteSlideCounter == 0); + + if(doTrigger) + { + chn.nNoteSlideCounter = chn.nNoteSlideSpeed; + // update it + const int32 delta = (slideUp ? 1 : -1) * chn.nNoteSlideStep; + if(chn.HasCustomTuning()) + chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount(); + else + chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod, 0, chn.nC5Speed), 0, chn.nC5Speed); + + if(retrig) + chn.position.Set(0); } } @@ -5622,9 +5629,9 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) if(!m_playBehaviour[kFT2Retrigger] || !(chn.rowCommand.volcmd == VOLCMD_VOLUME)) { if(retrigTable1[dv]) - vol = (vol * retrigTable1[dv]) >> 4; + vol = (vol * retrigTable1[dv]) / 16; else - vol += ((int)retrigTable2[dv]) << 2; + vol += ((int)retrigTable2[dv]) * 4; } Limit(vol, 0, 256); @@ -5675,7 +5682,7 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) if(m_playBehaviour[kITRetrigger]) chn.position.Set(0); offset--; - if(offset >= 0 && offset <= static_cast(CountOf(chn.pModSample->cues)) && chn.pModSample != nullptr) + if(chn.pModSample != nullptr && offset >= 0 && offset <= static_cast(CountOf(chn.pModSample->cues))) { if(offset == 0) offset = chn.oldOffset; @@ -6034,6 +6041,8 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe // MDL uses non-linear slides, but their effectiveness does not depend on the middle-C frequency. return (FreqS3MTable[note % 12u] << 4) >> (note / 12); } + if(!nC5Speed) + nC5Speed = 8363; if(m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_669) { // In IT linear slide mode, directly use frequency in Hertz rather than periods. @@ -6043,8 +6052,6 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe return (FreqS3MTable[note % 12u] << 5) >> (note / 12); } else { - if (!nC5Speed) - nC5Speed = 8363; LimitMax(nC5Speed, uint32_max >> (note / 12u)); //(a*b)/c return Util::muldiv_unsigned(8363, (FreqS3MTable[note % 12u] << 5), nC5Speed << (note / 12u)); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp index 6ec23911d..bfe6ae131 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp @@ -484,6 +484,8 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) LimitMax(ChnSettings[chn].nVolume, uint16(64)); if(ChnSettings[chn].nPan > 256) ChnSettings[chn].nPan = 128; + if(ChnSettings[chn].nMixPlugin > MAX_MIXPLUGINS) + ChnSettings[chn].nMixPlugin = 0; m_PlayState.Chn[chn].Reset(ModChannel::resetTotal, *this, chn, muteFlag); } @@ -550,8 +552,15 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) LimitMax(m_nDefaultTempo, TEMPO(uint16_max, 0)); if(!m_nDefaultSpeed) m_nDefaultSpeed = 6; + if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; + LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT); + LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT); + LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME); + if(!m_tempoSwing.empty()) + m_tempoSwing.resize(m_nDefaultRowsPerBeat); + m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp index 61be7833c..1edb97333 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp @@ -125,10 +125,10 @@ bool CSoundFile::FadeSong(uint32 msec) { ModChannel &pramp = m_PlayState.Chn[m_PlayState.ChnMix[noff]]; pramp.newRightVol = pramp.newLeftVol = 0; - pramp.leftRamp = (-pramp.leftVol << VOLUMERAMPPRECISION) / nRampLength; - pramp.rightRamp = (-pramp.rightVol << VOLUMERAMPPRECISION) / nRampLength; - pramp.rampLeftVol = pramp.leftVol << VOLUMERAMPPRECISION; - pramp.rampRightVol = pramp.rightVol << VOLUMERAMPPRECISION; + pramp.leftRamp = -pramp.leftVol * (1 << VOLUMERAMPPRECISION) / nRampLength; + pramp.rightRamp = -pramp.rightVol * (1 << VOLUMERAMPPRECISION) / nRampLength; + pramp.rampLeftVol = pramp.leftVol * (1 << VOLUMERAMPPRECISION); + pramp.rampRightVol = pramp.rightVol * (1 << VOLUMERAMPPRECISION); pramp.nRampLength = nRampLength; pramp.dwFlags.set(CHN_VOLUMERAMP); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp index ef46acb5b..b0c91f128 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp @@ -112,6 +112,12 @@ struct UpgradePatternData m.vol = 0; } } + + // Command I11 accidentally behaved the same as command I00 with compatible IT tremor and old effects disabled + if(m.command == CMD_TREMOR && m.param == 0x11 && version < MPT_V("1.29.12.02") && sndFile.m_playBehaviour[kITTremor] && !sndFile.m_SongFlags[SONG_ITOLDEFFECTS]) + { + m.param = 0; + } } else if(modType == MOD_TYPE_XM) @@ -250,7 +256,7 @@ void CSoundFile::UpgradeModule() if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { - // Previously, Pitch/Pan Separation was only half depth. + // Previously, Pitch/Pan Separation was only half depth (plot twist: it was actually only quarter depth). // This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well. ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h index 7ac894508..760cda20a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h @@ -645,6 +645,8 @@ Opal::Channel::Channel() { ModulationType = 0; ChannelPair = 0; Enable = true; + LeftEnable = true; + RightEnable = true; } @@ -672,12 +674,13 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) { else { if (clk & 1) vibrato >>= 1; // Odd positions are half the magnitude + + vibrato <<= Octave; + if (clk & 4) vibrato = -vibrato; // The second half positions are negative } - vibrato <<= Octave; - // Combine individual operator outputs int16_t out, acc; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp index 5df1fcb78..9583ebf95 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp @@ -500,13 +500,14 @@ void ReadModPattern(std::istream& iStrm, CPattern& pat, const size_t) return; ssb.ReadItem(pat, "data", &ReadData); // pattern time signature - uint32 nRPB = 0, nRPM = 0; - ssb.ReadItem(nRPB, "RPB."); - ssb.ReadItem(nRPM, "RPM."); - pat.SetSignature(nRPB, nRPM); + uint32 rpb = 0, rpm = 0; + ssb.ReadItem(rpb, "RPB."); + ssb.ReadItem(rpm, "RPM."); + pat.SetSignature(rpb, rpm); TempoSwing swing; ssb.ReadItem(swing, "SWNG", TempoSwing::Deserialize); - if(!swing.empty()) swing.resize(nRPB); + if(!swing.empty()) + swing.resize(pat.GetRowsPerBeat()); pat.SetTempoSwing(swing); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp index bfb3b66f5..8da52e43f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp @@ -165,8 +165,6 @@ void LFOPlugin::SetParameter(PlugParamIndex index, PlugParamValue value) break; case kWaveform: m_waveForm = ParamToWaveform(value); - if(m_waveForm >= kNumWaveforms) - m_waveForm = static_cast(kNumWaveforms - 1); break; case kPolarity: m_polarity = (value >= 0.5f); break; case kBypassed: m_bypassed = (value >= 0.5f); break; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h index d8ab3fd5c..24966d81a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h @@ -144,7 +144,7 @@ protected: IMixPlugin *GetOutputPlugin() const; public: - static LFOWaveform ParamToWaveform(float param) { return static_cast(mpt::saturate_round(param * 32.0f)); } + static LFOWaveform ParamToWaveform(float param) { return static_cast(std::clamp(mpt::saturate_round(param * 32.0f), 0, kNumWaveforms - 1)); } static float WaveformToParam(LFOWaveform waveform) { return static_cast(waveform) / 32.0f; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp index fb9da9b89..cb2df3aff 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp @@ -27,7 +27,7 @@ namespace DMO // Computes (log2(x) + 1) * 2 ^ (shiftL - shiftR) (x = -2^31...2^31) float logGain(float x, int32 shiftL, int32 shiftR) { - uint32 intSample = static_cast(static_cast(x)); + uint32 intSample = static_cast(static_cast(x)); const uint32 sign = intSample & 0x80000000; if(sign) intSample = (~intSample) + 1;