From: <sv...@op...> - 2024-05-11 21:37:30
|
Author: sagamusix Date: Sat May 11 23:37:18 2024 New Revision: 20782 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=20782 Log: Merged revision(s) 20780 from branches/OpenMPT-1.31: Merged revision(s) 20736 from trunk/OpenMPT: [Imp] STK: Allow romantic.stk to load, which has a sample longer than 74KB. [Imp] STK: Also allow files to load that have garbage in the song title, but whose sample names are properly null-terminated non-empty ASCII strings. Fixes STK.Sharks-NewIntro. ........ ........ Modified: branches/OpenMPT-1.29/ (props changed) branches/OpenMPT-1.29/soundlib/Load_mod.cpp Modified: branches/OpenMPT-1.29/soundlib/Load_mod.cpp ============================================================================== --- branches/OpenMPT-1.29/soundlib/Load_mod.cpp Sat May 11 23:36:49 2024 (r20781) +++ branches/OpenMPT-1.29/soundlib/Load_mod.cpp Sat May 11 23:37:18 2024 (r20782) @@ -1348,6 +1348,34 @@ } +enum class NameClassification +{ + Empty, + ValidASCII, + Invalid, +}; + +// Check if a name is a valid null-terminated ASCII string with no garbage after the null terminator, or if it's empty +static NameClassification ClassifyName(const mpt::span<const char> name) noexcept +{ + bool foundNull = false, foundNormal = false; + for(auto c : name) + { + if(c > 0 && c < ' ') + return NameClassification::Invalid; + if(c == 0) + foundNull = true; + else if(foundNull) + return NameClassification::Invalid; + else + foundNormal = true; + } + if(!foundNull) + return NameClassification::Invalid; + return foundNormal ? NameClassification::ValidASCII : NameClassification::Empty; +} + + // We'll have to do some heuristic checks to find out whether this is an old Ultimate Soundtracker module // or if it was made with the newer Soundtracker versions. // Thanks for Fraggie for this information! (https://www.un4seen.com/forum/?topic=14471.msg100829#msg100829) @@ -1386,7 +1414,8 @@ SmpLength totalSampleLen = 0; uint8 allVolumes = 0; - uint8 diskNameCount = 0; + uint8 validNameCount = 0; + bool invalidNames = false; for(SAMPLEINDEX smp = 0; smp < 15; smp++) { @@ -1397,13 +1426,16 @@ // schmokk.mod has a non-zero value here but it should not be treated as finetune if(sampleHeader.finetune != 0) invalidChars += 16; - if(sampleHeader.HasDiskName()) - diskNameCount++; + if(const auto nameType = ClassifyName(sampleHeader.name); nameType == NameClassification::ValidASCII) + validNameCount++; + else if(nameType == NameClassification::Invalid) + invalidNames = true; // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) + // Sample length adjusted for romantic.stk which has a (valid) sample of length 72222 if(invalidChars > 48 || sampleHeader.volume > 64 - || sampleHeader.length > 32768) + || sampleHeader.length > 37000) { return false; } @@ -1412,8 +1444,8 @@ allVolumes |= sampleHeader.volume; } - // scramble_2.mod has a lot of garbage in the song title, but it has lots of sample names starting with st-01, so we consider those to be more important than the garbage bytes. - if(invalidCharsInTitle > 5 && diskNameCount < 4) + // scramble_2.mod has a lot of garbage in the song title, but it has lots of properly-formatted sample names, so we consider those to be more important than the garbage bytes. + if(invalidCharsInTitle > 5 && (validNameCount < 4 || invalidNames)) { return false; } |