...but discard the extra channels and samples! Also did some other fixes.
Showing the changes to src (1 of 1 file). Show all
Unified · Split
| @@ -136,17 +136,18 @@ static SDL_Thread *thread; · 135 unmodified lines | ||
| 136 | 136 | |
| 137 | 137 | // these temporarily read to, then copied to real struct if load was OK (should not need to be volatile'd) |
| 138 | 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 | 140 | static tonTyp *pattTmp[MAX_PATTERNS]; |
| 140 | −static instrTyp *instrTmp[1 + MAX_INST]; | |
| 141 | +static instrTyp *instrTmp[1+MAX_INST]; | |
| 141 | 142 | static songTyp songTmp; |
| 142 | 143 | |
| 143 | 144 | static void setupLoadedModule(void); |
| 144 | 145 | static void freeTmpModule(void); |
| 145 | −static bool loadInstrHeader(FILE *f, uint16_t i); | |
| 146 | −static bool loadInstrSample(FILE *f, uint16_t i); | |
| 146 | +static bool loadInstrHeader(FILE *f, uint16_t i, bool externalThreadFlag); | |
| 147 | +static bool loadInstrSample(FILE *f, uint16_t i, bool externalThreadFlag); | |
| 147 | 148 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn); |
| 148 | 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 | 152 | void checkSampleRepeat(sampleTyp *s); |
| 152 | 153 | |
| @@ -160,13 +161,11 @@ static bool allocateTmpInstr(int16_t nr) · 7 unmodified lines | ||
| 160 | 161 | if (instrTmp[nr] != NULL) |
| 161 | 162 | return false; // already allocated |
| 162 | 163 | |
| 163 | − p = (instrTyp *)malloc(sizeof (instrTyp)); | |
| 164 | + p = (instrTyp *)calloc(1, sizeof (instrTyp)); | |
| 164 | 165 | if (p == NULL) |
| 165 | 166 | return false; |
| 166 | 167 | |
| 167 | − memset(p, 0, sizeof (instrTyp)); | |
| 168 | − | |
| 169 | − for (int8_t i = 0; i < 16; i++) // set standard sample pan/vol | |
| 168 | + for (int8_t i = 0; i < MAX_SMP_PER_INST; i++) // set standard sample pan/vol | |
| 170 | 169 | { |
| 171 | 170 | p->samp[i].pan = 128; |
| 172 | 171 | p->samp[i].vol = 64; |
| @@ -213,7 +212,7 @@ static uint8_t getModType(uint8_t *numChannels, const char *id) · 40 unmodified lines | ||
| 213 | 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 | 217 | char ID[16]; |
| 219 | 218 | bool mightBeSTK, lateSTKVerFlag, veryLateSTKVerFlag; |
| @@ -226,7 +225,7 @@ static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread) · 6 unmodified lines | ||
| 226 | 225 | songMOD15HeaderTyp h_MOD15; |
| 227 | 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 | 230 | veryLateSTKVerFlag = false; // "DFJ SoundTracker III" nad later |
| 232 | 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 | 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 | 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 | 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 | 739 | uint8_t typ, tempo; |
| 741 | 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 | 744 | songSTMHeaderTyp h_STM; |
| 746 | 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 | 749 | rewind(f); |
| 751 | 750 | |
| 752 | 751 | // start loading STM |
| 753 | 752 | |
| 754 | 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 | 756 | if (memcmp(h_STM.sig, "!Scream!", 8) && memcmp(h_STM.sig, "BMOD2STM", 8) && |
| 758 | 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 | 762 | loadedFormat = FORMAT_STM; |
| @@ -1007,7 +1006,7 @@ static int8_t countS3MChannels(uint16_t antPtn) · 243 unmodified lines | ||
| 1007 | 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 | 1011 | int8_t *tmpSmp; |
| 1013 | 1012 | uint8_t ha[2048]; |
| @@ -1022,17 +1021,17 @@ static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool fromExternalThread) · 8 unmodified lines | ||
| 1022 | 1021 | songS3MinstrHeaderTyp h_S3MInstr; |
| 1023 | 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 | 1026 | rewind(f); |
| 1028 | 1027 | |
| 1029 | 1028 | // start loading S3M |
| 1030 | 1029 | |
| 1031 | 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 | 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 | 1036 | loadedFormat = FORMAT_S3M; |
| 1038 | 1037 | |
| @@ -1651,7 +1650,7 @@ s3mLoadError: · 612 unmodified lines | ||
| 1651 | 1650 | return false; |
| 1652 | 1651 | } |
| 1653 | 1652 | |
| 1654 | −bool doLoadMusic(bool fromExternalThread) | |
| 1653 | +bool doLoadMusic(bool externalThreadFlag) | |
| 1655 | 1654 | { |
| 1656 | 1655 | char tmpText[128]; |
| 1657 | 1656 | int16_t k; |
| @@ -1661,7 +1660,7 @@ bool doLoadMusic(bool fromExternalThread) · 3 unmodified lines | ||
| 1661 | 1660 | FILE *f; |
| 1662 | 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 | 1665 | linearFreqTable = false; |
| 1667 | 1666 | |
| @@ -1686,10 +1685,10 @@ bool doLoadMusic(bool fromExternalThread) · 18 unmodified lines | ||
| 1686 | 1685 | |
| 1687 | 1686 | // start loading |
| 1688 | 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 | 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 | 1693 | loadedFormat = FORMAT_XM; |
| 1695 | 1694 | |
| @@ -1700,7 +1699,7 @@ bool doLoadMusic(bool fromExternalThread) · 4 unmodified lines | ||
| 1700 | 1699 | const int32_t major = (h.ver >> 8) & 0x0F; |
| 1701 | 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 | 1703 | showMsg(0, "System message", tmpText); |
| 1705 | 1704 | |
| 1706 | 1705 | moduleFailedToLoad = true; |
| @@ -1709,29 +1708,26 @@ bool doLoadMusic(bool fromExternalThread) · 2 unmodified lines | ||
| 1709 | 1708 | |
| 1710 | 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 | 1712 | goto xmLoadError; |
| 1714 | 1713 | } |
| 1715 | 1714 | |
| 1716 | 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 | 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 | 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 | 1727 | fseek(f, 60 + h.headerSize, SEEK_SET); |
| 1732 | 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 | 1731 | goto xmLoadError; |
| 1736 | 1732 | } |
| 1737 | 1733 | |
| @@ -1768,62 +1764,81 @@ bool doLoadMusic(bool fromExternalThread) · 30 unmodified lines | ||
| 1768 | 1764 | else |
| 1769 | 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 | 1781 | if (songTmp.ver < 0x0104) |
| 1772 | 1782 | { |
| 1773 | − // old FT2 format | |
| 1783 | + // XM v1.02 and XM v1.03 | |
| 1774 | 1784 | |
| 1775 | 1785 | for (i = 1; i <= h.antInstrs; i++) |
| 1776 | 1786 | { |
| 1777 | − if (!loadInstrHeader(f, i)) | |
| 1778 | − { | |
| 1779 | − showMsg(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!"); | |
| 1787 | + if (!loadInstrHeader(f, i, externalThreadFlag)) | |
| 1780 | 1788 | goto xmLoadError; |
| 1781 | − } | |
| 1782 | 1789 | } |
| 1783 | 1790 | |
| 1784 | − if (!loadPatterns(f, h.antPtn)) | |
| 1785 | − { | |
| 1786 | − // error message is shown inside loadPattern() | |
| 1791 | + if (!loadPatterns(f, h.antPtn, externalThreadFlag)) | |
| 1787 | 1792 | goto xmLoadError; |
| 1788 | − } | |
| 1789 | 1793 | |
| 1790 | 1794 | for (i = 1; i <= h.antInstrs; i++) |
| 1791 | 1795 | { |
| 1792 | − if (!loadInstrSample(f, i)) | |
| 1793 | − { | |
| 1794 | − showMsg(0, "System message", "Not enough memory!"); | |
| 1796 | + if (!loadInstrSample(f, i, externalThreadFlag)) | |
| 1795 | 1797 | goto xmLoadError; |
| 1796 | − } | |
| 1797 | 1798 | } |
| 1798 | 1799 | } |
| 1799 | 1800 | else |
| 1800 | 1801 | { |
| 1801 | − // current FT2 format | |
| 1802 | + // XM v1.04 (latest version) | |
| 1802 | 1803 | |
| 1803 | − if (!loadPatterns(f, h.antPtn)) | |
| 1804 | − { | |
| 1805 | − // error message is shown inside loadPattern() | |
| 1804 | + if (!loadPatterns(f, h.antPtn, externalThreadFlag)) | |
| 1806 | 1805 | goto xmLoadError; |
| 1807 | − } | |
| 1808 | 1806 | |
| 1809 | 1807 | for (i = 1; i <= h.antInstrs; i++) |
| 1810 | 1808 | { |
| 1811 | − if (!loadInstrHeader(f, i)) | |
| 1812 | − { | |
| 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 | − } | |
| 1809 | + if (!loadInstrHeader(f, i, externalThreadFlag)) goto xmLoadError; | |
| 1810 | + if (!loadInstrSample(f, i, externalThreadFlag)) goto xmLoadError; | |
| 1822 | 1811 | } |
| 1823 | 1812 | } |
| 1824 | 1813 | |
| 1825 | 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 | 1842 | moduleLoaded = true; |
| 1828 | 1843 | return true; |
| 1829 | 1844 | |
| @@ -1908,9 +1923,9 @@ bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay) · 78 unmodified lines | ||
| 1908 | 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 | 1930 | // free all patterns |
| 1916 | 1931 | for (i = 0; i < MAX_PATTERNS; i++) |
| @@ -1927,10 +1942,10 @@ static void freeTmpModule(void) · 10 unmodified lines | ||
| 1927 | 1942 | { |
| 1928 | 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) | |
| 1933 | − free(instrTmp[i]->samp[j].pek); | |
| 1947 | + if (instrTmp[i]->samp[j].origPek != NULL) | |
| 1948 | + free(instrTmp[i]->samp[j].origPek); | |
| 1934 | 1949 | } |
| 1935 | 1950 | |
| 1936 | 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 | 1959 | int8_t k; |
| 1945 | 1960 | uint8_t j; |
| @@ -1948,24 +1963,38 @@ static bool loadInstrHeader(FILE *f, uint16_t i) · 2 unmodified lines | ||
| 1948 | 1963 | instrTyp *ins; |
| 1949 | 1964 | sampleHeaderTyp *src; |
| 1950 | 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 | 1972 | fread(&ih.instrSize, 4, 1, f); |
| 1955 | 1973 | |
| 1956 | 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 | 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 | 1986 | // load instrument data into temp buffer |
| 1961 | 1987 | fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize |
| 1962 | 1988 | |
| 1963 | 1989 | // FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE |
| 1964 | 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 | 1996 | return false; |
| 1997 | + } | |
| 1969 | 1998 | |
| 1970 | 1999 | if (i <= MAX_INST) |
| 1971 | 2000 | { |
| @@ -1987,7 +2016,10 @@ static bool loadInstrHeader(FILE *f, uint16_t i) · 15 unmodified lines | ||
| 1987 | 2016 | if (i <= MAX_INST) |
| 1988 | 2017 | { |
| 1989 | 2018 | if (!allocateTmpInstr(i)) |
| 2019 | + { | |
| 2020 | + showMsg(0, "System message", "Not enough memory!"); | |
| 1990 | 2021 | return false; |
| 2022 | + } | |
| 1991 | 2023 | |
| 1992 | 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 | 2061 | for (j = 0; j < 96; j++) |
| 2030 | 2062 | { |
| 2031 | − if (ins->ta[j] > 15) | |
| 2032 | − ins->ta[j] = 15; | |
| 2063 | + if (ins->ta[j] >= MAX_SMP_PER_INST) | |
| 2064 | + ins->ta[j] = MAX_SMP_PER_INST-1; | |
| 2033 | 2065 | } |
| 2034 | 2066 | |
| 2035 | 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 | 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 | 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 | 2110 | s = &instrTmp[i]->samp[j]; |
| 2061 | 2111 | src = &ih.samp[j]; |
| @@ -2105,17 +2155,23 @@ void checkSampleRepeat(sampleTyp *s) · 43 unmodified lines | ||
| 2105 | 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 | 2160 | int8_t *newPtr; |
| 2111 | 2161 | uint16_t j, k; |
| 2112 | 2162 | int32_t l, bytesToSkip; |
| 2113 | 2163 | sampleTyp *s; |
| 2164 | + int16_t (*showMsg)(int16_t, const char *, const char *); | |
| 2165 | + | |
| 2166 | + showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | |
| 2114 | 2167 | |
| 2115 | 2168 | if (i > MAX_INST || instrTmp[i] == NULL) |
| 2116 | 2169 | return true; // yes, let's just pretend they got loaded |
| 2117 | 2170 | |
| 2118 | 2171 | k = instrTmp[i]->antSamp; |
| 2172 | + if (k > MAX_SMP_PER_INST) | |
| 2173 | + k = MAX_SMP_PER_INST; | |
| 2174 | + | |
| 2119 | 2175 | for (j = 0; j < k; j++) |
| 2120 | 2176 | { |
| 2121 | 2177 | s = &instrTmp[i]->samp[j]; |
| @@ -2147,14 +2203,17 @@ static bool loadInstrSample(FILE *f, uint16_t i) · 25 unmodified lines | ||
| 2147 | 2203 | s->pek = NULL; |
| 2148 | 2204 | s->origPek = (int8_t *)malloc(l + LOOP_FIX_LEN); |
| 2149 | 2205 | if (s->origPek == NULL) |
| 2206 | + { | |
| 2207 | + showMsg(0, "System message", "Not enough memory!"); | |
| 2150 | 2208 | return false; |
| 2209 | + } | |
| 2151 | 2210 | |
| 2152 | 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 | 2214 | if (bytesRead < l) |
| 2156 | 2215 | { |
| 2157 | − int32_t bytesToClear = l - bytesRead; | |
| 2216 | + const int32_t bytesToClear = l - bytesRead; | |
| 2158 | 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 | 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 | 2264 | return true; |
| 2196 | 2265 | } |
| 2197 | 2266 | |
| 2198 | 2267 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) |
| 2199 | 2268 | { |
| 2200 | 2269 | uint8_t note, data; |
| 2201 | − int32_t srcEnd, srcIdx; | |
| 2270 | + int32_t srcEnd, srcIdx, j; | |
| 2202 | 2271 | |
| 2203 | 2272 | if (dst == NULL) |
| 2204 | 2273 | return; |
| 2205 | 2274 | |
| 2206 | − srcEnd = len * TRACK_WIDTH; | |
| 2275 | + srcEnd = len * (sizeof (tonTyp) * antChn); | |
| 2207 | 2276 | srcIdx = 0; |
| 2208 | 2277 | |
| 2278 | + int32_t numChannels = antChn; | |
| 2279 | + if (numChannels > MAX_VOICES) | |
| 2280 | + numChannels = MAX_VOICES; | |
| 2281 | + | |
| 2209 | 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 | 2286 | if (srcIdx >= srcEnd) |
| 2214 | 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 | 2318 | srcIdx += sizeof (tonTyp); |
| 2246 | 2319 | } |
| 2247 | 2320 | |
| 2248 | − // skip unused channels | |
| 2249 | − dst += sizeof (tonTyp) * (MAX_VOICES - antChn); | |
| 2321 | + // if more than 32 channels, skip rest of the channels for this row | |
| 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 | 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 | 2384 | bool pattLenWarn; |
| 2285 | 2385 | uint8_t tmpLen; |
| 2286 | 2386 | uint16_t i; |
| 2287 | 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 | 2393 | for (i = 0; i < antPtn; i++) |
| 2292 | 2394 | { |
| 2293 | 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 | 2439 | pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); |
| 2338 | 2440 | if (pattTmp[i] == NULL) |
| 2339 | 2441 | { |
| 2340 | − okBoxThreadSafe(0, "System message", "Not enough memory!"); | |
| 2442 | + showMsg(0, "System message", "Not enough memory!"); | |
| 2341 | 2443 | return false; |
| 2342 | 2444 | } |
| 2343 | 2445 | |
| @@ -2345,7 +2447,6 @@ static bool loadPatterns(FILE *f, uint16_t antPtn) · 1 unmodified line | ||
| 2345 | 2447 | goto pattCorrupt; |
| 2346 | 2448 | |
| 2347 | 2449 | unpackPatt((uint8_t *)pattTmp[i], packedPattData, pattLensTmp[i], songTmp.antChn); |
| 2348 | − | |
| 2349 | 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 | 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 | 2468 | return true; |
| 2368 | 2469 | |
| 2369 | 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 | 2472 | return false; |
| 2372 | 2473 | } |
| 2373 | 2474 | |