...but discard the extra channels and samples! Also did some other fixes.
Showing the changes to src/ft2_module_loader.c (1 of 1 file). Show all
Unified · Split
| @@ -136,17 +136,18 @@ static SDL_Thread *thread; · 135 unmodified lines | |||
| 136 | 136 | ||
| 137 | // these temporarily read to, then copied to real struct if load was OK (should not need to be volatile'd) | 137 | // these temporarily read to, then copied to real struct if load was OK (should not need to be volatile'd) |
| 138 | static int16_t pattLensTmp[MAX_PATTERNS]; | 138 | static int16_t pattLensTmp[MAX_PATTERNS]; |
| 139 | static uint32_t extraSampleLengths[32-MAX_SMP_PER_INST]; // ModPlug Tracker & OpenMPT supports up to 32 samples per instrument in .XM | ||
| 139 | static tonTyp *pattTmp[MAX_PATTERNS]; | 140 | static tonTyp *pattTmp[MAX_PATTERNS]; |
| 140 | static instrTyp *instrTmp[1 + MAX_INST]; | 141 | static instrTyp *instrTmp[1+MAX_INST]; |
| 141 | static songTyp songTmp; | 142 | static songTyp songTmp; |
| 142 | 143 | ||
| 143 | static void setupLoadedModule(void); | 144 | static void setupLoadedModule(void); |
| 144 | static void freeTmpModule(void); | 145 | static void freeTmpModule(void); |
| 145 | static bool loadInstrHeader(FILE *f, uint16_t i); | 146 | static bool loadInstrHeader(FILE *f, uint16_t i, bool externalThreadFlag); |
| 146 | static bool loadInstrSample(FILE *f, uint16_t i); | 147 | static bool loadInstrSample(FILE *f, uint16_t i, bool externalThreadFlag); |
| 147 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn); | 148 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn); |
| 148 | static bool tmpPatternEmpty(uint16_t nr); | 149 | static bool tmpPatternEmpty(uint16_t nr); |
| 149 | static bool loadPatterns(FILE *f, uint16_t antPtn); | 150 | static bool loadPatterns(FILE *f, uint16_t antPtn, bool externalThreadFlag); |
| 150 | 151 | ||
| 151 | void checkSampleRepeat(sampleTyp *s); | 152 | void checkSampleRepeat(sampleTyp *s); |
| 152 | 153 | ||
| @@ -160,13 +161,11 @@ static bool allocateTmpInstr(int16_t nr) · 7 unmodified lines | |||
| 160 | if (instrTmp[nr] != NULL) | 161 | if (instrTmp[nr] != NULL) |
| 161 | return false; // already allocated | 162 | return false; // already allocated |
| 162 | 163 | ||
| 163 | p = (instrTyp *)malloc(sizeof (instrTyp)); | 164 | p = (instrTyp *)calloc(1, sizeof (instrTyp)); |
| 164 | if (p == NULL) | 165 | if (p == NULL) |
| 165 | return false; | 166 | return false; |
| 166 | 167 | ||
| 167 | memset(p, 0, sizeof (instrTyp)); | 168 | for (int8_t i = 0; i < MAX_SMP_PER_INST; i++) // set standard sample pan/vol |
| 168 | |||
| 169 | for (int8_t i = 0; i < 16; i++) // set standard sample pan/vol | ||
| 170 | { | 169 | { |
| 171 | p->samp[i].pan = 128; | 170 | p->samp[i].pan = 128; |
| 172 | p->samp[i].vol = 64; | 171 | p->samp[i].vol = 64; |
| @@ -213,7 +212,7 @@ static uint8_t getModType(uint8_t *numChannels, const char *id) · 40 unmodified lines | |||
| 213 | return modFormat; | 212 | return modFormat; |
| 214 | } | 213 | } |
| 215 | 214 | ||
| 216 | static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread) | 215 | static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool externalThreadFlag) |
| 217 | { | 216 | { |
| 218 | char ID[16]; | 217 | char ID[16]; |
| 219 | bool mightBeSTK, lateSTKVerFlag, veryLateSTKVerFlag; | 218 | bool mightBeSTK, lateSTKVerFlag, veryLateSTKVerFlag; |
| @@ -226,7 +225,7 @@ static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread) · 6 unmodified lines | |||
| 226 | songMOD15HeaderTyp h_MOD15; | 225 | songMOD15HeaderTyp h_MOD15; |
| 227 | int16_t (*showMsg)(int16_t, const char *, const char *); | 226 | int16_t (*showMsg)(int16_t, const char *, const char *); |
| 228 | 227 | ||
| 229 | showMsg = fromExternalThread ? okBoxThreadSafe : okBox; | 228 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; |
| 230 | 229 | ||
| 231 | veryLateSTKVerFlag = false; // "DFJ SoundTracker III" nad later | 230 | veryLateSTKVerFlag = false; // "DFJ SoundTracker III" nad later |
| 232 | lateSTKVerFlag = false; // "TJC SoundTracker II" and later | 231 | lateSTKVerFlag = false; // "TJC SoundTracker II" and later |
| @@ -318,7 +317,7 @@ static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread) · 85 unmodified lines | |||
| 318 | } | 317 | } |
| 319 | 318 | ||
| 320 | if (modFormat == FORMAT_MK && songTmp.len == 129) | 319 | if (modFormat == FORMAT_MK && songTmp.len == 129) |
| 321 | songTmp.len = 127; // fixes a specific copy of beatwave.mod | 320 | songTmp.len = 127; // fixes a specific copy of beatwave.mod (FIXME: Do we want to keep this digsuting hack?) |
| 322 | 321 | ||
| 323 | if (songTmp.antChn == 0 || songTmp.len < 1 || songTmp.len > 128 || (mightBeSTK && songTmp.repS > 220)) | 322 | if (songTmp.antChn == 0 || songTmp.len < 1 || songTmp.len > 128 || (mightBeSTK && songTmp.repS > 220)) |
| 324 | { | 323 | { |
| @@ -735,7 +734,7 @@ static uint8_t stmTempoToBPM(uint8_t tempo) // ported from original ST2.3 replay · 410 unmodified lines | |||
| 735 | return (uint8_t)CLAMP(bpm, 32, 255); // result can be slightly off, but close enough... | 734 | return (uint8_t)CLAMP(bpm, 32, 255); // result can be slightly off, but close enough... |
| 736 | } | 735 | } |
| 737 | 736 | ||
| 738 | static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool fromExternalThread) | 737 | static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool externalThreadFlag) |
| 739 | { | 738 | { |
| 740 | uint8_t typ, tempo; | 739 | uint8_t typ, tempo; |
| 741 | int16_t i, j, k, ap, tmp; | 740 | int16_t i, j, k, ap, tmp; |
| @@ -745,19 +744,19 @@ static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool fromExternalThread) · 3 unmodified lines | |||
| 745 | songSTMHeaderTyp h_STM; | 744 | songSTMHeaderTyp h_STM; |
| 746 | int16_t (*showMsg)(int16_t, const char *, const char *); | 745 | int16_t (*showMsg)(int16_t, const char *, const char *); |
| 747 | 746 | ||
| 748 | showMsg = fromExternalThread ? okBoxThreadSafe : okBox; | 747 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; |
| 749 | 748 | ||
| 750 | rewind(f); | 749 | rewind(f); |
| 751 | 750 | ||
| 752 | // start loading STM | 751 | // start loading STM |
| 753 | 752 | ||
| 754 | if (fread(&h_STM, 1, sizeof (h_STM), f) != sizeof (h_STM)) | 753 | if (fread(&h_STM, 1, sizeof (h_STM), f) != sizeof (h_STM)) |
| 755 | return loadMusicMOD(f, fileLength, fromExternalThread); // file is not a .stm, try to load as .mod | 754 | return loadMusicMOD(f, fileLength, externalThreadFlag); // file is not a .stm, try to load as .mod |
| 756 | 755 | ||
| 757 | if (memcmp(h_STM.sig, "!Scream!", 8) && memcmp(h_STM.sig, "BMOD2STM", 8) && | 756 | if (memcmp(h_STM.sig, "!Scream!", 8) && memcmp(h_STM.sig, "BMOD2STM", 8) && |
| 758 | memcmp(h_STM.sig, "WUZAMOD!", 8) && memcmp(h_STM.sig, "SWavePro", 8)) | 757 | memcmp(h_STM.sig, "WUZAMOD!", 8) && memcmp(h_STM.sig, "SWavePro", 8)) |
| 759 | { | 758 | { |
| 760 | return loadMusicMOD(f, fileLength, fromExternalThread); // file is not a .stm, try to load as .mod | 759 | return loadMusicMOD(f, fileLength, externalThreadFlag); // file is not a .stm, try to load as .mod |
| 761 | } | 760 | } |
| 762 | 761 | ||
| 763 | loadedFormat = FORMAT_STM; | 762 | loadedFormat = FORMAT_STM; |
| @@ -1007,7 +1006,7 @@ static int8_t countS3MChannels(uint16_t antPtn) · 243 unmodified lines | |||
| 1007 | return channels; | 1006 | return channels; |
| 1008 | } | 1007 | } |
| 1009 | 1008 | ||
| 1010 | static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool fromExternalThread) | 1009 | static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool externalThreadFlag) |
| 1011 | { | 1010 | { |
| 1012 | int8_t *tmpSmp; | 1011 | int8_t *tmpSmp; |
| 1013 | uint8_t ha[2048]; | 1012 | uint8_t ha[2048]; |
| @@ -1022,17 +1021,17 @@ static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool fromExternalThread) · 8 unmodified lines | |||
| 1022 | songS3MinstrHeaderTyp h_S3MInstr; | 1021 | songS3MinstrHeaderTyp h_S3MInstr; |
| 1023 | int16_t (*showMsg)(int16_t, const char *, const char *); | 1022 | int16_t (*showMsg)(int16_t, const char *, const char *); |
| 1024 | 1023 | ||
| 1025 | showMsg = fromExternalThread ? okBoxThreadSafe : okBox; | 1024 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; |
| 1026 | 1025 | ||
| 1027 | rewind(f); | 1026 | rewind(f); |
| 1028 | 1027 | ||
| 1029 | // start loading S3M | 1028 | // start loading S3M |
| 1030 | 1029 | ||
| 1031 | if (fread(&h_S3M, 1, sizeof (h_S3M), f) != sizeof (h_S3M)) | 1030 | if (fread(&h_S3M, 1, sizeof (h_S3M), f) != sizeof (h_S3M)) |
| 1032 | return loadMusicSTM(f, dataLength, fromExternalThread); // not a .s3m, try loading as .stm | 1031 | return loadMusicSTM(f, dataLength, externalThreadFlag); // not a .s3m, try loading as .stm |
| 1033 | 1032 | ||
| 1034 | if (memcmp(h_S3M.id, "SCRM", 4)) | 1033 | if (memcmp(h_S3M.id, "SCRM", 4)) |
| 1035 | return loadMusicSTM(f, dataLength, fromExternalThread); // not a .s3m, try loading as .stm | 1034 | return loadMusicSTM(f, dataLength, externalThreadFlag); // not a .s3m, try loading as .stm |
| 1036 | 1035 | ||
| 1037 | loadedFormat = FORMAT_S3M; | 1036 | loadedFormat = FORMAT_S3M; |
| 1038 | 1037 | ||
| @@ -1651,7 +1650,7 @@ s3mLoadError: · 612 unmodified lines | |||
| 1651 | return false; | 1650 | return false; |
| 1652 | } | 1651 | } |
| 1653 | 1652 | ||
| 1654 | bool doLoadMusic(bool fromExternalThread) | 1653 | bool doLoadMusic(bool externalThreadFlag) |
| 1655 | { | 1654 | { |
| 1656 | char tmpText[128]; | 1655 | char tmpText[128]; |
| 1657 | int16_t k; | 1656 | int16_t k; |
| @@ -1661,7 +1660,7 @@ bool doLoadMusic(bool fromExternalThread) · 3 unmodified lines | |||
| 1661 | FILE *f; | 1660 | FILE *f; |
| 1662 | int16_t (*showMsg)(int16_t, const char *, const char *); | 1661 | int16_t (*showMsg)(int16_t, const char *, const char *); |
| 1663 | 1662 | ||
| 1664 | showMsg = fromExternalThread ? okBoxThreadSafe : okBox; | 1663 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; |
| 1665 | 1664 | ||
| 1666 | linearFreqTable = false; | 1665 | linearFreqTable = false; |
| 1667 | 1666 | ||
| @@ -1686,10 +1685,10 @@ bool doLoadMusic(bool fromExternalThread) · 18 unmodified lines | |||
| 1686 | 1685 | ||
| 1687 | // start loading | 1686 | // start loading |
| 1688 | if (fread(&h, 1, sizeof (h), f) != sizeof (h)) | 1687 | if (fread(&h, 1, sizeof (h), f) != sizeof (h)) |
| 1689 | return loadMusicS3M(f, filelength, fromExternalThread); // not a .xm file, try to load as .s3m | 1688 | return loadMusicS3M(f, filelength, externalThreadFlag); // not a .xm file, try to load as .s3m |
| 1690 | 1689 | ||
| 1691 | if (memcmp(h.sig, "Extended Module: ", 17)) | 1690 | if (memcmp(h.sig, "Extended Module: ", 17)) |
| 1692 | return loadMusicS3M(f, filelength, fromExternalThread); // not a .xm file, try to load as .s3m | 1691 | return loadMusicS3M(f, filelength, externalThreadFlag); // not a .xm file, try to load as .s3m |
| 1693 | 1692 | ||
| 1694 | loadedFormat = FORMAT_XM; | 1693 | loadedFormat = FORMAT_XM; |
| 1695 | 1694 | ||
| @@ -1700,7 +1699,7 @@ bool doLoadMusic(bool fromExternalThread) · 4 unmodified lines | |||
| 1700 | const int32_t major = (h.ver >> 8) & 0x0F; | 1699 | const int32_t major = (h.ver >> 8) & 0x0F; |
| 1701 | const int32_t minor = h.ver & 0xFF; | 1700 | const int32_t minor = h.ver & 0xFF; |
| 1702 | 1701 | ||
| 1703 | sprintf(tmpText, "Error loading .xm: Unsupported file version (v%01X.%02X)", major, minor); | 1702 | sprintf(tmpText, "Error loading XM: Unsupported file version (v%01X.%02X).", major, minor); |
| 1704 | showMsg(0, "System message", tmpText); | 1703 | showMsg(0, "System message", tmpText); |
| 1705 | 1704 | ||
| 1706 | moduleFailedToLoad = true; | 1705 | moduleFailedToLoad = true; |
| @@ -1709,29 +1708,26 @@ bool doLoadMusic(bool fromExternalThread) · 2 unmodified lines | |||
| 1709 | 1708 | ||
| 1710 | if (h.len > MAX_ORDERS) | 1709 | if (h.len > MAX_ORDERS) |
| 1711 | { | 1710 | { |
| 1712 | showMsg(0, "System message", "Error loading .xm: The song has more than 256 orders!"); | 1711 | showMsg(0, "System message", "Error loading XM: The song has more than 256 orders!"); |
| 1713 | goto xmLoadError; | 1712 | goto xmLoadError; |
| 1714 | } | 1713 | } |
| 1715 | 1714 | ||
| 1716 | if (h.antPtn > MAX_PATTERNS) | 1715 | if (h.antPtn > MAX_PATTERNS) |
| 1717 | { | 1716 | { |
| 1718 | showMsg(0, "System message", "Error loading .xm: The song has more than 256 patterns!"); | 1717 | showMsg(0, "System message", "Error loading XM: The song has more than 256 patterns!"); |
| 1719 | goto xmLoadError; | 1718 | goto xmLoadError; |
| 1720 | } | 1719 | } |
| 1721 | 1720 | ||
| 1722 | if (h.antChn == 0 || h.antChn > MAX_VOICES) | 1721 | if (h.antChn == 0) |
| 1723 | { | 1722 | { |
| 1724 | showMsg(0, "System message", "Error loading .xm: Incompatible amount of channels!"); | 1723 | showMsg(0, "System message", "Error loading XM: This file is corrupt."); |
| 1725 | goto xmLoadError; | 1724 | goto xmLoadError; |
| 1726 | } | 1725 | } |
| 1727 | 1726 | ||
| 1728 | if (h.antInstrs > MAX_INST) | ||
| 1729 | showMsg(0, "System message", "This module has over 128 instruments! Only the first 128 will be loaded."); | ||
| 1730 | |||
| 1731 | fseek(f, 60 + h.headerSize, SEEK_SET); | 1727 | fseek(f, 60 + h.headerSize, SEEK_SET); |
| 1732 | if (filelength != 336 && feof(f)) // 336 in length at this point = empty XM | 1728 | if (filelength != 336 && feof(f)) // 336 in length at this point = empty XM |
| 1733 | { | 1729 | { |
| 1734 | showMsg(0, "System message", "Error loading .xm: The module is empty!"); | 1730 | showMsg(0, "System message", "Error loading XM: The module is empty!"); |
| 1735 | goto xmLoadError; | 1731 | goto xmLoadError; |
| 1736 | } | 1732 | } |
| 1737 | 1733 | ||
| @@ -1768,62 +1764,81 @@ bool doLoadMusic(bool fromExternalThread) · 30 unmodified lines | |||
| 1768 | else | 1764 | else |
| 1769 | memcpy(songTmp.songTab, h.songTab, songTmp.len); | 1765 | memcpy(songTmp.songTab, h.songTab, songTmp.len); |
| 1770 | 1766 | ||
| 1767 | // some strange XMs have the order list padded with 0xFF, remove them! | ||
| 1768 | for (int16_t j = 255; j >= 0; j--) | ||
| 1769 | { | ||
| 1770 | if (songTmp.songTab[j] != 0xFF) | ||
| 1771 | break; | ||
| 1772 | |||
| 1773 | if (songTmp.len > j) | ||
| 1774 | songTmp.len = j; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | // even though XM supports 256 orders, FT2 supports only 255... | ||
| 1778 | if (songTmp.len > 0xFF) | ||
| 1779 | songTmp.len = 0xFF; | ||
| 1780 | |||
| 1771 | if (songTmp.ver < 0x0104) | 1781 | if (songTmp.ver < 0x0104) |
| 1772 | { | 1782 | { |
| 1773 | // old FT2 format | 1783 | // XM v1.02 and XM v1.03 |
| 1774 | 1784 | ||
| 1775 | for (i = 1; i <= h.antInstrs; i++) | 1785 | for (i = 1; i <= h.antInstrs; i++) |
| 1776 | { | 1786 | { |
| 1777 | if (!loadInstrHeader(f, i)) | 1787 | if (!loadInstrHeader(f, i, externalThreadFlag)) |
| 1778 | { | ||
| 1779 | showMsg(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!"); | ||
| 1780 | goto xmLoadError; | 1788 | goto xmLoadError; |
| 1781 | } | ||
| 1782 | } | 1789 | } |
| 1783 | 1790 | ||
| 1784 | if (!loadPatterns(f, h.antPtn)) | 1791 | if (!loadPatterns(f, h.antPtn, externalThreadFlag)) |
| 1785 | { | ||
| 1786 | // error message is shown inside loadPattern() | ||
| 1787 | goto xmLoadError; | 1792 | goto xmLoadError; |
| 1788 | } | ||
| 1789 | 1793 | ||
| 1790 | for (i = 1; i <= h.antInstrs; i++) | 1794 | for (i = 1; i <= h.antInstrs; i++) |
| 1791 | { | 1795 | { |
| 1792 | if (!loadInstrSample(f, i)) | 1796 | if (!loadInstrSample(f, i, externalThreadFlag)) |
| 1793 | { | ||
| 1794 | showMsg(0, "System message", "Not enough memory!"); | ||
| 1795 | goto xmLoadError; | 1797 | goto xmLoadError; |
| 1796 | } | ||
| 1797 | } | 1798 | } |
| 1798 | } | 1799 | } |
| 1799 | else | 1800 | else |
| 1800 | { | 1801 | { |
| 1801 | // current FT2 format | 1802 | // XM v1.04 (latest version) |
| 1802 | 1803 | ||
| 1803 | if (!loadPatterns(f, h.antPtn)) | 1804 | if (!loadPatterns(f, h.antPtn, externalThreadFlag)) |
| 1804 | { | ||
| 1805 | // error message is shown inside loadPattern() | ||
| 1806 | goto xmLoadError; | 1805 | goto xmLoadError; |
| 1807 | } | ||
| 1808 | 1806 | ||
| 1809 | for (i = 1; i <= h.antInstrs; i++) | 1807 | for (i = 1; i <= h.antInstrs; i++) |
| 1810 | { | 1808 | { |
| 1811 | if (!loadInstrHeader(f, i)) | 1809 | if (!loadInstrHeader(f, i, externalThreadFlag)) goto xmLoadError; |
| 1812 | { | 1810 | if (!loadInstrSample(f, i, externalThreadFlag)) goto xmLoadError; |
| 1813 | showMsg(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!"); | ||
| 1814 | goto xmLoadError; | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | if (!loadInstrSample(f, i)) | ||
| 1818 | { | ||
| 1819 | showMsg(0, "System message", "Not enough memory!"); | ||
| 1820 | goto xmLoadError; | ||
| 1821 | } | ||
| 1822 | } | 1811 | } |
| 1823 | } | 1812 | } |
| 1824 | 1813 | ||
| 1825 | fclose(f); | 1814 | fclose(f); |
| 1826 | 1815 | ||
| 1816 | /* We support loading XMs with up to 32 samples per instrument (ModPlug/OpenMPT), | ||
| 1817 | ** but only the first 16 will be loaded. Now make sure we set the number of samples | ||
| 1818 | ** back to max 16 in the headers before loading is done. | ||
| 1819 | */ | ||
| 1820 | bool instrHasMoreThan16Samples = false; | ||
| 1821 | for (i = 1; i <= MAX_INST; i++) | ||
| 1822 | { | ||
| 1823 | if (instrTmp[i] != NULL && instrTmp[i]->antSamp > MAX_SMP_PER_INST) | ||
| 1824 | { | ||
| 1825 | instrHasMoreThan16Samples = true; | ||
| 1826 | instrTmp[i]->antSamp = MAX_SMP_PER_INST; | ||
| 1827 | } | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | if (songTmp.antChn > MAX_VOICES) | ||
| 1831 | { | ||
| 1832 | songTmp.antChn = MAX_VOICES; | ||
| 1833 | showMsg(0, "System message", "Warning: This XM contains >32 channels. The extra channels will be discarded!"); | ||
| 1834 | } | ||
| 1835 | |||
| 1836 | if (h.antInstrs > MAX_INST) | ||
| 1837 | showMsg(0, "System message", "Warning: This XM contains >128 instruments. The extra instruments will be discarded!"); | ||
| 1838 | |||
| 1839 | if (instrHasMoreThan16Samples) | ||
| 1840 | showMsg(0, "System message", "Warning: This XM contains instrument(s) with >16 samples. The extra samples will be discarded!"); | ||
| 1841 | |||
| 1827 | moduleLoaded = true; | 1842 | moduleLoaded = true; |
| 1828 | return true; | 1843 | return true; |
| 1829 | 1844 | ||
| @@ -1908,9 +1923,9 @@ bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay) · 78 unmodified lines | |||
| 1908 | return false; | 1923 | return false; |
| 1909 | } | 1924 | } |
| 1910 | 1925 | ||
| 1911 | static void freeTmpModule(void) | 1926 | static void freeTmpModule(void) // called on module load error |
| 1912 | { | 1927 | { |
| 1913 | uint16_t i; | 1928 | int32_t i, j; |
| 1914 | 1929 | ||
| 1915 | // free all patterns | 1930 | // free all patterns |
| 1916 | for (i = 0; i < MAX_PATTERNS; i++) | 1931 | for (i = 0; i < MAX_PATTERNS; i++) |
| @@ -1927,10 +1942,10 @@ static void freeTmpModule(void) · 10 unmodified lines | |||
| 1927 | { | 1942 | { |
| 1928 | if (instrTmp[i] != NULL) | 1943 | if (instrTmp[i] != NULL) |
| 1929 | { | 1944 | { |
| 1930 | for (uint8_t j = 0; j < MAX_SMP_PER_INST; j++) | 1945 | for (j = 0; j < MAX_SMP_PER_INST; j++) |
| 1931 | { | 1946 | { |
| 1932 | if (instrTmp[i]->samp[j].pek != NULL) | 1947 | if (instrTmp[i]->samp[j].origPek != NULL) |
| 1933 | free(instrTmp[i]->samp[j].pek); | 1948 | free(instrTmp[i]->samp[j].origPek); |
| 1934 | } | 1949 | } |
| 1935 | 1950 | ||
| 1936 | free(instrTmp[i]); | 1951 | free(instrTmp[i]); |
| @@ -1939,7 +1954,7 @@ static void freeTmpModule(void) · 2 unmodified lines | |||
| 1939 | } | 1954 | } |
| 1940 | } | 1955 | } |
| 1941 | 1956 | ||
| 1942 | static bool loadInstrHeader(FILE *f, uint16_t i) | 1957 | static bool loadInstrHeader(FILE *f, uint16_t i, bool externalThreadFlag) |
| 1943 | { | 1958 | { |
| 1944 | int8_t k; | 1959 | int8_t k; |
| 1945 | uint8_t j; | 1960 | uint8_t j; |
| @@ -1948,24 +1963,38 @@ static bool loadInstrHeader(FILE *f, uint16_t i) · 2 unmodified lines | |||
| 1948 | instrTyp *ins; | 1963 | instrTyp *ins; |
| 1949 | sampleHeaderTyp *src; | 1964 | sampleHeaderTyp *src; |
| 1950 | sampleTyp *s; | 1965 | sampleTyp *s; |
| 1966 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 1951 | 1967 | ||
| 1952 | memset(&ih, 0, INSTR_HEADER_SIZE); | 1968 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; |
| 1953 | 1969 | ||
| 1970 | memset(extraSampleLengths, 0, sizeof (extraSampleLengths)); | ||
| 1971 | memset(&ih, 0, sizeof (ih)); | ||
| 1954 | fread(&ih.instrSize, 4, 1, f); | 1972 | fread(&ih.instrSize, 4, 1, f); |
| 1955 | 1973 | ||
| 1956 | readSize = ih.instrSize; | 1974 | readSize = ih.instrSize; |
| 1957 | if (readSize < 4 || readSize > INSTR_HEADER_SIZE) | 1975 | |
| 1976 | // yes, some XMs can have a header size of 0, and it usually means 263 bytes (INSTR_HEADER_SIZE) | ||
| 1977 | if (readSize == 0 || readSize > INSTR_HEADER_SIZE) | ||
| 1958 | readSize = INSTR_HEADER_SIZE; | 1978 | readSize = INSTR_HEADER_SIZE; |
| 1959 | 1979 | ||
| 1980 | if (readSize < 4) | ||
| 1981 | { | ||
| 1982 | showMsg(0, "System message", "Error loading XM: This file is corrupt (or not supported)!"); | ||
| 1983 | return false; | ||
| 1984 | } | ||
| 1985 | |||
| 1960 | // load instrument data into temp buffer | 1986 | // load instrument data into temp buffer |
| 1961 | fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize | 1987 | fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize |
| 1962 | 1988 | ||
| 1963 | // FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE | 1989 | // FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE |
| 1964 | if (ih.instrSize > INSTR_HEADER_SIZE) | 1990 | if (ih.instrSize > INSTR_HEADER_SIZE) |
| 1965 | fseek(f, ih.instrSize - INSTR_HEADER_SIZE, SEEK_CUR); | 1991 | fseek(f, ih.instrSize-INSTR_HEADER_SIZE, SEEK_CUR); |
| 1966 | 1992 | ||
| 1967 | if (ih.antSamp > MAX_SMP_PER_INST) | 1993 | if (ih.antSamp < 0 || ih.antSamp > 32) |
| 1994 | { | ||
| 1995 | showMsg(0, "System message", "Error loading XM: This file is corrupt (or not supported)!"); | ||
| 1968 | return false; | 1996 | return false; |
| 1997 | } | ||
| 1969 | 1998 | ||
| 1970 | if (i <= MAX_INST) | 1999 | if (i <= MAX_INST) |
| 1971 | { | 2000 | { |
| @@ -1987,7 +2016,10 @@ static bool loadInstrHeader(FILE *f, uint16_t i) · 15 unmodified lines | |||
| 1987 | if (i <= MAX_INST) | 2016 | if (i <= MAX_INST) |
| 1988 | { | 2017 | { |
| 1989 | if (!allocateTmpInstr(i)) | 2018 | if (!allocateTmpInstr(i)) |
| 2019 | { | ||
| 2020 | showMsg(0, "System message", "Not enough memory!"); | ||
| 1990 | return false; | 2021 | return false; |
| 2022 | } | ||
| 1991 | 2023 | ||
| 1992 | // copy instrument header elements to our instrument struct | 2024 | // copy instrument header elements to our instrument struct |
| 1993 | 2025 | ||
| @@ -2028,8 +2060,8 @@ static bool loadInstrHeader(FILE *f, uint16_t i) · 34 unmodified lines | |||
| 2028 | 2060 | ||
| 2029 | for (j = 0; j < 96; j++) | 2061 | for (j = 0; j < 96; j++) |
| 2030 | { | 2062 | { |
| 2031 | if (ins->ta[j] > 15) | 2063 | if (ins->ta[j] >= MAX_SMP_PER_INST) |
| 2032 | ins->ta[j] = 15; | 2064 | ins->ta[j] = MAX_SMP_PER_INST-1; |
| 2033 | } | 2065 | } |
| 2034 | 2066 | ||
| 2035 | if (ins->envVPAnt > 12) ins->envVPAnt = 12; | 2067 | if (ins->envVPAnt > 12) ins->envVPAnt = 12; |
| @@ -2050,12 +2082,30 @@ static bool loadInstrHeader(FILE *f, uint16_t i) · 14 unmodified lines | |||
| 2050 | } | 2082 | } |
| 2051 | } | 2083 | } |
| 2052 | 2084 | ||
| 2053 | if (fread(ih.samp, ih.antSamp * sizeof (sampleHeaderTyp), 1, f) != 1) | 2085 | int32_t sampleHeadersToRead = ih.antSamp; |
| 2086 | if (sampleHeadersToRead > MAX_SMP_PER_INST) | ||
| 2087 | sampleHeadersToRead = MAX_SMP_PER_INST; | ||
| 2088 | |||
| 2089 | if (fread(ih.samp, sampleHeadersToRead * sizeof (sampleHeaderTyp), 1, f) != 1) | ||
| 2090 | { | ||
| 2091 | showMsg(0, "System message", "General I/O error during loading!"); | ||
| 2054 | return false; | 2092 | return false; |
| 2093 | } | ||
| 2094 | |||
| 2095 | // if instrument contains more than 16 sample headers (unsupported), skip them | ||
| 2096 | if (ih.antSamp > MAX_SMP_PER_INST) // can only be 0..32 at this point | ||
| 2097 | { | ||
| 2098 | const int32_t samplesToSkip = ih.antSamp-MAX_SMP_PER_INST; | ||
| 2099 | for (j = 0; j < samplesToSkip; j++) | ||
| 2100 | { | ||
| 2101 | fread(&extraSampleLengths[j], 4, 1, f); // used for skipping data in loadInstrSample() | ||
| 2102 | fseek(f, sizeof (sampleHeaderTyp)-4, SEEK_CUR); | ||
| 2103 | } | ||
| 2104 | } | ||
| 2055 | 2105 | ||
| 2056 | if (i <= MAX_INST) | 2106 | if (i <= MAX_INST) |
| 2057 | { | 2107 | { |
| 2058 | for (j = 0; j < ih.antSamp; j++) | 2108 | for (j = 0; j < sampleHeadersToRead; j++) |
| 2059 | { | 2109 | { |
| 2060 | s = &instrTmp[i]->samp[j]; | 2110 | s = &instrTmp[i]->samp[j]; |
| 2061 | src = &ih.samp[j]; | 2111 | src = &ih.samp[j]; |
| @@ -2105,17 +2155,23 @@ void checkSampleRepeat(sampleTyp *s) · 43 unmodified lines | |||
| 2105 | if (s->repL == 0) s->typ &= ~3; // non-FT2 fix: force loop off if looplen is 0 | 2155 | if (s->repL == 0) s->typ &= ~3; // non-FT2 fix: force loop off if looplen is 0 |
| 2106 | } | 2156 | } |
| 2107 | 2157 | ||
| 2108 | static bool loadInstrSample(FILE *f, uint16_t i) | 2158 | static bool loadInstrSample(FILE *f, uint16_t i, bool externalThreadFlag) |
| 2109 | { | 2159 | { |
| 2110 | int8_t *newPtr; | 2160 | int8_t *newPtr; |
| 2111 | uint16_t j, k; | 2161 | uint16_t j, k; |
| 2112 | int32_t l, bytesToSkip; | 2162 | int32_t l, bytesToSkip; |
| 2113 | sampleTyp *s; | 2163 | sampleTyp *s; |
| 2164 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 2165 | |||
| 2166 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 2114 | 2167 | ||
| 2115 | if (i > MAX_INST || instrTmp[i] == NULL) | 2168 | if (i > MAX_INST || instrTmp[i] == NULL) |
| 2116 | return true; // yes, let's just pretend they got loaded | 2169 | return true; // yes, let's just pretend they got loaded |
| 2117 | 2170 | ||
| 2118 | k = instrTmp[i]->antSamp; | 2171 | k = instrTmp[i]->antSamp; |
| 2172 | if (k > MAX_SMP_PER_INST) | ||
| 2173 | k = MAX_SMP_PER_INST; | ||
| 2174 | |||
| 2119 | for (j = 0; j < k; j++) | 2175 | for (j = 0; j < k; j++) |
| 2120 | { | 2176 | { |
| 2121 | s = &instrTmp[i]->samp[j]; | 2177 | s = &instrTmp[i]->samp[j]; |
| @@ -2147,14 +2203,17 @@ static bool loadInstrSample(FILE *f, uint16_t i) · 25 unmodified lines | |||
| 2147 | s->pek = NULL; | 2203 | s->pek = NULL; |
| 2148 | s->origPek = (int8_t *)malloc(l + LOOP_FIX_LEN); | 2204 | s->origPek = (int8_t *)malloc(l + LOOP_FIX_LEN); |
| 2149 | if (s->origPek == NULL) | 2205 | if (s->origPek == NULL) |
| 2206 | { | ||
| 2207 | showMsg(0, "System message", "Not enough memory!"); | ||
| 2150 | return false; | 2208 | return false; |
| 2209 | } | ||
| 2151 | 2210 | ||
| 2152 | s->pek = s->origPek + SMP_DAT_OFFSET; | 2211 | s->pek = s->origPek + SMP_DAT_OFFSET; |
| 2153 | 2212 | ||
| 2154 | int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f); | 2213 | const int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f); |
| 2155 | if (bytesRead < l) | 2214 | if (bytesRead < l) |
| 2156 | { | 2215 | { |
| 2157 | int32_t bytesToClear = l - bytesRead; | 2216 | const int32_t bytesToClear = l - bytesRead; |
| 2158 | memset(&s->pek[bytesRead], 0, bytesToClear); | 2217 | memset(&s->pek[bytesRead], 0, bytesToClear); |
| 2159 | } | 2218 | } |
| 2160 | 2219 | ||
| @@ -2192,23 +2251,37 @@ static bool loadInstrSample(FILE *f, uint16_t i) · 31 unmodified lines | |||
| 2192 | fixSample(s); | 2251 | fixSample(s); |
| 2193 | } | 2252 | } |
| 2194 | 2253 | ||
| 2254 | if (instrTmp[i]->antSamp > MAX_SMP_PER_INST) | ||
| 2255 | { | ||
| 2256 | const int32_t samplesToSkip = instrTmp[i]->antSamp-MAX_SMP_PER_INST; | ||
| 2257 | for (i = 0; i < samplesToSkip; i++) | ||
| 2258 | { | ||
| 2259 | if (extraSampleLengths[i] > 0) | ||
| 2260 | fseek(f, extraSampleLengths[i], SEEK_CUR); | ||
| 2261 | } | ||
| 2262 | } | ||
| 2263 | |||
| 2195 | return true; | 2264 | return true; |
| 2196 | } | 2265 | } |
| 2197 | 2266 | ||
| 2198 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) | 2267 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) |
| 2199 | { | 2268 | { |
| 2200 | uint8_t note, data; | 2269 | uint8_t note, data; |
| 2201 | int32_t srcEnd, srcIdx; | 2270 | int32_t srcEnd, srcIdx, j; |
| 2202 | 2271 | ||
| 2203 | if (dst == NULL) | 2272 | if (dst == NULL) |
| 2204 | return; | 2273 | return; |
| 2205 | 2274 | ||
| 2206 | srcEnd = len * TRACK_WIDTH; | 2275 | srcEnd = len * (sizeof (tonTyp) * antChn); |
| 2207 | srcIdx = 0; | 2276 | srcIdx = 0; |
| 2208 | 2277 | ||
| 2278 | int32_t numChannels = antChn; | ||
| 2279 | if (numChannels > MAX_VOICES) | ||
| 2280 | numChannels = MAX_VOICES; | ||
| 2281 | |||
| 2209 | for (int32_t i = 0; i < len; i++) | 2282 | for (int32_t i = 0; i < len; i++) |
| 2210 | { | 2283 | { |
| 2211 | for (int32_t j = 0; j < antChn; j++) | 2284 | for (j = 0; j < numChannels; j++) |
| 2212 | { | 2285 | { |
| 2213 | if (srcIdx >= srcEnd) | 2286 | if (srcIdx >= srcEnd) |
| 2214 | return; // error! | 2287 | return; // error! |
| @@ -2245,8 +2318,35 @@ void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) · 30 unmodified lines | |||
| 2245 | srcIdx += sizeof (tonTyp); | 2318 | srcIdx += sizeof (tonTyp); |
| 2246 | } | 2319 | } |
| 2247 | 2320 | ||
| 2248 | // skip unused channels | 2321 | // if more than 32 channels, skip rest of the channels for this row |
| 2249 | dst += sizeof (tonTyp) * (MAX_VOICES - antChn); | 2322 | for (; j < antChn; j++) |
| 2323 | { | ||
| 2324 | if (srcIdx >= srcEnd) | ||
| 2325 | return; // error! | ||
| 2326 | |||
| 2327 | note = *src++; | ||
| 2328 | if (note & 0x80) | ||
| 2329 | { | ||
| 2330 | if (note & 0x01) src++; | ||
| 2331 | if (note & 0x02) src++; | ||
| 2332 | if (note & 0x04) src++; | ||
| 2333 | if (note & 0x08) src++; | ||
| 2334 | if (note & 0x10) src++; | ||
| 2335 | } | ||
| 2336 | else | ||
| 2337 | { | ||
| 2338 | src++; | ||
| 2339 | src++; | ||
| 2340 | src++; | ||
| 2341 | src++; | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | srcIdx += sizeof (tonTyp); | ||
| 2345 | } | ||
| 2346 | |||
| 2347 | // if song has <32 channels, align pointer to next row (skip unused channels) | ||
| 2348 | if (antChn < MAX_VOICES) | ||
| 2349 | dst += sizeof (tonTyp) * (MAX_VOICES - antChn); | ||
| 2250 | } | 2350 | } |
| 2251 | } | 2351 | } |
| 2252 | 2352 | ||
| @@ -2279,15 +2379,17 @@ void clearUnusedChannels(tonTyp *p, int16_t pattLen, int32_t antChn) · 26 unmodified lines | |||
| 2279 | memset(&p[(i * MAX_VOICES) + antChn], 0, sizeof (tonTyp) * (MAX_VOICES - antChn)); | 2379 | memset(&p[(i * MAX_VOICES) + antChn], 0, sizeof (tonTyp) * (MAX_VOICES - antChn)); |
| 2280 | } | 2380 | } |
| 2281 | 2381 | ||
| 2282 | static bool loadPatterns(FILE *f, uint16_t antPtn) | 2382 | static bool loadPatterns(FILE *f, uint16_t antPtn, bool externalThreadFlag) |
| 2283 | { | 2383 | { |
| 2284 | bool pattLenWarn; | 2384 | bool pattLenWarn; |
| 2285 | uint8_t tmpLen; | 2385 | uint8_t tmpLen; |
| 2286 | uint16_t i; | 2386 | uint16_t i; |
| 2287 | patternHeaderTyp ph; | 2387 | patternHeaderTyp ph; |
| 2388 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 2288 | 2389 | ||
| 2289 | pattLenWarn = false; | 2390 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; |
| 2290 | 2391 | ||
| 2392 | pattLenWarn = false; | ||
| 2291 | for (i = 0; i < antPtn; i++) | 2393 | for (i = 0; i < antPtn; i++) |
| 2292 | { | 2394 | { |
| 2293 | if (fread(&ph.patternHeaderSize, 4, 1, f) != 1) | 2395 | if (fread(&ph.patternHeaderSize, 4, 1, f) != 1) |
| @@ -2337,7 +2439,7 @@ static bool loadPatterns(FILE *f, uint16_t antPtn) · 43 unmodified lines | |||
| 2337 | pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); | 2439 | pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); |
| 2338 | if (pattTmp[i] == NULL) | 2440 | if (pattTmp[i] == NULL) |
| 2339 | { | 2441 | { |
| 2340 | okBoxThreadSafe(0, "System message", "Not enough memory!"); | 2442 | showMsg(0, "System message", "Not enough memory!"); |
| 2341 | return false; | 2443 | return false; |
| 2342 | } | 2444 | } |
| 2343 | 2445 | ||
| @@ -2345,7 +2447,6 @@ static bool loadPatterns(FILE *f, uint16_t antPtn) · 1 unmodified line | |||
| 2345 | goto pattCorrupt; | 2447 | goto pattCorrupt; |
| 2346 | 2448 | ||
| 2347 | unpackPatt((uint8_t *)pattTmp[i], packedPattData, pattLensTmp[i], songTmp.antChn); | 2449 | unpackPatt((uint8_t *)pattTmp[i], packedPattData, pattLensTmp[i], songTmp.antChn); |
| 2348 | |||
| 2349 | clearUnusedChannels(pattTmp[i], pattLensTmp[i], songTmp.antChn); | 2450 | clearUnusedChannels(pattTmp[i], pattLensTmp[i], songTmp.antChn); |
| 2350 | } | 2451 | } |
| 2351 | 2452 | ||
| @@ -2362,12 +2463,12 @@ static bool loadPatterns(FILE *f, uint16_t antPtn) · 10 unmodified lines | |||
| 2362 | } | 2463 | } |
| 2363 | 2464 | ||
| 2364 | if (pattLenWarn) | 2465 | if (pattLenWarn) |
| 2365 | okBoxThreadSafe(0, "System message", "This module contains pattern(s) with a length above 256! They will be truncated."); | 2466 | showMsg(0, "System message", "This module contains pattern(s) with a length above 256! They will be truncated."); |
| 2366 | 2467 | ||
| 2367 | return true; | 2468 | return true; |
| 2368 | 2469 | ||
| 2369 | pattCorrupt: | 2470 | pattCorrupt: |
| 2370 | okBoxThreadSafe(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!"); | 2471 | showMsg(0, "System message", "Error loading XM: This file is corrupt!"); |
| 2371 | return false; | 2472 | return false; |
| 2372 | } | 2473 | } |
| 2373 | 2474 | ||