Initial commit.
[amiga/xmodule.git] / Hooks / OktalyzerHook.c
1 /*
2 **      OktalyzerHook.c
3 **
4 **      Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
5 **
6 **      Originally based on Gerardo Iula's Tracker sources.
7 **
8 **      External hook for Oktalyzer 1.1-1.57 module format.
9 */
10
11
12 #include <exec/memory.h>
13 #include <dos/stdio.h>
14 #include <libraries/xmodule.h>
15 #include <libraries/songclass.h>
16
17 #include <proto/exec.h>
18 #include <proto/dos.h>
19 #include <proto/intuition.h>
20 #include <proto/xmodule.h>
21
22 #include "XModulePriv.h"
23 #include "Gui.h"
24
25
26
27 /* Local functions prototypes */
28
29 HOOKCALL APTR _GetEngine (
30         REG(a6, struct Library *MyBase));
31 HOOKCALL APTR _SetupXMHook (
32         REG(a0, struct XModuleBase *_XModuleBase),
33         REG(a6, struct Library *mybase));
34 static HOOKCALL struct XMHook *IdentifyOktalyzer (
35         REG(d0, BPTR fh),
36         REG(a0, struct XMHook *loader),
37         REG(a1, ULONG *tags));
38 static HOOKCALL LONG LoadOktalyzer (
39         REG(d0, BPTR fh),
40         REG(a0, struct SongInfo *si),
41         REG(a1, struct XMHook *loader),
42         REG(a2, ULONG *tags));
43 static HOOKCALL LONG SaveOktalyzer (
44         REG(d0, BPTR fh),
45         REG(a0, struct SongInfo *si),
46         REG(a1, struct XMHook *saver),
47         REG(a2, ULONG *tags));
48
49 INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval, UWORD patt, UWORD line, UWORD track);
50
51
52
53 #define OKT_MODE4       1       /* Mode 4: play 8 bit instruments */
54 #define OKT_MODE8       0       /* Mode 8: play 7 bit instruments */
55 #define OKT_MODEB       2       /* Mode B: play both 8 & 7 bit instruments */
56
57
58 /* Oktalyzer chunk IDs */
59
60 #define ID_OKTA 'OKTA'
61 #define ID_CMOD 'CMOD'
62 #define ID_SAMP 'SAMP'
63 #define ID_SPEE 'SPEE'
64 #define ID_SLEN 'SLEN'
65 #define ID_PLEN 'PLEN'
66 #define ID_PBOD 'PBOD'
67 #define ID_SBOD 'SBOD'
68
69 #ifndef ID_SONG
70 #define ID_SONG 'SONG'
71 #endif /* !ID_SONG */
72
73 #ifndef ID_PATT
74 #define ID_PATT 'PATT'
75 #endif /* !ID_PATT */
76
77
78
79 /* Library data */
80
81 const UBYTE LibName[] = "oktalyzer.xmhook";
82 const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
83 const UBYTE LibId[] = "oktalyzer.xmhook 1.0 (28.1.96) © 1993-96 by Bernardo Innocenti";
84
85
86
87 /* Effects conversion table
88  * Originally based on Gerardo Iula's "Tracker" source.
89  */
90 static const UBYTE Effects[MAXTABLEEFFECTS] =
91 {
92 /*  OKTA                XModule                 Val */
93
94         0x00,   /*      Null effect             $00     */
95
96         0x01,   /*      Portamento Up   $01     */
97         0x02,   /*      Portamento Down $02     */
98         0x00,   /*      Tone Portamento $03     */
99         0x00,   /*      Vibrato                 $04     */
100         0x00,   /*      ToneP + VolSl   $05     */
101         0x00,   /*      Vibra + VolSl   $06     */
102         0x00,   /*      Tremolo                 $07     */
103         0x00,   /*      Set Hold/Decay  $08     */
104         0x00,   /*      Sample Offset   $09     */
105         0x1E,   /*      Volume Slide    $0A     */
106         0x19,   /*      Position Jump   $0B     */
107         0x1F,   /*      Set Volume              $0C     */
108         0x00,   /*      Pattern break   $0D     */
109         0x00,   /*      Misc                    $0E     */
110         0x1C,   /*      Set Speed               $0F     */
111         0x00,   /*      Set Tempo               $10     */
112         0x1A,   /*      Arpeggio                $11     */
113
114         0x11,   /*      Oktalyzer H             */
115         0x15    /*      Oktalyzer L             */
116 };
117
118
119
120 /* Get around a SAS/C bug which causes some annoying warnings
121  * with the library bases defined below.  The problem happens
122  * when the GST has been compiled with the CODE=FAR switch and
123  * the source is being compiled with CODE=NEAR.
124  */
125 #ifdef __SASC
126         #pragma msg 72 ignore push
127 #endif /* __SASC */
128
129 struct ExecBase                 *SysBase                = NULL;
130 struct XModuleBase              *XModuleBase    = NULL;
131 struct DosLibrary               *DOSBase                = NULL;
132 struct IntuitionBase    *IntuitionBase  = NULL;
133
134 #ifdef __SASC
135 #pragma msg 72 pop
136 #endif /* __SASC */
137
138
139
140 static HOOKCALL struct XMHook *IdentifyOktalyzer (
141         REG(d0, BPTR fh),
142         REG(a0, struct XMHook *loader),
143         REG(a1, ULONG *tags))
144
145 /* Determine if the given file is an Oktalyzer module.
146  * Note: the file position will be changed on exit.
147  */
148 {
149         ULONG id[3];
150
151         Seek (fh, 0, OFFSET_BEGINNING);
152         if (FRead (fh, &id, 12, 1) != 1)
153                 return NULL;
154
155         if ((id[0] == ID_OKTA) && (id[1] == ID_SONG) && (id[2] == ID_CMOD))
156                 return loader;
157
158         return NULL;
159 }
160
161
162
163 INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval, UWORD patt, UWORD line, UWORD track)
164
165 /* Inputs: old effect & old type.
166  * Output: new effect in requested newtype.
167  */
168 {
169         UBYTE i;
170
171         for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
172                 if (eff == Effects[i])
173                         return i;
174
175         xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
176                 (APTR)MSG_UNKNOWN_EFF, 5, eff, patt, track, line);
177         return 0;
178 }
179
180
181
182 static HOOKCALL LONG LoadOktalyzer (
183         REG(d0, BPTR fh),
184         REG(a0, struct SongInfo *si),
185         REG(a1, struct XMHook *loader),
186         REG(a2, ULONG *tags))
187 {
188         struct Instrument       *instr;
189         struct Pattern          *patt;
190         ULONG i, j, k;  /* Loop counters        */
191         ULONG size;             /* Read buffer          */
192         ULONG l;                /* Read buffer          */
193         UWORD voices, numpatts, songlen, instr_mode[37];
194         UWORD w;
195         UBYTE oktanote[4];
196
197
198         /* Check file header OKTASONGCMOD */
199         {
200                 LONG id[3];
201
202                 if (FRead (fh, id, 12, 1) != 1) return ERROR_IOERR;
203                 if ((id[0] != ID_OKTA) || (id[1] != ID_SONG) || (id[2] != ID_CMOD))
204                         return ERROR_NOTMODULE;
205         }
206
207
208         /* TODO: set si->SongName */
209
210
211         /* CMOD Chunk size ($0000 0008) */
212         Seek (fh, 4, OFFSET_CURRENT); /* Skip 4 bytes */
213
214         voices = 4;             /* Set minimum voices and check others  */
215         for (i = 0 ; i < 4 ; i++)
216         {
217                 if (FRead (fh, &w, 2, 1) != 1) return ERROR_IOERR;
218                 if (w) voices++;
219         }
220
221         /* Get Sample Name, Length, Repeat Replen, Volume for each instr. */
222
223         /* Check header SAMP */
224         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
225         if (l != ID_SAMP) return ERROR_NOTMODULE;
226
227         /* SAMP Chunk Size ($0000 0480) */
228         Seek (fh, 4, OFFSET_CURRENT); /* Skip 4 bytes */
229
230         xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
231                 (APTR)MSG_READING_INSTS_INFO, NULL);
232
233         /* Read in 36 instruments */
234         for ( j = 1 ; j <= 36 ; j++ )
235         {
236                 struct
237                 {
238                         UBYTE   name[20];
239                         ULONG   size;
240                         UWORD   repeat,
241                                         replen,
242                                         volume,
243                                         mode;
244                 } oktainstr;
245
246                 /* Get instrument data */
247                 if (FRead (fh, &oktainstr, sizeof (oktainstr), 1) != 1)
248                         return ERROR_IOERR;
249
250                 oktainstr.name[19] = '\0';
251
252                 /* Oktalyzer sometimes saves odd lengths for instruments,
253                  * but the data saved in SBOD is always rounded _down_ to an even
254                  * number!  It took me two weeks to find and kill this bug :-(
255                  */
256                 oktainstr.size &= (~1);
257
258                 /* Oktalyzer instrument modes:
259                  *      mode 8 $00 7 bit instruments
260                  *      mode 4 $01 normal instruments
261                  *      mode B $02 both 8bit & 7bit
262                  */
263                 instr_mode[j] = oktainstr.mode;
264
265                 if (oktainstr.size)
266                         if (!(xmAddInstrument (si, j,
267                                 INSTRA_Name,    oktainstr.name,
268                                 INSTRA_Length,  oktainstr.size,
269                                 INSTRA_Repeat,  oktainstr.repeat << 1,
270                                 INSTRA_Replen,  oktainstr.replen << 1,
271                                 INSTRA_Volume,  oktainstr.volume,
272                                 TAG_DONE)))
273                                 return ERROR_NO_FREE_STORE;
274         }
275
276
277         /* Get global song speed */
278
279         /* Check speed header "SPEE" */
280         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
281         if (l != ID_SPEE) return ERROR_NOTMODULE;
282
283         /* SPEE Chunk size ($0000 0002) */
284         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
285
286         /* Get Song Global Speed */
287         if (FRead (fh, &w, 2, 1) != 1) return ERROR_IOERR;
288
289         SetAttrs (si,
290                 SNGA_GlobalSpeed, w,
291                 TAG_DONE);
292
293
294         /* Get number of patterns */
295
296         /* Check header SLEN */
297         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
298         if (l != ID_SLEN) return ERROR_NOTMODULE;
299
300         /* SLEN Chunk size ($0000 0002) */
301         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
302
303         /* Get number of patterns */
304         if (FRead (fh, &numpatts, 2, 1) != 1) return ERROR_IOERR;
305
306         /* Check value */
307         if (numpatts > MAXPATTERNS)
308         {
309                 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
310                         (APTR)MSG_SONG_HAS_TOO_MANY_PATT, NULL);
311                 numpatts = MAXPATTERNS - 1;
312         }
313
314
315         /* Get number of positions in song (Length) */
316
317         /* Check header PLEN */
318         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
319         if (l != ID_PLEN) return ERROR_NOTMODULE;
320
321         /* Chunk size ($0000 0002) */
322         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
323
324         /* Get number of patterns */
325         if (FRead (fh, &songlen, 2, 1) != 1) return ERROR_IOERR;
326
327         /* Check value */
328         if (songlen > MAXPOSITIONS)
329         {
330                 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
331                         (APTR)MSG_SONG_TOO_LONG, NULL);
332                 songlen = MAXPOSITIONS;
333         }
334
335
336         /* Get position table */
337
338         /* Check header PATT */
339         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
340         if (l != ID_PATT) return ERROR_NOTMODULE;
341
342         /* PATT Chunk size ($0000 0080) */
343         if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
344
345
346         /* Read song sequence */
347         {
348                 UBYTE postable[128];
349
350                 if (FRead (fh, postable, 1, 128) != 128) return ERROR_IOERR;
351
352                 if (!(xmSetSongLen (si, songlen)))
353                         return ERROR_NO_FREE_STORE;
354
355                 for (i = 0; i < si->Length; i++)
356                         si->Sequence[i] = postable[i];
357         }
358
359
360         /* Get pattern bodies and convert them */
361
362         xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
363                 (APTR)MSG_READING_PATTS, NULL);
364
365         for (j = 0; j < numpatts; j++)
366         {
367                 if (xmDisplayProgress (j + 1, numpatts))
368                         return ERROR_BREAK;
369
370                 /* Check header "PBOD" */
371                 if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
372                 if (l != ID_PBOD) return ERROR_NOTMODULE;
373
374                 /* Skip Chunk Length (Lines * Tracks * 4 + 2) */
375                 if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
376
377                 /* Get pattern length */
378                 if (FRead (fh, &w, 2, 1) != 1) return ERROR_IOERR;
379
380                 /* Allocate memory for all tracks */
381                 if (!(patt = xmAddPattern (si,
382                         PATTA_Tracks,   voices,
383                         PATTA_Lines,    w,
384                         TAG_DONE)))
385                         return ERROR_NO_FREE_STORE;
386
387                 for ( k = 0 ; k < patt->Lines ; k++)
388                 {
389                         for (i = 0; i < voices; i++)
390                         {
391                                 struct Note *n = &patt->Notes[i][k];
392
393                                 /* Read a whole track row */
394                                 if (FRead (fh, oktanote, 4, 1) != 1) return ERROR_IOERR;
395
396                                 /* Convert Note:
397                                  *
398                                  * Oktalyzer supports 3 octaves (1 to 3).
399                                  * Notes are numbered from 1 (C-1) to 36 (B-3).
400                                  * 0 means no note.
401                                  */
402                                 if (oktanote[0] <= 36)
403                                         n->Note = (oktanote[0] ? (oktanote[0] + 12) : 0);       /* Add one octave */
404                                 else
405                                         xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
406                                                 (APTR)MSG_INVALID_NOTE, oktanote[0], j, i, k);
407
408                                 /* Store Instrument Number */
409                                 n->Inst = (n->Note ? (oktanote[1] + 1) : 0);
410
411                                 /* Convert Effect */
412                                 n->EffNum = DecodeEff (oktanote[2], oktanote[3], j, k, i);
413
414                                 /* Store Effect Value */
415                                 n->EffVal = oktanote[3];
416
417                                 /* Effect Exceptions */
418                                 if (n->EffNum == EFF_MISC)
419                                 {
420                                         /* Oktalyzer has SetFilter values inverted! */
421                                         if ((n->EffVal >> 4) == 1)
422                                                 n->EffVal = 0x10 | (n->EffVal & 0x0F ? 0 : 1);
423                                 }
424                         }       /* end for (i) */
425                 }       /* End for (k) */
426         }       /* End for (j) */
427
428
429         /* Load instruments */
430
431         xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
432                 (APTR)MSG_READING_INSTS, NULL);
433
434         for (j = 1 ; j <= 36 ; j++)
435         {
436                 BYTE *sample;
437
438                 if (!(instr = si->Instr[j])) continue;
439
440                 if (xmDisplayProgress(j, 36))
441                         return ERROR_BREAK;
442
443                 /* Check header SBOD */
444                 if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
445                 if (l != ID_SBOD) return ERROR_NOTMODULE;
446
447                 /* Read chunk size (length of instrument) & check it */
448                 if (FRead (fh, &size, 4, 1) != 1) return ERROR_IOERR;
449                 if (size != instr->Length) return ERROR_NOTMODULE;
450
451                 if (!(sample = AllocVec (instr->Length, MEMF_SAMPLE)))
452                         return ERROR_NO_FREE_STORE;
453
454                 /* Read instrument body */
455                 if (FRead (fh, sample, 1, instr->Length) != instr->Length)
456                 {
457                         FreeVec (sample);
458                         return ERROR_IOERR;
459                 }
460
461                 /* Double sample data */
462                 if (instr_mode[j] == OKT_MODE8 || instr_mode[j] == OKT_MODEB)
463                 {
464                         for (i = 0; i < instr->Length; i++)
465                                 *sample++ <<= 1;
466                 }
467
468                 xmSetInstrument (si, j,
469                         INSTRA_Sample,  sample,
470                         TAG_DONE);
471         }
472
473         /* Check for extra data following the module */
474         if (FGetC (fh) != ENDSTREAMCH)
475                 xmDisplayMessageA (XMDMF_NOTE | XMDMF_USECATALOG,
476                         (APTR)MSG_EXTRA_DATA_AFTER_MOD, NULL);
477
478         return 0;
479 }
480
481
482
483 static HOOKCALL LONG SaveOktalyzer (
484         REG(d0, BPTR fh),
485         REG(a0, struct SongInfo *si),
486         REG(a1, struct XMHook *saver),
487         REG(a2, ULONG *tags))
488 {
489         struct Instrument *instr;
490         ULONG   i, j, k;
491         UWORD   voices, instr_mode, songlen;
492         ULONG   l;              /* Write buffers */
493         UWORD   w;
494         UBYTE   oktanote[4];
495
496         /* Check number of tracks and fix data */
497         voices = si->MaxTracks;
498         if (voices < 4) voices = 4;
499         if (voices > 8) voices = 8;
500
501
502         /* Oktalyzer does not support pattern break (D) command */
503
504         xmProcessSong (si, NULL,
505                 XMSNG_Optimize, XMOF_CUT_PATTERNS,
506                 TAG_DONE);
507
508
509         /* Write file header */
510         if (FWrite (fh, "OKTASONGCMOD", 12, 1) != 1) return ERROR_IOERR;
511
512
513         /* Write maximum number of tracks */
514         l = 8;
515         if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
516
517         /* Write active tracks (only 5 to 8: tracks 1..4 are always on)
518          * TODO: Ask user what tracks should be made active, or loadnig and
519          * saving back the same module will result in the lost of the original
520          * track order.
521          */
522         for (i = 5 ; i <= 8 ; i++)
523         {
524                 if (voices >= i) w = 1;
525                         else w = 0;
526                 if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
527         }
528
529         /* Choose mode for instruments.
530          * When the module is 4 channels, we can always use mode 4.
531          * On modules with 5-7 channels, it is hard to guess which instruments
532          * could be made mode 4, 8 or B, so we always choose mode B.
533          * for 8 channels modules, we just use mode 8 for all instruments.
534          */
535         switch (voices)
536         {
537                 case 4:
538                         instr_mode = OKT_MODE4;
539                         break;
540
541                 case 8:
542                         instr_mode = OKT_MODE8;
543                         break;
544
545                 default:
546                         instr_mode = OKT_MODEB;
547         }
548
549
550
551         /* Write sample names, length, effects, volume */
552         xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
553                 (APTR)MSG_WRITING_INSTINFO, NULL);
554
555         if (FWrite (fh, "SAMP", 4, 1) != 1) return ERROR_IOERR;
556
557         /* Write chunk length */
558         l = 0x480;
559         if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
560
561         for ( j = 1 ; j <= 36 ; j++ )
562         {
563                 struct
564                 {
565                         UBYTE   name[20];
566                         ULONG   size;
567                         UWORD   repeat,
568                                         replen,
569                                         volume,
570                                         mode;
571                 } oktainstr = { 0 };
572
573
574                 if (instr = si->Instr[j])
575                 {
576                         strncpy (oktainstr.name, instr->Name, 19);
577                         oktainstr.size          = instr->Length;
578                         oktainstr.repeat        = instr->Repeat >> 1;
579                         oktainstr.replen        = instr->Replen >> 1;
580                         oktainstr.volume        = instr->Volume;
581                         oktainstr.mode          = instr->Length ? instr_mode : 0;
582                 }
583
584                 /* Write instrument data */
585                 if (FWrite (fh, &oktainstr, sizeof (oktainstr), 1) != 1)
586                         return ERROR_IOERR;
587         }
588
589
590         /* Write global song speed */
591
592         if (FWrite (fh, "SPEE", 4, 1) != 1) return ERROR_IOERR;
593         l = 2;
594         if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
595
596         w = si->GlobalSpeed;
597         if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
598
599
600         if (FWrite (fh, "SLEN", 4, 1) != 1) return ERROR_IOERR;
601         l = 2;
602         if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
603
604         w = si->NumPatterns;
605         if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
606
607
608         /* Write patterns number */
609
610         if (FWrite (fh, "PLEN", 4, 1) != 1) return ERROR_IOERR;
611         l = 2;
612         if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
613
614         songlen = min (si->Length, 128);
615         if (FWrite (fh, &songlen, 2, 1) != 1) return ERROR_IOERR;
616
617
618         /* Write patterns sequence */
619
620         if (FWrite (fh, "PATT", 4, 1) != 1) return ERROR_IOERR;
621         l = 128;
622         if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
623
624         {
625                 UBYTE postable[128];
626
627                 memset (postable, 0, 128);
628
629                 for (i = 0; i < songlen; i++)
630                         postable[i] = si->Sequence[i];
631
632                 if (FWrite (fh, postable, 1, 128) != 128) return ERROR_IOERR;
633         }
634
635
636         /* Write patterns */
637         xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
638                 (APTR)MSG_WRITING_PATTS, NULL);
639
640         for ( j = 0 ; j < si->NumPatterns ; j++)
641         {
642                 if (xmDisplayProgress (j + 1, si->NumPatterns))
643                         return ERROR_BREAK;
644
645                 if (FWrite (fh, "PBOD", 4, 1) != 1) return ERROR_IOERR;
646                 l = si->Patt[j]->Lines * si->MaxTracks * 4 + 2;
647                 if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
648
649                 /* Write pattern length (WORD) */
650                 w = si->Patt[j]->Lines;
651                 if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
652
653                 for (k = 0 ; k < si->Patt[j]->Lines ; k++)
654                 {
655                         for ( i = 0 ; i < voices ; i++)
656                         {
657                                 struct Note *n = &(si->Patt[j]->Notes[i][k]);
658
659                                 if (n->Note)
660                                 {
661                                         if (n->Note < 13)
662                                         {
663                                                 xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
664                                                         (APTR)MSG_NOTE_TOO_LOW, j, i, k);
665                                                 oktanote[0] = n->Note;
666                                         }
667                                         else
668                                         {
669                                                 if (n->Note > 48)
670                                                 {
671                                                         xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
672                                                                 (APTR)MSG_NOTE_TOO_HIGH, j, i, k);
673                                                         oktanote[0] = n->Note - 24;
674                                                 }
675                                                 else
676                                                         oktanote[0] = n->Note - 12;
677                                         }
678                                 }
679                                 else oktanote[0] = 0;
680
681                                 oktanote[1] = (n->Inst ? (n->Inst-1) : 0);
682                                 oktanote[2] = Effects[n->EffNum];
683
684                                 /* Effect Exceptions */
685                                 switch (n->EffNum)
686                                 {
687                                         case EFF_MISC:
688                                                 if ((n->EffVal >> 4) == 1)      /* Filter On/Off */
689                                                         oktanote[3] = (n->EffVal ? 0 : 1);
690                                                 break;
691
692                                         case EFF_SETSPEED:
693                                                 if (n->EffVal > 0x0F)
694                                                         oktanote[3] = 0x0F;
695                                                 else
696                                                         oktanote[3] = n->EffVal;
697                                                 break;
698
699                                         default:
700                                                 oktanote[3] = n->EffVal;
701                                                 break;
702                                 }
703
704                                 if (FWrite (fh, oktanote, 4, 1) != 1) return ERROR_IOERR;
705
706                         }       /* end for(i) */
707                 }       /* End for(k) */
708         }       /* End for(j) */
709
710
711         /* Write Instruments Data */
712
713         xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
714                 (APTR)MSG_WRITING_INSTDATA, NULL);
715
716         for (j = 1 ; j <= 36 ; j++)
717         {
718                 BYTE *samp;
719                 BOOL free_samp = FALSE;
720
721                 /* Skip empty instruments slots */
722
723                 if (!(instr = si->Instr[j]))
724                         continue;
725                 if (!(l = instr->Length))
726                         continue;
727
728                 if (xmDisplayProgress(j, 36))
729                         return ERROR_BREAK;
730
731
732                 if (instr_mode == OKT_MODE8 || instr_mode == OKT_MODEB)
733                 {
734                         if (samp = AllocVec (l, MEMF_ANY))
735                         {
736                                 /* Halve volume */
737                                 for (i = 0; i < l; i++)
738                                         samp[i] = instr->Sample[i] >> 1;
739
740                                 free_samp = TRUE;       /* Free this when done */
741                         }
742                         else
743                         {
744                                 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
745                                         (APTR)MSG_NO_MEM_TO_HALVE, j);
746                                 samp = instr->Sample;
747                         }
748                 }
749                 else samp = instr->Sample;
750
751
752                 if (!samp) l = 0;
753
754                 if (FWrite (fh, "SBOD", 4, 1) != 1) return ERROR_IOERR;
755                 if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
756
757                 if (l)
758                 {
759                         Flush (fh);
760                         if (Write (fh, samp, l) != l)
761                                 return ERROR_IOERR;
762                 }
763
764                 if (free_samp) FreeVec (samp);
765         }
766
767         return 0;
768 }
769
770
771
772 HOOKCALL struct Library * _UserLibInit (REG(a6, struct Library *mybase))
773 {
774         return mybase;
775 }
776
777
778
779 HOOKCALL struct Library * _UserLibCleanup (REG(a6, struct Library *mybase))
780 {
781         return mybase;
782 }
783
784
785
786 HOOKCALL APTR _GetEngine (REG(a6, struct Library *mybase))
787 {
788         return NULL;
789 }
790
791
792
793 HOOKCALL APTR _SetupXMHook (
794         REG(a0, struct XModuleBase *_XModuleBase),
795         REG(a6, struct Library *mybase))
796 {
797         if (!(strcmp (_XModuleBase->xm_Library.lib_Node.ln_Name, "xmodule.library")))
798         {
799                 SysBase                 = *((struct ExecBase **)4);
800                 XModuleBase             = _XModuleBase;
801                 IntuitionBase   = XModuleBase->xm_IntuitionBase;
802                 DOSBase                 = XModuleBase->xm_DOSBase;
803
804                 xmAddHook (
805                         XMHOOK_Type,                    NT_XMLOADER,
806                         XMHOOK_Name,                    (LONG)"Oktalyzer",
807                         XMHOOK_Priority,                0,
808                         XMHOOK_Descr,                   (LONG)"Oktalyzer 1.57",
809                         XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
810                         XMHOOK_ID,                              ID_OKTA,
811                         XMHOOK_LibraryBase,             mybase,
812                         XMHOOK_LoadModFunc,             LoadOktalyzer,
813                         XMHOOK_IdentifyModFunc, IdentifyOktalyzer,
814                         XMHOOK_MaxTracks,               8,
815                         XMHOOK_MaxPatterns,             64,
816                         XMHOOK_MaxInstruments,  36,
817                         XMHOOK_MaxLength,               128,
818                         XMHOOK_MaxSampleLen,    131070,
819                         XMHOOK_MaxPattLen,              128,
820                         TAG_DONE);
821
822                 xmAddHook (
823                         XMHOOK_Type,                    NT_XMSAVER,
824                         XMHOOK_Name,                    (LONG)"Oktalyzer",
825                         XMHOOK_Priority,                0,
826                         XMHOOK_Descr,                   (LONG)"Oktalyzer 1.57",
827                         XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
828                         XMHOOK_ID,                              ID_OKTA,
829                         XMHOOK_LibraryBase,             mybase,
830                         XMHOOK_SaveModFunc,             SaveOktalyzer,
831                         XMHOOK_MaxTracks,               8,
832                         XMHOOK_MaxPatterns,             64,
833                         XMHOOK_MaxInstruments,  36,
834                         XMHOOK_MaxLength,               128,
835                         XMHOOK_MaxSampleLen,    131070,
836                         XMHOOK_MaxPattLen,              128,
837                         TAG_DONE);
838         }
839
840         return NULL;
841 }