4 ** Copyright (C) 1994,95 by Bernardo Innocenti
6 ** External loader and saver hook for MMD0 or MMD1 modules.
8 ** Note: Sorry, this source is a bit of a mess, but the MMD
9 ** format is awfully complex <grin>.
12 ** Structure of an MMD0/1/2 module as saved by SaveMED():
25 ** <BlockInfo> /* Only for MMD1/MMD2 */
26 ** <BlockName> /* Only for MMD1/MMD2 */
30 #include <exec/types.h>
32 #include <clib/exec_protos.h>
33 #include <clib/dos_protos.h>
34 #include <clib/xmodule_protos.h>
36 #include <pragmas/exec_sysbase_pragmas.h>
37 #include <pragmas/dos_pragmas.h>
38 #include <pragmas/xmodule_pragmas.h>
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))
51 static INLINE UBYTE DecodeEff (UBYTE eff, UBYTE *effval, UBYTE flags, UBYTE flags2);
55 /* Effects conversion table */
56 static const UBYTE Effects[MAXTABLEEFFECTS] =
60 0x00, // Null effect $00
62 0x01, // Portamento Up $01
63 0x02, // Portamento Down $02
64 0x03, // Tone Portamento $03
66 0x05, // ToneP + VolSl $05
67 0x06, // Vibra + VolSl $06
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
76 0x09, // Set Speed $0F
77 0x0F, // Set Tempo $10
86 LONG GetMED (struct SongInfo *si, BPTR fp)
88 struct Instrument *inst = si->Inst;
91 ULONG i, j, k, mmdtype, len;
93 /******************************/
94 /* Read MMD0 header */
95 /******************************/
97 if (Read (fp, &mmd0, sizeof (mmd0)) != sizeof (mmd0))
100 mmdtype = mmd0.id & 0xFF;
102 if (GuiSwitches.Verbose)
103 ShowMessage (MSG_READING_MMD, mmdtype);
109 ShowMessage (MSG_UNSUPPORTED_MMD_FORMAT);
110 return ERR_NOTMODULE;
114 /******************************/
115 /* Read MMD0song structure */
116 /******************************/
118 if (Seek (fp, (LONG)mmd0.song, OFFSET_BEGINNING) == -1)
119 return ERR_READWRITE;
121 if (Read (fp, &song, sizeof (song)) != sizeof (song))
122 return ERR_READWRITE;
125 /* Set instruments parameters */
127 for (i = 0; i <= song.numsamples; i++)
129 inst[i+1].Volume = song.sample[i].svol;
131 if (song.sample[i].rep || song.sample[i].replen != 1)
133 inst[i+1].Repeat = song.sample[i].rep << 1;
134 inst[i+1].Replen = song.sample[i].replen << 1;
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];
144 if (song.flags2 & FLAG2_BPM)
145 si->GlobalTempo = (song.deftempo * 4) / ((song.flags2 & FLAG2_BMASK) + 1);
147 si->GlobalTempo = song.deftempo;
149 si->GlobalSpeed = song.tempo2;
153 /******************************/
154 /* Read MMD0exp structure */
155 /******************************/
161 if (Seek (fp, (LONG)mmd0.expdata, OFFSET_BEGINNING) == -1)
162 return ERR_READWRITE;
164 if (Read (fp, &exp, sizeof (exp)) != sizeof (exp))
165 return ERR_READWRITE;
168 /******************************/
169 /* Read InstrExt structures */
170 /******************************/
172 if (exp.exp_smp && (exp.s_ext_entrsz >= 4))
174 struct InstrExt instrext;
175 ULONG size = min (exp.s_ext_entrsz, sizeof (instrext));
177 if (Seek (fp, (LONG)exp.exp_smp, OFFSET_BEGINNING) == -1)
178 return ERR_READWRITE;
180 for (i = 1; i <= exp.s_ext_entries; i++)
182 if (Read (fp, &instrext, size) != size)
183 return ERR_READWRITE;
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;
192 /******************************/
193 /* Read Annotation */
194 /******************************/
196 if (exp.annotxt && exp.annolen > 1)
198 ULONG len = min (exp.annolen, MAXAUTHNAME-1);
200 if (Seek (fp, (LONG)exp.annotxt, OFFSET_BEGINNING) == -1)
201 return ERR_READWRITE;
203 if (Read (fp, si->Author, len) != len)
204 return ERR_READWRITE;
208 /******************************/
209 /* Read InstrInfo structures */
210 /******************************/
212 if (exp.iinfo && (exp.i_ext_entrsz >= sizeof (struct MMDInstrInfo)))
214 struct MMDInstrInfo instrinfo;
216 if (Seek (fp, (LONG)exp.iinfo, OFFSET_BEGINNING) == -1)
217 return ERR_READWRITE;
219 for (i = 1; i <= exp.i_ext_entries; i++)
221 if (Read (fp, &instrinfo, sizeof (instrinfo)) != sizeof (instrinfo))
222 return ERR_READWRITE;
224 strncpy (inst[i].Name, instrinfo.name, MAXINSTNAME - 1);
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;
232 /******************************/
234 /******************************/
236 if (exp.songname && exp.songnamelen > 1)
238 ULONG size = min (exp.songnamelen, MAXSONGNAME-1);
240 if (Seek (fp, (LONG)exp.songname, OFFSET_BEGINNING) == -1)
241 return ERR_READWRITE;
243 if (Read (fp, si->SongName, size) != size)
244 return ERR_READWRITE;
249 /******************************/
251 /******************************/
257 DisplayAction (MSG_READING_PATTS);
259 if (Seek (fp, (LONG)mmd0.blockarr, OFFSET_BEGINNING) == -1)
260 return ERR_READWRITE;
262 if (!(blockarr = AllocMem (song.numblocks * 4, MEMF_ANY)))
263 return ERROR_NO_FREE_STORE;
265 if (Read (fp, blockarr, song.numblocks * 4) != song.numblocks * 4)
267 FreeMem (blockarr, song.numblocks * 4);
268 return ERR_READWRITE;
277 for (i = 0; i < song.numblocks; i++)
279 struct Pattern *patt;
281 struct MMD1block block;
282 struct MMD0block mmd0block;
283 struct BlockInfo blockinfo;
286 if (DisplayProgress (i, song.numblocks))
288 FreeMem (blockarr, song.numblocks * 4);
292 if (Seek (fp, (LONG)blockarr[i], OFFSET_BEGINNING) == -1)
293 ShowMessage (MSG_ERR_CANT_LOAD_PATT, i);
297 len = Read (fp, &mmd0block, sizeof (struct MMD0block));
299 len = Read (fp, &block, sizeof (struct MMD1block));
301 if (len != (mmdtype ? sizeof (struct MMD1block) : sizeof (struct MMD0block)))
302 ShowMessage (MSG_ERR_CANT_LOAD_PATT, i);
305 if (mmdtype == 0) /* Convert MMD0 block */
307 block.numtracks = mmd0block.numtracks;
308 block.lines = mmd0block.lines;
312 if (block.numtracks >= MAXTRACKS)
314 ShowMessage (MSG_PATT_TOO_MANY_TRACKS, i, MAXTRACKS);
318 if (si->MaxTracks < block.numtracks)
319 si->MaxTracks = block.numtracks;
321 if (block.lines > MAXPATTLINES - 1)
323 ShowMessage (MSG_PATT_TOO_MANY_LINES, i, MAXPATTLINES);
324 block.lines = MAXPATTLINES - 1;
327 if (!(patt = AddPattern (si, block.numtracks, block.lines + 1)))
329 FreeMem (blockarr, song.numblocks * 4);
330 return ERROR_NO_FREE_STORE;
337 for (k = 0; k < patt->Lines; k++)
339 for (j = 0; j < patt->Tracks; j++)
341 note = &patt->Notes[j][k];
345 if (Read (fp, &mednote, MMD0ROW) != MMD0ROW)
346 return ERR_READWRITE;
350 note->EffVal = mednote & 0xFF;
351 note->EffNum = DecodeEff ((mednote >> 8) & 0x0F, ¬e->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);
359 if (Read (fp, &mednote, MMD1ROW) != MMD1ROW)
360 return ERR_READWRITE;
362 note->EffVal = mednote & 0xFF;
363 note->EffNum = DecodeEff ((mednote >> 8) & 0xFF, ¬e->EffVal, song.flags, song.flags2);
364 note->Inst = ((mednote >> 16) & 0x3F);
365 note->Note = ((mednote >> 24) & 0x7F);
367 if (note->Note) note->Note += 12;
370 } /* End for (patt->Tracks) */
377 if (Seek (fp, (LONG)block.info, OFFSET_BEGINNING) != -1);
379 if (Read (fp, &blockinfo, sizeof (blockinfo)) == sizeof(blockinfo))
381 if (blockinfo.blockname)
382 if (Seek (fp, (LONG)blockinfo.blockname, OFFSET_BEGINNING) != -1);
384 Read (fp, patt->PattName, min (blockinfo.blocknamelen, MAXPATTNAME));
385 patt->PattName[MAXPATTNAME-1] = '\0'; /* Ensure NULL-termination */
391 } /* End for (song->numblocks) */
393 FreeMem (blockarr, song.numblocks * 4);
398 /******************************/
400 /******************************/
405 struct InstrHdr instrhdr;
407 DisplayAction (MSG_READING_INSTS);
409 if (Seek (fp, (LONG)mmd0.smplarr, OFFSET_BEGINNING) == -1)
410 return ERR_READWRITE;
412 if (!(smplarr = AllocMem (song.numsamples * 4, MEMF_ANY)))
413 return ERROR_NO_FREE_STORE;
415 if (Read (fp, smplarr, song.numsamples * 4) != song.numsamples * 4)
417 FreeMem (smplarr, song.numsamples * 4);
418 return ERR_READWRITE;
422 /******************************/
424 /******************************/
426 for (i = 1; i <= song.numsamples ; i++)
428 if (!smplarr[i-1]) continue;
430 if (DisplayProgress (i, song.numsamples))
432 FreeMem (smplarr, song.numsamples * 4);
436 if (Seek (fp, smplarr[i-1], OFFSET_BEGINNING) == -1)
437 ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
440 if (Read (fp, &instrhdr, sizeof (instrhdr)) != sizeof (instrhdr))
441 ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
444 switch (instrhdr.type)
447 inst[i].Length = instrhdr.length;
449 if (!(inst[i].SampleData = (UBYTE *) AllocVec (inst[i].Length, MEMF_SAMPLE)))
451 ShowMessage (MSG_ERR_NO_MEM_FOR_INST, i);
455 if (Read (fp, inst[i].SampleData, inst[i].Length) != inst[i].Length)
456 ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
458 /* Double sample data */
459 if (song.flags & FLAG_8CHANNEL)
461 BYTE *data = inst[i].SampleData;
463 for (j = 0; j < inst[j].Length; j++)
469 ShowMessage (MSG_ERR_NOT_A_SAMPLE, i);
476 FreeMem (smplarr, song.numsamples * 4);
484 static __inline UBYTE DecodeEff (UBYTE eff, UBYTE *effval, UBYTE flags, UBYTE flags2)
491 if (*effval) return EFF_ARPEGGIO;
492 else return EFF_NULL;
494 case 0x0C: /* Volume */
495 if (!(flags & FLAG_VOLHEX))
496 /* Convert decimal volumes */
497 *effval = (*effval & 0x0F) + (*effval >> 4) * 10;
498 return EFF_SETVOLUME;
500 case 0x0F: /* Tempo */
501 if (*effval == 0) return EFF_PATTERNBREAK;
502 if (*effval <= 0x0A) return EFF_SETSPEED;
505 *effval = (*effval * 4) / ((flags2 & FLAG2_BMASK) + 1);
511 return EFF_MISC; /* Filter off */
516 return EFF_MISC; /* Filter on */
521 for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
522 if (eff == Effects[i])
531 LONG SaveMED (struct SongInfo *si, BPTR fp, UWORD mmd_type)
533 struct Instrument *inst = si->Inst;
534 struct Pattern *patt = si->PattData;
535 ULONG modlen, blockslen, instlen, blockptr, offset_blockarr,
539 if (GuiSwitches.Verbose)
540 ShowMessage (MSG_WRITING_MMD, mmd_type + '0');
542 DisplayAction (MSG_WRITING_HEADER);
545 /*********************************/
546 /* Calculate total patterns size */
547 /*********************************/
549 if (mmd_type > 0) /* MMD1 */
551 for (i = 0, blockslen = 0; i < si->NumPatterns ; i++)
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)
559 if (blockslen & 1) blockslen++; /* Pad to words */
564 for (i = 0, blockslen = 0; i < si->NumPatterns ; i++)
566 blockslen += sizeof (struct MMD0block) +
567 (MMD0ROW * patt[i].Lines * patt[i].Tracks);
569 if (blockslen & 1) blockslen++; /* Pad to words */
574 /* Find last used instrument */
575 for (lastinstr = 63; lastinstr > 0 ; lastinstr--)
576 if (inst[lastinstr].Length || inst[lastinstr].Name[0]) break;
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;
582 /* Calculate blockarr offset */
583 offset_blockarr = sizeof (struct MMD0) + sizeof (struct MMD0song) + sizeof (struct MMD0exp) +
584 lastinstr * (sizeof (struct InstrExt) + sizeof (struct MMDInstrInfo));
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 */
591 /* Calculate Total module size */
592 modlen = offset_blockarr +
593 si->NumPatterns * 4 + /* blockarr */
594 lastinstr * 4 + /* samplearr */
598 /**********************************/
599 /* Fill-in & write module header */
600 /*********************************/
604 memset (&mmd0, 0, sizeof (mmd0));
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;
624 /* Write file header */
625 if (Write (fp, &mmd0, sizeof (mmd0)) != sizeof (mmd0))
626 return ERR_READWRITE;
630 /************************************/
631 /* Fill-in and write Song structure */
632 /************************************/
634 struct MMD0song song;
636 for (i = 0; i < 63 ; i++)
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;
646 song.numblocks = si->NumPatterns;
647 song.songlen = min (si->Length, 256);
649 for (i = 0; i < song.songlen; i++)
650 song.playseq[i] = si->Sequence[i];
652 song.deftempo = si->GlobalTempo;
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;
659 for (i = 0; i <16; i++)
663 song.numsamples = lastinstr;
665 if (Write (fp, &song, sizeof (song)) != sizeof (song))
666 return ERR_READWRITE;
669 /*****************************/
670 /* Write extension (MMD0exp) */
671 /*****************************/
673 struct MMD0exp mmd0exp;
676 memset (&mmd0exp, 0, sizeof (mmd0exp));
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;
689 if (Write (fp, &mmd0exp, sizeof (mmd0exp)) != sizeof (mmd0exp))
690 return ERR_READWRITE;
693 /************************/
694 /* Write InstrExt array */
695 /************************/
697 struct InstrExt instrext = { 0 };
699 for (i = 1; i <= lastinstr; i++)
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;
711 if (Write (fp, &instrext, sizeof (instrext)) != sizeof (instrext))
712 return ERR_READWRITE;
716 /*************************/
717 /* Write InstrInfo array */
718 /*************************/
720 struct MMDInstrInfo instrinfo = { 0 };
722 for (i = 1; i <= lastinstr; i++)
724 strcpy (instrinfo.name, si->Inst[i].Name);
726 if (Write (fp, &instrinfo, sizeof (instrinfo)) != sizeof (instrinfo))
727 return ERR_READWRITE;
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;
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;
746 /*************************************/
747 /* Write pattern pointers (blockarr) */
748 /*************************************/
750 blockptr = offset_blockarr + (si->NumPatterns * 4) + (lastinstr * 4);
752 for (i = 0; i < si->NumPatterns; i++)
754 if (Write (fp, &blockptr, 4) != 4)
755 return ERR_READWRITE;
757 if (mmd_type) /* MMD1 and up */
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)
767 blockptr += sizeof (struct MMD0block) +
768 (MMD0ROW * patt[i].Tracks * patt[i].Lines);
772 if (blockptr & 1) blockptr++;
776 /*************************************/
777 /* Write sample pointers (samplearr) */
778 /*************************************/
780 ULONG sampleptr = blockptr, x;
782 for (i = 1; i <= lastinstr; i++)
784 x = ((inst[i].Length && inst[i].SampleData) ? sampleptr : 0);
786 if (Write (fp, &x, 4) != 4)
787 return ERR_READWRITE;
789 if (x) sampleptr += inst[i].Length + sizeof (struct InstrHdr);
793 /********************************/
794 /* Write patterns (blocks) data */
795 /********************************/
799 LONG blockinfoptr = offset_blockarr + (si->NumPatterns * 4) + (lastinstr * 4);
804 DisplayAction (MSG_WRITING_PATTS);
807 for (i = 0; i < si->NumPatterns; i++)
809 if (DisplayProgress (i, si->NumPatterns))
814 if (mmd_type) /* MMD1 and higher */
816 struct MMD1block block;
818 blockinfoptr += sizeof (struct MMD1block) +
819 (MMD1ROW * patt[i].Tracks * patt[i].Lines);
821 block.numtracks = patt[i].Tracks;
822 block.lines = patt[i].Lines - 1;
824 /* Write BlockInfo only if this pattern has a name */
825 block.info = (struct BlockInfo *)(patt[i].PattName[0] ? blockinfoptr : 0);
827 if (Write (fp, &block, sizeof (block)) != sizeof (block))
828 return ERR_READWRITE;
832 struct MMD0block block;
834 block.numtracks = patt[i].Tracks;
835 block.lines = patt[i].Lines - 1;
837 if (Write (fp, &block, sizeof (block)) != sizeof (block))
838 return ERR_READWRITE;
843 for (k = 0; k < patt[i].Lines; k++)
845 for (j = 0; j < patt[i].Tracks; j++)
847 note = &patt[i].Notes[j][k];
848 eff = Effects[note->EffNum];
850 if (note->EffNum == EFF_MISC)
853 pt module xmod bad conversion octamed right conversion
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)
865 switch (note->EffNum >> 4)
869 effval = note->EffNum & 0x0F;
871 case 0xC: /* Note Cut */
874 case 0xE: /* Replay line */
876 effval = note->EffNum & 0x0F;
880 else if (note->EffNum == EFF_PATTERNBREAK)
883 effval = note->EffVal;
885 // if (note->EffNum == EFF_SETTEMPO)
888 if (mmd_type) /* MMD1 and up */
892 ((ULONG)(note->Inst & 0x3F) << 16) |
893 ((ULONG)((note->Note ? (note->Note - 12) : 0) & 0x7F) << 24);
895 if (Write (fp, &mednote, MMD1ROW) != MMD1ROW)
896 return ERR_READWRITE;
900 if ((eff > 0x0F) && !quiet)
902 ShowMessage (MSG_WRONG_EFFECT_IN_MMD0, eff);
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;
913 if (Write (fp, &mednote, MMD0ROW) != MMD0ROW)
914 return ERR_READWRITE;
921 if (patt[i].PattName[0])
923 /*****************************/
924 /* Write BlockInfo structure */
925 /*****************************/
927 struct BlockInfo blockinfo;
929 blockinfoptr += sizeof (blockinfo);
931 memset (&blockinfo, 0, sizeof (blockinfo));
932 blockinfo.blockname = (UBYTE *)blockinfoptr;
933 blockinfo.blocknamelen = strlen (patt[i].PattName) + 1;
935 if (Write (fp, &blockinfo, sizeof (blockinfo)) != sizeof (blockinfo))
936 return ERR_READWRITE;
938 if (Write (fp, patt[i].PattName, blockinfo.blocknamelen) != blockinfo.blocknamelen)
939 return ERR_READWRITE;
941 blockinfoptr += sizeof (struct BlockInfo) + blockinfo.blocknamelen;
944 if (blockinfoptr & 1)
948 if (Write (fp, &dummy, 1) != 1)
949 return ERR_READWRITE;
957 /* Add a pad byte when length is not even */
958 if ((patt[i].Lines * patt[i].Tracks) & 1)
962 if (Write (fp, &dummy, 1) != 1)
963 return ERR_READWRITE;
966 } /* End for (si->NumPatterns) */
970 /**********************/
971 /* Write samples data */
972 /**********************/
974 struct InstrHdr instrhdr;
979 DisplayAction (MSG_WRITING_INSTDATA);
981 for (i = 1; i <= lastinstr; i++)
986 if (!l || (!inst[i].SampleData)) continue;
988 if (DisplayProgress (i, lastinstr))
993 if (samp = AllocVec (l, MEMF_ANY))
996 for (k = 0; k < l; k++)
997 samp[k] = inst[i].SampleData[k] >> 1;
999 free_samp = TRUE; /* Free this buffer when you are done */
1003 ShowMessage (MSG_NO_MEM_TO_HALVE, i);
1004 samp = inst[i].SampleData;
1007 else samp = inst[i].SampleData;
1009 instrhdr.length = l;
1010 instrhdr.type = SAMPLE;
1012 if (Write (fp, &instrhdr, sizeof (instrhdr)) != sizeof (instrhdr))
1013 return ERR_READWRITE;
1016 if (Write (fp, samp, l) != l)
1017 return ERR_READWRITE;