A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 370 lines 8.9 kB view raw
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: */