534cda209c26224ff9745f8f4b714e9394afadef
[platform/upstream/gstreamer.git] / gst / modplug / libmodplug / load_mtm.cpp
1 /*
2  * This source code is public domain.
3  *
4  * Authors: Olivier Lapicque <olivierl@jps.net>
5 */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include "stdafx.h"
12 #include "sndfile.h"
13
14 //#pragma warning(disable:4244)
15
16 //////////////////////////////////////////////////////////
17 // MTM file support (import only)
18
19 #pragma pack(1)
20
21
22 typedef struct tagMTMSAMPLE
23 {
24         char samplename[22];      // changed from CHAR
25         DWORD length;
26         DWORD reppos;
27         DWORD repend;
28         CHAR finetune;
29         BYTE volume;
30         BYTE attribute;
31 } MTMSAMPLE;
32
33
34 typedef struct tagMTMHEADER
35 {
36         char id[4];             // MTM file marker + version // changed from CHAR
37         char songname[20];      // ASCIIZ songname  // changed from CHAR
38         WORD numtracks;         // number of tracks saved
39         BYTE lastpattern;       // last pattern number saved
40         BYTE lastorder;         // last order number to play (songlength-1)
41         WORD commentsize;       // length of comment field
42         BYTE numsamples;        // number of samples saved
43         BYTE attribute;         // attribute byte (unused)
44         BYTE beatspertrack;
45         BYTE numchannels;       // number of channels used
46         BYTE panpos[32];        // voice pan positions
47 } MTMHEADER;
48
49
50 #pragma pack()
51
52
53 BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength)
54 //-----------------------------------------------------------
55 {
56         MTMHEADER *pmh = (MTMHEADER *)lpStream;
57         DWORD dwMemPos = 66;
58
59         if ((!lpStream) || (dwMemLength < 0x100)) return FALSE;
60         if ((strncmp(pmh->id, "MTM", 3)) || (pmh->numchannels > 32)
61          || (pmh->numsamples >= MAX_SAMPLES) || (!pmh->numsamples)
62          || (!pmh->numtracks) || (!pmh->numchannels)
63          || (!pmh->lastpattern) || (pmh->lastpattern > MAX_PATTERNS)) return FALSE;
64         strncpy(m_szNames[0], pmh->songname, 20);
65         m_szNames[0][20] = 0;
66         if (dwMemPos + 37*pmh->numsamples + 128 + 192*pmh->numtracks
67          + 64 * (pmh->lastpattern+1) + pmh->commentsize >= dwMemLength) return FALSE;
68         m_nType = MOD_TYPE_MTM;
69         m_nSamples = pmh->numsamples;
70         m_nChannels = pmh->numchannels;
71         // Reading instruments
72         for     (UINT i=1; i<=m_nSamples; i++)
73         {
74                 MTMSAMPLE *pms = (MTMSAMPLE *)(lpStream + dwMemPos);
75                 strncpy(m_szNames[i], pms->samplename, 22);
76                 m_szNames[i][22] = 0;
77                 Ins[i].nVolume = pms->volume << 2;
78                 Ins[i].nGlobalVol = 64;
79                 DWORD len = pms->length;
80                 if ((len > 4) && (len <= MAX_SAMPLE_LENGTH))
81                 {
82                         Ins[i].nLength = len;
83                         Ins[i].nLoopStart = pms->reppos;
84                         Ins[i].nLoopEnd = pms->repend;
85                         if (Ins[i].nLoopEnd > Ins[i].nLength) Ins[i].nLoopEnd = Ins[i].nLength;
86                         if (Ins[i].nLoopStart + 4 >= Ins[i].nLoopEnd) Ins[i].nLoopStart = Ins[i].nLoopEnd = 0;
87                         if (Ins[i].nLoopEnd) Ins[i].uFlags |= CHN_LOOP;
88                         Ins[i].nFineTune = MOD2XMFineTune(pms->finetune);
89                         if (pms->attribute & 0x01)
90                         {
91                                 Ins[i].uFlags |= CHN_16BIT;
92                                 Ins[i].nLength >>= 1;
93                                 Ins[i].nLoopStart >>= 1;
94                                 Ins[i].nLoopEnd >>= 1;
95                         }
96                         Ins[i].nPan = 128;
97                 }
98                 dwMemPos += 37;
99         }
100         // Setting Channel Pan Position
101         for (UINT ich=0; ich<m_nChannels; ich++)
102         {
103                 ChnSettings[ich].nPan = ((pmh->panpos[ich] & 0x0F) << 4) + 8;
104                 ChnSettings[ich].nVolume = 64;
105         }
106         // Reading pattern order
107         memcpy(Order, lpStream + dwMemPos, pmh->lastorder+1);
108         dwMemPos += 128;
109         // Reading Patterns
110         LPCBYTE pTracks = lpStream + dwMemPos;
111         dwMemPos += 192 * pmh->numtracks;
112         LPWORD pSeq = (LPWORD)(lpStream + dwMemPos);
113         for (UINT pat=0; pat<=pmh->lastpattern; pat++)
114         {
115                 PatternSize[pat] = 64;
116                 if ((Patterns[pat] = AllocatePattern(64, m_nChannels)) == NULL) break;
117                 for (UINT n=0; n<32; n++) if ((pSeq[n]) && (pSeq[n] <= pmh->numtracks) && (n < m_nChannels))
118                 {
119                         LPCBYTE p = pTracks + 192 * (pSeq[n]-1);
120                         MODCOMMAND *m = Patterns[pat] + n;
121                         for (UINT i=0; i<64; i++, m+=m_nChannels, p+=3)
122                         {
123                                 if (p[0] & 0xFC) m->note = (p[0] >> 2) + 37;
124                                 m->instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
125                                 UINT cmd = p[1] & 0x0F;
126                                 UINT param = p[2];
127                                 if (cmd == 0x0A)
128                                 {
129                                         if (param & 0xF0) param &= 0xF0; else param &= 0x0F;
130                                 }
131                                 m->command = cmd;
132                                 m->param = param;
133                                 if ((cmd) || (param)) ConvertModCommand(m);
134                         }
135                 }
136                 pSeq += 32;
137         }
138         dwMemPos += 64*(pmh->lastpattern+1);
139         if ((pmh->commentsize) && (dwMemPos + pmh->commentsize < dwMemLength))
140         {
141                 UINT n = pmh->commentsize;
142                 m_lpszSongComments = new char[n+1];
143                 if (m_lpszSongComments)
144                 {
145                         memcpy(m_lpszSongComments, lpStream+dwMemPos, n);
146                         m_lpszSongComments[n] = 0;
147                         for (UINT i=0; i<n; i++)
148                         {
149                                 if (!m_lpszSongComments[i])
150                                 {
151                                         m_lpszSongComments[i] = ((i+1) % 40) ? 0x20 : 0x0D;
152                                 }
153                         }
154                 }
155         }
156         dwMemPos += pmh->commentsize;
157         // Reading Samples
158         for (UINT ismp=1; ismp<=m_nSamples; ismp++)
159         {
160                 if (dwMemPos >= dwMemLength) break;
161                 dwMemPos += ReadSample(&Ins[ismp], (Ins[ismp].uFlags & CHN_16BIT) ? RS_PCM16U : RS_PCM8U,
162                                                                 (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
163         }
164         m_nMinPeriod = 64;
165         m_nMaxPeriod = 32767;
166         return TRUE;
167 }
168