A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/* MikMod sound library
2 (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
3 AUTHORS for complete list.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 02111-1307, USA.
19*/
20
21/*==============================================================================
22
23 Composer 669 module loader
24
25==============================================================================*/
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#ifdef HAVE_UNISTD_H
32#include <unistd.h>
33#endif
34
35#include <stdio.h>
36#ifdef HAVE_MEMORY_H
37#include <memory.h>
38#endif
39#include <string.h>
40
41#include "mikmod_internals.h"
42
43#ifdef SUNOS
44extern int fprintf(FILE *, const char *, ...);
45#endif
46
47/*========== Module structure */
48
49/* header */
50typedef struct S69HEADER {
51 UBYTE marker[2];
52 CHAR message[108];
53 UBYTE nos;
54 UBYTE RBnop;
55 UBYTE looporder;
56 UBYTE orders[0x80];
57 UBYTE tempos[0x80];
58 UBYTE breaks[0x80];
59} S69HEADER;
60
61/* sample information */
62typedef struct S69SAMPLE {
63 CHAR filename[13];
64 SLONG length;
65 SLONG loopbeg;
66 SLONG loopend;
67} S69SAMPLE;
68
69/* encoded note */
70typedef struct S69NOTE {
71 UBYTE a,b,c;
72} S69NOTE;
73
74/*========== Loader variables */
75
76/* current pattern */
77static S69NOTE* s69pat=NULL;
78/* Module header */
79static S69HEADER* mh=NULL;
80
81/* file type identification */
82static const CHAR* S69_Version[]={
83 "Composer 669",
84 "Extended 669"
85};
86
87/*========== Loader code */
88
89static int S69_Test(void)
90{
91 UBYTE buf[0x80];
92
93 if(!_mm_read_UBYTES(buf,2,modreader))
94 return 0;
95 /* look for id */
96 if(!memcmp(buf,"if",2) || !memcmp(buf,"JN",2)) {
97 int i;
98
99 /* skip song message */
100 _mm_fseek(modreader,108,SEEK_CUR);
101 /* sanity checks */
102 if(_mm_read_UBYTE(modreader) > 64) return 0;
103 if(_mm_read_UBYTE(modreader) > 128) return 0;
104 if(_mm_read_UBYTE(modreader) > 127) return 0;
105 /* check order table */
106 if(!_mm_read_UBYTES(buf,0x80,modreader)) return 0;
107 for(i=0;i<0x80;i++)
108 if((buf[i]>=0x80)&&(buf[i]!=0xff)) return 0;
109 /* check tempos table */
110 if(!_mm_read_UBYTES(buf,0x80,modreader)) return 0;
111 for(i=0;i<0x80;i++)
112 if((!buf[i])||(buf[i]>32)) return 0;
113 /* check pattern length table */
114 if(!_mm_read_UBYTES(buf,0x80,modreader)) return 0;
115 for(i=0;i<0x80;i++)
116 if(buf[i]>0x3f) return 0;
117 } else
118 return 0;
119
120 return 1;
121}
122
123static int S69_Init(void)
124{
125 if(!(s69pat=(S69NOTE *)MikMod_malloc(64*8*sizeof(S69NOTE)))) return 0;
126 if(!(mh=(S69HEADER *)MikMod_malloc(sizeof(S69HEADER)))) return 0;
127
128 return 1;
129}
130
131static void S69_Cleanup(void)
132{
133 MikMod_free(s69pat);
134 MikMod_free(mh);
135 mh=NULL;
136 s69pat=NULL;
137}
138
139static int S69_LoadPatterns(void)
140{
141 int track,row,channel;
142 UBYTE note,inst,vol,effect,lastfx,lastval;
143 S69NOTE *cur;
144 int tracks=0;
145
146 if(!AllocPatterns()) return 0;
147 if(!AllocTracks()) return 0;
148
149 for(track=0;track<of.numpat;track++) {
150 /* set pattern break locations */
151 of.pattrows[track]=mh->breaks[track]+1;
152
153 /* load the 669 pattern */
154 cur=s69pat;
155 for(row=0;row<64;row++) {
156 for(channel=0;channel<8;channel++,cur++) {
157 cur->a = _mm_read_UBYTE(modreader);
158 cur->b = _mm_read_UBYTE(modreader);
159 cur->c = _mm_read_UBYTE(modreader);
160 }
161 }
162
163 if(_mm_eof(modreader)) {
164 _mm_errno = MMERR_LOADING_PATTERN;
165 return 0;
166 }
167
168 /* translate the pattern */
169 for(channel=0;channel<8;channel++) {
170 UniReset();
171 /* set pattern tempo */
172 UniPTEffect(0xf,78);
173 UniPTEffect(0xf,mh->tempos[track]);
174
175 lastfx=0xff,lastval=0;
176
177 for(row=0;row<=mh->breaks[track];row++) {
178 int a,b,c;
179
180 /* fetch the encoded note */
181 a=s69pat[(row*8)+channel].a;
182 b=s69pat[(row*8)+channel].b;
183 c=s69pat[(row*8)+channel].c;
184
185 /* decode it */
186 note=a>>2;
187 inst=((a&0x3)<<4)|((b&0xf0)>>4);
188 vol=b&0xf;
189
190 if (a<0xff) {
191 if (a<0xfe) {
192 UniInstrument(inst);
193 UniNote(note+2*OCTAVE);
194 lastfx=0xff; /* reset background effect memory */
195 }
196 UniPTEffect(0xc,vol<<2);
197 }
198
199 if ((c!=0xff)||(lastfx!=0xff)) {
200 if(c==0xff)
201 c=lastfx,effect=lastval;
202 else
203 effect=c&0xf;
204
205 switch(c>>4) {
206 case 0: /* porta up */
207 UniPTEffect(0x1,effect);
208 lastfx=c,lastval=effect;
209 break;
210 case 1: /* porta down */
211 UniPTEffect(0x2,effect);
212 lastfx=c,lastval=effect;
213 break;
214 case 2: /* porta to note */
215 UniPTEffect(0x3,effect);
216 lastfx=c,lastval=effect;
217 break;
218 case 3: /* frequency adjust */
219 /* DMP converts this effect to S3M FF1. Why not ? */
220 UniEffect(UNI_S3MEFFECTF,0xf0|effect);
221 break;
222 case 4: /* vibrato */
223 UniPTEffect(0x4,effect);
224 lastfx=c,lastval=effect;
225 break;
226 case 5: /* set speed */
227 if (effect)
228 UniPTEffect(0xf,effect);
229 else
230 if(mh->marker[0]!=0x69) {
231#ifdef MIKMOD_DEBUG
232 fprintf(stderr,"\r669: unsupported super fast tempo at pat=%d row=%d chan=%d\n",
233 track,row,channel);
234#endif
235 }
236 break;
237 }
238 }
239 UniNewline();
240 }
241 if(!(of.tracks[tracks++]=UniDup())) return 0;
242 }
243 }
244
245 return 1;
246}
247
248static int S69_Load(int curious)
249{
250 int i;
251 SAMPLE *current;
252 S69SAMPLE sample;
253 (void)curious;
254
255 /* module header */
256 _mm_read_UBYTES(mh->marker,2,modreader);
257 _mm_read_UBYTES(mh->message,108,modreader);
258 mh->nos=_mm_read_UBYTE(modreader);
259 mh->RBnop=_mm_read_UBYTE(modreader);
260 mh->looporder=_mm_read_UBYTE(modreader);
261 _mm_read_UBYTES(mh->orders,0x80,modreader);
262 for(i=0;i<0x80;i++)
263 if ((mh->orders[i]>=0x80)&&(mh->orders[i]!=0xff)) {
264 _mm_errno=MMERR_NOT_A_MODULE;
265 return 1;
266 }
267 _mm_read_UBYTES(mh->tempos,0x80,modreader);
268 for(i=0;i<0x80;i++)
269 if ((!mh->tempos[i])||(mh->tempos[i]>32)) {
270 _mm_errno=MMERR_NOT_A_MODULE;
271 return 1;
272 }
273 _mm_read_UBYTES(mh->breaks,0x80,modreader);
274 for(i=0;i<0x80;i++)
275 if (mh->breaks[i]>0x3f) {
276 _mm_errno=MMERR_NOT_A_MODULE;
277 return 1;
278 }
279
280 /* set module variables */
281 of.initspeed=4;
282 of.inittempo=78;
283 of.songname=DupStr(mh->message,36,1);
284 of.modtype=MikMod_strdup(S69_Version[memcmp(mh->marker,"JN",2)==0]);
285 of.numchn=8;
286 of.numpat=mh->RBnop;
287 of.numins=of.numsmp=mh->nos;
288 of.numtrk=of.numchn*of.numpat;
289 of.flags=UF_XMPERIODS|UF_LINEAR;
290
291 for(i= 35;(i>= 0)&&(mh->message[i]==' ');i--) mh->message[i]=0;
292 for(i=36+35;(i>=36+0)&&(mh->message[i]==' ');i--) mh->message[i]=0;
293 for(i=72+35;(i>=72+0)&&(mh->message[i]==' ');i--) mh->message[i]=0;
294 if((mh->message[0])||(mh->message[36])||(mh->message[72]))
295 if((of.comment=(CHAR*)MikMod_malloc(3*(36+1)+1)) != NULL) {
296 strncpy(of.comment,mh->message,36);
297 strcat(of.comment,"\r");
298 if (mh->message[36]) strncat(of.comment,mh->message+36,36);
299 strcat(of.comment,"\r");
300 if (mh->message[72]) strncat(of.comment,mh->message+72,36);
301 strcat(of.comment,"\r");
302 of.comment[3*(36+1)]=0;
303 }
304
305 if(!AllocPositions(0x80)) return 0;
306 for(i=0;i<0x80;i++) {
307 if(mh->orders[i]>=mh->RBnop) break;
308 of.positions[i]=mh->orders[i];
309 }
310 of.numpos=i;
311 of.reppos=mh->looporder<of.numpos?mh->looporder:0;
312
313 if(!AllocSamples()) return 0;
314 current=of.samples;
315
316 for(i=0;i<of.numins;i++) {
317 /* sample information */
318 _mm_read_UBYTES((UBYTE*)sample.filename,13,modreader);
319 sample.length=_mm_read_I_SLONG(modreader);
320 sample.loopbeg=_mm_read_I_SLONG(modreader);
321 sample.loopend=_mm_read_I_SLONG(modreader);
322 /* Note: 'Lost in Germany' has 0xf0ffff as marker */
323 if (sample.loopend>=0xfffff) sample.loopend=0;
324
325 if((sample.length<0)||(sample.loopbeg<-1)||(sample.loopend<-1)) {
326 _mm_errno = MMERR_LOADING_HEADER;
327 return 0;
328 }
329
330 current->samplename=DupStr(sample.filename,13,1);
331 current->seekpos=0;
332 current->speed=128; /* Used as finetune when UF_XMPERIODS is enabled; 128 is centered. */
333 current->length=sample.length;
334 current->loopstart=sample.loopbeg;
335 current->loopend=sample.loopend;
336 current->flags=(sample.loopbeg<sample.loopend)?SF_LOOP:0;
337 current->volume=64;
338
339 current++;
340 }
341
342 if(!S69_LoadPatterns()) return 0;
343
344 return 1;
345}
346
347static CHAR *S69_LoadTitle(void)
348{
349 CHAR s[36];
350
351 _mm_fseek(modreader,2,SEEK_SET);
352 if(!_mm_read_UBYTES(s,36,modreader)) return NULL;
353
354 return(DupStr(s,36,1));
355}
356
357/*========== Loader information */
358
359MIKMODAPI MLOADER load_669={
360 NULL,
361 "669",
362 "669 (Composer 669, Unis 669)",
363 S69_Init,
364 S69_Test,
365 S69_Load,
366 S69_Cleanup,
367 S69_LoadTitle
368};
369
370/* ex:set ts=4: */