Update libopenmpt to version 0.5.7
parent
9a427cf03c
commit
7139f5aa08
|
@ -1,4 +1,4 @@
|
||||||
|
|
||||||
MPT_SVNVERSION=14311
|
MPT_SVNVERSION=14391
|
||||||
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6
|
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.7
|
||||||
MPT_SVNDATE=2021-03-14T15:27:41.476009Z
|
MPT_SVNDATE=2021-03-20T17:06:12.434258Z
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
CC = contrib/fuzzing/afl/afl-clang-fast
|
CC = contrib/fuzzing/afl/afl-clang-lto
|
||||||
CXX = contrib/fuzzing/afl/afl-clang-fast++
|
CXX = contrib/fuzzing/afl/afl-clang-lto++
|
||||||
LD = contrib/fuzzing/afl/afl-clang-fast++
|
LD = contrib/fuzzing/afl/afl-clang-lto++
|
||||||
AR = ar
|
AR = ar
|
||||||
|
|
||||||
ifneq ($(STDCXX),)
|
ifneq ($(STDCXX),)
|
||||||
|
@ -15,6 +15,10 @@ CFLAGS_STDC = -std=c99
|
||||||
CXXFLAGS += $(CXXFLAGS_STDCXX)
|
CXXFLAGS += $(CXXFLAGS_STDCXX)
|
||||||
CFLAGS += $(CFLAGS_STDC)
|
CFLAGS += $(CFLAGS_STDC)
|
||||||
|
|
||||||
|
DYNLINK=0
|
||||||
|
SHARED_LIB=0
|
||||||
|
STATIC_LIB=1
|
||||||
|
|
||||||
CPPFLAGS +=
|
CPPFLAGS +=
|
||||||
CXXFLAGS += -fPIC -fno-strict-aliasing
|
CXXFLAGS += -fPIC -fno-strict-aliasing
|
||||||
CFLAGS += -fPIC -fno-strict-aliasing
|
CFLAGS += -fPIC -fno-strict-aliasing
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#define OPENMPT_VERSION_SVNVERSION "14311"
|
#define OPENMPT_VERSION_SVNVERSION "14391"
|
||||||
#define OPENMPT_VERSION_REVISION 14311
|
#define OPENMPT_VERSION_REVISION 14391
|
||||||
#define OPENMPT_VERSION_DIRTY 0
|
#define OPENMPT_VERSION_DIRTY 0
|
||||||
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
||||||
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6"
|
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.7"
|
||||||
#define OPENMPT_VERSION_DATE "2021-03-14T15:27:41.476009Z"
|
#define OPENMPT_VERSION_DATE "2021-03-20T17:06:12.434258Z"
|
||||||
#define OPENMPT_VERSION_IS_PACKAGE 1
|
#define OPENMPT_VERSION_IS_PACKAGE 1
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,6 @@ OPENMPT_NAMESPACE_BEGIN
|
||||||
#define VER_MAJORMAJOR 1
|
#define VER_MAJORMAJOR 1
|
||||||
#define VER_MAJOR 29
|
#define VER_MAJOR 29
|
||||||
#define VER_MINOR 08
|
#define VER_MINOR 08
|
||||||
#define VER_MINORMINOR 00
|
#define VER_MINORMINOR 03
|
||||||
|
|
||||||
OPENMPT_NAMESPACE_END
|
OPENMPT_NAMESPACE_END
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
cd "${0%/*}"
|
cd "${0%/*}"
|
||||||
cd ../..
|
cd ../..
|
||||||
AFL_HARDEN=1 CONFIG=afl make clean all EXAMPLES=0 TEST=0 OPENMPT123=0 NO_VORBIS=1 NO_VORBISFILE=1 NO_MPG123=1
|
AFL_HARDEN=1 CONFIG=afl make clean all EXAMPLES=0 TEST=0 OPENMPT123=0 NO_VORBIS=1 NO_VORBISFILE=1 NO_MPG123=1 CHECKED_ADDRESS=1
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
cd "${0%/*}"
|
||||||
|
. ./fuzz-settings.sh
|
||||||
|
|
||||||
|
# Create tmpfs for storing temporary fuzzing data
|
||||||
|
mkdir $FUZZING_TEMPDIR
|
||||||
|
sudo mount -t tmpfs -o size=300M none $FUZZING_TEMPDIR
|
||||||
|
rm -rf $FUZZING_TEMPDIR/bin
|
||||||
|
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
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
cd "${0%/*}"
|
|
||||||
. ./fuzz-settings.sh
|
|
||||||
|
|
||||||
# Create tmpfs for storing temporary fuzzing data
|
|
||||||
mkdir $FUZZING_TEMPDIR
|
|
||||||
sudo mount -t tmpfs -o size=200M none $FUZZING_TEMPDIR
|
|
||||||
rm -rf $FUZZING_TEMPDIR/bin
|
|
||||||
mkdir $FUZZING_TEMPDIR/bin
|
|
||||||
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
|
|
||||||
|
|
||||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -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
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
cd "${0%/*}"
|
||||||
|
. ./fuzz-settings.sh
|
||||||
|
|
||||||
|
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
||||||
|
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
cd "${0%/*}"
|
||||||
|
. ./fuzz-settings.sh
|
||||||
|
|
||||||
|
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
||||||
|
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -f $FUZZING_TEMPDIR/infile03 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile03
|
|
@ -15,4 +15,4 @@ FUZZING_FINDINGS_DIR=~/libopenmpt-fuzzing
|
||||||
# Fuzzer timeout in ms, + = don't abort on timeout
|
# Fuzzer timeout in ms, + = don't abort on timeout
|
||||||
FUZZING_TIMEOUT=5000+
|
FUZZING_TIMEOUT=5000+
|
||||||
# Path to afl-fuzz binary
|
# Path to afl-fuzz binary
|
||||||
AFL_BIN=afl/afl-fuzz
|
AFL_DIR=afl
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
cd "${0%/*}"
|
|
||||||
. ./fuzz-settings.sh
|
|
||||||
|
|
||||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include <libopenmpt/libopenmpt.h>
|
#include <libopenmpt/libopenmpt.h>
|
||||||
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
|
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
|
||||||
|
|
||||||
#define BUFFERSIZE 512
|
#define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512)
|
||||||
#define SAMPLERATE 22050
|
#define SAMPLERATE 22050
|
||||||
|
|
||||||
static int16_t buffer[BUFFERSIZE];
|
static int16_t buffer[BUFFERSIZE];
|
||||||
|
@ -46,6 +46,11 @@ int main( int argc, char * argv[] ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
openmpt_module_set_position_seconds( mod, 1.0 );
|
||||||
|
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||||
|
openmpt_module_set_position_order_row( mod, 3, 16 );
|
||||||
|
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||||
|
|
||||||
/* fuzz string-related stuff */
|
/* fuzz string-related stuff */
|
||||||
openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) );
|
openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) );
|
||||||
openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) );
|
openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) );
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
cd "${0%/*}"
|
cd "${0%/*}"
|
||||||
|
|
||||||
AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')"
|
GET_AFL_VERSION?="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')"
|
||||||
AFL_FILENAME="$AFL_VERSION.tar.gz"
|
AFL_FILENAME="$GET_AFL_VERSION.tar.gz"
|
||||||
AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME"
|
AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME"
|
||||||
|
|
||||||
rm $AFL_FILENAME
|
rm $AFL_FILENAME
|
||||||
|
|
|
@ -2,45 +2,50 @@ libopenmpt fuzz suite
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
In this directory, you can find the necessary tools for fuzzing libopenmpt with
|
In this directory, you can find the necessary tools for fuzzing libopenmpt with
|
||||||
the American Fuzzy Lop fuzzer (afl-fuzz).
|
the American Fuzzy Lop fuzzer (afl++).
|
||||||
|
|
||||||
Contents:
|
Contents:
|
||||||
|
|
||||||
* `all_formats.dict`: A dictionary containing magic bytes from all supported
|
* `all_formats.dict`: A dictionary containing magic bytes from all supported
|
||||||
module formats to make the life of the fuzzer a bit easier.
|
module formats to make the life of the fuzzer a bit easier.
|
||||||
* `fuzz-master.sh`: Script to launch the main fuzzing process. If you want to
|
* `fuzz-main.sh`: Script to launch the main fuzzing process. If you want to
|
||||||
use just one fuzzer instance, run this one.
|
use just one fuzzer instance, run this one.
|
||||||
* `fuzz-slave.sh`: Script to launch the secondary fuzzing process. It is
|
* `fuzz-secondary[1|2].sh`: Scripts to launch the secondary fuzzing process. It
|
||||||
recommended to run at least two fuzzer instances, as the deterministic and
|
is recommended to run at least two fuzzer instances, as the deterministic and
|
||||||
random fuzz mode have been found to complement each other really well.
|
random fuzz mode have been found to complement each other really well. The two
|
||||||
|
scripts are set up to use different exploration strategies
|
||||||
* `fuzz-settings.sh`: Set up your preferences and afl settings here before the
|
* `fuzz-settings.sh`: Set up your preferences and afl settings here before the
|
||||||
first run.
|
first run.
|
||||||
* `fuzz.c`: A tiny C program that is used by the fuzzer to test libopenmpt.
|
* `fuzz.c`: A tiny C program that is used by the fuzzer to test libopenmpt.
|
||||||
* `get-afl.sh`: A simple script to obtain the latest version of the fuzzer.
|
* `get-afl.sh`: A simple script to obtain the latest version of afl++.
|
||||||
|
You can also make it download from a specific branch or tag, e.g.
|
||||||
|
`GET_AFL_VERSION=stable ./get-afl.sh` to download the latest stable but
|
||||||
|
unreleased code.
|
||||||
|
|
||||||
Prerequisites
|
Prerequisites
|
||||||
=============
|
=============
|
||||||
* afl from http://lcamtuf.coredump.cx/afl/ - the makefile expects this to be
|
* [afl++](https://github.com/AFLplusplus/AFLplusplus) - the makefile expects
|
||||||
installed in `contrib/fuzzing/afl`, as it is automatically done by the
|
this to be installed in `contrib/fuzzing/afl`, as it is automatically done by
|
||||||
`get-afl.sh` install script.
|
the `get-afl.sh` install script.
|
||||||
* Clang with LLVM dev headers (llvm-config needs to be installed).
|
* Clang with LLVM dev headers (llvm-config needs to be installed).
|
||||||
afl also works with gcc, but our makefile has been set up to make use of afl's
|
afl also works with gcc, but our makefile has been set up to make use of afl's
|
||||||
faster LLVM mode.
|
faster LLVM-LTO mode.
|
||||||
|
|
||||||
How to use
|
How to use
|
||||||
==========
|
==========
|
||||||
* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use `make`
|
* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use
|
||||||
to build afl-fuzz, `cd llvm_mode`, `make` to build afl-clang-fast.
|
`make source-only` to build. If building fails because `llvm-config` cannot be
|
||||||
If building with either option fails because `llvm-config` cannot be found,
|
found, try prepending `LLVM_CONFIG=/usr/bin/llvm-config-12` or similar, and
|
||||||
try prepending `LLVM_CONFIG=/usr/bin/llvm-config-3.8` or similar, and read the
|
read the afl manual.
|
||||||
afl manual.
|
|
||||||
* Build libopenmpt with the `build.sh` script in this directory.
|
* Build libopenmpt with the `build.sh` script in this directory.
|
||||||
* Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to
|
* Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to
|
||||||
specify the input directory for first use.
|
specify the input directory for first use.
|
||||||
The default setup mounts a tmpfs folder for all temporary files. You may
|
The default setup mounts a tmpfs folder for all temporary files. You may
|
||||||
change this behaviour if you do not have root privileges.
|
change this behaviour if you do not have root privileges.
|
||||||
* Run `fuzz-master.sh` for the first (deterministic) instance of afl-fuzz.
|
* Run `fuzz-main.sh` for the first (deterministic) instance of afl-fuzz.
|
||||||
* For a "slave" instance to run on another core, run `fuzz-slave.sh`.
|
* For a "secondary" instance to run on another core, run `fuzz-secondary1.sh`
|
||||||
* If you want to make use of even more cores, make a copy of `fuzz-slave.sh`
|
and/or `fuzz-secondary2.sh`.
|
||||||
and adjust "infile02" / "fuzzer02" to "infile03" / "fuzzer03" (they need to be
|
* If you want to make use of even more cores, create more copies
|
||||||
unique)
|
`fuzz-secondary2.sh` and adjust "infile03" / "fuzzer03" to
|
||||||
|
"infile04" / "fuzzer04" and so o (they need to be unique). Try variying the
|
||||||
|
fuzzing strategey (the -p parameter) to get results more quickly.
|
||||||
|
|
|
@ -5,6 +5,17 @@ Changelog {#changelog}
|
||||||
For fully detailed change log, please see the source repository directly. This
|
For fully detailed change log, please see the source repository directly. This
|
||||||
is just a high-level summary.
|
is just a high-level summary.
|
||||||
|
|
||||||
|
### libopenmpt 0.5.7 (2021-03-20)
|
||||||
|
|
||||||
|
* [**Sec**] Possible null-pointer dereference read caused by a sequence of
|
||||||
|
`openmpt::module::read`, `openmpt::module::set_position_seconds` with a
|
||||||
|
position past the song end, and another `openmpt::module::read` call.
|
||||||
|
(r14363)
|
||||||
|
|
||||||
|
* IT: Instrument / sample panning was reset on note-off / fade commands.
|
||||||
|
* IMF: Set Finetune is now implemented correctly.
|
||||||
|
* Fixed excessive memory consumption with malformed files in various formats.
|
||||||
|
|
||||||
### libopenmpt 0.5.6 (2021-03-14)
|
### libopenmpt 0.5.6 (2021-03-14)
|
||||||
|
|
||||||
* AMS: Avoid allocating excessive amount of memory for compressed song message
|
* AMS: Avoid allocating excessive amount of memory for compressed song message
|
||||||
|
|
|
@ -24,7 +24,6 @@ Dependencies
|
||||||
used for building:
|
used for building:
|
||||||
* `std::numeric_limits<unsigned char>::digits == 8` (enforced by
|
* `std::numeric_limits<unsigned char>::digits == 8` (enforced by
|
||||||
static_assert)
|
static_assert)
|
||||||
* `sizeof(char) == 1` (enforced by static_assert)
|
|
||||||
* existence of `std::uintptr_t` (enforced by static_assert)
|
* existence of `std::uintptr_t` (enforced by static_assert)
|
||||||
* in C++20 mode, `std::endian::little != std::endian::big` (enforced
|
* in C++20 mode, `std::endian::little != std::endian::big` (enforced
|
||||||
by static_assert)
|
by static_assert)
|
||||||
|
@ -33,8 +32,6 @@ Dependencies
|
||||||
assumed)
|
assumed)
|
||||||
* representation of basic source character set is identical in char
|
* representation of basic source character set is identical in char
|
||||||
and `wchar_t` (implicitly assumed)
|
and `wchar_t` (implicitly assumed)
|
||||||
* libopenmpt also has experimental support for platforms without
|
|
||||||
`wchar_t` support like DJGPP
|
|
||||||
|
|
||||||
libopenmpt does not rely on any specific implementation defined or
|
libopenmpt does not rely on any specific implementation defined or
|
||||||
undefined behaviour (if it does, that's a bug in libopenmpt). In
|
undefined behaviour (if it does, that's a bug in libopenmpt). In
|
||||||
|
@ -44,22 +41,25 @@ Dependencies
|
||||||
* `signed` integer overflow is undefined
|
* `signed` integer overflow is undefined
|
||||||
* `float` and `double` can be non-IEEE754
|
* `float` and `double` can be non-IEEE754
|
||||||
|
|
||||||
|
libopenmpt can optionally support for certain incomplete C++
|
||||||
|
implementations:
|
||||||
|
* platforms without `wchar_t` support (like DJGPP)
|
||||||
|
* platforms without working `std::random_device` (like Emscripten when
|
||||||
|
running in `AudioWorkletProcessor` context)
|
||||||
|
* platforms without working `std::high_resolution_clock` (like
|
||||||
|
Emscripten when running in `AudioWorkletProcessor` context)
|
||||||
|
|
||||||
* Required compilers to use libopenmpt:
|
* Required compilers to use libopenmpt:
|
||||||
* Any **C89** / **C99** / **C11** compatible compiler should work with
|
* Any **C89** / **C99** / **C11** compatible compiler should work with
|
||||||
the C API as long as a **C99** compatible **stdint.h** is available.
|
the C API as long as a **C99** compatible **stdint.h** is available.
|
||||||
* Any **C++17** compatible compiler should work with the C++ API.
|
* Any **C++17** compatible compiler should work with the C++ API.
|
||||||
* **J2B** support requires an inflate (deflate decompression) implementation:
|
* **J2B** support requires an inflate (deflate decompression) implementation:
|
||||||
* **zlib**
|
* **zlib** (or **miniz** can be used internally)
|
||||||
* **miniz** can be used internally if no zlib is available.
|
* **MO3** support requires:
|
||||||
* Built-in **MO3** support requires:
|
* **libmpg123 >= 1.14.0** (or **minimp3 by Lion (github.com/lieff)** can
|
||||||
* **libmpg123 >= 1.14.0**
|
be used internally)
|
||||||
* **libogg**
|
* **libogg**, **libvorbis**, and **libvorbisfile** (or **stb_vorbis** can
|
||||||
* **libvorbis**
|
be used internally)
|
||||||
* **libvorbisfile**
|
|
||||||
* Instead of libmpg123, **minimp3 by Lion (github.com/lieff)** can be used
|
|
||||||
internally to decode MP3 samples.
|
|
||||||
* Instead of libogg, libvorbis and libvorbisfile, **stb_vorbis** can be
|
|
||||||
used internally to decode Vorbis samples.
|
|
||||||
* Building on Unix-like systems requires:
|
* Building on Unix-like systems requires:
|
||||||
* **GNU make**
|
* **GNU make**
|
||||||
* **pkg-config**
|
* **pkg-config**
|
||||||
|
|
|
@ -1108,8 +1108,9 @@ double module_impl::set_position_seconds( double seconds ) {
|
||||||
}
|
}
|
||||||
m_sndFile->SetCurrentOrder( static_cast<ORDERINDEX>( subsong->start_order ) );
|
m_sndFile->SetCurrentOrder( static_cast<ORDERINDEX>( subsong->start_order ) );
|
||||||
GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back();
|
GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back();
|
||||||
m_sndFile->m_PlayState.m_nNextOrder = m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder;
|
m_sndFile->m_PlayState.m_nNextOrder = m_sndFile->m_PlayState.m_nCurrentOrder = t.targetReached ? t.lastOrder : t.endOrder;
|
||||||
m_sndFile->m_PlayState.m_nNextRow = t.lastRow;
|
m_sndFile->m_PlayState.m_nNextRow = t.targetReached ? t.lastRow : t.endRow;
|
||||||
|
m_sndFile->m_PlayState.m_nTickCount = Util::MaxValueOfType(m_sndFile->m_PlayState.m_nTickCount) - 1;
|
||||||
m_currentPositionSeconds = base_seconds + t.duration;
|
m_currentPositionSeconds = base_seconds + t.duration;
|
||||||
return m_currentPositionSeconds;
|
return m_currentPositionSeconds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
/*! \brief libopenmpt minor version number */
|
/*! \brief libopenmpt minor version number */
|
||||||
#define OPENMPT_API_VERSION_MINOR 5
|
#define OPENMPT_API_VERSION_MINOR 5
|
||||||
/*! \brief libopenmpt patch version number */
|
/*! \brief libopenmpt patch version number */
|
||||||
#define OPENMPT_API_VERSION_PATCH 6
|
#define OPENMPT_API_VERSION_PATCH 7
|
||||||
/*! \brief libopenmpt pre-release tag */
|
/*! \brief libopenmpt pre-release tag */
|
||||||
#define OPENMPT_API_VERSION_PREREL ""
|
#define OPENMPT_API_VERSION_PREREL ""
|
||||||
/*! \brief libopenmpt pre-release flag */
|
/*! \brief libopenmpt pre-release flag */
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
LIBOPENMPT_VERSION_MAJOR=0
|
LIBOPENMPT_VERSION_MAJOR=0
|
||||||
LIBOPENMPT_VERSION_MINOR=5
|
LIBOPENMPT_VERSION_MINOR=5
|
||||||
LIBOPENMPT_VERSION_PATCH=6
|
LIBOPENMPT_VERSION_PATCH=7
|
||||||
LIBOPENMPT_VERSION_PREREL=
|
LIBOPENMPT_VERSION_PREREL=
|
||||||
|
|
||||||
LIBOPENMPT_LTVER_CURRENT=2
|
LIBOPENMPT_LTVER_CURRENT=2
|
||||||
LIBOPENMPT_LTVER_REVISION=6
|
LIBOPENMPT_LTVER_REVISION=7
|
||||||
LIBOPENMPT_LTVER_AGE=2
|
LIBOPENMPT_LTVER_AGE=2
|
||||||
|
|
|
@ -77,8 +77,9 @@ bool UnpackUMX(std::vector<ContainerItem> &containerItems, FileReader &file, Con
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int32> classes;
|
std::vector<int32> classes;
|
||||||
classes.reserve(fileHeader.importCount);
|
const uint32 importCount = std::min(fileHeader.importCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 4u));
|
||||||
for(uint32 i = 0; i < fileHeader.importCount && file.CanRead(4); i++)
|
classes.reserve(importCount);
|
||||||
|
for(uint32 i = 0; i < importCount && file.CanRead(4); i++)
|
||||||
{
|
{
|
||||||
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
|
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
|
||||||
if(static_cast<size_t>(objName) < names.size())
|
if(static_cast<size_t>(objName) < names.size())
|
||||||
|
|
|
@ -168,6 +168,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_nSamples = 64;
|
m_nSamples = 64;
|
||||||
m_nChannels = 4 + 9;
|
m_nChannels = 4 + 9;
|
||||||
m_playBehaviour.set(kOPLBeatingOscillators);
|
m_playBehaviour.set(kOPLBeatingOscillators);
|
||||||
|
m_SongFlags.set(SONG_IMPORTED);
|
||||||
|
|
||||||
// Pan PCM channels only
|
// Pan PCM channels only
|
||||||
for(CHANNELINDEX chn = 0; chn < 4; chn++)
|
for(CHANNELINDEX chn = 0; chn < 4; chn++)
|
||||||
|
|
|
@ -934,10 +934,10 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.Read(chunkHeader);
|
file.Read(chunkHeader);
|
||||||
uint32 chunkLength = chunkHeader.length, chunkSkip = 0;
|
uint32 chunkLength = chunkHeader.length, chunkSkip = 0;
|
||||||
// When loop start was added to version 3, the chunk size was not updated...
|
// When loop start was added to version 3, the chunk size was not updated...
|
||||||
if(fileHeader.version == 3 && chunkHeader.GetID() == DMFChunk::idSEQU && chunkLength < uint32_max - 2)
|
if(fileHeader.version == 3 && chunkHeader.GetID() == DMFChunk::idSEQU)
|
||||||
chunkSkip = 2;
|
chunkSkip = 2;
|
||||||
// ...and when the loop end was added to version 4, it was also note updated! Luckily they fixed it in version 5.
|
// ...and when the loop end was added to version 4, it was also note updated! Luckily they fixed it in version 5.
|
||||||
else if(fileHeader.version == 4 && chunkHeader.GetID() == DMFChunk::idSEQU && chunkLength < uint32_max - 4)
|
else if(fileHeader.version == 4 && chunkHeader.GetID() == DMFChunk::idSEQU)
|
||||||
chunkSkip = 4;
|
chunkSkip = 4;
|
||||||
// Earlier X-Tracker versions also write a garbage length for the SMPD chunk if samples are compressed.
|
// Earlier X-Tracker versions also write a garbage length for the SMPD chunk if samples are compressed.
|
||||||
// I don't know when exactly this stopped, but I have no version 5-7 files to check (and no X-Tracker version that writes those versions).
|
// I don't know when exactly this stopped, but I have no version 5-7 files to check (and no X-Tracker version that writes those versions).
|
||||||
|
|
|
@ -163,6 +163,7 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]);
|
InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]);
|
||||||
|
m_SongFlags.set(SONG_IMPORTED);
|
||||||
|
|
||||||
m_modFormat.formatName = U_("General Digital Music");
|
m_modFormat.formatName = U_("General Digital Music");
|
||||||
m_modFormat.type = U_("gdm");
|
m_modFormat.type = U_("gdm");
|
||||||
|
|
|
@ -2086,19 +2086,11 @@ void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin)
|
||||||
plugin.editorX = plugin.editorY = int32_min;
|
plugin.editorX = plugin.editorY = int32_min;
|
||||||
|
|
||||||
// Plugin user data
|
// Plugin user data
|
||||||
const uint32 pluginDataChunkSize = file.ReadUint32LE();
|
FileReader pluginDataChunk = file.ReadChunk(file.ReadUint32LE());
|
||||||
FileReader pluginDataChunk = file.ReadChunk(pluginDataChunkSize);
|
plugin.pluginData.resize(mpt::saturate_cast<size_t>(pluginDataChunk.BytesLeft()));
|
||||||
|
pluginDataChunk.ReadRaw(mpt::as_span(plugin.pluginData));
|
||||||
|
|
||||||
if(pluginDataChunk.IsValid())
|
if(FileReader modularData = file.ReadChunk(file.ReadUint32LE()); modularData.IsValid())
|
||||||
{
|
|
||||||
plugin.pluginData.resize(pluginDataChunkSize);
|
|
||||||
pluginDataChunk.ReadRaw(plugin.pluginData.data(), pluginDataChunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileReader modularData = file.ReadChunk(file.ReadUint32LE());
|
|
||||||
|
|
||||||
//if dwMPTExtra is positive and there are dwMPTExtra bytes left in nPluginSize, we have some more data!
|
|
||||||
if(modularData.IsValid())
|
|
||||||
{
|
{
|
||||||
while(modularData.CanRead(5))
|
while(modularData.CanRead(5))
|
||||||
{
|
{
|
||||||
|
|
|
@ -160,6 +160,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
m_SongFlags.set(SONG_IMPORTED);
|
||||||
if(songFlags & ITP_ITOLDEFFECTS)
|
if(songFlags & ITP_ITOLDEFFECTS)
|
||||||
m_SongFlags.set(SONG_ITOLDEFFECTS);
|
m_SongFlags.set(SONG_ITOLDEFFECTS);
|
||||||
if(songFlags & ITP_ITCOMPATGXX)
|
if(songFlags & ITP_ITCOMPATGXX)
|
||||||
|
|
|
@ -399,40 +399,38 @@ struct MO3SampleChunk
|
||||||
} while(carry); \
|
} while(carry); \
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size)
|
|
||||||
|
static bool UnpackMO3Data(FileReader &file, std::vector<uint8> &uncompressed, const uint32 size)
|
||||||
{
|
{
|
||||||
if(!size)
|
if(!size)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
uint16 data = 0;
|
uint16 data = 0;
|
||||||
int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another)
|
int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another)
|
||||||
int32 strLen = 0; // length of previous string
|
int32 strLen = 0; // length of previous string
|
||||||
int32 strOffset; // string offset
|
int32 strOffset; // string offset
|
||||||
uint8 *initDst = dst;
|
uint32 previousPtr = 0;
|
||||||
uint32 ebp, previousPtr = 0;
|
|
||||||
uint32 initSize = size;
|
|
||||||
|
|
||||||
// Read first uncompressed byte
|
// Read first uncompressed byte
|
||||||
*dst++ = file.ReadUint8();
|
uncompressed.push_back(file.ReadUint8());
|
||||||
size--;
|
uint32 remain = size - 1;
|
||||||
|
|
||||||
while(size > 0)
|
while(remain > 0)
|
||||||
{
|
{
|
||||||
READ_CTRL_BIT;
|
READ_CTRL_BIT;
|
||||||
if(!carry)
|
if(!carry)
|
||||||
{
|
{
|
||||||
// a 0 ctrl bit means 'copy', not compressed byte
|
// a 0 ctrl bit means 'copy', not compressed byte
|
||||||
if(!file.Read(*dst))
|
if(uint8 b; file.Read(b))
|
||||||
|
uncompressed.push_back(b);
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
dst++;
|
remain--;
|
||||||
size--;
|
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// a 1 ctrl bit means compressed bytes are following
|
// a 1 ctrl bit means compressed bytes are following
|
||||||
ebp = 0; // length adjustment
|
uint8 lengthAdjust = 0; // length adjustment
|
||||||
DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value
|
DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value
|
||||||
strLen -= 3;
|
strLen -= 3;
|
||||||
if(strLen < 0)
|
if(strLen < 0)
|
||||||
{
|
{
|
||||||
|
@ -442,17 +440,17 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size)
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// LZ ptr in ctrl stream
|
// LZ ptr in ctrl stream
|
||||||
uint8 b;
|
if(uint8 b; file.Read(b))
|
||||||
if(!file.Read(b))
|
strOffset = (strLen << 8) | b; // read less significant offset byte from stream
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
strOffset = (strLen << 8) | b; // read less significant offset byte from stream
|
|
||||||
strLen = 0;
|
strLen = 0;
|
||||||
strOffset = ~strOffset;
|
strOffset = ~strOffset;
|
||||||
if(strOffset < -1280)
|
if(strOffset < -1280)
|
||||||
ebp++;
|
lengthAdjust++;
|
||||||
ebp++; // length is always at least 1
|
lengthAdjust++; // length is always at least 1
|
||||||
if(strOffset < -32000)
|
if(strOffset < -32000)
|
||||||
ebp++;
|
lengthAdjust++;
|
||||||
previousPtr = strOffset; // save current Ptr
|
previousPtr = strOffset; // save current Ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,36 +465,33 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size)
|
||||||
DECODE_CTRL_BITS; // decode length: 1 is the most significant bit,
|
DECODE_CTRL_BITS; // decode length: 1 is the most significant bit,
|
||||||
strLen += 2; // then first bit of each bits pairs (noted n1), until n0.
|
strLen += 2; // then first bit of each bits pairs (noted n1), until n0.
|
||||||
}
|
}
|
||||||
strLen += ebp; // length adjustment
|
strLen += lengthAdjust; // length adjustment
|
||||||
if(size >= static_cast<uint32>(strLen) && strLen > 0)
|
|
||||||
{
|
if(remain < static_cast<uint32>(strLen) || strLen <= 0)
|
||||||
// Copy previous string
|
|
||||||
if(strOffset >= 0 || static_cast<std::ptrdiff_t>(dst - initDst) + strOffset < 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
size -= strLen;
|
|
||||||
const uint8 *string = dst + strOffset;
|
|
||||||
while(strLen > 0)
|
|
||||||
{
|
|
||||||
*dst++ = *string++;
|
|
||||||
strLen--;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
if(strOffset >= 0 || -static_cast<ptrdiff_t>(uncompressed.size()) > strOffset)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Copy previous string
|
||||||
|
// Need to do this in two steps as source and destination may overlap (e.g. strOffset = -1, strLen = 2 repeats last character twice)
|
||||||
|
uncompressed.insert(uncompressed.end(), strLen, 0);
|
||||||
|
remain -= strLen;
|
||||||
|
auto src = uncompressed.cend() - strLen + strOffset;
|
||||||
|
auto dst = uncompressed.end() - strLen;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
strLen--;
|
||||||
|
*dst++ = *src++;
|
||||||
|
} while(strLen > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef MPT_BUILD_FUZZER
|
#ifdef MPT_BUILD_FUZZER
|
||||||
// When using a fuzzer, we should not care if the decompressed buffer has the correct size.
|
// When using a fuzzer, we should not care if the decompressed buffer has the correct size.
|
||||||
// This makes finding new interesting test cases much easier.
|
// This makes finding new interesting test cases much easier.
|
||||||
while(size-- > 0)
|
return true;
|
||||||
{
|
#else
|
||||||
*dst++ = 0;
|
return remain == 0;
|
||||||
}
|
|
||||||
#endif // MPT_BUILD_FUZZER
|
#endif // MPT_BUILD_FUZZER
|
||||||
return (dst - initDst) == static_cast<std::ptrdiff_t>(initSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -742,7 +737,7 @@ static bool ValidateHeader(const MO3ContainerHeader &containerHeader)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(containerHeader.musicSize <= sizeof(MO3FileHeader))
|
if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= uint32_max / 2u)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -789,24 +784,25 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8 version = containerHeader.version;
|
const uint8 version = containerHeader.version;
|
||||||
const uint32 musicSize = containerHeader.musicSize;
|
|
||||||
|
|
||||||
uint32 compressedSize = uint32_max;
|
uint32 compressedSize = uint32_max, reserveSize = 1024 * 1024; // Generous estimate based on biggest pre-v5 MO3s found in the wild (~350K music data)
|
||||||
if(version >= 5)
|
if(version >= 5)
|
||||||
{
|
{
|
||||||
// Size of compressed music chunk
|
// Size of compressed music chunk
|
||||||
compressedSize = file.ReadUint32LE();
|
compressedSize = file.ReadUint32LE();
|
||||||
#ifndef MPT_BUILD_FUZZER
|
|
||||||
if(!file.CanRead(compressedSize))
|
if(!file.CanRead(compressedSize))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
// Generous estimate based on highest real-world compression ratio I found in a module (~20:1)
|
||||||
#endif // !MPT_BUILD_FUZZER
|
reserveSize = std::min(Util::MaxValueOfType(reserveSize) / 32u, compressedSize) * 32u;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8> musicData(musicSize);
|
std::vector<uint8> musicData;
|
||||||
|
// We don't always reserve the whole uncompressed size as claimed by the module to guard against broken files
|
||||||
if(!UnpackMO3Data(file, musicData.data(), musicSize))
|
// that e.g. claim that the uncompressed size is 1GB while the MO3 file itself is only 100 bytes.
|
||||||
|
// As the LZ compression used in MO3 doesn't allow for establishing a clear upper bound for the maximum size,
|
||||||
|
// this is probably the only sensible way we can prevent DoS due to huge allocations.
|
||||||
|
musicData.reserve(std::min(reserveSize, containerHeader.musicSize.get()));
|
||||||
|
if(!UnpackMO3Data(file, musicData, containerHeader.musicSize))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -849,6 +845,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
else
|
else
|
||||||
SetType(MOD_TYPE_XM);
|
SetType(MOD_TYPE_XM);
|
||||||
|
|
||||||
|
m_SongFlags.set(SONG_IMPORTED);
|
||||||
if(fileHeader.flags & MO3FileHeader::linearSlides)
|
if(fileHeader.flags & MO3FileHeader::linearSlides)
|
||||||
m_SongFlags.set(SONG_LINEARSLIDES);
|
m_SongFlags.set(SONG_LINEARSLIDES);
|
||||||
if((fileHeader.flags & MO3FileHeader::s3mAmigaLimits) && m_nType == MOD_TYPE_S3M)
|
if((fileHeader.flags & MO3FileHeader::s3mAmigaLimits) && m_nType == MOD_TYPE_S3M)
|
||||||
|
|
|
@ -1951,7 +1951,7 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_nMinPeriod = 14 * 4;
|
m_nMinPeriod = 14 * 4;
|
||||||
m_nMaxPeriod = 3424 * 4;
|
m_nMaxPeriod = 3424 * 4;
|
||||||
m_nSamplePreAmp = 64;
|
m_nSamplePreAmp = 64;
|
||||||
m_SongFlags.set(SONG_PT_MODE);
|
m_SongFlags.set(SONG_PT_MODE | SONG_IMPORTED);
|
||||||
|
|
||||||
// Setup channel pan positions and volume
|
// Setup channel pan positions and volume
|
||||||
SetupMODPanning();
|
SetupMODPanning();
|
||||||
|
|
|
@ -333,7 +333,9 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
STPLoopList &loopList = loopInfo[actualSmp - 1];
|
STPLoopList &loopList = loopInfo[actualSmp - 1];
|
||||||
loopList.clear();
|
loopList.clear();
|
||||||
|
|
||||||
uint16 numLoops = file.ReadUint16BE();
|
const uint16 numLoops = file.ReadUint16BE();
|
||||||
|
if(!file.CanRead(numLoops * 8u))
|
||||||
|
return false;
|
||||||
loopList.reserve(numLoops);
|
loopList.reserve(numLoops);
|
||||||
|
|
||||||
STPLoopInfo loop;
|
STPLoopInfo loop;
|
||||||
|
|
|
@ -70,8 +70,9 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int32> classes;
|
std::vector<int32> classes;
|
||||||
classes.reserve(fileHeader.importCount);
|
const uint32 importCount = std::min(fileHeader.importCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 4u));
|
||||||
for(uint32 i = 0; i < fileHeader.importCount && file.CanRead(4); i++)
|
classes.reserve(importCount);
|
||||||
|
for(uint32 i = 0; i < importCount && file.CanRead(4); i++)
|
||||||
{
|
{
|
||||||
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
|
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
|
||||||
if(static_cast<size_t>(objName) < names.size())
|
if(static_cast<size_t>(objName) < names.size())
|
||||||
|
|
|
@ -244,26 +244,27 @@ enum DuplicateNoteAction : uint8
|
||||||
// Module flags - contains both song configuration and playback state... Use SONG_FILE_FLAGS and SONG_PLAY_FLAGS distinguish between the two.
|
// Module flags - contains both song configuration and playback state... Use SONG_FILE_FLAGS and SONG_PLAY_FLAGS distinguish between the two.
|
||||||
enum SongFlags
|
enum SongFlags
|
||||||
{
|
{
|
||||||
SONG_FASTVOLSLIDES = 0x0002, // Old Scream Tracker 3.0 volume slides
|
SONG_FASTVOLSLIDES = 0x02, // Old Scream Tracker 3.0 volume slides
|
||||||
SONG_ITOLDEFFECTS = 0x0004, // Old Impulse Tracker effect implementations
|
SONG_ITOLDEFFECTS = 0x04, // Old Impulse Tracker effect implementations
|
||||||
SONG_ITCOMPATGXX = 0x0008, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
|
SONG_ITCOMPATGXX = 0x08, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
|
||||||
SONG_LINEARSLIDES = 0x0010, // Linear slides vs. Amiga slides
|
SONG_LINEARSLIDES = 0x10, // Linear slides vs. Amiga slides
|
||||||
SONG_PATTERNLOOP = 0x0020, // Loop current pattern (pattern editor)
|
SONG_PATTERNLOOP = 0x20, // Loop current pattern (pattern editor)
|
||||||
SONG_STEP = 0x0040, // Song is in "step" mode (pattern editor)
|
SONG_STEP = 0x40, // Song is in "step" mode (pattern editor)
|
||||||
SONG_PAUSED = 0x0080, // Song is paused (no tick processing, just rendering audio)
|
SONG_PAUSED = 0x80, // Song is paused (no tick processing, just rendering audio)
|
||||||
SONG_FADINGSONG = 0x0100, // Song is fading out
|
SONG_FADINGSONG = 0x0100, // Song is fading out
|
||||||
SONG_ENDREACHED = 0x0200, // Song is finished
|
SONG_ENDREACHED = 0x0200, // Song is finished
|
||||||
SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row
|
SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row
|
||||||
SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note)
|
SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note)
|
||||||
SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels
|
SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels
|
||||||
SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz)
|
SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz)
|
||||||
SONG_AMIGALIMITS = 0x10000, // Enforce amiga frequency limits
|
SONG_AMIGALIMITS = 0x1'0000, // Enforce amiga frequency limits
|
||||||
SONG_S3MOLDVIBRATO = 0x20000, // ScreamTracker 2 vibrato in S3M files
|
SONG_S3MOLDVIBRATO = 0x2'0000, // ScreamTracker 2 vibrato in S3M files
|
||||||
SONG_BREAKTOROW = 0x80000, // Break to row command encountered (internal flag, do not touch)
|
SONG_BREAKTOROW = 0x8'0000, // Break to row command encountered (internal flag, do not touch)
|
||||||
SONG_POSJUMP = 0x100000, // Position jump encountered (internal flag, do not touch)
|
SONG_POSJUMP = 0x10'0000, // Position jump encountered (internal flag, do not touch)
|
||||||
SONG_PT_MODE = 0x200000, // ProTracker 1/2 playback mode
|
SONG_PT_MODE = 0x20'0000, // ProTracker 1/2 playback mode
|
||||||
SONG_PLAYALLSONGS = 0x400000, // Play all subsongs consecutively (libopenmpt)
|
SONG_PLAYALLSONGS = 0x40'0000, // Play all subsongs consecutively (libopenmpt)
|
||||||
SONG_ISAMIGA = 0x800000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler
|
SONG_ISAMIGA = 0x80'0000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler
|
||||||
|
SONG_IMPORTED = 0x100'0000, // Song type does not represent actual module format / was imported from a different format (OpenMPT)
|
||||||
};
|
};
|
||||||
DECLARE_FLAGSET(SongFlags)
|
DECLARE_FLAGSET(SongFlags)
|
||||||
|
|
||||||
|
|
|
@ -243,8 +243,6 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
{
|
{
|
||||||
std::vector<GetLengthType> results;
|
std::vector<GetLengthType> results;
|
||||||
GetLengthType retval;
|
GetLengthType retval;
|
||||||
retval.startOrder = target.startOrder;
|
|
||||||
retval.startRow = target.startRow;
|
|
||||||
|
|
||||||
// Are we trying to reach a certain pattern position?
|
// Are we trying to reach a certain pattern position?
|
||||||
const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs;
|
const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs;
|
||||||
|
@ -259,8 +257,18 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
// Temporary visited rows vector (so that GetLength() won't interfere with the player code if the module is playing at the same time)
|
// Temporary visited rows vector (so that GetLength() won't interfere with the player code if the module is playing at the same time)
|
||||||
RowVisitor visitedRows(*this, sequence);
|
RowVisitor visitedRows(*this, sequence);
|
||||||
|
|
||||||
playState.m_nNextRow = playState.m_nRow = target.startRow;
|
// If sequence starts with some non-existent patterns, find a better start
|
||||||
playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder;
|
{
|
||||||
|
ORDERINDEX startOrder = target.startOrder;
|
||||||
|
ROWINDEX startRow = target.startRow;
|
||||||
|
if(visitedRows.GetFirstUnvisitedRow(startOrder, startRow, true))
|
||||||
|
{
|
||||||
|
target.startOrder = startOrder;
|
||||||
|
target.startRow = startRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval.startRow = playState.m_nNextRow = playState.m_nRow = target.startRow;
|
||||||
|
retval.startOrder = playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder;
|
||||||
|
|
||||||
// Fast LUTs for commands that are too weird / complicated / whatever to emulate in sample position adjust mode.
|
// Fast LUTs for commands that are too weird / complicated / whatever to emulate in sample position adjust mode.
|
||||||
std::bitset<MAX_EFFECTS> forbiddenCommands;
|
std::bitset<MAX_EFFECTS> forbiddenCommands;
|
||||||
|
@ -1317,6 +1325,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
m_opl->Patch(n, chn.pModSample->adlib);
|
m_opl->Patch(n, chn.pModSample->adlib);
|
||||||
m_opl->NoteCut(n);
|
m_opl->NoteCut(n);
|
||||||
}
|
}
|
||||||
|
chn.pCurrentSample = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_PLUGINS
|
#ifndef NO_PLUGINS
|
||||||
|
@ -2928,10 +2937,7 @@ bool CSoundFile::ProcessEffects()
|
||||||
{
|
{
|
||||||
CheckNNA(nChn, instr, note, false);
|
CheckNNA(nChn, instr, note, false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(note)
|
|
||||||
{
|
|
||||||
if(chn.nRestorePanOnNewNote > 0)
|
if(chn.nRestorePanOnNewNote > 0)
|
||||||
{
|
{
|
||||||
chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1;
|
chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1;
|
||||||
|
@ -4699,7 +4705,14 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param)
|
||||||
// S2x: Set FineTune
|
// S2x: Set FineTune
|
||||||
case 0x20: if(!m_SongFlags[SONG_FIRSTTICK])
|
case 0x20: if(!m_SongFlags[SONG_FIRSTTICK])
|
||||||
break;
|
break;
|
||||||
if(GetType() != MOD_TYPE_669)
|
if(GetType() == MOD_TYPE_IMF)
|
||||||
|
{
|
||||||
|
if(chn.nPeriod && chn.pModSample)
|
||||||
|
{
|
||||||
|
chn.nC5Speed = Util::muldivr(chn.pModSample->nC5Speed, 1712, ProTrackerTunedPeriods[param * 12]);
|
||||||
|
chn.nPeriod = GetPeriodFromNote(chn.nNote, 0, chn.nC5Speed);
|
||||||
|
}
|
||||||
|
} else if(GetType() != MOD_TYPE_669)
|
||||||
{
|
{
|
||||||
chn.nC5Speed = S3MFineTuneTable[param];
|
chn.nC5Speed = S3MFineTuneTable[param];
|
||||||
chn.nFineTune = MOD2XMFineTune(param);
|
chn.nFineTune = MOD2XMFineTune(param);
|
||||||
|
|
|
@ -455,15 +455,20 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
|
||||||
m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment()));
|
m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment()));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef MODPLUG_TRACKER
|
||||||
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
|
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
|
||||||
{
|
{
|
||||||
MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e);
|
MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e);
|
||||||
|
return false;
|
||||||
|
#endif // MODPLUG_TRACKER
|
||||||
|
} catch(const std::exception &)
|
||||||
|
{
|
||||||
#ifdef MODPLUG_TRACKER
|
#ifdef MODPLUG_TRACKER
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
// libopenmpt already handles this.
|
// libopenmpt already handles this.
|
||||||
throw;
|
throw;
|
||||||
#endif // MODPLUG_TRACKER
|
#endif // MODPLUG_TRACKER
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
|
@ -961,10 +966,10 @@ void CSoundFile::LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow)
|
||||||
m_PlayState.m_nTickCount = m_PlayState.m_nMusicSpeed;
|
m_PlayState.m_nTickCount = m_PlayState.m_nMusicSpeed;
|
||||||
m_PlayState.m_nPatternDelay = 0;
|
m_PlayState.m_nPatternDelay = 0;
|
||||||
m_PlayState.m_nFrameDelay = 0;
|
m_PlayState.m_nFrameDelay = 0;
|
||||||
m_PlayState.m_nBufferCount = 0;
|
|
||||||
m_PlayState.m_nNextPatStartRow = 0;
|
m_PlayState.m_nNextPatStartRow = 0;
|
||||||
m_SongFlags.set(SONG_PATTERNLOOP);
|
m_SongFlags.set(SONG_PATTERNLOOP);
|
||||||
}
|
}
|
||||||
|
m_PlayState.m_nBufferCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion)
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
name.reserve(length);
|
name.reserve(std::min(length, mpt::saturate_cast<int32>(chunk.BytesLeft())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple zero-terminated string
|
// Simple zero-terminated string
|
||||||
|
@ -174,8 +174,9 @@ std::vector<std::string> ReadUMXNameTable(FileReader &file, const UMXFileHeader
|
||||||
{
|
{
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
names.reserve(fileHeader.nameCount);
|
const uint32 nameCount = std::min(fileHeader.nameCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 5u));
|
||||||
for(uint32 i = 0; i < fileHeader.nameCount && file.CanRead(4); i++)
|
names.reserve(nameCount);
|
||||||
|
for(uint32 i = 0; i < nameCount && file.CanRead(5); i++)
|
||||||
{
|
{
|
||||||
names.push_back(ReadUMXNameTableEntry(file, fileHeader.packageVersion));
|
names.push_back(ReadUMXNameTableEntry(file, fileHeader.packageVersion));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue