Initial commit.
[amiga/xmodule.git] / Hooks / MMDHook.c
1 /*
2 **      MMDHook.c
3 **
4 **      Copyright (C) 1994,95 by Bernardo Innocenti
5 **
6 **      External loader and saver hook for MMD0 or MMD1 modules.
7 **
8 **      Note: Sorry, this source is a bit of a mess, but the MMD
9 **            format is awfully complex <grin>.
10 **
11 **
12 **      Structure of an MMD0/1/2 module as saved by SaveMED():
13 **
14 **      MMD0
15 **      MMD0song
16 **      MMD0exp
17 **      InstrExt[]
18 **      InstrInfo[]
19 **      <AnnoTxt>
20 **      <SongName>
21 **      NotationInfo
22 **      MMD0blockarr
23 **      MMD0samparr
24 **              <blocks>
25 **              <BlockInfo>             /* Only for MMD1/MMD2 */
26 **              <BlockName>             /* Only for MMD1/MMD2 */
27 **      <samples>
28 */
29
30 #include <exec/types.h>
31
32 #include <clib/exec_protos.h>
33 #include <clib/dos_protos.h>
34 #include <clib/xmodule_protos.h>
35
36 #include <pragmas/exec_sysbase_pragmas.h>
37 #include <pragmas/dos_pragmas.h>
38 #include <pragmas/xmodule_pragmas.h>
39
40 #include "XModule.h"
41 #include "Gui.h"
42 #include "OctaMed.h"
43
44
45 #define OFFSET_MMD0song         sizeof (struct MMD0)
46 #define OFFSET_MMD0exp          (sizeof (struct MMD0) + sizeof (struct MMD0song))
47 #define OFFSET_InstrExt         (sizeof (struct MMD0) + sizeof (struct MMD0song) + sizeof (struct MMD0exp))
48
49
50
51 static INLINE UBYTE DecodeEff (UBYTE eff, UBYTE *effval, UBYTE flags, UBYTE flags2);
52
53
54
55 /* Effects conversion table */
56 static const UBYTE Effects[MAXTABLEEFFECTS] =
57 {
58 /*   MED                XModule                 Val */
59
60         0x00,   //      Null effect             $00
61
62         0x01,   //      Portamento Up   $01
63         0x02,   //      Portamento Down $02
64         0x03,   //      Tone Portamento $03
65         0x14,   //      Vibrato                 $04
66         0x05,   //      ToneP + VolSl   $05
67         0x06,   //      Vibra + VolSl   $06
68         0x07,   //      Tremolo                 $07
69         0x08,   //      Set Hold/Decay  $08
70         0x19,   //      Sample Offset   $09
71         0x0A,   //      Volume Slide    $0A
72         0x0B,   //      Position Jump   $0B
73         0x0C,   //      Set Volume              $0C
74         0x1D,   //      Pattern break   $0D
75         0x00,   //      Misc                    $0E
76         0x09,   //      Set Speed               $0F
77         0x0F,   //      Set Tempo               $10
78         0x00,   //      Arpeggio                $11
79
80         0x00,   //      Oktalyzer H
81         0x00,   //      Oktalyzer L
82 };
83
84
85
86 LONG GetMED (struct SongInfo *si, BPTR fp)
87 {
88         struct Instrument *inst = si->Inst;
89         struct MMD0 mmd0;
90         struct MMD0song song;
91         ULONG i, j, k, mmdtype, len;
92
93         /******************************/
94         /*      Read MMD0 header      */
95         /******************************/
96
97         if (Read (fp, &mmd0, sizeof (mmd0)) != sizeof (mmd0))
98                 return ERR_READWRITE;
99
100         mmdtype = mmd0.id & 0xFF;
101
102         if (GuiSwitches.Verbose)
103                 ShowMessage (MSG_READING_MMD, mmdtype);
104
105         mmdtype -= '0';
106
107         if (mmdtype > 1)
108         {
109                 ShowMessage (MSG_UNSUPPORTED_MMD_FORMAT);
110                 return ERR_NOTMODULE;
111         }
112
113
114         /******************************/
115         /*  Read MMD0song structure   */
116         /******************************/
117
118         if (Seek (fp, (LONG)mmd0.song, OFFSET_BEGINNING) == -1)
119                 return ERR_READWRITE;
120
121         if (Read (fp, &song, sizeof (song)) != sizeof (song))
122                 return ERR_READWRITE;
123
124
125         /* Set instruments parameters */
126
127         for (i = 0; i <= song.numsamples; i++)
128         {
129                 inst[i+1].Volume = song.sample[i].svol;
130
131                 if (song.sample[i].rep || song.sample[i].replen != 1)
132                 {
133                         inst[i+1].Repeat = song.sample[i].rep << 1;
134                         inst[i+1].Replen = song.sample[i].replen << 1;
135                 }
136         }
137
138         /* Set position table */
139         if (xmSetSongLen (si, song.songlen))
140                 for (i = 0; i < si->Length; i++)
141                         si->Sequence[i] = song.playseq[i];
142
143
144         if (song.flags2 & FLAG2_BPM)
145                 si->GlobalTempo = (song.deftempo * 4) / ((song.flags2 & FLAG2_BMASK) + 1);
146         else
147                 si->GlobalTempo = song.deftempo;
148
149         si->GlobalSpeed = song.tempo2;
150
151
152
153         /******************************/
154         /*   Read MMD0exp structure   */
155         /******************************/
156
157         if (mmd0.expdata)
158         {
159                 struct MMD0exp exp;
160
161                 if (Seek (fp, (LONG)mmd0.expdata, OFFSET_BEGINNING) == -1)
162                         return ERR_READWRITE;
163
164                 if (Read (fp, &exp, sizeof (exp)) != sizeof (exp))
165                         return ERR_READWRITE;
166
167
168                 /******************************/
169                 /*  Read InstrExt structures  */
170                 /******************************/
171
172                 if (exp.exp_smp && (exp.s_ext_entrsz >= 4))
173                 {
174                         struct InstrExt instrext;
175                         ULONG                   size = min (exp.s_ext_entrsz, sizeof (instrext));
176
177                         if (Seek (fp, (LONG)exp.exp_smp, OFFSET_BEGINNING) == -1)
178                                 return ERR_READWRITE;
179
180                         for (i = 1; i <= exp.s_ext_entries; i++)
181                         {
182                                 if (Read (fp, &instrext, size) != size)
183                                         return ERR_READWRITE;
184
185                                 inst[i].FineTune = instrext.finetune;
186                                 if (exp.s_ext_entrsz > size)
187                                         if (Seek (fp, exp.s_ext_entrsz - size, OFFSET_CURRENT) == -1)
188                                                 return ERR_READWRITE;
189                         }
190                 }
191
192                 /******************************/
193                 /*      Read Annotation       */
194                 /******************************/
195
196                 if (exp.annotxt && exp.annolen > 1)
197                 {
198                         ULONG len = min (exp.annolen, MAXAUTHNAME-1);
199
200                         if (Seek (fp, (LONG)exp.annotxt, OFFSET_BEGINNING) == -1)
201                                 return ERR_READWRITE;
202
203                         if (Read (fp, si->Author, len) != len)
204                                 return ERR_READWRITE;
205                 }
206
207
208                 /******************************/
209                 /*  Read InstrInfo structures */
210                 /******************************/
211
212                 if (exp.iinfo && (exp.i_ext_entrsz >= sizeof (struct MMDInstrInfo)))
213                 {
214                         struct MMDInstrInfo instrinfo;
215
216                         if (Seek (fp, (LONG)exp.iinfo, OFFSET_BEGINNING) == -1)
217                                 return ERR_READWRITE;
218
219                         for (i = 1; i <= exp.i_ext_entries; i++)
220                         {
221                                 if (Read (fp, &instrinfo, sizeof (instrinfo)) != sizeof (instrinfo))
222                                         return ERR_READWRITE;
223
224                                 strncpy (inst[i].Name, instrinfo.name, MAXINSTNAME - 1);
225
226                                 if (exp.i_ext_entrsz > sizeof (struct MMDInstrInfo))
227                                         if (Seek (fp, exp.i_ext_entrsz - sizeof (struct MMDInstrInfo), OFFSET_CURRENT) == -1)
228                                                 return ERR_READWRITE;
229                         }
230                 }
231
232                 /******************************/
233                 /*       Read SongName        */
234                 /******************************/
235
236                 if (exp.songname && exp.songnamelen > 1)
237                 {
238                         ULONG size = min (exp.songnamelen, MAXSONGNAME-1);
239
240                         if (Seek (fp, (LONG)exp.songname, OFFSET_BEGINNING) == -1)
241                                 return ERR_READWRITE;
242
243                         if (Read (fp, si->SongName, size) != size)
244                                 return ERR_READWRITE;
245                 }
246         }
247
248
249         /******************************/
250         /*        Read blocarr        */
251         /******************************/
252
253         if (mmd0.blockarr)
254         {
255                 LONG *blockarr;
256
257                 DisplayAction (MSG_READING_PATTS);
258
259                 if (Seek (fp, (LONG)mmd0.blockarr, OFFSET_BEGINNING) == -1)
260                         return ERR_READWRITE;
261
262                 if (!(blockarr = AllocMem (song.numblocks * 4, MEMF_ANY)))
263                         return ERROR_NO_FREE_STORE;
264
265                 if (Read (fp, blockarr, song.numblocks * 4) != song.numblocks * 4)
266                 {
267                         FreeMem (blockarr, song.numblocks * 4);
268                         return ERR_READWRITE;
269                 }
270
271
272
273                 /***************/
274                 /* Read blocks */
275                 /***************/
276
277                 for (i = 0; i < song.numblocks; i++)
278                 {
279                         struct Pattern          *patt;
280                         struct Note                     *note;
281                         struct MMD1block         block;
282                         struct MMD0block         mmd0block;
283                         struct BlockInfo         blockinfo;
284                         ULONG mednote;
285
286                         if (DisplayProgress (i, song.numblocks))
287                         {
288                                 FreeMem (blockarr, song.numblocks * 4);
289                                 return ERROR_BREAK;
290                         }
291
292                         if (Seek (fp, (LONG)blockarr[i], OFFSET_BEGINNING) == -1)
293                                 ShowMessage (MSG_ERR_CANT_LOAD_PATT, i);
294                         else
295                         {
296                                 if (mmdtype == 0)
297                                         len = Read (fp, &mmd0block, sizeof (struct MMD0block));
298                                 else
299                                         len = Read (fp, &block, sizeof (struct MMD1block));
300
301                                 if (len != (mmdtype ? sizeof (struct MMD1block) : sizeof (struct MMD0block)))
302                                         ShowMessage (MSG_ERR_CANT_LOAD_PATT, i);
303                                 else
304                                 {
305                                         if (mmdtype == 0)       /* Convert MMD0 block */
306                                         {
307                                                 block.numtracks = mmd0block.numtracks;
308                                                 block.lines     = mmd0block.lines;
309                                                 block.info      = 0;
310                                         }
311
312                                         if (block.numtracks >= MAXTRACKS)
313                                         {
314                                                 ShowMessage (MSG_PATT_TOO_MANY_TRACKS, i, MAXTRACKS);
315                                                 continue;
316                                         }
317
318                                         if (si->MaxTracks < block.numtracks)
319                                                 si->MaxTracks = block.numtracks;
320
321                                         if (block.lines > MAXPATTLINES - 1)
322                                         {
323                                                 ShowMessage (MSG_PATT_TOO_MANY_LINES, i, MAXPATTLINES);
324                                                 block.lines = MAXPATTLINES - 1;
325                                         }
326
327                                         if (!(patt = AddPattern (si, block.numtracks, block.lines + 1)))
328                                         {
329                                                 FreeMem (blockarr, song.numblocks * 4);
330                                                 return ERROR_NO_FREE_STORE;
331                                         }
332
333                                         /***************/
334                                         /* Read Tracks */
335                                         /***************/
336
337                                         for (k = 0; k < patt->Lines; k++)
338                                         {
339                                                 for (j = 0; j < patt->Tracks; j++)
340                                                 {
341                                                         note = &patt->Notes[j][k];
342
343                                                         if (mmdtype == 0)
344                                                         {
345                                                                 if (Read (fp, &mednote, MMD0ROW) != MMD0ROW)
346                                                                         return ERR_READWRITE;
347
348                                                                 mednote >>= 8;
349
350                                                                 note->EffVal = mednote & 0xFF;
351                                                                 note->EffNum = DecodeEff ((mednote >> 8) & 0x0F, &note->EffVal, song.flags, song.flags2);
352                                                                 note->Note = ((mednote >> 16) & 0x3F);
353                                                                 if (note->Note) note->Note += 12;
354                                                                 note->Inst = ((mednote >> 12) & 0xF) |
355                                                                         ((mednote & (1<<22)) ? 0x20 : 0) | ((mednote & (1<<23)) ? 0x10 : 0);
356                                                         }
357                                                         else
358                                                         {
359                                                                 if (Read (fp, &mednote, MMD1ROW) != MMD1ROW)
360                                                                         return ERR_READWRITE;
361
362                                                                 note->EffVal = mednote & 0xFF;
363                                                                 note->EffNum = DecodeEff ((mednote >> 8) & 0xFF, &note->EffVal, song.flags, song.flags2);
364                                                                 note->Inst = ((mednote >> 16) & 0x3F);
365                                                                 note->Note = ((mednote >> 24) & 0x7F);
366
367                                                                 if (note->Note) note->Note += 12;
368                                                         }
369                                                 }
370                                         }       /* End for (patt->Tracks) */
371
372                                         /******************/
373                                         /* Read BlockInfo */
374                                         /******************/
375
376                                         if (block.info)
377                                                 if (Seek (fp, (LONG)block.info, OFFSET_BEGINNING) != -1);
378                                                 {
379                                                         if (Read (fp, &blockinfo, sizeof (blockinfo)) == sizeof(blockinfo))
380                                                         {
381                                                                 if (blockinfo.blockname)
382                                                                         if (Seek (fp, (LONG)blockinfo.blockname, OFFSET_BEGINNING) != -1);
383                                                                         {
384                                                                                 Read (fp, patt->PattName, min (blockinfo.blocknamelen, MAXPATTNAME));
385                                                                                 patt->PattName[MAXPATTNAME-1] = '\0';   /* Ensure NULL-termination */
386                                                                         }
387                                                         }
388                                                 }
389                                 }
390                         }
391                 }       /* End for (song->numblocks) */
392
393                 FreeMem (blockarr, song.numblocks * 4);
394         }
395
396
397
398         /******************************/
399         /*        Read smplarr        */
400         /******************************/
401
402         if (mmd0.smplarr)
403         {
404                 LONG *smplarr;
405                 struct InstrHdr instrhdr;
406
407                 DisplayAction (MSG_READING_INSTS);
408
409                 if (Seek (fp, (LONG)mmd0.smplarr, OFFSET_BEGINNING) == -1)
410                         return ERR_READWRITE;
411
412                 if (!(smplarr = AllocMem (song.numsamples * 4, MEMF_ANY)))
413                         return ERROR_NO_FREE_STORE;
414
415                 if (Read (fp, smplarr, song.numsamples * 4) != song.numsamples * 4)
416                 {
417                         FreeMem (smplarr, song.numsamples * 4);
418                         return ERR_READWRITE;
419                 }
420
421
422                 /******************************/
423                 /*        Read samples        */
424                 /******************************/
425
426                 for (i = 1; i <= song.numsamples ; i++)
427                 {
428                         if (!smplarr[i-1]) continue;
429
430                         if (DisplayProgress (i, song.numsamples))
431                         {
432                                 FreeMem (smplarr, song.numsamples * 4);
433                                 return ERROR_BREAK;
434                         }
435
436                         if (Seek (fp, smplarr[i-1], OFFSET_BEGINNING) == -1)
437                                 ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
438                         else
439                         {
440                                 if (Read (fp, &instrhdr, sizeof (instrhdr)) != sizeof (instrhdr))
441                                         ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
442                                 else
443                                 {
444                                         switch (instrhdr.type)
445                                         {
446                                                 case SAMPLE:
447                                                         inst[i].Length = instrhdr.length;
448
449                                                         if (!(inst[i].SampleData = (UBYTE *) AllocVec (inst[i].Length, MEMF_SAMPLE)))
450                                                         {
451                                                                 ShowMessage (MSG_ERR_NO_MEM_FOR_INST, i);
452                                                                 break;
453                                                         }
454
455                                                         if (Read (fp, inst[i].SampleData, inst[i].Length) != inst[i].Length)
456                                                                 ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
457
458                                                         /* Double sample data */
459                                                         if (song.flags & FLAG_8CHANNEL)
460                                                         {
461                                                                 BYTE *data = inst[i].SampleData;
462
463                                                                 for (j = 0; j < inst[j].Length; j++)
464                                                                         *data++ <<= 1;
465                                                         }
466                                                         break;
467
468                                                 default:
469                                                         ShowMessage (MSG_ERR_NOT_A_SAMPLE, i);
470                                                         break;
471                                         }
472                                 }
473                         }
474                 }
475
476                 FreeMem (smplarr, song.numsamples * 4);
477         }
478
479         return RETURN_OK;
480 }
481
482
483
484 static __inline UBYTE DecodeEff (UBYTE eff, UBYTE *effval, UBYTE flags, UBYTE flags2)
485 {
486         UBYTE i;
487
488         switch (eff)
489         {
490                 case 0x00:
491                         if (*effval) return EFF_ARPEGGIO;
492                         else return EFF_NULL;
493
494                 case 0x0C:      /* Volume */
495                         if (!(flags & FLAG_VOLHEX))
496                                 /* Convert decimal volumes */
497                                 *effval = (*effval & 0x0F) + (*effval >> 4) * 10;
498                         return EFF_SETVOLUME;
499
500                 case 0x0F:      /* Tempo */
501                         if (*effval == 0) return EFF_PATTERNBREAK;
502                         if (*effval <= 0x0A) return EFF_SETSPEED;
503                         if (*effval <= 0xF0)
504                         {
505                                 *effval = (*effval * 4) / ((flags2 & FLAG2_BMASK) + 1);
506                                 return EFF_SETTEMPO;
507                         }
508                         if (*effval == 0xF8)
509                         {
510                                 *effval = 1;
511                                 return EFF_MISC;        /* Filter off */
512                         }
513                         if (*effval == 0xF9)
514                         {
515                                 *effval = 0;
516                                 return EFF_MISC;        /* Filter on */
517                         }
518                         return EFF_NULL;
519
520                 default:
521                         for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
522                                 if (eff == Effects[i])
523                                         return i;
524         }
525
526         return 0;
527 }
528
529
530
531 LONG SaveMED (struct SongInfo *si, BPTR fp, UWORD mmd_type)
532 {
533         struct Instrument       *inst = si->Inst;
534         struct Pattern          *patt = si->PattData;
535         ULONG   modlen, blockslen, instlen, blockptr, offset_blockarr,
536                         i, j, k, lastinstr;
537         BOOL    halve_instr;
538
539         if (GuiSwitches.Verbose)
540                 ShowMessage (MSG_WRITING_MMD, mmd_type + '0');
541
542         DisplayAction (MSG_WRITING_HEADER);
543
544
545         /*********************************/
546         /* Calculate total patterns size */
547         /*********************************/
548         {
549                 if (mmd_type > 0)       /* MMD1 */
550                 {
551                         for (i = 0, blockslen = 0; i < si->NumPatterns ; i++)
552                         {
553                                 blockslen += sizeof (struct MMD1block) +
554                                         (MMD1ROW * patt[i].Lines * patt[i].Tracks) +
555                                         (patt[i].PattName[0] ?
556                                                  (sizeof (struct BlockInfo) + strlen (patt[i].PattName) + 1)
557                                                  : 0);
558
559                                 if (blockslen & 1) blockslen++; /* Pad to words */
560                         }
561                 }
562                 else    /* MMD0 */
563                 {
564                         for (i = 0, blockslen = 0; i < si->NumPatterns ; i++)
565                         {
566                                 blockslen += sizeof (struct MMD0block) +
567                                         (MMD0ROW * patt[i].Lines * patt[i].Tracks);
568
569                                 if (blockslen & 1) blockslen++; /* Pad to words */
570                         }
571                 }
572         }
573
574         /* Find last used instrument */
575         for (lastinstr = 63; lastinstr > 0 ; lastinstr--)
576                 if (inst[lastinstr].Length || inst[lastinstr].Name[0]) break;
577
578         /* Calculate Total instruments size */
579         for (i = 1, instlen = 0; i <= lastinstr; i++)
580                 instlen += (inst[i].Length) ? (si->Inst[i].Length + sizeof (struct InstrHdr)) : 0;
581
582         /* Calculate blockarr offset */
583         offset_blockarr = sizeof (struct MMD0) + sizeof (struct MMD0song) + sizeof (struct MMD0exp) +
584                 lastinstr * (sizeof (struct InstrExt) + sizeof (struct MMDInstrInfo));
585
586         offset_blockarr += strlen (si->Author) + 1;
587         if (offset_blockarr & 1) offset_blockarr++;             /* Pad to word */
588         offset_blockarr += strlen (si->SongName) + 1;
589         if (offset_blockarr & 1) offset_blockarr++;             /* Pad to word */
590
591         /* Calculate Total module size */
592         modlen =        offset_blockarr +
593                                 si->NumPatterns * 4 +           /* blockarr             */
594                                 lastinstr * 4 +                         /* samplearr    */
595                                 blockslen +
596                                 instlen;
597
598         /**********************************/
599         /* Fill-in & write module header */
600         /*********************************/
601         {
602                 struct MMD0 mmd0;
603
604                 memset (&mmd0, 0, sizeof (mmd0));
605
606                 mmd0.id                 = mmd_type ? ID_MMD1 : ID_MMD0;
607                 mmd0.modlen     = modlen;
608                 mmd0.song               = (struct MMD0song *) OFFSET_MMD0song;
609 //              mmd0.reserved0  = 0;
610                 mmd0.blockarr   = (struct MMD0Block **) offset_blockarr;
611 //              mmd0.reserved1  = si->NumPatterns;
612                 mmd0.smplarr    = (struct InstrHdr **) (offset_blockarr + (si->NumPatterns * 4));
613 //              mmd0.reserved2  = lastinst * sizeof (LONG);
614                 mmd0.expdata    = (struct MMD0exp *) OFFSET_MMD0exp;
615 //              mmd0.reserved3  = 0;                                    /* expsize */
616 //              mmd0.pstate             = 0;                                    /* the state of the player */
617                 mmd0.pblock             = si->CurrentPatt;              /* current block */
618 //              mmd0.pline              = 0;                                    /* current line */
619                 mmd0.pseqnum    = si->CurrentPos;               /* current # of playseqlist */
620                 mmd0.actplayline= -1;                                   /* OBSOLETE!! DON'T TOUCH! */
621 //              mmd0.counter    = 0;                                    /* delay between notes */
622 //              mmd0.extra_songs= 0;
623
624                 /* Write file header */
625                 if (Write (fp, &mmd0, sizeof (mmd0)) != sizeof (mmd0))
626                         return ERR_READWRITE;
627
628         }
629
630         /************************************/
631         /* Fill-in and write Song structure */
632         /************************************/
633         {
634                 struct MMD0song song;
635
636                 for (i = 0; i < 63 ; i++)
637                 {
638                         song.sample[i].rep                      = inst[i+1].Repeat >> 1;
639                         song.sample[i].replen           = inst[i+1].Replen >> 1;
640                         song.sample[i].midich           = 0;
641                         song.sample[i].midipreset       = 0;
642                         song.sample[i].svol                     = inst[i+1].Volume;
643                         song.sample[i].strans           = 0;
644                 }
645
646                 song.numblocks  = si->NumPatterns;
647                 song.songlen    = min (si->Length, 256);
648
649                 for (i = 0; i < song.songlen; i++)
650                         song.playseq[i] = si->Sequence[i];
651
652                 song.deftempo   = si->GlobalTempo;
653                 song.playtransp = 0;
654                 halve_instr             = si->MaxTracks > 4;
655                 song.flags              = (halve_instr ? FLAG_8CHANNEL : 0) | FLAG_STSLIDE | FLAG_VOLHEX;
656                 song.flags2             = 3 | FLAG2_BPM;
657                 song.tempo2             = si->GlobalSpeed;
658
659                 for (i = 0; i <16; i++)
660                         song.trkvol[i] = 64;
661
662                 song.mastervol  = 64;
663                 song.numsamples = lastinstr;
664
665                 if (Write (fp, &song, sizeof (song)) != sizeof (song))
666                         return ERR_READWRITE;
667         }
668
669         /*****************************/
670         /* Write extension (MMD0exp) */
671         /*****************************/
672         {
673                 struct MMD0exp mmd0exp;
674                 LONG len;
675
676                 memset (&mmd0exp, 0, sizeof (mmd0exp));
677
678                 mmd0exp.exp_smp                 = (struct InstrExt *) OFFSET_InstrExt;
679                 mmd0exp.s_ext_entries   = lastinstr;
680                 mmd0exp.s_ext_entrsz    = sizeof (struct InstrExt);
681                 mmd0exp.annotxt                 = (STRPTR) (OFFSET_InstrExt + lastinstr * (sizeof (struct InstrExt) + sizeof (struct MMDInstrInfo)));
682                 mmd0exp.annolen                 = strlen (si->Author) + 1;
683                 mmd0exp.iinfo                   = (struct MMDInstrInfo *) (OFFSET_InstrExt + lastinstr * sizeof (struct InstrExt));
684                 mmd0exp.i_ext_entries   = lastinstr;
685                 mmd0exp.i_ext_entrsz    = sizeof (struct MMDInstrInfo);
686                 mmd0exp.songname                = mmd0exp.annotxt + mmd0exp.annolen + ((mmd0exp.annolen & 1) ? 1 : 0);
687                 mmd0exp.songnamelen             = strlen (si->SongName) + 1;
688
689                 if (Write (fp, &mmd0exp, sizeof (mmd0exp)) != sizeof (mmd0exp))
690                         return ERR_READWRITE;
691
692
693                 /************************/
694                 /* Write InstrExt array */
695                 /************************/
696                 {
697                         struct InstrExt instrext = { 0 };
698
699                         for (i = 1; i <= lastinstr; i++)
700                         {
701                                 // instrext.hold                                = 0;
702                                 // instrext.decay                               = 0;
703                                 // instrext.suppress_midi_off   = 0;
704                                 instrext.finetune                               = si->Inst[i].FineTune;
705                                 // instrext.default_pitch               = 0;
706                                 // instrext.instr_flags                 = 0;
707                                 // instrext.long_midi_preset    = 0;
708                                 // instrext.output_device               = 0;
709                                 // instrext.reserved                    = 0;
710
711                                 if (Write (fp, &instrext, sizeof (instrext)) != sizeof (instrext))
712                                         return ERR_READWRITE;
713                         }
714                 }
715
716                 /*************************/
717                 /* Write InstrInfo array */
718                 /*************************/
719                 {
720                         struct MMDInstrInfo instrinfo = { 0 };
721
722                         for (i = 1; i <= lastinstr; i++)
723                         {
724                                 strcpy (instrinfo.name, si->Inst[i].Name);
725
726                                 if (Write (fp, &instrinfo, sizeof (instrinfo)) != sizeof (instrinfo))
727                                         return ERR_READWRITE;
728                         }
729                 }
730
731                 /* Write AnnoTxt */
732
733                 len = strlen (si->Author) + 1;
734                 if (len & 1) len++;     /* Pad to WORD */
735                 if (Write (fp, si->Author, len) != len)
736                         return ERR_READWRITE;
737
738                 /* Write SongName */
739
740                 len = strlen (si->SongName) + 1;
741                 if (len & 1) len++;     /* Pad to WORD */
742                 if (Write (fp, si->SongName, len) != len)
743                         return ERR_READWRITE;
744         }
745
746         /*************************************/
747         /* Write pattern pointers (blockarr) */
748         /*************************************/
749         {
750                 blockptr = offset_blockarr + (si->NumPatterns * 4) + (lastinstr * 4);
751
752                 for (i = 0; i < si->NumPatterns; i++)
753                 {
754                         if (Write (fp, &blockptr, 4) != 4)
755                                 return ERR_READWRITE;
756
757                         if (mmd_type)   /* MMD1 and up */
758                         {
759                                 blockptr += sizeof (struct MMD1block) +
760                                         (MMD1ROW * patt[i].Tracks * patt[i].Lines) +
761                                         ((patt[i].PattName[0]) ?
762                                         (sizeof (struct BlockInfo) + strlen (patt[i].PattName) + 1)
763                                         : 0);
764                         }
765                         else    /* MMD0 */
766                         {
767                                 blockptr += sizeof (struct MMD0block) +
768                                         (MMD0ROW * patt[i].Tracks * patt[i].Lines);
769                         }
770
771                         /* Pad to words */
772                         if (blockptr & 1)       blockptr++;
773                 }
774         }
775
776         /*************************************/
777         /* Write sample pointers (samplearr) */
778         /*************************************/
779         {
780                 ULONG sampleptr = blockptr, x;
781
782                 for (i = 1; i <= lastinstr; i++)
783                 {
784                         x = ((inst[i].Length && inst[i].SampleData) ? sampleptr : 0);
785
786                         if (Write (fp, &x, 4) != 4)
787                                 return ERR_READWRITE;
788
789                         if (x) sampleptr += inst[i].Length + sizeof (struct InstrHdr);
790                 }
791         }
792
793         /********************************/
794         /* Write patterns (blocks) data */
795         /********************************/
796         {
797                 struct Note     *note;
798                 ULONG            mednote;
799                 LONG             blockinfoptr = offset_blockarr + (si->NumPatterns * 4) + (lastinstr * 4);
800                 UBYTE            eff, effval;
801                 BOOL             quiet = FALSE;
802
803
804                 DisplayAction (MSG_WRITING_PATTS);
805
806
807                 for (i = 0; i < si->NumPatterns; i++)
808                 {
809                         if (DisplayProgress (i, si->NumPatterns))
810                                 return ERROR_BREAK;
811
812                         /* Write Header */
813
814                         if (mmd_type)   /* MMD1 and higher */
815                         {
816                                 struct MMD1block         block;
817
818                                 blockinfoptr +=  sizeof (struct MMD1block) +
819                                         (MMD1ROW * patt[i].Tracks * patt[i].Lines);
820
821                                 block.numtracks = patt[i].Tracks;
822                                 block.lines = patt[i].Lines - 1;
823
824                                 /* Write BlockInfo only if this pattern has a name */
825                                 block.info = (struct BlockInfo *)(patt[i].PattName[0] ? blockinfoptr : 0);
826
827                                 if (Write (fp, &block, sizeof (block)) != sizeof (block))
828                                         return ERR_READWRITE;
829                         }
830                         else    /* MMD0 */
831                         {
832                                 struct MMD0block         block;
833
834                                 block.numtracks = patt[i].Tracks;
835                                 block.lines = patt[i].Lines - 1;
836
837                                 if (Write (fp, &block, sizeof (block)) != sizeof (block))
838                                         return ERR_READWRITE;
839                         }
840
841
842                         /* Write Tracks */
843                         for (k = 0; k < patt[i].Lines; k++)
844                         {
845                                 for (j = 0; j < patt[i].Tracks; j++)
846                                 {
847                                         note = &patt[i].Notes[j][k];
848                                         eff = Effects[note->EffNum];
849
850                                         if (note->EffNum == EFF_MISC)
851                                         {
852 /*
853                 pt module       xmod bad conversion     octamed right conversion
854
855                 E01                     006                                     0FF8 (Led OFF)
856                 E11                     not converted           1101 (Slide pitch up once)
857                 EC3                     not converted           1803 (Cut note)
858                 E62                     not converted           1602 (Loop)
859                 601                     1601                            0601 (Vibrato and fade)
860                 E93                     not converted           1F03 (Note delay and retrigger)
861                 ED2                     ????                            1F20 (Note delay and retrigger)
862                 EB1                     not converted           1B01 (Slide volume down once)
863 */
864
865                                                 switch (note->EffNum >> 4)
866                                                 {
867                                                         case 0x6:       /* Loop */
868                                                                 eff = 0x16;
869                                                                 effval = note->EffNum & 0x0F;
870                                                                 break;
871                                                         case 0xC:       /* Note Cut */
872                                                                 eff = 0x18;
873                                                                 break;
874                                                         case 0xE:       /* Replay line */
875                                                                 eff = 0x1E;
876                                                                 effval = note->EffNum & 0x0F;
877                                                                 break;
878                                                 }
879                                         }
880                                         else if (note->EffNum == EFF_PATTERNBREAK)
881                                                 effval = 0;
882                                         else
883                                                 effval = note->EffVal;
884
885                                 //      if (note->EffNum == EFF_SETTEMPO)
886                                 //              effval /= 4;
887
888                                         if (mmd_type)   /* MMD1 and up */
889                                         {
890                                                 mednote = effval |
891                                                         ((ULONG)eff << 8) |
892                                                         ((ULONG)(note->Inst & 0x3F) << 16) |
893                                                         ((ULONG)((note->Note ? (note->Note - 12) : 0) & 0x7F) << 24);
894
895                                                 if (Write (fp, &mednote, MMD1ROW) != MMD1ROW)
896                                                         return ERR_READWRITE;
897                                         }
898                                         else
899                                         {
900                                                 if ((eff > 0x0F) && !quiet)
901                                                 {
902                                                         ShowMessage (MSG_WRONG_EFFECT_IN_MMD0, eff);
903                                                         quiet = TRUE;
904                                                 }
905
906                                                 mednote = (effval |
907                                                         ((ULONG)(eff & 0x0F) << 8) |
908                                                         ((ULONG)(note->Inst & 0x0F) << 12) |
909                                                         ((ULONG)((note->Note ? (note->Note - 12) : 0) & 0x3F) << 16) |
910                                                         ((ULONG)(note->Inst & 0x20) << 17) |
911                                                         ((ULONG)(note->Inst & 0x10) << 19)) << 8;
912
913                                                 if (Write (fp, &mednote, MMD0ROW) != MMD0ROW)
914                                                         return ERR_READWRITE;
915                                         }
916                                 }
917                         }
918
919                         if (mmd_type)
920                         {
921                                 if (patt[i].PattName[0])
922                                 {
923                                         /*****************************/
924                                         /* Write BlockInfo structure */
925                                         /*****************************/
926
927                                         struct BlockInfo blockinfo;
928
929                                         blockinfoptr += sizeof (blockinfo);
930
931                                         memset (&blockinfo, 0, sizeof (blockinfo));
932                                         blockinfo.blockname = (UBYTE *)blockinfoptr;
933                                         blockinfo.blocknamelen = strlen (patt[i].PattName) + 1;
934
935                                         if (Write (fp, &blockinfo, sizeof (blockinfo)) != sizeof (blockinfo))
936                                                 return ERR_READWRITE;
937
938                                         if (Write (fp, patt[i].PattName, blockinfo.blocknamelen) != blockinfo.blocknamelen)
939                                                 return ERR_READWRITE;
940
941                                         blockinfoptr += sizeof (struct BlockInfo) + blockinfo.blocknamelen;
942
943                                         /* Pad to words */
944                                         if (blockinfoptr & 1)
945                                         {
946                                                 UBYTE dummy;
947
948                                                 if (Write (fp, &dummy, 1) != 1)
949                                                         return ERR_READWRITE;
950
951                                                 blockinfoptr++;
952                                         }
953                                 }
954                         }
955                         else    /* MMD0 */
956                         {
957                                 /* Add a pad byte when length is not even */
958                                 if ((patt[i].Lines * patt[i].Tracks) & 1)
959                                 {
960                                         UBYTE dummy = 0;
961
962                                         if (Write (fp, &dummy, 1) != 1)
963                                                 return ERR_READWRITE;
964                                 }
965                         }
966                 }       /* End for (si->NumPatterns) */
967         }
968
969
970         /**********************/
971         /* Write samples data */
972         /**********************/
973         {
974                 struct InstrHdr instrhdr;
975                 UBYTE *samp;
976                 LONG l;
977                 BOOL free_samp;
978
979                 DisplayAction (MSG_WRITING_INSTDATA);
980
981                 for (i = 1; i <= lastinstr; i++)
982                 {
983                         free_samp = FALSE;
984
985                         l = inst[i].Length;
986                         if (!l || (!inst[i].SampleData)) continue;
987
988                         if (DisplayProgress (i, lastinstr))
989                                 return ERROR_BREAK;
990
991                         if (halve_instr)
992                         {
993                                 if (samp = AllocVec (l, MEMF_ANY))
994                                 {
995                                         /* Halve Volume */
996                                         for (k = 0; k < l; k++)
997                                                 samp[k] = inst[i].SampleData[k] >> 1;
998
999                                         free_samp = TRUE;       /* Free this buffer when you are done */
1000                                 }
1001                                 else
1002                                 {
1003                                         ShowMessage (MSG_NO_MEM_TO_HALVE, i);
1004                                         samp = inst[i].SampleData;
1005                                 }
1006                         }
1007                         else samp = inst[i].SampleData;
1008
1009                         instrhdr.length = l;
1010                         instrhdr.type = SAMPLE;
1011
1012                         if (Write (fp, &instrhdr, sizeof (instrhdr)) != sizeof (instrhdr))
1013                                 return ERR_READWRITE;
1014
1015                         if (l)
1016                                 if (Write (fp, samp, l) != l)
1017                                         return ERR_READWRITE;
1018
1019                         if (free_samp)
1020                                 FreeVec (samp);
1021                 }
1022         }
1023
1024         return RETURN_OK;
1025 }