Updated libOpenMPT to version 0.7.12
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
18fe6c0563
commit
75a4f68feb
34 changed files with 278 additions and 139 deletions
|
@ -1,4 +1,4 @@
|
|||
|
||||
MPT_SVNVERSION=21953
|
||||
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.11
|
||||
MPT_SVNDATE=2024-10-26T13:09:27.804941Z
|
||||
MPT_SVNVERSION=22406
|
||||
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.12
|
||||
MPT_SVNDATE=2024-12-01T13:10:15.135688Z
|
||||
|
|
|
@ -53,7 +53,8 @@ ifneq ($(SSE),0)
|
|||
FPU_SSSE3 := -m80387 -mmmx -mfxsr -msse -msse2 -msse3 -mssse3 -mfpmath=sse
|
||||
FPU_SSE4_1 := -m80387 -mmmx -mfxsr -msse -msse2 -msse3 -mssse3 -msse4.1 -mfpmath=sse
|
||||
FPU_SSE4_2 := -m80387 -mmmx -mfxsr -msse -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -mfpmath=sse
|
||||
FPU_SSE4A := -m80387 -mmmx -mfxsr -msse -msse2 -msse3 -mssse3 -msse4a -mfpmath=sse
|
||||
FPU_SSE4A := -m80387 -mmmx -mfxsr -msse -msse2 -msse3 -msse4a -mfpmath=sse
|
||||
FPU_SSSE4A := -m80387 -mmmx -mfxsr -msse -msse2 -msse3 -mssse3 -msse4a -mfpmath=sse
|
||||
else
|
||||
FPU_NONE := -mno-80387
|
||||
FPU_287 := -m80387 -mfpmath=387 -mno-fancy-math-387
|
||||
|
@ -68,7 +69,8 @@ else
|
|||
FPU_SSSE3 := -mno-ssse3 -mno-sse3 -mno-sse2 -mno-sse -mno-fxsr -m80387 -mmmx -mfpmath=387
|
||||
FPU_SSE4_1 := -mno-sse4.1 -mno-ssse3 -mno-sse3 -mno-sse2 -mno-sse -mno-fxsr -m80387 -mmmx -mfpmath=387
|
||||
FPU_SSE4_2 := -mno-sse4.2 -mno-sse4.1 -mno-ssse3 -mno-sse3 -mno-sse2 -mno-sse -mno-fxsr -m80387 -mmmx -mfpmath=387
|
||||
FPU_SSE4A := -mno-sse4a -mno-ssse3 -mno-sse3 -mno-sse2 -mno-sse -mno-fxsr -m80387 -mmmx -mfpmath=387
|
||||
FPU_SSE4A := -mno-sse4a -mno-sse3 -mno-sse2 -mno-sse -mno-fxsr -m80387 -mmmx -mfpmath=387
|
||||
FPU_SSSE4A := -mno-sse4a -mno-ssse3 -mno-sse3 -mno-sse2 -mno-sse -mno-fxsr -m80387 -mmmx -mfpmath=387
|
||||
endif
|
||||
|
||||
OPT_DEF := -Os
|
||||
|
@ -202,11 +204,11 @@ amd/sempron64 := $(___) -march=k8 $(FPU_SSE2) -mtune=k8
|
|||
amd/geode-gx := $(___) -march=geode $(FPU_3DNOW) -mtune=geode $(OPT_SIMD) --param l1-cache-size=16 --param l2-cache-size=0
|
||||
amd/geode-lx := $(___) -march=geode $(FPU_3DNOW) -mtune=geode $(OPT_SIMD) --param l1-cache-size=64 --param l2-cache-size=128
|
||||
amd/geode-nx := $(___) -march=athlon-xp $(FPU_3DASSE) -mtune=athlon-xp $(OPT_SIMD) --param l1-cache-size=64 --param l2-cache-size=256
|
||||
amd/bobcat := $(___) -march=btver1 $(FPU_SSE4A) -mtune=btver1 $(OPT_SIMD) --param l1-cache-size=32 --param l2-cache-size=512
|
||||
amd/jaguar := $(___) -march=btver2 $(FPU_SSE4A) -mtune=btver2 $(OPT_SIMD) --param l1-cache-size=32 --param l2-cache-size=1024
|
||||
amd/bobcat := $(___) -march=btver1 $(FPU_SSSE4A) -mtune=btver1 $(OPT_SIMD) --param l1-cache-size=32 --param l2-cache-size=512
|
||||
amd/jaguar := $(___) -march=btver2 $(FPU_SSSE4A) -mtune=btver2 $(OPT_SIMD) --param l1-cache-size=32 --param l2-cache-size=1024
|
||||
|
||||
amd/late-3dnow := $(XX_) -march=athlon-xp $(FPU_3DASSE) -mtune=athlon-xp $(OPT_SIMD) --param l1-cache-size=64 --param l2-cache-size=512
|
||||
amd/late := $(XX_) -march=i686 $(FPU_SSE4A) -mtune=generic $(OPT_SIMD)
|
||||
amd/late := $(XX_) -march=i686 $(FPU_SSSE4A) -mtune=generic $(OPT_SIMD)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
#pragma once
|
||||
#define OPENMPT_VERSION_SVNVERSION "21953"
|
||||
#define OPENMPT_VERSION_REVISION 21953
|
||||
#define OPENMPT_VERSION_SVNVERSION "22406"
|
||||
#define OPENMPT_VERSION_REVISION 22406
|
||||
#define OPENMPT_VERSION_DIRTY 0
|
||||
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
||||
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.11"
|
||||
#define OPENMPT_VERSION_DATE "2024-10-26T13:09:27.804941Z"
|
||||
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.12"
|
||||
#define OPENMPT_VERSION_DATE "2024-12-01T13:10:15.135688Z"
|
||||
#define OPENMPT_VERSION_IS_PACKAGE 1
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ TempFileGuard::~TempFileGuard()
|
|||
{
|
||||
if(!filename.empty())
|
||||
{
|
||||
DeleteFile(filename.AsNative().c_str());
|
||||
DeleteFile(mpt::support_long_path(filename.AsNative()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ TempDirGuard::TempDirGuard(const mpt::TemporaryPathname &pathname)
|
|||
{
|
||||
return;
|
||||
}
|
||||
if(::CreateDirectory(dirname.AsNative().c_str(), NULL) == 0)
|
||||
if(::CreateDirectory(mpt::support_long_path(dirname.AsNative()).c_str(), NULL) == 0)
|
||||
{ // fail
|
||||
dirname = mpt::PathString();
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ mpt::PathString AbsolutePathToRelative(const mpt::PathString &path, const mpt::P
|
|||
using namespace path_literals;
|
||||
using char_type = RawPathString::value_type;
|
||||
mpt::PathString result = path;
|
||||
if(path.empty())
|
||||
if(path.empty() || relativeTo.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ mpt::PathString RelativePathToAbsolute(const mpt::PathString &path, const mpt::P
|
|||
using namespace path_literals;
|
||||
using char_type = RawPathString::value_type;
|
||||
mpt::PathString result = path;
|
||||
if(path.empty())
|
||||
if(path.empty() || relativeTo.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -621,6 +621,7 @@ mpt::ustring GetFullCreditsString()
|
|||
"Revenant (https://revenant1.net/)\n"
|
||||
"SYRiNX\n"
|
||||
"xaimus (http://xaimus.com/)\n"
|
||||
"zersal\n"
|
||||
"\n"
|
||||
"Thanks to:\n"
|
||||
"\n"
|
||||
|
@ -660,7 +661,7 @@ mpt::ustring GetFullCreditsString()
|
|||
"https://github.com/iamgreaser/it2everything/\n"
|
||||
"\n"
|
||||
"Antti S. Lankila for Amiga resampler implementation\n"
|
||||
"https://bel.fi/alankila/modguide/interpolate.txt\n"
|
||||
"https://web.archive.org/web/20221228071135/https://bel.fi/alankila/modguide/\n"
|
||||
"\n"
|
||||
"Shayde / Reality Productions for Opal OPL3 emulator\n"
|
||||
"https://www.3eality.com/\n"
|
||||
|
@ -774,7 +775,7 @@ mpt::ustring GetFullCreditsString()
|
|||
"https://www.behance.net/ulfurkolka\n"
|
||||
"\n"
|
||||
"Nobuyuki for file icon\n"
|
||||
"https://twitter.com/nobuyukinyuu\n"
|
||||
"https://github.com/nobuyukinyuu/\n"
|
||||
"\n"
|
||||
#endif
|
||||
"Daniel Collin (emoon/TBL) for providing test infrastructure\n"
|
||||
|
@ -784,8 +785,8 @@ mpt::ustring GetFullCreditsString()
|
|||
"in the form of ideas, testing and support;\n"
|
||||
"thanks particularly to:\n"
|
||||
"33, 8bitbubsy, AliceLR, Anboi, BooT-SectoR-ViruZ, Bvanoudtshoorn\n"
|
||||
"christofori, cubaxd, Diamond, Ganja, Georg, Goor00,\n"
|
||||
"Harbinger, jmkz, KrazyKatz, LPChip, Nofold, Rakib, Sam Zen\n"
|
||||
"a11cf0, christofori, cubaxd, Diamond, Ganja, Georg, Goor00,\n"
|
||||
"Harbinger, jmkz, KrazyKatz, LPChip, MiDoRi, Nofold, Rakib, Sam Zen\n"
|
||||
"Skaven, Skilletaudio, Snu, Squirrel Havoc, Teimoso, Waxhead\n"
|
||||
"\n"
|
||||
#ifdef MPT_WITH_VST
|
||||
|
|
|
@ -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 31
|
||||
#define VER_MINOR 11
|
||||
#define VER_MINOR 13
|
||||
#define VER_MINORMINOR 00
|
||||
|
||||
OPENMPT_NAMESPACE_END
|
||||
|
|
|
@ -169,11 +169,11 @@ namespace openmpt {
|
|||
if ( volume < 0.0 || volume > 1.0 ) {
|
||||
throw openmpt::exception("invalid global volume");
|
||||
}
|
||||
m_sndFile->m_PlayState.m_nGlobalVolume = mpt::saturate_round<uint32_t>( volume * MAX_GLOBAL_VOLUME );
|
||||
m_sndFile->m_PlayState.m_nGlobalVolume = mpt::saturate_round<uint32_t>( volume * OpenMPT::MAX_GLOBAL_VOLUME );
|
||||
}
|
||||
|
||||
double module_ext_impl::get_global_volume( ) const {
|
||||
return m_sndFile->m_PlayState.m_nGlobalVolume / static_cast<double>( MAX_GLOBAL_VOLUME );
|
||||
return m_sndFile->m_PlayState.m_nGlobalVolume / static_cast<double>( OpenMPT::MAX_GLOBAL_VOLUME );
|
||||
}
|
||||
|
||||
void module_ext_impl::set_channel_volume( std::int32_t channel, double volume ) {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/*! \brief libopenmpt minor version number */
|
||||
#define OPENMPT_API_VERSION_MINOR 7
|
||||
/*! \brief libopenmpt patch version number */
|
||||
#define OPENMPT_API_VERSION_PATCH 11
|
||||
#define OPENMPT_API_VERSION_PATCH 12
|
||||
/*! \brief libopenmpt pre-release tag */
|
||||
#define OPENMPT_API_VERSION_PREREL ""
|
||||
/*! \brief libopenmpt pre-release flag */
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
LIBOPENMPT_VERSION_MAJOR=0
|
||||
LIBOPENMPT_VERSION_MINOR=7
|
||||
LIBOPENMPT_VERSION_PATCH=11
|
||||
LIBOPENMPT_VERSION_PATCH=12
|
||||
LIBOPENMPT_VERSION_PREREL=
|
||||
|
||||
LIBOPENMPT_LTVER_CURRENT=4
|
||||
LIBOPENMPT_LTVER_REVISION=11
|
||||
LIBOPENMPT_LTVER_REVISION=12
|
||||
LIBOPENMPT_LTVER_AGE=4
|
||||
|
|
|
@ -231,6 +231,7 @@ bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, C
|
|||
#ifdef MMCMP_LOG
|
||||
MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" 16-bit block: pos={} size={} {} {}")(psubblk->position, psubblk->size, (blk.flags & MMCMP_DELTA) ? U_("DELTA ") : U_(""), (blk.flags & MMCMP_ABS16) ? U_("ABS16 ") : U_("")));
|
||||
#endif
|
||||
if(numbits > 15) return false;
|
||||
if(!file.Seek(memPos + blk.tt_entries)) return false;
|
||||
if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false;
|
||||
BitReader bitFile{ file.GetChunk(blk.pk_size - blk.tt_entries) };
|
||||
|
@ -316,6 +317,7 @@ bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, C
|
|||
uint32 numbits = blk.num_bits;
|
||||
uint32 oldval = 0;
|
||||
if(blk.tt_entries > sizeof(ptable)
|
||||
|| numbits > 7
|
||||
|| !file.Seek(memPos)
|
||||
|| file.ReadRaw(mpt::span(ptable, blk.tt_entries)).size() < blk.tt_entries)
|
||||
return false;
|
||||
|
|
|
@ -2127,9 +2127,9 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
|
|||
}
|
||||
}
|
||||
pIns->nFadeOut = 1024;
|
||||
pIns->nMidiProgram = (uint8)(dlsIns.ulInstrument & 0x7F) + 1;
|
||||
pIns->nMidiChannel = (uint8)(isDrum ? 10 : 0);
|
||||
pIns->wMidiBank = (uint16)(((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F));
|
||||
pIns->nMidiProgram = static_cast<uint8>(1 + (dlsIns.ulInstrument & 0x7F));
|
||||
pIns->nMidiChannel = static_cast<uint8>(isDrum ? 10 : 0);
|
||||
pIns->wMidiBank = static_cast<uint16>(1 + (((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F)));
|
||||
pIns->nNNA = NewNoteAction::NoteOff;
|
||||
pIns->nDCT = DuplicateCheckType::Note;
|
||||
pIns->nDNA = DuplicateNoteAction::NoteFade;
|
||||
|
@ -2146,7 +2146,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
|
|||
const DLSREGION &rgn = dlsIns.Regions[nRgn];
|
||||
if(rgn.IsDummy())
|
||||
continue;
|
||||
// Elimitate Duplicate Regions
|
||||
// Eliminate Duplicate Regions
|
||||
uint32 dupRegion;
|
||||
for(dupRegion = minRegion; dupRegion < nRgn; dupRegion++)
|
||||
{
|
||||
|
|
|
@ -530,10 +530,9 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr
|
|||
// Convert an ITSample to OpenMPT's internal sample representation.
|
||||
uint32 ITSample::ConvertToMPT(ModSample &mptSmp) const
|
||||
{
|
||||
if(memcmp(id, "IMPS", 4))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// IT does not check for the IMPS magic, and some bad XM->IT converter out there doesn't write the magic bytes for empty sample slots.
|
||||
//if(memcmp(id, "IMPS", 4))
|
||||
// return 0;
|
||||
|
||||
mptSmp.Initialize(MOD_TYPE_IT);
|
||||
mptSmp.SetDefaultCuePoints(); // For old IT/MPTM files
|
||||
|
|
|
@ -109,7 +109,7 @@ struct DBMInstrument
|
|||
|
||||
void ConvertToMPT(ModSample &mptSmp) const
|
||||
{
|
||||
mptSmp.Initialize();
|
||||
mptSmp.Initialize(MOD_TYPE_DBM);
|
||||
mptSmp.nVolume = std::min(static_cast<uint16>(volume), uint16(64)) * 4u;
|
||||
mptSmp.nC5Speed = Util::muldivr(sampleRate, 8303, 8363);
|
||||
|
||||
|
@ -583,7 +583,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
cmd1 = CMD_NONE;
|
||||
}
|
||||
|
||||
const auto lostCommand = m.FillInTwoCommands(cmd1, param1, cmd2, param2);
|
||||
const auto lostCommand = m.FillInTwoCommands(cmd1, param1, cmd2, param2, true);
|
||||
if(ModCommand::IsGlobalCommand(lostCommand.first, lostCommand.second))
|
||||
lostGlobalCommands.insert(lostGlobalCommands.begin(), lostCommand); // Insert at front so that the last command of same type "wins"
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ struct IMFSample
|
|||
// Convert an IMFSample to OpenMPT's internal sample representation.
|
||||
void ConvertToMPT(ModSample &mptSmp) const
|
||||
{
|
||||
mptSmp.Initialize();
|
||||
mptSmp.Initialize(MOD_TYPE_IMF);
|
||||
mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename);
|
||||
|
||||
mptSmp.nLength = length;
|
||||
|
@ -556,7 +556,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags)
|
|||
const auto [e1c, e1d, e2c, e2d] = patternChunk.ReadArray<uint8, 4>(); // Command 1, Data 1, Command 2, Data 2
|
||||
const auto [command1, param1] = TranslateIMFEffect(e1c, e1d);
|
||||
const auto [command2, param2] = TranslateIMFEffect(e2c, e2d);
|
||||
m.FillInTwoCommands(command1, param1, command2, param2);
|
||||
m.FillInTwoCommands(command1, param1, command2, param2, true);
|
||||
} else if(mask & 0xC0)
|
||||
{
|
||||
// There's one effect, just stick it in the effect column (unless it's a volume command)
|
||||
|
|
|
@ -798,6 +798,19 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
bool possibleXMconversion = false;
|
||||
|
||||
// There's a bug in IT somewhere that resets the "sample data present" flag in sample headers, but keeps the sample length
|
||||
// of a previously deleted sample (presumably).
|
||||
// As old ModPlug versions didn't set this flag under some circumstances (if a sample wasn't referenced by any instruments in instrument mode),
|
||||
// and because there appear to be some external tools that forget to set this flag at all, we only respect the flag if the file
|
||||
// vaguely looks like it was saved with IT. Some files that play garbage data if we don't do this:
|
||||
// astral projection.it by Lord Jon Ray
|
||||
// classic illusions.it by Blackstar
|
||||
// deep in dance.it by Simply DJ
|
||||
// There are many more such files but they don't reference the broken samples in their pattern data, or the sample data pointer
|
||||
// points right to the end of the file, so in both cases no audible problem can be observed.
|
||||
const bool muteBuggySamples = !interpretModPlugMade && fileHeader.cwtv >= 0x0100 && fileHeader.cwtv <= 0x0217
|
||||
&& (fileHeader.cwtv < 0x0207 || fileHeader.reserved != 0);
|
||||
|
||||
// Reading Samples
|
||||
m_nSamples = std::min(static_cast<SAMPLEINDEX>(fileHeader.smpnum), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1));
|
||||
bool lastSampleCompressed = false, anyADPCM = false;
|
||||
|
@ -806,9 +819,10 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
ITSample sampleHeader;
|
||||
if(smpPos[i] > 0 && file.Seek(smpPos[i]) && file.ReadStruct(sampleHeader))
|
||||
{
|
||||
// IT does not check for the IMPS magic, and some bad XM->IT converter out there doesn't write the magic bytes for empty sample slots.
|
||||
ModSample &sample = Samples[i + 1];
|
||||
size_t sampleOffset = sampleHeader.ConvertToMPT(sample);
|
||||
if(muteBuggySamples && !(sampleHeader.flags & ITSample::sampleDataPresent))
|
||||
sample.nLength = 0;
|
||||
|
||||
m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
|
||||
|
||||
|
@ -1257,6 +1271,10 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
} else if(fileHeader.cwtv == 0 && madeWithTracker.empty())
|
||||
{
|
||||
madeWithTracker = U_("Unknown");
|
||||
} else if(fileHeader.cwtv >= 0x0208 && fileHeader.cwtv <= 0x0214 && !fileHeader.reserved && m_FileHistory.empty() && madeWithTracker.empty())
|
||||
{
|
||||
// Any file made with IT starting from v2.07 onwards should have an edit history
|
||||
madeWithTracker = UL_("Unknown");
|
||||
} else if(fileHeader.cmwt < 0x0300 && madeWithTracker.empty())
|
||||
{
|
||||
madeWithTracker = GetImpulseTrackerVersion(fileHeader.cwtv, fileHeader.cmwt);
|
||||
|
@ -2650,14 +2668,6 @@ bool CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel
|
|||
m_nMixLevels = MixLevels::Original;
|
||||
//m_dwCreatedWithVersion
|
||||
//m_dwLastSavedWithVersion
|
||||
//m_nSamplePreAmp
|
||||
//m_nVSTiVolume
|
||||
//m_nDefaultGlobalVolume
|
||||
LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
|
||||
//m_nRestartPos
|
||||
//m_ModFlags
|
||||
LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT);
|
||||
LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -368,7 +368,7 @@ struct MMDDump
|
|||
MPT_BINARY_STRUCT(MMDDump, 10)
|
||||
|
||||
|
||||
static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPerBeat)
|
||||
static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool softwareMixing, bool bpmMode, uint8 rowsPerBeat)
|
||||
{
|
||||
if(bpmMode && !is8Ch)
|
||||
{
|
||||
|
@ -382,10 +382,14 @@ static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPer
|
|||
// MED Soundstudio uses these tempos when importing old files
|
||||
static constexpr uint8 tempos[10] = {179, 164, 152, 141, 131, 123, 116, 110, 104, 99};
|
||||
return TEMPO(tempos[tempo - 1], 0);
|
||||
} else if(tempo > 0 && tempo <= 10)
|
||||
} else if(!softwareMixing && tempo > 0 && tempo <= 10)
|
||||
{
|
||||
// SoundTracker compatible tempo
|
||||
return TEMPO((6.0 * 1773447.0 / 14500.0) / tempo);
|
||||
} else if(softwareMixing && tempo < 8)
|
||||
{
|
||||
// Observed in MED SoundStudio 1.03 with 1-64ch mixing mode and SPD tempo mode (bug?)
|
||||
return TEMPO(157.86);
|
||||
}
|
||||
|
||||
return TEMPO(tempo / 0.264);
|
||||
|
@ -398,10 +402,11 @@ struct TranslateMEDPatternContext
|
|||
const CHANNELINDEX numTracks;
|
||||
const uint8 version;
|
||||
const uint8 rowsPerBeat;
|
||||
const bool hardwareMixSamples : 1;
|
||||
const bool is8Ch : 1;
|
||||
const bool softwareMixing : 1;
|
||||
const bool bpmMode : 1;
|
||||
const bool volHex : 1;
|
||||
const bool vol7bit : 1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -410,9 +415,29 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
|
|||
const uint8 nibbleLo = std::min(param, uint8(0x0F));
|
||||
switch(command)
|
||||
{
|
||||
case 0x01: // Portamento Up (avoid effect memory when importing as XM)
|
||||
if(param)
|
||||
m.SetEffectCommand(CMD_PORTAMENTOUP, param);
|
||||
break;
|
||||
case 0x02: // Portamento Down (avoid effect memory when importing as XM)
|
||||
if(param)
|
||||
m.SetEffectCommand(CMD_PORTAMENTODOWN, param);
|
||||
break;
|
||||
case 0x04: // Vibrato (twice as deep as in ProTracker)
|
||||
m.SetEffectCommand(CMD_VIBRATO, (param & 0xF0) | std::min<uint8>((param & 0x0F) * 2, 0x0F));
|
||||
break;
|
||||
case 0x05: // Tone Porta + Volume Slide (avoid effect memory when importing as XM)
|
||||
if(param)
|
||||
m.SetEffectCommand(CMD_TONEPORTAVOL, param);
|
||||
else
|
||||
m.SetEffectCommand(CMD_TONEPORTAMENTO, 0);
|
||||
break;
|
||||
case 0x06: // Vibrato + Volume Slide (avoid effect memory when importing as XM)
|
||||
if(param)
|
||||
m.SetEffectCommand(CMD_VIBRATOVOL, param);
|
||||
else
|
||||
m.SetEffectCommand(CMD_VIBRATO, 0);
|
||||
break;
|
||||
case 0x08: // Hold and decay
|
||||
break;
|
||||
case 0x09: // Set secondary speed
|
||||
|
@ -422,13 +447,14 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
|
|||
case 0x0C: // Set Volume (note: parameters >= 0x80 (only in hex mode?) should set the default instrument volume, which we don't support)
|
||||
if(!ctx.volHex && param < 0x99)
|
||||
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>((param >> 4) * 10 + (param & 0x0F)));
|
||||
else if(ctx.volHex && ctx.version < 3)
|
||||
else if(ctx.volHex && !ctx.vol7bit)
|
||||
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>(std::min(param & 0x7F, 64)));
|
||||
else if(ctx.volHex)
|
||||
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>(((param & 0x7F) + 1) / 2));
|
||||
break;
|
||||
case 0x0D:
|
||||
m.SetEffectCommand(CMD_VOLUMESLIDE, param);
|
||||
if(param)
|
||||
m.SetEffectCommand(CMD_VOLUMESLIDE, param);
|
||||
break;
|
||||
case 0x0E: // Synth jump
|
||||
m.command = CMD_NONE;
|
||||
|
@ -446,7 +472,7 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
|
|||
m.param = 0x70;
|
||||
} else
|
||||
{
|
||||
uint16 tempo = mpt::saturate_round<uint16>(MMDTempoToBPM(param, ctx.is8Ch, ctx.bpmMode, ctx.rowsPerBeat).ToDouble());
|
||||
uint16 tempo = mpt::saturate_round<uint16>(MMDTempoToBPM(param, ctx.is8Ch, ctx.softwareMixing, ctx.bpmMode, ctx.rowsPerBeat).ToDouble());
|
||||
if(tempo <= Util::MaxValueOfType(m.param))
|
||||
{
|
||||
m.param = static_cast<ModCommand::PARAM>(tempo);
|
||||
|
@ -602,7 +628,7 @@ static bool TranslateMEDPattern(FileReader &file, FileReader &cmdExt, CPattern &
|
|||
param1 = param;
|
||||
}
|
||||
// Octave wrapping for 4-channel modules
|
||||
if(ctx.hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12)
|
||||
if(note >= NOTE_MIDDLEC + 2 * 12)
|
||||
needInstruments = true;
|
||||
|
||||
if(note >= NOTE_MIN && note <= NOTE_MAX)
|
||||
|
@ -615,7 +641,7 @@ static bool TranslateMEDPattern(FileReader &file, FileReader &cmdExt, CPattern &
|
|||
if(oldCmd.first != CMD_NONE && m->command != oldCmd.first)
|
||||
{
|
||||
if(!ModCommand::CombineEffects(m->command, m->param, oldCmd.first, oldCmd.second) && m->volcmd == VOLCMD_NONE)
|
||||
m->FillInTwoCommands(m->command, m->param, oldCmd.first, oldCmd.second);
|
||||
m->FillInTwoCommands(m->command, m->param, oldCmd.first, oldCmd.second, true);
|
||||
// Reset X-Param to 8-bit value if this cell was overwritten with a "useful" effect
|
||||
if(row > 0 && oldCmd.first == CMD_XPARAM && m->command != CMD_XPARAM)
|
||||
pattern.GetpModCommand(row - 1, chn)->param = Util::MaxValueOfType(m->param);
|
||||
|
@ -895,13 +921,14 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
needInstruments = true;
|
||||
instr.Transpose(-24);
|
||||
} else if(!isSynth && hardwareMixSamples)
|
||||
} else if(!isSynth && (hardwareMixSamples || sampleHeader.sampleTranspose))
|
||||
{
|
||||
int offset = NOTE_MIDDLEC + (hardwareMixSamples ? 24 : 36);
|
||||
for(auto ¬e : instr.NoteMap)
|
||||
{
|
||||
int realNote = note + sampleHeader.sampleTranspose;
|
||||
if(realNote >= NOTE_MIDDLEC + 24)
|
||||
note -= static_cast<uint8>(mpt::align_down(realNote - NOTE_MIDDLEC - 12, 12));
|
||||
if(realNote >= offset)
|
||||
note -= static_cast<uint8>(mpt::align_down(realNote - offset + 12, 12));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -968,6 +995,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
sampleIO |= SampleIO::stereoSplit;
|
||||
length /= 2;
|
||||
m_SongFlags.reset(SONG_ISAMIGA); // Amiga resampler does not handle stereo samples
|
||||
}
|
||||
if(instrHeader.type & MMDInstrHeader::DELTA)
|
||||
{
|
||||
|
@ -1167,9 +1195,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if((header.mixEchoType == 1 || header.mixEchoType == 2) && numPlugins < MAX_MIXPLUGINS)
|
||||
{
|
||||
// Emulating MED echo using the DMO echo requires to compensate for the differences in initial feedback in the latter.
|
||||
const float feedback = 1.0f / (1 << std::max(header.mixEchoDepth, uint8(1))); // The feedback we want
|
||||
const float initialFeedback = std::sqrt(1.0f - (feedback * feedback)); // Actual strength of first delay's feedback
|
||||
const float wetFactor = feedback / initialFeedback; // Factor to compensate for this
|
||||
const float feedback = 1.0f / (1 << std::clamp(header.mixEchoDepth, uint8(1), uint8(9))); // The feedback we want
|
||||
const float initialFeedback = std::sqrt(1.0f - (feedback * feedback)); // Actual strength of first delay's feedback
|
||||
const float wetFactor = feedback / initialFeedback; // Factor to compensate for this
|
||||
const float delay = (std::max(header.mixEchoLength.get(), uint16(1)) - 1) / 1999.0f;
|
||||
SNDMIXPLUGIN &mixPlug = m_MixPlugins[numPlugins];
|
||||
mpt::reconstruct(mixPlug);
|
||||
|
@ -1257,10 +1285,11 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
const bool volHex = (songHeader.flags & MMDSong::FLAG_VOLHEX) != 0;
|
||||
const bool is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0;
|
||||
const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0;
|
||||
const bool softwareMixing = (songHeader.flags2 & MMDSong::FLAG2_MIX) != 0;
|
||||
const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK);
|
||||
if(song == 0)
|
||||
{
|
||||
m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat);
|
||||
m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, softwareMixing, bpmMode, rowsPerBeat);
|
||||
m_nDefaultSpeed = Clamp<uint8, uint8>(songHeader.tempo2, 1, 32);
|
||||
if(bpmMode)
|
||||
{
|
||||
|
@ -1275,6 +1304,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
// For MED, this affects both volume and pitch slides
|
||||
m_SongFlags.set(SONG_FASTVOLSLIDES, !(songHeader.flags & MMDSong::FLAG_STSLIDE));
|
||||
m_playBehaviour.set(kST3OffsetWithoutInstrument);
|
||||
m_playBehaviour.set(kST3PortaSampleChange);
|
||||
m_playBehaviour.set(kFT2PortaNoNote);
|
||||
|
||||
if(expData.songNameOffset && file.Seek(expData.songNameOffset))
|
||||
{
|
||||
|
@ -1382,6 +1414,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
int16 transpose = NOTE_MIN + 47 + songHeader.playTranspose;
|
||||
uint16 numPages = 0;
|
||||
FileReader cmdExt, commandPages;
|
||||
bool vol7bit = false;
|
||||
|
||||
if(version < 1)
|
||||
{
|
||||
|
@ -1423,6 +1456,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
&& file.Seek(blockInfo.cmdExtTableOffset)
|
||||
&& file.Seek(file.ReadUint32BE()))
|
||||
{
|
||||
vol7bit = true;
|
||||
cmdExt = file.ReadChunk(numTracks * numRows * (1 + numPages));
|
||||
}
|
||||
|
||||
|
@ -1437,7 +1471,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
pattern.SetName(patName);
|
||||
LimitMax(numTracks, m_nChannels);
|
||||
|
||||
TranslateMEDPatternContext context{transpose, numTracks, version, rowsPerBeat, hardwareMixSamples, is8Ch, bpmMode, volHex};
|
||||
TranslateMEDPatternContext context{transpose, numTracks, version, rowsPerBeat, is8Ch, softwareMixing, bpmMode, volHex, vol7bit};
|
||||
needInstruments |= TranslateMEDPattern(file, cmdExt, pattern, context, false);
|
||||
|
||||
for(uint16 page = 0; page < numPages; page++)
|
||||
|
|
|
@ -617,8 +617,12 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN
|
|||
const size_t patternStartOffset = file.GetPosition();
|
||||
const size_t sizeWithoutPatterns = totalSampleLen + patternStartOffset;
|
||||
const size_t sizeWithOfficialPatterns = sizeWithoutPatterns + officialPatterns * numChannels * 256;
|
||||
// There are some WOW files with an extra byte at the end, and also a MOD file (idntmind.mod, MD5 a3af5c3e1af269e32dfb6677c41c8453, SHA1 4884717c298575f9884b2211c762bb1725f73743)
|
||||
// where only the "official" patterns should be counted but the file also has an extra byte at the end.
|
||||
// Since MOD files can technically not have an odd file size, we just always round the actual file size down.
|
||||
const auto fileSize = mpt::align_down(file.GetLength(), FileReader::pos_type{2});
|
||||
|
||||
if(wowSampleLen && (wowSampleLen + patternStartOffset) + numPatterns * 8 * 256 == (file.GetLength() & ~1))
|
||||
if(wowSampleLen && (wowSampleLen + patternStartOffset) + numPatterns * 8 * 256 == fileSize)
|
||||
{
|
||||
// Check if this is a Mod's Grave WOW file... WOW files use the M.K. magic but are actually 8CHN files.
|
||||
// We do a simple pattern validation as well for regular MOD files that have non-module data attached at the end
|
||||
|
@ -627,7 +631,7 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN
|
|||
if(ValidateMODPatternData(file, 16, true))
|
||||
numChannels = 8;
|
||||
file.Seek(patternStartOffset);
|
||||
} else if(numPatterns != officialPatterns && (validateHiddenPatterns || sizeWithOfficialPatterns == file.GetLength()))
|
||||
} else if(numPatterns != officialPatterns && (validateHiddenPatterns || sizeWithOfficialPatterns == fileSize))
|
||||
{
|
||||
// 15-sample SoundTracker specifics:
|
||||
// Fix SoundTracker modules where "hidden" patterns should be ignored.
|
||||
|
@ -657,7 +661,7 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN
|
|||
file.Seek(patternStartOffset);
|
||||
}
|
||||
|
||||
if(numPatternsIllegal > numPatterns && sizeWithoutPatterns + numPatternsIllegal * numChannels * 256 == file.GetLength())
|
||||
if(numPatternsIllegal > numPatterns && sizeWithoutPatterns + numPatternsIllegal * numChannels * 256 == fileSize)
|
||||
{
|
||||
// Even those illegal pattern indexes (> 128) appear to be valid... What a weird file!
|
||||
// e.g. NIETNU.MOD, where the end of the order list is filled with FF rather than 00, and the file actually contains 256 patterns.
|
||||
|
@ -1072,6 +1076,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
// Reading patterns
|
||||
Patterns.ResizeArray(numPatterns);
|
||||
std::bitset<32> referencedSamples;
|
||||
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
||||
{
|
||||
ModCommand *rowBase = nullptr;
|
||||
|
@ -1179,6 +1184,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(m.instr != 0)
|
||||
{
|
||||
lastInstrument[chn] = m.instr;
|
||||
if(isStartrekker)
|
||||
referencedSamples.set(m.instr & 0x1F);
|
||||
}
|
||||
}
|
||||
if(hasSpeedOnRow && hasTempoOnRow)
|
||||
|
@ -1215,7 +1222,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
m_SongFlags.set(SONG_ISAMIGA);
|
||||
}
|
||||
if(isGenericMultiChannel || isMdKd)
|
||||
if(isGenericMultiChannel || isMdKd || IsMagic(magic, "M!K!"))
|
||||
{
|
||||
m_playBehaviour.set(kFT2MODTremoloRampWaveform);
|
||||
}
|
||||
|
@ -1316,7 +1323,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_nInstruments = 31;
|
||||
#endif
|
||||
|
||||
for(SAMPLEINDEX smp = 1; smp <= m_nInstruments; smp++)
|
||||
for(SAMPLEINDEX smp = 1; smp <= GetNumInstruments(); smp++)
|
||||
{
|
||||
// For Startrekker AM synthesis, we need instrument envelopes.
|
||||
ModInstrument *ins = AllocateInstrument(smp, smp);
|
||||
|
@ -1339,6 +1346,32 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
#endif // MPT_EXTERNAL_SAMPLES || MPT_BUILD_FUZZER
|
||||
|
||||
if((loadFlags & loadSampleData) && isStartrekker && !m_nInstruments)
|
||||
{
|
||||
uint8 emptySampleReferences = 0;
|
||||
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
||||
{
|
||||
if(referencedSamples[smp] && !Samples[smp].nLength)
|
||||
{
|
||||
if(++emptySampleReferences > 1)
|
||||
{
|
||||
#ifdef MPT_EXTERNAL_SAMPLES
|
||||
mpt::ustring filenameHint;
|
||||
if(file.GetOptionalFileName())
|
||||
{
|
||||
const auto filename = file.GetOptionalFileName()->GetFilename().ToUnicode();
|
||||
filenameHint = MPT_UFORMAT(" ({}.nt or {}.as)")(filename, filename);
|
||||
}
|
||||
AddToLog(LogWarning, MPT_UFORMAT("This Startrekker AM file is most likely missing its companion file{}. Synthesized instruments will not play.")(filenameHint));
|
||||
#else
|
||||
AddToLog(LogWarning, U_("This appears to be a Startrekker AM file with external synthesizes instruments. External instruments are currently not supported."));
|
||||
#endif // MPT_EXTERNAL_SAMPLES
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix VBlank MODs. Arbitrary threshold: 8 minutes (enough for "frame of mind" by Dascon...).
|
||||
// Basically, this just converts all tempo commands into speed commands
|
||||
// for MODs which are supposed to have VBlank timing (instead of CIA timing).
|
||||
|
|
|
@ -232,6 +232,15 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_nMinPeriod = 64;
|
||||
m_nMaxPeriod = 32767;
|
||||
|
||||
ReadOrderFromFile<uint8>(Order(), file, fileHeader.ordNum, 0xFF, 0xFE);
|
||||
|
||||
// Read sample header offsets
|
||||
std::vector<uint16le> sampleOffsets;
|
||||
file.ReadVector(sampleOffsets, fileHeader.smpNum);
|
||||
// Read pattern offsets
|
||||
std::vector<uint16le> patternOffsets;
|
||||
file.ReadVector(patternOffsets, fileHeader.patNum);
|
||||
|
||||
// ST3 ignored Zxx commands, so if we find that a file was made with ST3, we should erase all MIDI macros.
|
||||
bool keepMidiMacros = false;
|
||||
|
||||
|
@ -241,6 +250,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
bool isST3 = false;
|
||||
bool isSchism = false;
|
||||
const bool usePanningTable = fileHeader.usePanningTable == S3MFileHeader::idPanning;
|
||||
const bool offsetsAreCanonical = !patternOffsets.empty() && !sampleOffsets.empty() && patternOffsets[0] > sampleOffsets[0];
|
||||
const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x4FFF) ? fileHeader.reserved2 : (fileHeader.cwtv - 0x4050));
|
||||
switch(fileHeader.cwtv & S3MFileHeader::trackerMask)
|
||||
{
|
||||
|
@ -252,18 +262,25 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(!memcmp(&fileHeader.reserved2, "SCLUB2.0", 8))
|
||||
{
|
||||
madeWithTracker = UL_("Sound Club 2");
|
||||
} else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0 && usePanningTable)
|
||||
} else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x01) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0 && usePanningTable && offsetsAreCanonical)
|
||||
{
|
||||
// MPT and OpenMPT before 1.17.03.02 - Simply keep default (filter) MIDI macros
|
||||
if((fileHeader.masterVolume & 0x80) != 0)
|
||||
// Canonical offset check avoids mis-detection of an automatic conversion of Vic's "Paper" demo track
|
||||
if((fileHeader.ordNum & 0x0F) == 0)
|
||||
{
|
||||
m_dwLastSavedWithVersion = MPT_V("1.16");
|
||||
madeWithTracker = UL_("ModPlug Tracker / OpenMPT 1.17");
|
||||
} else
|
||||
// MPT and OpenMPT before 1.17.03.02 - Simply keep default (filter) MIDI macros
|
||||
if((fileHeader.masterVolume & 0x80) != 0)
|
||||
{
|
||||
m_dwLastSavedWithVersion = MPT_V("1.16");
|
||||
madeWithTracker = UL_("ModPlug Tracker / OpenMPT 1.17");
|
||||
} else
|
||||
{
|
||||
// MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 alpha6 does.
|
||||
m_dwLastSavedWithVersion = MPT_V("1.00.00.A0");
|
||||
madeWithTracker = UL_("ModPlug Tracker 1.0 alpha");
|
||||
}
|
||||
} else if((fileHeader.masterVolume & 0x80) != 0)
|
||||
{
|
||||
// MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 alpha6 does.
|
||||
m_dwLastSavedWithVersion = MPT_V("1.00.00.A0");
|
||||
madeWithTracker = UL_("ModPlug Tracker 1.0 alpha");
|
||||
madeWithTracker = UL_("Schism Tracker");
|
||||
}
|
||||
keepMidiMacros = true;
|
||||
nonCompatTracker = true;
|
||||
|
@ -511,15 +528,6 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_nChannels = 1;
|
||||
}
|
||||
|
||||
ReadOrderFromFile<uint8>(Order(), file, fileHeader.ordNum, 0xFF, 0xFE);
|
||||
|
||||
// Read sample header offsets
|
||||
std::vector<uint16le> sampleOffsets;
|
||||
file.ReadVector(sampleOffsets, fileHeader.smpNum);
|
||||
// Read pattern offsets
|
||||
std::vector<uint16le> patternOffsets;
|
||||
file.ReadVector(patternOffsets, fileHeader.patNum);
|
||||
|
||||
// Read extended channel panning
|
||||
if(usePanningTable)
|
||||
{
|
||||
|
@ -538,6 +546,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(m_nChannels < 32 && m_dwLastSavedWithVersion == MPT_V("1.16"))
|
||||
{
|
||||
// MPT 1.0 alpha 6 up to 1.16.203 set ths panning bit for all channels, regardless of whether they are used or not.
|
||||
// Note: Schism Tracker fixed the same bug in git commit f21fe8bcae8b6dde2df27ede4ac9fe563f91baff
|
||||
if(hasChannelsWithoutPanning)
|
||||
m_modFormat.madeWithTracker = UL_("ModPlug Tracker 1.16 / OpenMPT 1.17");
|
||||
else
|
||||
|
|
|
@ -49,7 +49,7 @@ struct STMSampleHeader
|
|||
&& mptSmp.nLoopEnd != 0xFFFF)
|
||||
{
|
||||
mptSmp.uFlags = CHN_LOOP;
|
||||
mptSmp.nLoopEnd = std::min(mptSmp.nLoopEnd, mptSmp.nLength);
|
||||
mptSmp.nLength = std::max(mptSmp.nLoopEnd, mptSmp.nLength);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -41,6 +41,13 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
|
|||
nC5Speed = Util::muldivr_unsigned(nC5Speed, 8363, 8287);
|
||||
FrequencyToTranspose();
|
||||
}
|
||||
if(toType == MOD_TYPE_MOD)
|
||||
{
|
||||
if(RelativeTone == -1 && nFineTune == 0)
|
||||
nFineTune = -128;
|
||||
RelativeTone = 0;
|
||||
nFineTune &= ~0x0F;
|
||||
}
|
||||
|
||||
// No ping-pong loop, panning and auto-vibrato for MOD / S3M samples
|
||||
if(toType & (MOD_TYPE_MOD | MOD_TYPE_S3M))
|
||||
|
@ -51,8 +58,6 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
|
|||
nVibRate = 0;
|
||||
nVibSweep = 0;
|
||||
nVibType = VIB_SINE;
|
||||
|
||||
RelativeTone = 0;
|
||||
}
|
||||
|
||||
// No global volume / sustain loops for MOD/S3M/XM
|
||||
|
@ -85,7 +90,6 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
|
|||
LimitMax(nVibRate, uint8(63));
|
||||
}
|
||||
|
||||
|
||||
// Autovibrato sweep setting is inverse in XM (0 = "no sweep") and IT (0 = "no vibrato")
|
||||
if(((fromType & MOD_TYPE_XM) && (toType & (MOD_TYPE_IT | MOD_TYPE_MPT))) || ((toType & MOD_TYPE_XM) && (fromType & (MOD_TYPE_IT | MOD_TYPE_MPT))))
|
||||
{
|
||||
|
@ -150,7 +154,16 @@ void ModSample::Initialize(MODTYPE type)
|
|||
rootNote = 0;
|
||||
filename = "";
|
||||
|
||||
RemoveAllCuePoints();
|
||||
if(type & (MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_MED))
|
||||
{
|
||||
for(SmpLength i = 1; i < 10; i++)
|
||||
{
|
||||
cues[i - 1] = Util::muldiv_unsigned(i, 255 * 256, 9);
|
||||
}
|
||||
} else
|
||||
{
|
||||
RemoveAllCuePoints();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -558,7 +558,7 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const
|
|||
{
|
||||
#ifdef MPT_WITH_FLAC
|
||||
const ModSample &sample = Samples[nSample];
|
||||
if(sample.uFlags[CHN_ADLIB])
|
||||
if(sample.uFlags[CHN_ADLIB] || !sample.HasSampleData())
|
||||
return false;
|
||||
|
||||
FLAC__StreamEncoder_RAII encoder(f);
|
||||
|
|
|
@ -559,7 +559,7 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo
|
|||
bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const
|
||||
{
|
||||
const ModSample &sample = Samples[nSample];
|
||||
if(sample.uFlags[CHN_ADLIB])
|
||||
if(sample.uFlags[CHN_ADLIB] || !sample.HasSampleData())
|
||||
return false;
|
||||
|
||||
mpt::IO::OFile<std::ostream> ff(f);
|
||||
|
@ -843,6 +843,8 @@ bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNo
|
|||
bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, std::ostream &f) const
|
||||
{
|
||||
const ModSample &sample = Samples[nSample];
|
||||
if(!sample.HasSampleData())
|
||||
return false;
|
||||
SampleIO(
|
||||
sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
|
||||
sample.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved : SampleIO::mono,
|
||||
|
@ -1188,6 +1190,8 @@ bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file)
|
|||
bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const
|
||||
{
|
||||
const ModSample &sample = Samples[smp];
|
||||
if(!sample.uFlags[CHN_ADLIB] && !sample.HasSampleData())
|
||||
return false;
|
||||
S3MSampleHeader sampleHeader{};
|
||||
SmpLength length = sampleHeader.ConvertToS3M(sample);
|
||||
mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader.name) = m_szNames[smp];
|
||||
|
@ -2762,7 +2766,7 @@ static uint32 WriteIFFStringChunk(std::ostream &f, IFFChunk::ChunkIdentifiers id
|
|||
bool CSoundFile::SaveIFFSample(SAMPLEINDEX smp, std::ostream &f) const
|
||||
{
|
||||
const ModSample &sample = Samples[smp];
|
||||
if(sample.uFlags[CHN_ADLIB])
|
||||
if(sample.uFlags[CHN_ADLIB] || !sample.HasSampleData())
|
||||
return false;
|
||||
|
||||
mpt::IO::OFile<std::ostream> ff(f);
|
||||
|
|
|
@ -299,7 +299,8 @@ DECLARE_FLAGSET(SongFlags)
|
|||
#define SNDMIX_MUTECHNMODE 0x100000 // Notes are not played on muted channels
|
||||
|
||||
|
||||
#define MAX_GLOBAL_VOLUME 256u
|
||||
inline constexpr uint32 MAX_GLOBAL_VOLUME = 256;
|
||||
inline constexpr uint32 MAX_PREAMP = 2000;
|
||||
|
||||
// Resampling modes
|
||||
enum ResamplingMode : uint8
|
||||
|
|
|
@ -1391,7 +1391,7 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo
|
|||
|
||||
if(pIns->NoteMap[note - NOTE_MIN] > NOTE_MAX) return;
|
||||
uint32 n = pIns->Keyboard[note - NOTE_MIN];
|
||||
pSmp = ((n) && (n < MAX_SAMPLES)) ? &Samples[n] : nullptr;
|
||||
pSmp = (n <= GetNumSamples()) ? &Samples[n] : &Samples[0];
|
||||
} else if(GetNumInstruments())
|
||||
{
|
||||
// No valid instrument, or not a valid note.
|
||||
|
@ -1751,9 +1751,9 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE
|
|||
if((pIns) && (note - NOTE_MIN < (int)std::size(pIns->Keyboard)))
|
||||
{
|
||||
uint32 n = pIns->Keyboard[note - NOTE_MIN];
|
||||
if((n) && (n < MAX_SAMPLES))
|
||||
if(n > 0)
|
||||
{
|
||||
pSmp = &Samples[n];
|
||||
pSmp = &Samples[(n <= GetNumSamples()) ? n : 0];
|
||||
} else if(m_playBehaviour[kITEmptyNoteMapSlot] && !chn.HasMIDIOutput())
|
||||
{
|
||||
// Impulse Tracker ignores empty slots.
|
||||
|
@ -1958,7 +1958,7 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE
|
|||
chn.nLoopEnd = pSmp->nLength;
|
||||
chn.nLoopStart = 0;
|
||||
chn.position.Set(0);
|
||||
if((m_SongFlags[SONG_PT_MODE] || m_playBehaviour[kST3OffsetWithoutInstrument]) && !chn.rowCommand.instr)
|
||||
if((m_SongFlags[SONG_PT_MODE] || m_playBehaviour[kST3OffsetWithoutInstrument] || GetType() == MOD_TYPE_MED) && !chn.rowCommand.instr)
|
||||
{
|
||||
chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - SmpLength(1)));
|
||||
} else
|
||||
|
@ -2229,9 +2229,9 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
|
|||
// Test case: dct_smp_note_test.it
|
||||
if(!m_playBehaviour[kITDCTBehaviour] || !m_playBehaviour[kITRealNoteMapping])
|
||||
dnaNote = pIns->NoteMap[note - NOTE_MIN];
|
||||
if(smp > 0 && smp < MAX_SAMPLES)
|
||||
if(smp > 0)
|
||||
{
|
||||
pSample = &Samples[smp];
|
||||
pSample = &Samples[(smp <= GetNumSamples()) ? smp : 0];
|
||||
} else if(m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel())
|
||||
{
|
||||
// Impulse Tracker ignores empty slots.
|
||||
|
@ -4993,6 +4993,7 @@ void CSoundFile::ProcessMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool
|
|||
} else
|
||||
{
|
||||
// SysEx message, find end of message
|
||||
sendLen = outSize - sendPos;
|
||||
for(uint32 i = sendPos + 1; i < outSize; i++)
|
||||
{
|
||||
if(out[i] == 0xF7)
|
||||
|
@ -5002,12 +5003,6 @@ void CSoundFile::ProcessMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool
|
|||
break;
|
||||
}
|
||||
}
|
||||
if(sendLen == 0)
|
||||
{
|
||||
// Didn't find end, so "invent" end of SysEx message
|
||||
out[outSize++] = 0xF7;
|
||||
sendLen = outSize - sendPos;
|
||||
}
|
||||
}
|
||||
} else if(!(out[sendPos] & 0x80))
|
||||
{
|
||||
|
@ -5100,16 +5095,16 @@ void CSoundFile::ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool is
|
|||
// Velocity
|
||||
// This is "almost" how IT does it - apparently, IT seems to lag one row behind on global volume or channel volume changes.
|
||||
const int swing = (m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) ? chn.nVolSwing : 0;
|
||||
const int vol = Util::muldiv((chn.nVolume + swing) * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 20);
|
||||
const int vol = Util::muldiv((chn.nVolume + swing) * playState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 20);
|
||||
data = static_cast<uint8>(Clamp(vol / 2, 1, 127));
|
||||
//data = (unsigned char)std::min((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127);
|
||||
//data = (unsigned char)std::min((chn.nVolume * chn.nGlobalVol * playState.m_nGlobalVolume) >> (1 + 6 + 8), 127);
|
||||
} else if(macro[pos] == 'u')
|
||||
{
|
||||
// Calculated volume
|
||||
// Same note as with velocity applies here, but apparently also for instrument / sample volumes?
|
||||
const int vol = Util::muldiv(chn.nCalcVolume * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 26);
|
||||
const int vol = Util::muldiv(chn.nCalcVolume * playState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 26);
|
||||
data = static_cast<uint8>(Clamp(vol / 2, 1, 127));
|
||||
//data = (unsigned char)std::min((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127);
|
||||
//data = (unsigned char)std::min((chn.nCalcVolume * chn.nGlobalVol * playState.m_nGlobalVolume) >> (7 + 6 + 8), 127);
|
||||
} else if(macro[pos] == 'x')
|
||||
{
|
||||
// Pan set
|
||||
|
@ -5214,14 +5209,32 @@ void CSoundFile::ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool is
|
|||
firstNibble = true;
|
||||
}
|
||||
}
|
||||
// Finish current byte
|
||||
if(!firstNibble)
|
||||
{
|
||||
// Finish current byte
|
||||
outPos++;
|
||||
}
|
||||
if(updateZxxParam < 0x80)
|
||||
chn.lastZxxParam = updateZxxParam;
|
||||
|
||||
// Add end of SysEx byte if necessary
|
||||
for(size_t i = 0; i < outPos; i++)
|
||||
{
|
||||
if(out[i] != 0xF0)
|
||||
continue;
|
||||
if(outPos - i >= 4 && (out[i + 1] == 0xF0 || out[i + 1] == 0xF1))
|
||||
{
|
||||
// Internal message
|
||||
i += 3;
|
||||
} else
|
||||
{
|
||||
// Real SysEx
|
||||
while(i < outPos && out[i] != 0xF7)
|
||||
i++;
|
||||
if(i == outPos && outPos < out.size())
|
||||
out[outPos++] = 0xF7;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out = out.first(outPos);
|
||||
}
|
||||
|
||||
|
@ -5438,7 +5451,7 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const
|
|||
{
|
||||
// ST3 compatibility: Instrument-less note recalls previous note's offset
|
||||
// Test case: OxxMemory.s3m
|
||||
if(m_playBehaviour[kST3OffsetWithoutInstrument])
|
||||
if(m_playBehaviour[kST3OffsetWithoutInstrument] || GetType() == MOD_TYPE_MED)
|
||||
chn.prevNoteOffset = 0;
|
||||
|
||||
chn.prevNoteOffset += param;
|
||||
|
@ -5717,7 +5730,10 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset)
|
|||
|
||||
const bool fading = chn.dwFlags[CHN_NOTEFADE];
|
||||
const auto oldPrevNoteOffset = chn.prevNoteOffset;
|
||||
chn.prevNoteOffset = 0; // Retriggered notes should not use previous offset (test case: OxxMemoryWithRetrig.s3m)
|
||||
// Retriggered notes should not use previous offset in S3M
|
||||
// Test cases: OxxMemoryWithRetrig.s3m, PTOffsetRetrigger.mod
|
||||
if(GetType() == MOD_TYPE_S3M)
|
||||
chn.prevNoteOffset = 0;
|
||||
// IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase)
|
||||
// Test cases: retrig.it, RetrigSlide.s3m
|
||||
const bool itS3Mstyle = m_playBehaviour[kITRetrigger] || (GetType() == MOD_TYPE_S3M && chn.nLength && !oplRealRetrig);
|
||||
|
|
|
@ -594,6 +594,8 @@ bool CSoundFile::CreateInternal(FileReader file, ModLoadingFlags loadFlags)
|
|||
for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++)
|
||||
{
|
||||
ModSample &sample = Samples[nSmp];
|
||||
LimitMax(sample.nLength, MAX_SAMPLE_LENGTH);
|
||||
sample.SanitizeLoops();
|
||||
|
||||
#ifdef MPT_EXTERNAL_SAMPLES
|
||||
if(SampleHasPath(nSmp))
|
||||
|
@ -662,6 +664,8 @@ bool CSoundFile::CreateInternal(FileReader file, ModLoadingFlags loadFlags)
|
|||
LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT);
|
||||
LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT);
|
||||
LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
|
||||
LimitMax(m_nSamplePreAmp, MAX_PREAMP);
|
||||
LimitMax(m_nVSTiVolume, MAX_PREAMP);
|
||||
if(!m_tempoSwing.empty())
|
||||
m_tempoSwing.resize(m_nDefaultRowsPerBeat);
|
||||
|
||||
|
|
|
@ -1840,8 +1840,15 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int32 &period, Tuning
|
|||
vdelta += Util::muldiv(period, fineUpTable[l & 0x03], 0x10000) - period;
|
||||
}
|
||||
}
|
||||
period = (period + vdelta) / 256;
|
||||
nPeriodFrac = vdelta & 0xFF;
|
||||
if(Util::MaxValueOfType(period) - period >= vdelta)
|
||||
{
|
||||
period = (period + vdelta) / 256;
|
||||
nPeriodFrac = vdelta & 0xFF;
|
||||
} else
|
||||
{
|
||||
period = Util::MaxValueOfType(period) / 256;
|
||||
nPeriodFrac = 0;
|
||||
}
|
||||
} else
|
||||
{
|
||||
// MPT's autovibrato code
|
||||
|
|
|
@ -1346,7 +1346,7 @@ bool ModCommand::CombineEffects(EffectCommand &eff1, uint8 ¶m1, EffectComman
|
|||
}
|
||||
|
||||
|
||||
std::pair<EffectCommand, ModCommand::PARAM> ModCommand::FillInTwoCommands(EffectCommand effect1, uint8 param1, EffectCommand effect2, uint8 param2)
|
||||
std::pair<EffectCommand, ModCommand::PARAM> ModCommand::FillInTwoCommands(EffectCommand effect1, uint8 param1, EffectCommand effect2, uint8 param2, bool allowLowResOffset)
|
||||
{
|
||||
if(effect1 == effect2)
|
||||
{
|
||||
|
@ -1401,6 +1401,12 @@ std::pair<EffectCommand, ModCommand::PARAM> ModCommand::FillInTwoCommands(Effect
|
|||
std::swap(effect1, effect2);
|
||||
std::swap(param1, param2);
|
||||
}
|
||||
if(effect2 == CMD_OFFSET && (allowLowResOffset || param2 == 0))
|
||||
{
|
||||
SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(param2 ? std::max(param2 * 9 / 255, 1) : 0));
|
||||
SetEffectCommand(effect1, param1);
|
||||
return {CMD_NONE, ModCommand::PARAM(0)};
|
||||
}
|
||||
SetVolumeCommand(VOLCMD_NONE, 0);
|
||||
SetEffectCommand(effect2, param2);
|
||||
return {effect1, param1};
|
||||
|
|
|
@ -230,7 +230,7 @@ public:
|
|||
// Try to convert a an effect into a volume column effect. Returns converted effect on success.
|
||||
[[nodiscard]] static std::pair<VolumeCommand, VOL> ConvertToVolCommand(const EffectCommand effect, PARAM param, bool force);
|
||||
// Takes two "normal" effect commands and converts them to volume column + effect column commands. Returns the dropped command + param (CMD_NONE if nothing had to be dropped).
|
||||
std::pair<EffectCommand, PARAM> FillInTwoCommands(EffectCommand effect1, uint8 param1, EffectCommand effect2, uint8 param2);
|
||||
std::pair<EffectCommand, PARAM> FillInTwoCommands(EffectCommand effect1, uint8 param1, EffectCommand effect2, uint8 param2, bool allowLowResOffset = false);
|
||||
// Try to combine two commands into one. Returns true on success and the combined command is placed in eff1 / param1.
|
||||
static bool CombineEffects(EffectCommand &eff1, uint8 ¶m1, EffectCommand &eff2, uint8 ¶m2);
|
||||
|
||||
|
|
|
@ -505,7 +505,7 @@ void I3DL2Reverb::RecalculateI3DL2ReverbParams()
|
|||
m_roomFilter = 0.0f;
|
||||
} else
|
||||
{
|
||||
float freq = std::cos(HFReference() * (2.0f * mpt::numbers::pi_v<float>) / m_effectiveSampleRate);
|
||||
float freq = std::min(std::cos(HFReference() * (2.0f * mpt::numbers::pi_v<float>) / m_effectiveSampleRate), 0.9999f);
|
||||
float roomFilter = (freq * (roomHF + roomHF) - 2.0f + std::sqrt(freq * (roomHF * roomHF * freq * 4.0f) + roomHF * 8.0f - roomHF * roomHF * 4.0f - roomHF * freq * 8.0f)) / (roomHF + roomHF - 2.0f);
|
||||
m_roomFilter = Clamp(roomFilter, 0.0f, 1.0f);
|
||||
}
|
||||
|
|
|
@ -224,7 +224,8 @@ private:
|
|||
|
||||
static bool IsValidRatio(RATIOTYPE ratio)
|
||||
{
|
||||
return (ratio > static_cast<RATIOTYPE>(0.0));
|
||||
// Arbitrary epsilon > 0 to avoid NaNs and infinite values in ratio calculation
|
||||
return (ratio > static_cast<RATIOTYPE>(0.02f));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -50,7 +50,9 @@
|
|||
#elif defined(_MSC_VER)
|
||||
|
||||
#define MPT_COMPILER_MSVC 1
|
||||
#if (_MSC_VER >= 1941)
|
||||
#if (_MSC_VER >= 1942)
|
||||
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 12)
|
||||
#elif (_MSC_VER >= 1941)
|
||||
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 11)
|
||||
#elif (_MSC_VER >= 1940)
|
||||
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 10)
|
||||
|
|
|
@ -46,18 +46,17 @@ public:
|
|||
inline WriteBuffer(Tfile & f_, mpt::byte_span buffer_)
|
||||
: buffer(buffer_)
|
||||
, f(f_) {
|
||||
return;
|
||||
}
|
||||
inline ~WriteBuffer() noexcept(false) {
|
||||
if (!writeError)
|
||||
{
|
||||
if (!writeError) {
|
||||
FlushLocal();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
inline Tfile & file() const {
|
||||
if (IsDirty())
|
||||
{
|
||||
if (IsDirty()) {
|
||||
FlushLocal();
|
||||
}
|
||||
return f;
|
||||
|
@ -84,30 +83,24 @@ public:
|
|||
}
|
||||
inline bool Write(mpt::const_byte_span data) {
|
||||
bool result = true;
|
||||
for (std::size_t i = 0; i < data.size(); ++i)
|
||||
{
|
||||
for (std::size_t i = 0; i < data.size(); ++i) {
|
||||
buffer[size] = data[i];
|
||||
size++;
|
||||
if (IsFull())
|
||||
{
|
||||
if (IsFull()) {
|
||||
FlushLocal();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
inline void FlushLocal() {
|
||||
if (IsClean())
|
||||
{
|
||||
if (IsClean()) {
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!mpt::IO::WriteRaw(f, mpt::as_span(buffer.data(), size)))
|
||||
{
|
||||
try {
|
||||
if (!mpt::IO::WriteRaw(f, mpt::as_span(buffer.data(), size))) {
|
||||
writeError = true;
|
||||
}
|
||||
} catch (const std::exception &)
|
||||
{
|
||||
} catch (const std::exception &) {
|
||||
writeError = true;
|
||||
throw;
|
||||
}
|
||||
|
|
|
@ -2405,6 +2405,8 @@ static MPT_NOINLINE void TestCharsets()
|
|||
VERIFY_EQUAL(mpt::RelativePathToAbsolute(P_("\\foo"), exePath), P_("C:\\foo"));
|
||||
VERIFY_EQUAL(mpt::AbsolutePathToRelative(P_("\\\\server\\path\\file"), exePath), P_("\\\\server\\path\\file"));
|
||||
VERIFY_EQUAL(mpt::RelativePathToAbsolute(P_("\\\\server\\path\\file"), exePath), P_("\\\\server\\path\\file"));
|
||||
VERIFY_EQUAL(mpt::AbsolutePathToRelative(P_("C:\\OpenMPT"), mpt::PathString{}), P_("C:\\OpenMPT"));
|
||||
VERIFY_EQUAL(mpt::RelativePathToAbsolute(P_("C:\\OpenMPT"), mpt::PathString{}), P_("C:\\OpenMPT"));
|
||||
#endif
|
||||
|
||||
#ifdef MODPLUG_TRACKER
|
||||
|
|
Loading…
Reference in a new issue