Initial commit.
[amiga/xmodule.git] / Operators.c
1 /*
2 **      Operators.c
3 **
4 **      Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
5 **
6 **      General pourpose module handling/processing functions.
7 */
8
9 #include <exec/memory.h>
10
11 #include <proto/exec.h>
12 #include <proto/xmodule.h>
13
14 #include "XModulePriv.h"
15 #include "Gui.h"
16
17
18 /* Local function prototypes */
19
20 static void     BreakPattern    (struct Note **arr, UWORD rows, UWORD tracks);
21
22
23
24 GLOBALCALL ULONG InsertPattern (struct SongInfo *si, struct Pattern *patt, UWORD patnum)
25
26 /* Inserts a pattern at any song position.  Patterns >= patnum will be moved
27  * ahead one slot.  The position table is updated inserting references to
28  * the new pattern immediately before each occurence of patnum, so that the
29  * two patterns are always played together.
30  *
31  * RESULT
32  *      0 to mean succes,
33  *      any other value means failure.
34  */
35 {
36         ULONG i, k;
37
38
39         if (xmAddPattern (si,
40                 PATTA_Num,              patnum,
41                 PATTA_Pattern,  patt,
42                 TAG_DONE))
43         {
44                 xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
45                         (APTR)MSG_CANT_INSERT_PATT, NULL);
46                 return 1;
47         }
48
49         /* Adjust position table */
50         for (i = 0 ; i < si->Length ; i++)
51         {
52                 /* Song can't grow bigger than MAXPOSITIONS */
53                 if (si->Length >= MAXPOSITIONS)
54                         return 2;       /* TODO: better error handling */
55
56                 /* Fix pattern numbers */
57                 if (si->Sequence[i] > patnum) si->Sequence[i]++;
58
59                 /* Insert references to the new pattern in the position table */
60                 if (si->Sequence[i] == patnum)
61                 {
62                         /* Grow song */
63                         if (xmSetSongLen (si, si->Length + 1))
64                         {
65                                 /* Shift subsequent positions ahead 1 slot */
66                                 for (k = si->Length - 1; k > i ; k--)
67                                         si->Sequence[k] = si->Sequence[k-1];
68
69                                 si->Sequence[i+1] = patnum+1;   /* Restore old pattern */
70
71                                 i++;                    /* Avoid processing this pattern again */
72
73                                 /* TODO: It would be nice to fix Pattern Jump commands too... */
74                         }
75                         else return 1;
76                 }
77         }
78
79         return 0;
80 }
81
82
83
84 GLOBALCALL void DiscardPatterns (struct SongInfo *si)
85
86 /* Discard patterns beyond the last pattern referenced
87  * in the song sequence.
88  */
89 {
90         ULONG i, j;
91         UBYTE *used;
92         struct Pattern *patt;
93
94         if (!(used = AllocVecPooled (Pool, si->NumPatterns)))
95                 return;
96
97         /* Flag patterns REALLY used in the song */
98         for (i = 0; i < si->Length ; i++)
99                 used[si->Sequence[i]]++;
100
101
102         for (i = 0; i < si->NumPatterns; i++)
103         {
104                 if (!(patt = si->Patt[i])) continue;
105
106                 if (!used[i] && patt)
107                 {
108                         xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
109                                 (APTR)MSG_PATT_UNUSED, i);
110
111                         xmRemPattern (si, i, 0);
112
113                         /* Shift all subsequent patterns one position back */
114                         for (j = i; j < si->NumPatterns; j++)
115                                 used[j] = used[j+1];
116
117                         /* Rearrange Position Table */
118                         for (j = 0; j < si->Length; j++)
119                                 if (si->Sequence[j] > i) si->Sequence[j]--;
120
121                         /* TODO: It would be nice to fix Pattern Jump commands too... */
122
123                         i--; /* Process this pattern # again, since it's now another pattern */
124                 }
125         }
126
127         FreeVecPooled (Pool, used);
128 }
129
130
131
132 GLOBALCALL void CutPatterns (struct SongInfo *si)
133
134 /* Find out what patterns are breaked (effect D) and resize them */
135 {
136         ULONG i, j, k, l;
137         struct Pattern *patt, *newpatt;
138
139         for (i = 0; i < si->NumPatterns; i++)
140         {
141                 if (!(patt = si->Patt[i])) continue;
142
143                 for (j = 0; j < patt->Lines; j++)
144                         for (k = 0; k < patt->Tracks; k++)
145                                 if (patt->Notes[k][j].EffNum == EFF_PATTERNBREAK)
146                                 {
147                                         xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
148                                                 (APTR)MSG_PATT_CUT, i, j+1);
149
150                                         if (newpatt = xmAddPattern (si,
151                                                 PATTA_Name,             patt->Name,
152                                                 PATTA_Tracks,   patt->Tracks,
153                                                 PATTA_Lines,    j + 1,
154                                                 PATTA_Num,              -1,                     /* Do not add it */
155                                                 TAG_DONE))
156                                         {
157                                                 /* Remove break command */
158                                                 patt->Notes[k][j].EffNum = 0;
159                                                 patt->Notes[k][j].EffVal = 0;
160
161                                                 /* Copy notes */
162                                                 for (l = 0; l < patt->Tracks; l++)
163                                                         CopyMem (patt->Notes[l], newpatt->Notes[l], sizeof (struct Note) * (j+1));
164
165                                                 /* Replace with new pattern */
166                                                 xmAddPattern (si,
167                                                         PATTA_Pattern,  patt,
168                                                         PATTA_Num,              i,
169                                                         PATTA_Replace,  TRUE,
170                                                         TAG_DONE);
171
172                                                 /* stop the loop on this pattern */
173                                                 patt = newpatt;
174                                                 j = patt->Lines;
175                                                 k = patt->Tracks;
176                                         }
177                                 }
178         }
179 }
180
181
182
183 GLOBALCALL void RemDupPatterns (struct SongInfo *si)
184
185 /* Find out identical patterns and cut them out */
186 {
187         ULONG i, j, k;
188         struct Pattern *patta, *pattb;
189
190         if (si->NumPatterns < 2) return;
191
192         for (i = 0; i < si->NumPatterns-1; i++)
193         {
194                 if (!(patta = si->Patt[i])) continue;
195
196                 for (j = i+1; j < si->NumPatterns; j++)
197                 {
198                         if (!(pattb = si->Patt[j])) continue;
199
200                         if ((patta->Lines == pattb->Lines) && (patta->Tracks == pattb->Tracks))
201                         {
202                                 for (k = 0; k < patta->Tracks; k++)
203                                         if (memcmp (patta->Notes[k], pattb->Notes[k], sizeof (struct Note) * patta->Lines))
204                                                 break;
205
206                                 if (k == patta->Tracks)
207                                 {
208                                         xmRemPattern (si, j, i);
209                                         xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
210                                                 (APTR)MSG_PATT_DUPE, i, j);
211                                         j--;
212                                 }
213                         }
214                 }
215         }
216 }
217
218
219
220 static void BreakPattern (struct Note **arr, UWORD row, UWORD tracks)
221
222 /* Put a break command at the end of a pattern */
223 {
224         ULONG i;
225
226         /* Try to find a free effect slot in the row... */
227         for (i = 0 ; i < tracks ; i++)
228                 if (arr[i][row].EffNum == 0 && arr[i][row].EffVal == 0)
229                         break;
230
231         if (i == tracks) i = 0;
232
233         arr[i][row].EffNum = EFF_PATTERNBREAK; /* ...and break the pattern */
234         arr[i][row].EffVal = 0;
235 }
236
237
238
239 GLOBALCALL LONG ResizePatterns (struct SongInfo *si, ULONG min, ULONG max)
240
241 /* Find out what patterns are less than <min> and more than <max>
242  * lines long and either grow them or split them in shorter
243  * patterns.  Pattern splitting will automatically recurse when
244  * more than one split is required.
245  *
246  * RESULT
247  *      0 to mean succes, any other value means failure.
248  */
249 {
250         struct Pattern  *patt, *newpatt, *newpatt2;
251         ULONG i, j;
252         UWORD len;
253         BOOL do_update = FALSE;
254
255         for (i = 0 ; i < si->NumPatterns ; i++)
256         {
257                 if (!(patt = si->Patt[i])) continue;
258
259                 if ((len = patt->Lines) < min)
260                 {
261                         /* BREAK PATTERN */
262
263                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_COMMENT,
264                                 (APTR)MSG_PATT_WILL_GROW, i, len);
265
266                         if (!(newpatt = xmAddPattern (si,
267                                 PATTA_Num,              -1,
268                                 PATTA_Lines,    min,
269                                 PATTA_Tracks,   patt->Tracks,
270                                 TAG_DONE)))
271                                 return ERROR_NO_FREE_STORE;
272
273
274                         /* Copy the old notes in the new (longer) pattern */
275                         for (j = 0 ; j < patt->Tracks ; j++)
276                                 memcpy (newpatt->Notes[j], patt->Notes[j],
277                                                 (sizeof (struct Note)) * len);
278
279                         /* Break the new pattern */
280                         BreakPattern (newpatt->Notes, len-1, newpatt->Tracks);
281
282                         xmAddPattern (si,
283                                 PATTA_Num, i,
284                                 PATTA_Pattern, newpatt,
285                                 PATTA_Replace, TRUE,
286                                 TAG_END);
287                 }
288                 else if (len > max)
289                 {
290                         /* SPLIT PATTERN */
291
292                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_COMMENT,
293                                 (APTR)MSG_SPLITTING_PATT, i, len);
294
295                         if (!(newpatt = xmAddPattern (si,
296                                 PATTA_Num,              -1,
297                                 PATTA_Lines,    max,
298                                 PATTA_Tracks,   patt->Tracks,
299                                 TAG_DONE)))
300                                 return ERROR_NO_FREE_STORE;
301
302
303                         /* If len - max is still above max, this pattern
304                          * will be splitted or breaked once again in the
305                          * next loop.
306                          */
307
308                         if (!(newpatt2 = xmAddPattern (si,
309                                 PATTA_Num,              -1,
310                                 PATTA_Lines,    len - max,
311                                 PATTA_Tracks,   patt->Tracks,
312                                 TAG_DONE)))
313                         {
314                                 xmRemPattern (si, -1, (ULONG)newpatt);
315                                 return ERROR_NO_FREE_STORE;
316                         }
317
318                         /* Copy first <max> rows of the pattern */
319                         for (j = 0 ; j < si->MaxTracks ; j++)
320                                 memcpy (newpatt->Notes[j], patt->Notes[j],
321                                                 (sizeof (struct Note)) * max);
322
323                         /* Copy the rest of the pattern */
324                         for (j = 0 ; j < si->MaxTracks ; j++)
325                                 memcpy (newpatt2->Notes[j], patt->Notes[j] + max,
326                                                 (sizeof (struct Note)) * (len - max));
327
328
329                         /* Make room for the new pattern */
330                         if (InsertPattern (si, newpatt, i))
331                         {
332                                 xmRemPattern (si, -1, (ULONG)newpatt);
333                                 xmRemPattern (si, -1, (ULONG)newpatt2);
334                                 continue;
335                         }
336
337                         /* Substitute old pattern */
338                         if (!(xmAddPattern (si,
339                                 PATTA_Num,              i + 1,
340                                 PATTA_Pattern,  newpatt2,
341                                 TAG_DONE)))
342                         {
343                                 xmRemPattern (si, -1, (ULONG)newpatt2);
344                                 continue;
345                         }
346                 }
347         }       /* End for(i) */
348
349         if (do_update)
350                 UpdateSongInfo();
351
352         return 0;
353
354 }       /* End ResizePatterns */
355
356
357
358 GLOBALCALL struct Pattern *CopyPattern (struct SongInfo *si, struct Pattern *src, ULONG destNum)
359
360 /* Makes a copy of the notes and attributes of the <src>
361  * pattern to the <dest> pattern.  The destination pattern is
362  * created with xmAddPattern().
363  */
364 {
365         struct Pattern *dest;
366         ULONG i;
367
368         if (!src) return NULL;
369
370         if (dest = xmAddPattern (si, destNum,
371                 PATTA_Lines, src->Lines,
372                 PATTA_Tracks, src->Tracks,
373                 PATTA_Name, src->Name))
374         {
375                 for (i = 0; i < src->Tracks; i++)
376                         CopyMem (src->Notes[i], dest->Notes[i],
377                                 sizeof (struct Note) * src->Lines);
378         }
379
380         return dest;
381 }
382
383
384
385 GLOBALCALL struct SongInfo *MergeSongs (struct SongInfo *songa, struct SongInfo *songb)
386
387 /* Merges <songa> with <songb> in a new song where the notes of the
388  * two sources are played at the same time.
389  */
390 {
391         struct SongInfo         *songc;
392         struct Pattern          *patta, *pattb, *pattc;
393         struct Note             *note;
394         struct Instrument       *source;
395         BYTE                            *newsample;
396         LONG                             slen, plen, ntrk, i, j, k;
397
398
399         if (songa->Length != songb->Length)
400                 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
401                         (APTR)MSG_SONG_LEN_DIFFERENT, NULL);
402
403         /* Create new song */
404         {
405                 UBYTE newtitle[64], newauthor[64];
406
407                 /* Make new title */
408                 newtitle[63] = '\0';
409                 strncpy (newtitle, songa->Title, 63);
410                 strncat (newtitle, " + ", 63);
411                 strncat (newtitle, songb->Title, 63);
412
413                 /* Make new author */
414
415                 newauthor[63] = '\0';
416                 if (songa->Author && songa->Author[0])
417                         strncpy (newauthor, songa->Author, 63);
418                 if (songb->Author && songb->Author[0])
419                 {
420                         if (songa->Author && songa->Author[0])
421                         {
422                                 strncat (newauthor, " & ", 63);
423                                 strncat (newauthor, songb->Author, 63);
424                         }
425                         else strncpy (newauthor, songb->Author, 63);
426                 }
427
428                 if (!(songc = xmCreateSong (
429                         SNGA_GlobalSpeed,       songa->GlobalSpeed,
430                         SNGA_GlobalTempo,       songa->GlobalTempo,
431                         SNGA_RestartPos,        songa->RestartPos,
432                         SNGA_Title,                     newtitle,
433                         SNGA_Author,            newauthor,
434                         TAG_DONE)))
435                         return NULL;
436         }
437
438         slen = min (songa->Length, songb->Length);
439         xmSetSongLen (songc, slen);
440
441
442         for (i = 0; i < slen; i++)
443         {
444                 UBYTE pattname[64];
445
446                 patta = songa->Patt[songa->Sequence[i]];
447                 pattb = songb->Patt[songb->Sequence[i]];
448
449                 if (patta->Lines != pattb->Lines)
450                         xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
451                                 (APTR)MSG_PATT_LEN_DIFFERENT, i);
452
453                 plen = min (patta->Lines, pattb->Lines);
454                 ntrk = patta->Tracks + pattb->Tracks;
455
456                 if (ntrk > MAXTRACKS)
457                 {
458                         xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
459                                 (APTR)MSG_PATT_TOO_MANY_TRACKS, MAXTRACKS);
460
461                         ntrk = MAXTRACKS;
462                 }
463
464                 /* Set pattern name */
465                 if (patta->Name && pattb->Name)
466                 {
467                         pattname[63] = '\0';
468                         strncpy (pattname, patta->Name, 63);
469                         strncat (pattname, " + ", 63);
470                         strncat (pattname, pattb->Name, 63);
471                 }
472                 else
473                         SPrintf (pattname, "%ld + %ld", songa->Sequence[i], songb->Sequence[i]);
474
475                 if (!(pattc = xmAddPattern (songc,
476                         PATTA_Lines,    plen,
477                         PATTA_Tracks,   ntrk,
478                         PATTA_Name,             pattname,
479                         TAG_DONE)))
480                 {
481                         xmDeleteSong (songc);
482                         LastErr = ERROR_NO_FREE_STORE;
483                         return NULL;
484                 }
485
486                 songc->Sequence[i] = i;
487
488                 /* Copy tracks from source A */
489                 for (j = 0; j < patta->Tracks; j++)
490                         CopyMem (patta->Notes[j], pattc->Notes[j], sizeof (struct Note) * plen);
491
492                 /* Copy tracks from source B */
493                 for (j = 0; j < pattb->Tracks; j++)
494                 {
495                         if (j + patta->Tracks >= MAXTRACKS) break;
496
497                         note = pattc->Notes[j + patta->Tracks];
498                         CopyMem (pattb->Notes[j], note, sizeof (struct Note) * plen);
499
500                         for (k = 0; k < plen; k++)
501                                 if (note[k].Note)
502                                 {
503                                         if ((note[k].Inst + songa->LastInstrument) >= MAXINSTRUMENTS)
504                                         {
505                                                 xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
506                                                         (APTR)MSG_ERR_INSTR_OVERFLOW, NULL);
507                                                 xmDeleteSong (songc);
508                                                 return NULL;
509                                         }
510
511                                         note[k].Inst += songa->LastInstrument - 1;
512                                 }
513                 }
514         }
515
516
517         /* Copy instruments */
518
519         for (i = 1; i < songa->LastInstrument + songb->LastInstrument; i++)
520         {
521                 if (i <= songa->LastInstrument)
522                         source = songa->Instr[i];
523                 else
524                         source = songb->Instr[i - songa->LastInstrument + 1];
525
526                 if (source)
527                 {
528                         if (source->Sample)
529                         {
530                                 if (!(newsample = AllocVec (source->Length, MEMF_SAMPLE)))
531                                 {
532                                         xmDeleteSong (songc);
533                                         LastErr = ERROR_NO_FREE_STORE;
534                                         return NULL;
535                                 }
536                                 CopyMem (source->Sample, newsample, source->Length);
537                         }
538                         else
539                                 newsample = NULL;
540
541                         if (!xmAddInstrument (songc, i,
542                                 INSTRA_Type,            source->Type,
543                                 INSTRA_Name,            source->Name,
544                                 INSTRA_Volume,          source->Volume,
545                                 INSTRA_Sample,          newsample,
546                                 INSTRA_Length,          source->Length,
547                                 INSTRA_LoopStart,       source->Repeat,
548                                 INSTRA_LoopLen,         source->Replen,
549                                 INSTRA_FineTune,        source->FineTune,
550                                 TAG_DONE))
551                         {
552                                 FreeVec (newsample);
553                                 xmDeleteSong (songc);
554                                 LastErr = ERROR_NO_FREE_STORE;
555                                 return NULL;
556                         }
557                 }
558         }
559
560         return songc;
561 }
562
563
564
565 GLOBALCALL struct SongInfo *JoinSongs (struct SongInfo *songa, struct SongInfo *songb)
566
567 /* Joins <songa> with <songb> in a new song where the notes of the
568  * two sources are played one after the oder.
569  */
570 {
571         struct SongInfo         *songc;
572         struct Pattern          *patta, *pattb, *pattc;
573         struct Note                     *note;
574         struct Instrument       *inst, *source;
575         BYTE                            *newsample;
576         ULONG                            plen, ntrk, i, j, k;
577         UWORD                            songclen;
578
579
580         /* Check maximum values */
581
582         if ((songa->Length + songb->Length) > MAXPOSITIONS)
583         {
584                 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
585                         (APTR)MSG_SONG_TOO_LONG, NULL);
586                 songclen = MAXPOSITIONS;
587         }
588         else songclen = songa->Length + songb->Length;
589
590         if ((songa->NumPatterns + songb->NumPatterns) > MAXPATTERNS)
591                 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
592                         (APTR)MSG_SONG_HAS_TOO_MANY_PATT, NULL);
593
594
595         /* Create new song */
596         {
597                 UBYTE newtitle[64], newauthor[64];
598
599                 /* Make new title */
600                 newtitle[63] = '\0';
601                 strncpy (newtitle, songa->Title, 63);
602                 strncat (newtitle, " & ", 63);
603                 strncat (newtitle, songb->Title, 63);
604
605                 /* Make new author */
606
607                 newauthor[63] = '\0';
608                 if (songa->Author && songa->Author[0])
609                         strncpy (newauthor, songa->Author, 63);
610                 if (songb->Author && songb->Author[0])
611                 {
612                         if (songa->Author && songa->Author[0])
613                         {
614                                 strncat (newauthor, " & ", 63);
615                                 strncat (newauthor, songb->Author, 63);
616                         }
617                         else strncpy (newauthor, songb->Author, 63);
618                 }
619
620                 if (!(songc = xmCreateSong (
621                         SNGA_GlobalSpeed,       songa->GlobalSpeed,
622                         SNGA_GlobalTempo,       songa->GlobalTempo,
623                         SNGA_RestartPos,        songa->RestartPos,
624                         SNGA_Title,                     newtitle,
625                         SNGA_Author,            newauthor,
626                         TAG_DONE)))
627                         return NULL;
628         }
629
630         if (xmSetSongLen (songc, songclen))
631         {
632                 /* Copy position table of song A */
633                 memcpy (songc->Sequence, songa->Sequence, songa->Length * sizeof (UWORD));
634
635                 /* Append position table of song B */
636                 for (i = 0; i < songb->Length; i++)
637                 {
638                         if (i + songa->Length >= songc->Length) break;
639                         songc->Sequence[i + songa->Length] = (songb->Sequence[i] + songa->NumPatterns > MAXPATTERNS) ?
640                                 songb->Sequence[i] : (songb->Sequence[i] + songa->NumPatterns);
641                 }
642         }
643         else
644         {
645                 xmDeleteSong (songc);
646                 LastErr = ERROR_NO_FREE_STORE;
647                 return NULL;
648         }
649
650
651         /* Copy song A patterns */
652
653         for (i = 0; i < songa->NumPatterns; i++)
654         {
655                 patta = songa->Patt[i];
656                 plen = patta->Lines;
657                 ntrk = patta->Tracks;
658
659                 if (!(pattc = xmAddPattern (songc,
660                         PATTA_Lines,    plen,
661                         PATTA_Tracks,   ntrk,
662                         PATTA_Name,             patta->Name,
663                         TAG_DONE)))
664                 {
665                         xmDeleteSong (songc);
666                         LastErr = ERROR_NO_FREE_STORE;
667                         return NULL;
668                 }
669
670                 /* Copy tracks from source A */
671
672                 for (j = 0; j < ntrk; j++)
673                         CopyMem (patta->Notes[j], pattc->Notes[j], sizeof (struct Note) * plen);
674         }
675
676
677         /* Append song B patterns */
678
679         for (i = 0; i < songb->NumPatterns; i++)
680         {
681                 pattb = songb->Patt[i];
682                 plen = pattb->Lines;
683                 ntrk = pattb->Tracks;
684
685                 if (!(pattc = xmAddPattern (songc,
686                         PATTA_Lines,    plen,
687                         PATTA_Tracks,   ntrk,
688                         PATTA_Name,             pattb->Name,
689                         TAG_DONE)))
690                 {
691                         xmDeleteSong (songc);
692                         LastErr = ERROR_NO_FREE_STORE;
693                         return NULL;
694                 }
695
696                 /* Copy tracks from source B */
697
698                 for (j = 0; j < ntrk; j++)
699                         CopyMem (pattb->Notes[j], pattc->Notes[j], sizeof (struct Note) * plen);
700
701                 /* Adjust instruments references */
702
703                 for (j = 0; j < ntrk; j++)
704                         for (k = 0; k < plen; k++)
705                         {
706                                 note = &pattc->Notes[j][k];
707
708                                 if (note->Inst)
709                                 {
710                                         if ((note->Inst + songa->LastInstrument) >= MAXINSTRUMENTS)
711                                         {
712                                                 xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
713                                                         (APTR)MSG_ERR_INSTR_OVERFLOW, NULL);
714                                                 xmDeleteSong (songc);
715                                                 return NULL;
716                                         }
717
718                                         note->Inst += songa->LastInstrument - 1;
719                                 }
720                         }
721         }
722
723
724
725         /* Copy instruments */
726
727         for (i = 1; i < songa->LastInstrument + songb->LastInstrument; i++)
728         {
729                 if (i <= songa->LastInstrument)
730                         source = songa->Instr[i];
731                 else
732                         source = songb->Instr[i - songa->LastInstrument + 1];
733
734                 if (source)
735                 {
736                         if (source->Sample)
737                         {
738                                 if (!(newsample = AllocVec (source->Length, MEMF_ANY)))
739                                 {
740                                         xmDeleteSong (songc);
741                                         LastErr = ERROR_NO_FREE_STORE;
742                                         return NULL;
743                                 }
744                                 CopyMem (source->Sample, newsample, source->Length);
745                         }
746                         else
747                                 newsample = NULL;
748
749                         if (!(inst = xmAddInstrument (songc, i,
750                                 INSTRA_Type,            source->Type,
751                                 INSTRA_Name,            source->Name,
752                                 INSTRA_Volume,          source->Volume,
753                                 INSTRA_Sample,          newsample,
754                                 INSTRA_Length,          source->Length,
755                                 INSTRA_Repeat,          source->Repeat,
756                                 INSTRA_Replen,          source->Replen,
757                                 INSTRA_FineTune,        source->FineTune,
758                                 TAG_DONE)))
759                         {
760                                 FreeVec (newsample);
761                                 xmDeleteSong (songc);
762                                 LastErr = ERROR_NO_FREE_STORE;
763                                 return NULL;
764                         }
765                 }
766         }
767
768         return songc;
769 }
770
771
772
773 GLOBALCALL void FixSong (struct SongInfo *si)
774
775 /* Fixes dangerous errors in song structure */
776 {
777         struct Instrument *instr;
778         ULONG i;
779
780         /* Check instruments */
781
782         for (i = 1 ; i < si->LastInstrument ; i++)
783         {
784                 if (!(instr = si->Instr[i])) continue;
785
786                 /* Filter instrument names */
787                 FilterName (instr->Name);
788
789                 /* Some sensible sanity checks on loops */
790
791                 if (instr->Repeat && (instr->Repeat >= instr->Length))
792                 {
793                         instr->Repeat = instr->Replen = 0;
794                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
795                                 (APTR)MSG_INVALID_LOOP_REMOVED, i);
796                 }
797                 else if (instr->Repeat + instr->Replen > instr->Length)
798                 {
799                         instr->Replen = instr->Length - instr->Repeat;
800                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
801                                 (APTR)MSG_INVALID_LOOP_FIXED, i);
802                 }
803         }
804
805         /* Check patterns */
806         for (i = 0; i < si->NumPatterns; i++)
807                 if (si->Patt[i]) FilterName (si->Patt[i]->Name);
808
809         /* Can't have a song with no patterns!!! */
810         if (si->NumPatterns == 0)
811         {
812                 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_NOTE,
813                         (APTR)MSG_SONG_HAS_NO_PATTS, NULL);
814                 xmAddPattern (si, NULL);
815         }
816
817         /* Better forcing the song to a minimum length of 1. */
818         if (si->Length == 0)
819         {
820                 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_NOTE,
821                         (APTR)MSG_SONG_HAS_NO_SEQ, NULL);
822                 xmSetSongLen (si, 1);
823         }
824
825         /* Check sequence */
826         for (i = 0; i < si->Length; i++)
827         {
828                 if (si->Sequence[i] >= si->NumPatterns)
829                 {
830                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
831                                 (APTR)MSG_INVALID_SONG_POS, i, si->Sequence[i]);
832                         si->Sequence[i] = si->NumPatterns - 1;
833                 }
834         }
835
836         FilterName (si->Title);
837         FilterName (si->Author);
838 }