4
|
1 /*
|
|
2
|
|
3 Name: LOAD_ULT.C
|
|
4
|
|
5 Description:
|
|
6 Ultratracker (ULT) module loader
|
|
7
|
|
8 Portability:
|
|
9 All systems - all compilers (hopefully)
|
|
10
|
|
11 If this module is found to not be portable to any particular platform,
|
|
12 please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
|
|
13 more information on contacting the author).
|
|
14
|
|
15 */
|
|
16
|
|
17 #include <string.h>
|
|
18 #include "mikmod.h"
|
|
19
|
|
20
|
|
21 #define ULTS_16BITS 4
|
|
22 #define ULTS_LOOP 8
|
|
23 #define ULTS_REVERSE 16
|
|
24
|
|
25
|
|
26 // Raw ULT header struct:
|
|
27
|
|
28 typedef struct ULTHEADER
|
|
29 { CHAR id[16];
|
|
30 CHAR songtitle[32];
|
|
31 UBYTE reserved;
|
|
32 } ULTHEADER;
|
|
33
|
|
34
|
|
35 // Raw ULT sampleinfo struct:
|
|
36
|
|
37 typedef struct ULTSAMPLE
|
|
38 { CHAR samplename[32];
|
|
39 CHAR dosname[12];
|
|
40 SLONG loopstart;
|
|
41 SLONG loopend;
|
|
42 SLONG sizestart;
|
|
43 SLONG sizeend;
|
|
44 UBYTE volume;
|
|
45 UBYTE flags;
|
|
46 SWORD finetune;
|
|
47 } ULTSAMPLE;
|
|
48
|
|
49
|
|
50 typedef struct ULTEVENT
|
|
51 { UBYTE note,sample,eff,dat1,dat2;
|
|
52 } ULTEVENT;
|
|
53
|
|
54
|
|
55 CHAR *ULT_Version[]=
|
|
56 { "Ultra Tracker V1.3",
|
|
57 "Ultra Tracker V1.4",
|
|
58 "Ultra Tracker V1.5",
|
|
59 "Ultra Tracker V1.6"
|
|
60 };
|
|
61
|
|
62
|
|
63 BOOL ULT_Test(void)
|
|
64 {
|
|
65 CHAR id[16];
|
|
66
|
|
67 if(!_mm_read_string(id,15,modfp)) return 0;
|
|
68 return(!strncmp(id,"MAS_UTrack_V00",14));
|
|
69 }
|
|
70
|
|
71
|
|
72 BOOL ULT_Init(void)
|
|
73 {
|
|
74 return 1;
|
|
75 }
|
|
76
|
|
77
|
|
78 void ULT_Cleanup(void)
|
|
79 {
|
|
80 }
|
|
81
|
|
82 ULTEVENT ev;
|
|
83
|
|
84
|
|
85
|
|
86 int ReadUltEvent(ULTEVENT *event)
|
|
87 {
|
|
88 UBYTE flag,rep=1;
|
|
89
|
|
90 flag = _mm_read_UBYTE(modfp);
|
|
91
|
|
92 if(flag==0xfc)
|
|
93 { rep = _mm_read_UBYTE(modfp);
|
|
94 event->note =_mm_read_UBYTE(modfp);
|
|
95 } else
|
|
96 event->note = flag;
|
|
97
|
|
98 event->sample =_mm_read_UBYTE(modfp);
|
|
99 event->eff =_mm_read_UBYTE(modfp);
|
|
100 event->dat1 =_mm_read_UBYTE(modfp);
|
|
101 event->dat2 =_mm_read_UBYTE(modfp);
|
|
102
|
|
103 return rep;
|
|
104 }
|
|
105
|
|
106
|
|
107 BOOL ULT_Load(void)
|
|
108 {
|
|
109 int t,u,tracks=0;
|
|
110 SAMPLE *q;
|
|
111 ULTSAMPLE s;
|
|
112 ULTHEADER mh;
|
|
113 UBYTE nos,noc,nop;
|
|
114
|
|
115 // try to read module header
|
|
116
|
|
117 _mm_read_string(mh.id,15,modfp);
|
|
118 _mm_read_string(mh.songtitle,32,modfp);
|
|
119 mh.reserved = _mm_read_UBYTE(modfp);
|
|
120
|
|
121 if(feof(modfp))
|
|
122 { _mm_errno = MMERR_LOADING_HEADER;
|
|
123 return 0;
|
|
124 }
|
|
125
|
|
126 if(mh.id[14]<'1' || mh.id[14]>'4')
|
|
127 { _mm_errno = MMERR_NOT_A_MODULE;
|
|
128 return 0;
|
|
129 }
|
|
130
|
|
131 of.modtype = strdup(ULT_Version[mh.id[14]-'1']);
|
|
132 of.initspeed = 6;
|
|
133 of.inittempo = 125;
|
|
134
|
|
135 // read songtext
|
|
136
|
|
137 if(!ReadComment((UWORD)mh.reserved*32)) return 0;
|
|
138
|
|
139 nos = _mm_read_UBYTE(modfp);
|
|
140
|
|
141 if(feof(modfp))
|
|
142 { _mm_errno = MMERR_LOADING_HEADER;
|
|
143 return 0;
|
|
144 }
|
|
145
|
|
146 of.songname = DupStr(mh.songtitle,32);
|
|
147 of.numins = nos;
|
|
148
|
|
149 if(!AllocSamples()) return 0;
|
|
150
|
|
151 q = of.samples;
|
|
152
|
|
153 for(t=0; t<nos; t++)
|
|
154 { // try to read sample info
|
|
155
|
|
156 _mm_read_string(s.samplename,32,modfp);
|
|
157 _mm_read_string(s.dosname,12,modfp);
|
|
158 s.loopstart =_mm_read_I_ULONG(modfp);
|
|
159 s.loopend =_mm_read_I_ULONG(modfp);
|
|
160 s.sizestart =_mm_read_I_ULONG(modfp);
|
|
161 s.sizeend =_mm_read_I_ULONG(modfp);
|
|
162 s.volume =_mm_read_UBYTE(modfp);
|
|
163 s.flags =_mm_read_UBYTE(modfp);
|
|
164 s.finetune =_mm_read_I_SWORD(modfp);
|
|
165
|
|
166 if(feof(modfp))
|
|
167 { _mm_errno = MMERR_LOADING_SAMPLEINFO;
|
|
168 return 0;
|
|
169 }
|
|
170
|
|
171 q->samplename = DupStr(s.samplename,32);
|
|
172 q->speed = 8363;
|
|
173
|
|
174 if(mh.id[14]>='4')
|
|
175 { _mm_read_I_UWORD(modfp); // read 1.6 extra info(??) word
|
|
176 q->speed=s.finetune;
|
|
177 }
|
|
178
|
|
179 q->length = s.sizeend-s.sizestart;
|
|
180 q->volume = s.volume>>2;
|
|
181 q->loopstart = s.loopstart;
|
|
182 q->loopend = s.loopend;
|
|
183
|
|
184 q->flags = SF_SIGNED;
|
|
185
|
|
186 if(s.flags&ULTS_LOOP)
|
|
187 q->flags|=SF_LOOP;
|
|
188
|
|
189 if(s.flags&ULTS_16BITS)
|
|
190 { q->flags|=SF_16BITS;
|
|
191 q->loopstart>>=1;
|
|
192 q->loopend>>=1;
|
|
193 }
|
|
194
|
|
195 q++;
|
|
196 }
|
|
197
|
|
198 if(!AllocPositions(256)) return 0;
|
|
199 for(t=0; t<256; t++)
|
|
200 of.positions[t] = _mm_read_UBYTE(modfp);
|
|
201 for(t=0; t<256; t++)
|
|
202 if(of.positions[t]==255) break;
|
|
203
|
|
204 of.numpos = t;
|
|
205
|
|
206 noc = _mm_read_UBYTE(modfp);
|
|
207 nop = _mm_read_UBYTE(modfp);
|
|
208
|
|
209 of.numchn = noc+1;
|
|
210 of.numpat = nop+1;
|
|
211 of.numtrk = of.numchn*of.numpat;
|
|
212
|
|
213 if(!AllocTracks()) return 0;
|
|
214 if(!AllocPatterns()) return 0;
|
|
215
|
|
216 for(u=0; u<of.numchn; u++)
|
|
217 { for(t=0; t<of.numpat; t++)
|
|
218 of.patterns[(t*of.numchn)+u]=tracks++;
|
|
219 }
|
|
220
|
|
221 // read pan position table for v1.5 and higher
|
|
222
|
|
223 if(mh.id[14]>='3')
|
|
224 for(t=0; t<of.numchn; t++) of.panning[t]=_mm_read_UBYTE(modfp)<<4;
|
|
225
|
|
226 for(t=0; t<of.numtrk; t++)
|
|
227 { int rep,s,done;
|
|
228
|
|
229 UniReset();
|
|
230 done=0;
|
|
231
|
|
232 while(done<64)
|
|
233 { rep=ReadUltEvent(&ev);
|
|
234
|
|
235 if(feof(modfp))
|
|
236 { _mm_errno = MMERR_LOADING_TRACK;
|
|
237 return 0;
|
|
238 }
|
|
239
|
|
240 for(s=0; s<rep; s++)
|
|
241 { UBYTE eff;
|
|
242
|
|
243 if(ev.sample) UniInstrument(ev.sample-1);
|
|
244 if(ev.note) UniNote(ev.note+23);
|
|
245
|
|
246 eff = ev.eff>>4;
|
|
247
|
|
248 // ULT panning effect fixed by Alexander Kerkhove :
|
|
249
|
|
250 if(eff==0xc) UniPTEffect(eff,ev.dat2>>2);
|
|
251 else if(eff==0xb) UniPTEffect(8,ev.dat2*0xf);
|
|
252 else UniPTEffect(eff,ev.dat2);
|
|
253
|
|
254 eff=ev.eff&0xf;
|
|
255
|
|
256 if(eff==0xc) UniPTEffect(eff,ev.dat1>>2);
|
|
257 else if(eff==0xb) UniPTEffect(8,ev.dat1*0xf);
|
|
258 else UniPTEffect(eff,ev.dat1);
|
|
259
|
|
260 UniNewline();
|
|
261 done++;
|
|
262 }
|
|
263 }
|
|
264 if(!(of.tracks[t]=UniDup())) return 0;
|
|
265 }
|
|
266
|
|
267 return 1;
|
|
268 }
|
|
269
|
|
270
|
|
271 CHAR *ULT_LoadTitle(void)
|
|
272 {
|
|
273 CHAR s[32];
|
|
274
|
|
275 _mm_fseek(modfp,15,SEEK_SET);
|
|
276 if(!fread(s,32,1,modfp)) return NULL;
|
|
277
|
|
278 return(DupStr(s,32));
|
|
279 }
|
|
280
|
|
281
|
|
282 MLOADER load_ult =
|
|
283 { NULL,
|
|
284 "ULT",
|
|
285 "Portable ULT loader v0.1",
|
|
286 ULT_Init,
|
|
287 ULT_Test,
|
|
288 ULT_Load,
|
|
289 ULT_Cleanup,
|
|
290 ULT_LoadTitle
|
|
291 };
|
|
292
|