diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/soundlib/ContainerXPK.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/soundlib/ContainerXPK.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/soundlib/ContainerXPK.cpp | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/soundlib/ContainerXPK.cpp b/Src/external_dependencies/openmpt-trunk/soundlib/ContainerXPK.cpp new file mode 100644 index 00000000..87b89408 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/soundlib/ContainerXPK.cpp @@ -0,0 +1,429 @@ +/* + * ContainerXPK.cpp + * ---------------- + * Purpose: Handling of XPK compressed modules + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "../common/FileReader.h" +#include "Container.h" +#include "Sndfile.h" + +#include <stdexcept> + + +OPENMPT_NAMESPACE_BEGIN + + +#if !defined(MPT_WITH_ANCIENT) + + +#ifdef MPT_ALL_LOGGING +#define MMCMP_LOG +#endif + + +struct XPKFILEHEADER +{ + char XPKF[4]; + uint32be SrcLen; + char SQSH[4]; + uint32be DstLen; + char Name[16]; + uint32be Reserved; +}; + +MPT_BINARY_STRUCT(XPKFILEHEADER, 36) + + +struct XPK_error : public std::range_error +{ + XPK_error() : std::range_error("invalid XPK data") { } +}; + +struct XPK_BufferBounds +{ + const uint8 *pSrcBeg; + std::size_t SrcSize; + + inline uint8 SrcRead(std::size_t index) + { + if(index >= SrcSize) throw XPK_error(); + return pSrcBeg[index]; + } +}; + +static int32 bfextu(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs) +{ + uint32 r; + + p += bo / 8; + r = bufs.SrcRead(p); p++; + r <<= 8; + r |= bufs.SrcRead(p); p++; + r <<= 8; + r |= bufs.SrcRead(p); + r <<= bo % 8; + r &= 0xffffff; + r >>= 24 - bc; + + return r; +} + +static int32 bfexts(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs) +{ + uint32 r; + + p += bo / 8; + r = bufs.SrcRead(p); p++; + r <<= 8; + r |= bufs.SrcRead(p); p++; + r <<= 8; + r |= bufs.SrcRead(p); + r <<= (bo % 8) + 8; + return mpt::rshift_signed(static_cast<int32>(r), 32 - bc); +} + + +static uint8 XPK_ReadTable(int32 index) +{ + static constexpr uint8 xpk_table[] = { + 2,3,4,5,6,7,8,0,3,2,4,5,6,7,8,0,4,3,5,2,6,7,8,0,5,4,6,2,3,7,8,0,6,5,7,2,3,4,8,0,7,6,8,2,3,4,5,0,8,7,6,2,3,4,5,0 + }; + if(index < 0) throw XPK_error(); + if(static_cast<std::size_t>(index) >= std::size(xpk_table)) throw XPK_error(); + return xpk_table[index]; +} + +static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector<char> &unpackedData, int32 len) +{ + if(len <= 0) return false; + int32 d0,d1,d2,d3,d4,d5,d6,a2,a5; + int32 cp, cup1, type; + std::size_t c; + std::size_t src; + std::size_t phist = 0; + + unpackedData.reserve(std::min(static_cast<uint32>(len), std::min(srcLen, uint32_max / 20u) * 20u)); + + XPK_BufferBounds bufs; + bufs.pSrcBeg = src_; + bufs.SrcSize = srcLen; + + src = 0; + c = src; + while(len > 0) + { + type = bufs.SrcRead(c+0); + cp = (bufs.SrcRead(c+4)<<8) | (bufs.SrcRead(c+5)); // packed + cup1 = (bufs.SrcRead(c+6)<<8) | (bufs.SrcRead(c+7)); // unpacked + //Log(" packed=%6d unpacked=%6d bytes left=%d dst=%08X(%d)\n", cp, cup1, len, dst, dst); + c += 8; + src = c+2; + if (type == 0) + { + // RAW chunk + if(cp < 0 || cp > len) throw XPK_error(); + for(int32 i = 0; i < cp; ++i) + { + unpackedData.push_back(bufs.SrcRead(c + i)); + } + c+=cp; + len -= cp; + continue; + } + + if (type != 1) + { + #ifdef MMCMP_LOG + MPT_LOG_GLOBAL(LogDebug, "XPK", MPT_UFORMAT("Invalid XPK type! ({} bytes left)")(len)); + #endif + break; + } + LimitMax(cup1, len); + len -= cup1; + cp = (cp + 3) & 0xfffc; + c += cp; + + d0 = d1 = d2 = a2 = 0; + d3 = bufs.SrcRead(src); src++; + unpackedData.push_back(static_cast<char>(d3)); + cup1--; + + while (cup1 > 0) + { + if (d1 >= 8) goto l6dc; + if (bfextu(src,d0,1,bufs)) goto l75a; + d0 += 1; + d5 = 0; + d6 = 8; + goto l734; + + l6dc: + if (bfextu(src,d0,1,bufs)) goto l726; + d0 += 1; + if (! bfextu(src,d0,1,bufs)) goto l75a; + d0 += 1; + if (bfextu(src,d0,1,bufs)) goto l6f6; + d6 = 2; + goto l708; + + l6f6: + d0 += 1; + if (!bfextu(src,d0,1,bufs)) goto l706; + d6 = bfextu(src,d0,3,bufs); + d0 += 3; + goto l70a; + + l706: + d6 = 3; + l708: + d0 += 1; + l70a: + d6 = XPK_ReadTable((8*a2) + d6 -17); + if (d6 != 8) goto l730; + l718: + if (d2 >= 20) + { + d5 = 1; + goto l732; + } + d5 = 0; + goto l734; + + l726: + d0 += 1; + d6 = 8; + if (d6 == a2) goto l718; + d6 = a2; + l730: + d5 = 4; + l732: + d2 += 8; + l734: + while ((d5 >= 0) && (cup1 > 0)) + { + d4 = bfexts(src,d0,d6,bufs); + d0 += d6; + d3 -= d4; + unpackedData.push_back(static_cast<char>(d3)); + cup1--; + d5--; + } + if (d1 != 31) d1++; + a2 = d6; + l74c: + d6 = d2; + d6 >>= 3; + d2 -= d6; + } + } + return !unpackedData.empty(); + +l75a: + d0 += 1; + if (bfextu(src,d0,1,bufs)) goto l766; + d4 = 2; + goto l79e; + +l766: + d0 += 1; + if (bfextu(src,d0,1,bufs)) goto l772; + d4 = 4; + goto l79e; + +l772: + d0 += 1; + if (bfextu(src,d0,1,bufs)) goto l77e; + d4 = 6; + goto l79e; + +l77e: + d0 += 1; + if (bfextu(src,d0,1,bufs)) goto l792; + d0 += 1; + d6 = bfextu(src,d0,3,bufs); + d0 += 3; + d6 += 8; + goto l7a8; + +l792: + d0 += 1; + d6 = bfextu(src,d0,5,bufs); + d0 += 5; + d4 = 16; + goto l7a6; + +l79e: + d0 += 1; + d6 = bfextu(src,d0,1,bufs); + d0 += 1; +l7a6: + d6 += d4; +l7a8: + if(bfextu(src, d0, 1, bufs)) + { + d5 = 12; + a5 = -0x100; + } else + { + d0 += 1; + if(bfextu(src, d0, 1, bufs)) + { + d5 = 14; + a5 = -0x1100; + } else + { + d5 = 8; + a5 = 0; + } + } + + d0 += 1; + d4 = bfextu(src,d0,d5,bufs); + d0 += d5; + d6 -= 3; + if (d6 >= 0) + { + if (d6 > 0) d1 -= 1; + d1 -= 1; + if (d1 < 0) d1 = 0; + } + d6 += 2; + phist = unpackedData.size() + a5 - d4 - 1; + if(phist >= unpackedData.size()) + throw XPK_error(); + + while ((d6 >= 0) && (cup1 > 0)) + { + d3 = unpackedData[phist]; + phist++; + unpackedData.push_back(static_cast<char>(d3)); + cup1--; + d6--; + } + goto l74c; +} + + +static bool ValidateHeader(const XPKFILEHEADER &header) +{ + if(std::memcmp(header.XPKF, "XPKF", 4) != 0) + { + return false; + } + if(std::memcmp(header.SQSH, "SQSH", 4) != 0) + { + return false; + } + if(header.SrcLen == 0) + { + return false; + } + if(header.DstLen == 0) + { + return false; + } + static_assert(sizeof(XPKFILEHEADER) >= 8); + if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) + { + return false; + } + return true; +} + + +static bool ValidateHeaderFileSize(const XPKFILEHEADER &header, uint64 filesize) +{ + if(filesize < header.SrcLen - 8) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize) +{ + XPKFILEHEADER header; + if(!file.ReadStruct(header)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(header)) + { + return ProbeFailure; + } + if(pfilesize) + { + if(!ValidateHeaderFileSize(header, *pfilesize)) + { + return ProbeFailure; + } + } + return ProbeSuccess; +} + + +bool UnpackXPK(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) +{ + file.Rewind(); + containerItems.clear(); + + XPKFILEHEADER header; + if(!file.ReadStruct(header)) + { + return false; + } + if(!ValidateHeader(header)) + { + return false; + } + if(loadFlags == ContainerOnlyVerifyHeader) + { + return true; + } + + if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) + { + return false; + } + + containerItems.emplace_back(); + containerItems.back().data_cache = std::make_unique<std::vector<char> >(); + std::vector<char> & unpackedData = *(containerItems.back().data_cache); + + #ifdef MMCMP_LOG + MPT_LOG_GLOBAL(LogDebug, "XPK", MPT_UFORMAT("XPK detected (SrcLen={} DstLen={}) filesize={}")(static_cast<uint32>(header.SrcLen), static_cast<uint32>(header.DstLen), file.GetLength())); + #endif + bool result = false; + try + { + result = XPK_DoUnpack(file.GetRawData<uint8>().data(), header.SrcLen - (sizeof(XPKFILEHEADER) - 8), unpackedData, header.DstLen); + } catch(mpt::out_of_memory e) + { + mpt::delete_out_of_memory(e); + return false; + } catch(const XPK_error &) + { + return false; + } + + if(result) + { + containerItems.back().file = FileReader(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(unpackedData))); + } + return result; +} + + +#endif // !MPT_WITH_ANCIENT + + +OPENMPT_NAMESPACE_END |