Initial commit.
[amiga/xmodule.git] / Library.c
1 /*
2 **      Library.c
3 **
4 **      Copyright (C) 1995,96,97 Bernardo Innocenti
5 **
6 **      xmodule.library functions
7 */
8
9 #include <exec/libraries.h>
10 #include <exec/memory.h>
11 #include <dos/dos.h>
12 #include <dos/stdio.h>
13 #include <libraries/xmodule.h>
14
15 #include <proto/exec.h>
16 #include <proto/dos.h>
17 #include <proto/utility.h>
18 #include <proto/intuition.h>
19 #include <proto/utility.h>
20 #include <proto/iffparse.h>
21 #include <proto/xmodule.h>
22
23 #include "XModulePriv.h"
24 #include "Gui.h"
25
26
27
28 /****** xmodule/--background-- *********************************************
29 *
30 *       INTRODUCTION
31 *               The xmodule.library is an API that provides access to the XModule
32 *               song management engine, as well as other general pourpose
33 *               services.  Hooks and external applications can use this library
34 *               to create, load, save and modify songs.
35 *
36 *               The xmodule.library does not exist as a stand alone shared libray.
37 *               Instead, it's code is contained inside the main XModule executable
38 *               (altrough it's logically independant from the rest of the XModule
39 *               code).
40 *
41 *               XModule adds the xmodule.library to the exec libray list at
42 *               startup time, unless it finds one already in the list (this might
43 *               happen when multiple copies of XModule are running at the same
44 *               time).
45 *
46 *       PUBLIC SONGS
47 *               The xmodule.library maintains a list of public songs which can be
48 *               used by all applications which opened the library.  Each song in
49 *               the list is protected from multiple task access by a
50 *               SignalSemaphore, and whole list is also protected by another
51 *               semaphore.  The locking mechanism is very simple: before you can
52 *               access a song you must obtain its semaphore, and you must release
53 *               it as soon as you are done with it.  If you are locking a song to
54 *               just to read some information (i.e.: you don't want to modify
55 *               anything), you should obtain a shared lock instead of an exclusive
56 *               one.  The list must be locked whenever you want to add or remove
57 *               a song, or when you scan it in any way.
58 *
59 *               Since public songs could be modified or even deleted by other
60 *               tasks, do not make your songs public unless your code is smart
61 *               enough to handle all possible situations.
62 *
63 *
64 *       HOOKS
65 *               Actually, most modular programs call them `modules', but it would
66 *               have created a lot of confusion with a program like XModule :-).
67 *
68 *               Hooks are programs that add some kind of functionality
69 *               to XModule.  External hook files are standard shared libraries
70 *               which will open the xmodule.library when they are loaded and
71 *               call xmAddHookA() to add themselves to a list of hooks maintained
72 *               by the library.
73 *
74 *               Currently, XModule supports two kinds of hooks: loaders and
75 *               savers.  Loader hooks can also provide a function which
76 *               identifies a particular module format.
77 *
78 *               An external hook libray may also contain several hooks.
79 *               Putting a loader and a saver for one particolar format together
80 *               in one libray is generally a good idea, while making a hook
81 *               with hundereds of loaders and savers isn't a good move because
82 *               it makes the whole concept of external hooks quite useless.
83 *               Grouping different versions or variants of one format together
84 *               in one external hook is acceptable.
85 *
86 *       SEE ALSO
87 *               songclass/--background--, exec/ObtainSemaphore()
88 *
89 ****************************************************************************
90 */
91
92
93
94 /* Library function prototypes */
95
96 static LIBCALL struct SongInfo * XModuleCreateSong (
97         REG(a0, struct TagItem *tags),
98         REG(a6, struct XModuleBase *XModuleBase));
99 static LIBCALL void XModuleDeleteSong (
100         REG(a0, struct SongInfo *si),
101         REG(a6, struct XModuleBase *XModuleBase));
102 static LIBCALL ULONG XModuleAddSong (
103         REG(a0, struct SongInfo *si),
104         REG(a1, struct SongInfo *position),
105         REG(a2, struct TagItem *tags),
106         REG(a6, struct XModuleBase *XModuleBase));
107 static LIBCALL ULONG XModuleRemSong (
108         REG(a0, struct SongInfo *si),
109         REG(a6, struct XModuleBase *XModuleBase));
110 static LIBCALL ULONG XModuleActivateSong (
111         REG(a0, struct SongInfo *si),
112         REG(a6, struct XModuleBase *XModuleBase));
113 static LIBCALL struct SongInfo *XModuleLockActiveSong (
114         REG(d0, UWORD mode),
115         REG(a6, struct XModuleBase *XModuleBase));
116 static LIBCALL struct XMHook *XModuleAddHook (
117         REG(a0, struct TagItem *tags),
118         REG(a6, struct XModuleBase *XModuleBase));
119 static LIBCALL void XModuleRemHook (
120         REG(a0, struct XMHook *hook),
121         REG(a6, struct XModuleBase *XModuleBase));
122 static LIBCALL struct XMHook *XModuleIdentifyModule(
123         REG(d0, BPTR fh),
124         REG(a0, struct TagItem *tags),
125         REG(a6, struct XModuleBase *XModuleBase));
126 static LIBCALL struct SongInfo * XModuleLoadModule (
127         REG(a0, CONST_STRPTR filename),
128         REG(a1, struct TagItem *tags),
129         REG(a6, struct XModuleBase *XModuleBase));
130 static LIBCALL LONG XModuleSaveModule (
131         REG(a0, struct SongInfo *si),
132         REG(a1, CONST_STRPTR filename),
133         REG(a2, struct XMHook *saver),
134         REG(a3, struct TagItem *tags),
135         REG(a6, struct XModuleBase *XModuleBase));
136 static LIBCALL UWORD *XModuleSetSongLen(
137         REG(a0, struct SongInfo *si),
138         REG(d0, UWORD length),
139         REG(a6, struct XModuleBase *XModuleBase));
140 static LIBCALL void XModuleAddPattern (
141         REG(a0, struct SongInfo *si),
142         REG(a1, struct TagItem *tags),
143         REG(a6, struct XModuleBase *XModuleBase));
144 static LIBCALL void XModuleSetPattern (
145         REG(a0, struct SongInfo *si),
146         REG(d0, ULONG pattNum),
147         REG(a1, struct TagItem *tags),
148         REG(a6, struct XModuleBase *XModuleBase));
149 static LIBCALL void XModuleRemPattern (
150         REG(a0, struct SongInfo *si),
151         REG(d0, ULONG pattNum),
152         REG(d1, ULONG replaceWith));
153 static LIBCALL void XModuleAddInstrument (
154         REG(a0, struct SongInfo *si),
155         REG(d0, LONG num),
156         REG(a1, struct TagItem *tags),
157         REG(a6, struct XModuleBase *XModuleBase));
158 static LIBCALL void XModuleSetInstrument (
159         REG(a0, struct SongInfo *si),
160         REG(d0, LONG num),
161         REG(a1, struct TagItem *tags),
162         REG(a6, struct XModuleBase *XModuleBase));
163 static LIBCALL void XModuleRemInstrument (
164         REG(a0, struct SongInfo *si),
165         REG(d0, ULONG num),
166         REG(a6, struct XModuleBase *XModuleBase));
167 static LIBCALL LONG XModuleProcessSong (
168         REG(a0, struct SongInfo *si),
169         REG(a1, void *reserved),
170         REG(a2, struct TagItem *tags),
171         REG(a6, struct XModuleBase *XModuleBase));
172 static LIBCALL void XModuleDisplayMessage (
173         REG(d0, ULONG level),
174         REG(a0, CONST_STRPTR msg),
175         REG(a1, LONG *args),
176         REG(a6, struct XModuleBase *XModuleBase));
177 static LIBCALL void XModuleDisplayProgress (
178         REG(d0, ULONG actual),
179         REG(d1, ULONG max),
180         REG(a6, struct XModuleBase *XModuleBase));
181
182
183
184 /* Local function prototypes */
185
186 static LIBCALL void XModuleLibOpen              (void);
187 static LIBCALL void XModuleLibClose             (void);
188 static LIBCALL void XModuleLibExpunge   (void);
189 static LIBCALL void XModuleLibExtFunc   (void);
190 static LIBCALL LONG XModuleRexxServer   (void);
191
192
193
194 /* Library vector table */
195
196 static APTR XModuleVectors[] =
197 {
198         XModuleLibOpen,
199         XModuleLibClose,
200         XModuleLibExpunge,
201         XModuleLibExtFunc,
202         XModuleRexxServer,
203         XModuleRexxServer,
204         XModuleRexxServer,
205         XModuleRexxServer,
206
207         XModuleCreateSong,
208         XModuleDeleteSong,
209         XModuleAddSong,
210         XModuleRemSong,
211         XModuleActivateSong,
212         XModuleLockActiveSong,
213         XModuleAddHook,
214         XModuleRemHook,
215         XModuleIdentifyModule,
216         XModuleLoadModule,
217         XModuleSaveModule,
218         XModuleSetSongLen,
219         XModuleAddPattern,
220         XModuleSetPattern,
221         XModuleRemPattern,
222         XModuleAddInstrument,
223         XModuleSetInstrument,
224         XModuleRemInstrument,
225         XModuleProcessSong,
226         XModuleDisplayMessage,
227         XModuleDisplayProgress,
228
229         (APTR)-1
230 };
231
232
233
234 GLOBALCALL ULONG MakeXModuleLibrary (void)
235 {
236         if (XModuleBase = (struct XModuleBase *)MakeLibrary (XModuleVectors,
237                 NULL, NULL, sizeof (struct XModuleBase), NULL))
238         {
239                 /* Clear base variables following the library node
240                  * (InitSemaphore reqires a clean memory block!)
241                  *
242                  * GOOD NEWS: Clearing the library base is not required because
243                  * MakeLibrary() allocates a MEMF_CLEAR memory block.
244                  *
245                  * memset (((UBYTE *)XModuleBase) + sizeof (struct Library), 0,
246                  *      sizeof (struct XModuleBase) - sizeof (struct Library));
247                  */
248
249                 XModuleBase->xm_Library.lib_Node.ln_Name = (STRPTR)"xmodule.library";
250                 XModuleBase->xm_Library.lib_Version = VERSION;
251                 XModuleBase->xm_Library.lib_Revision = REVISION;
252                 XModuleBase->xm_Library.lib_IdString = (STRPTR)Version + 6;
253
254                 XModuleBase->xm_DOSBase = DOSBase;
255                 XModuleBase->xm_UtilityBase = UtilityBase;
256                 XModuleBase->xm_IFFParseBase = IFFParseBase;
257                 XModuleBase->xm_IntuitionBase = IntuitionBase;
258
259                 NEWLIST ((struct List *)&XModuleBase->xm_Songs);
260                 NEWLIST ((struct List *)&XModuleBase->xm_Loaders);
261                 NEWLIST ((struct List *)&XModuleBase->xm_Savers);
262                 InitSemaphore (&XModuleBase->xm_BaseLock);
263
264                 /* Create global memory pool */
265                 if (!(XModuleBase->xm_Pool = CreatePool (MEMF_ANY, 16*1024, 4*1024)))
266                         return ERROR_NO_FREE_STORE;
267
268                 if (!(XModuleBase->xm_SongClass = InitSongClass()))
269                         return ERROR_NO_FREE_STORE;
270
271                 return RETURN_OK;
272         }
273
274         return ERROR_NO_FREE_STORE;
275 }
276
277
278
279 GLOBALCALL void DisposeXModuleLibrary (void)
280 {
281         if (XModuleBase)
282         {
283                 ObtainSemaphore (&XModuleBase->xm_BaseLock);
284
285                 /* Free all songs in SongList */
286                 while (!IsListEmpty ((struct List *)&XModuleBase->xm_Songs))
287                         xmDeleteSong ((struct SongInfo *)XModuleBase->xm_Songs.mlh_Head);
288
289                 /* Remove all loaders */
290                 while (!IsListEmpty ((struct List *)&XModuleBase->xm_Loaders))
291                         xmRemHook ((struct XMHook *)XModuleBase->xm_Loaders.mlh_Head);
292
293                 /* Remove all savers */
294                 while (!IsListEmpty ((struct List *)&XModuleBase->xm_Savers))
295                         xmRemHook ((struct XMHook *)XModuleBase->xm_Savers.mlh_Head);
296
297                 /* Free the songclass */
298                 FreeSongClass (XModuleBase->xm_SongClass);
299
300                 /* Dispose the global memory pool */
301                 if (XModuleBase->xm_Pool) DeletePool (XModuleBase->xm_Pool);
302
303                 FreeMem (((UBYTE *)XModuleBase) - XModuleBase->xm_Library.lib_NegSize,
304                         XModuleBase->xm_Library.lib_NegSize + XModuleBase->xm_Library.lib_PosSize);
305                 XModuleBase = NULL;
306         }
307 }
308
309
310
311 static LIBCALL void XModuleLibOpen (void)
312 {
313 }
314
315
316
317 static LIBCALL void XModuleLibClose (void)
318 {
319 }
320
321
322
323 static LIBCALL void XModuleLibExpunge (void)
324 {
325 }
326
327
328
329 static LIBCALL void XModuleLibExtFunc (void)
330 {
331 }
332
333
334
335 static LIBCALL LONG XModuleRexxServer (void)
336 {
337         return 0;
338 }
339
340
341
342 /****** xmodule/xmAddHook **************************************************
343 *
344 *       NAME
345 *               xmAddHookA -- Creates a new XModule Hook
346 *               xmAddHook -- Varargs stub for xmAddHookA
347 *
348 *       SYNOPSIS
349 *               hook = xmAddHookA(tagList)
350 *               D0                A0
351 *
352 *               struct XMHook *xmAddHookA(struct TagItem *);
353 *
354 *
355 *               hook = xmAddHook(Tag1,...)
356 *
357 *               struct XMHook *xmAddHook(ULONG,...);
358 *
359 *       FUNCTION
360 *               Creates a new XMHook structure and fills it in with data supplied
361 *               with the TagList. Adds the newly created Hook to the appropriate
362 *               list.
363 *
364 *       INPUTS
365 *               tagList - pointer to a tag list specifying how to initialize the
366 *                       XMHook structure.
367 *
368 *       TAGS
369 *               XMHOOK_Type - (ULONG) Defines the pourpose of this hook. Possible
370 *                       values are currently NT_XMLOADER and NT_XMSAVER. (This
371 *                       tag is REQUIRED).
372 *
373 *               XMHOOK_Name - (STRPTR) ti_Data contains a short name for the
374 *                       hook (e.g: "SoundTracker").  This name will appear in the
375 *                       Savers list if this hook is a saver and will be passed as an
376 *                       argument for some ARexx commands, so please use a single
377 *                       word name.  (This tag is REQUIRED).
378 *
379 *               XMHOOK_Priority - (BYTE) Priority to give to this hook.  Hooks
380 *                       with higher priorities will be used before lower priority
381 *                       ones and will come first in lists shown to the user. Valid
382 *                       range is -128..+127, but please restrict to a -20..+20
383 *                       interval for normal cases. (Defaults to 0).
384 *
385 *               XMHOOK_Descr - (STRPTR) Verbose description of the hook
386 *                       (without newlines). (Defaults to NULL).
387 *
388 *               XMHOOK_Author - (STRPTR) Author's name.  Please, just put
389 *                       your full name here; no greetings, copyright notices,
390 *                       etc. (Defaults to NULL).
391 *
392 *               XMHOOK_ID - (ULONG) This is a unique, IFF-style identifier for
393 *                       the format.  If the format is an IFF format, it must be the
394 *                       same of the FORM ID. (Defaults to 0, this tag is required
395 *                       for IFF loaders and savers).
396 *
397 *               XMHOOK_Flags - (ULONG) Sets miscellaneous flags for this hook.
398 *                       See xmodule.h for possible flags.
399 *
400 *               XMHOOK_LibraryBase - (struct Library *) Pointer to the library
401 *                       base for external hooks.  This pointer will be used to open
402 *                       the library when the hook is created and to close it when
403 *                       the hook is deleted.  If you do not pass this tag, you must
404 *                       find some other way to keep your code in memory until one
405 *                       or more of your hooks are active.  XModule will close your
406 *                       library just after calling the SetupXMHook() function.
407 *                       (Defaults to NULL).
408 *
409 *               XMHOOK_UserData - (APTR) ti_Data will be stored in the
410 *                       xmh_UserData field of the XMHook structure. This field can
411 *                       come andy to store private data. (Defaults to NULL).
412 *
413 *               XMHOOK_RemoveHookFunc - (APTR) Pointer to a function which will be
414 *                       called upon removing the hook.  This function can be used to
415 *                       free any private resources allocated by the hook when it was
416 *                       created. The template for the function is:
417 *
418 *                               void RemoveHookFunc (struct XMHook *hook);
419 *                                                                        A0
420 *
421 *               XMHOOK_LoadModFunc - (APTR) Pointer to the hook function which
422 *                       loads a module. The template for the function is:
423 *
424 *                               LONG LoadModFunc (BPTR fileHandle, struct SongInfo *song,
425 *                                                                 D0               A0
426 *                                       struct XMHook *loader);
427 *                                       A1
428 *
429 *                       `fileHandle' is an open file to load the module from. The
430 *                       caller will take care to close it for you. The loader should
431 *                       return RETURN_OK for success, or any other AmigaDOS error
432 *                       code to mean failure.  In particular, RETURN_WARN indicates
433 *                       that something went wrong, but the song is ok.  The special
434 *                       error code ERROR_IOERR can be used when some dos.library
435 *                       call (e.g.: Read()) failed.  In the latter case, the
436 *                       AmigaDOS IoErr value should be set to explain the specific
437 *                       cause of the problem.
438 *                       (This tag is required by all NT_XMLOADER type hooks).
439 *
440 *               XMHOOK_SaveModFunc - (APTR) Pointer to the hook function which
441 *                       saves a module. The template for the function is:
442 *
443 *                               LONG SaveModFunc (BPTR fileHandle, struct SongInfo *song,
444 *                                                                 D0               A0
445 *                                       struct XMHook *saver);
446 *                                       A1
447 *
448 *                       fileHandle is an open file to save the module to. The caller
449 *                       will take care to close it for you. The saver should return
450 *                       RETURN_OK for success, or any other AmigaDOS error code to
451 *                       mean failure.  In particular, RETURN_WARN indicates that
452 *                       something went wrong, but the song is ok.  The special
453 *                       error code ERROR_IOERR can be used when some dos.library
454 *                       call (e.g.: Read()) failed.  In the latter case, the
455 *                       AmigaDOS IoErr value should be set to explain the specific
456 *                       cause of the problem.
457 *                       (This tag is required by all NT_XMSAVER type hooks).
458 *
459 *
460 *               XMHOOK_IdentifyModFunc - (APTR) Pointer to the hook function
461 *                       which identifies a module format. The template for the
462 *                       function is:
463 *
464 *                               struct XMHook *IdentifyModFunc (BPTR fileHandle,
465 *                                                                                   D0
466 *                                       struct XMHook *hook,struct TagItem *tagList);
467 *                                       A0                  A1
468 *
469 *                       fileHandle is an open file to try the identification on. The
470 *                       caller will take care to close it. NOTE: Do not make assumptions
471 *                       on the initial file position (e.g: seek yourself to position 0
472 *                       if you need to). This funtion should return a pointer to a valid
473 *                       loader (usually the one passed in) if the format is recognized,
474 *                       NULL otherwhise.  (This tag is required by all NT_XMLOADER type
475 *                       hooks).
476 *
477 *       RESULT
478 *               hook - Pointer to the newly allocated XMHook structure, or
479 *                       NULL for failure.
480 *
481 *       SEE ALSO
482 *               xmRemHook(), xmIdentifyModule(), xmLoadSong(), xmSaveSong()
483 *
484 ****************************************************************************
485 */
486 static LIBCALL struct XMHook *XModuleAddHook (
487         REG(a0, struct TagItem *tags),
488         REG(a6, struct XModuleBase *XModuleBase))
489 {
490         struct XMHook *hook;
491
492         if (hook = AllocPooled (XModuleBase->xm_Pool, sizeof (struct XMHook)))
493         {
494                 hook->xmh_Link.ln_Name  = (UBYTE *)     GetTagData (XMHOOK_Name,                NULL,   tags);
495                 hook->xmh_Link.ln_Type  = (UBYTE)       GetTagData (XMHOOK_Type,                0,              tags);
496                 hook->xmh_Link.ln_Pri   = (BYTE)        GetTagData (XMHOOK_Priority,    0,              tags);
497                 hook->xmh_Descr                 = (STRPTR)      GetTagData (XMHOOK_Descr,               0,              tags);
498                 hook->xmh_Author                = (STRPTR)      GetTagData (XMHOOK_Author,              0,              tags);
499                 hook->xmh_ID                    = (ULONG)       GetTagData (XMHOOK_ID,                  0,              tags);
500                 hook->xmh_Flags                 = (ULONG)       GetTagData (XMHOOK_Flags,               0,              tags);
501                 hook->xmh_UserData              = (APTR)        GetTagData (XMHOOK_UserData,    NULL,   tags);
502                 hook->xmh_LibraryBase   = (APTR)        GetTagData (XMHOOK_LibraryBase, NULL,   tags);
503                 hook->xmh_RemoveHook    = (void (* ASMCALL)()) GetTagData (XMHOOK_RemoveFunc,   NULL, tags);
504                 hook->xmh_LoadMod               = (LONG (* ASMCALL)()) GetTagData (XMHOOK_LoadModFunc,  NULL, tags);
505                 hook->xmh_SaveMod               = (LONG (* ASMCALL)()) GetTagData (XMHOOK_SaveModFunc,  NULL, tags);
506                 hook->xmh_IdentifyMod   = (struct XMHook * (* ASMCALL)()) GetTagData (XMHOOK_IdentifyModFunc, NULL, tags);
507                 hook->xmh_MaxTracks             = GetTagData (XMHOOK_MaxTracks,                 MAXTRACKS,              tags);
508                 hook->xmh_MaxPatterns   = GetTagData (XMHOOK_MaxPatterns,               MAXPATTERNS,    tags);
509                 hook->xmh_MaxInstruments= GetTagData (XMHOOK_MaxInstruments,    MAXINSTRUMENTS, tags);
510                 hook->xmh_MaxLength             = GetTagData (XMHOOK_MaxLength,                 MAXPOSITIONS,   tags);
511                 hook->xmh_MaxSampleLen  = GetTagData (XMHOOK_MaxSampleLen,              ((2<<31)-1),    tags);
512                 hook->xmh_MaxPattLen    = GetTagData (XMHOOK_MaxPattLen,                MAXPATTLINES,   tags);
513                 hook->xmh_Version               = XMHOOK_VERSION;
514
515
516                 if (hook->xmh_LibraryBase)
517                         if (!(OpenLibrary (hook->xmh_LibraryBase->lib_Node.ln_Name, 0)))
518                                 return NULL;
519
520                 ObtainSemaphore (&XModuleBase->xm_BaseLock);
521                 Enqueue ((struct List *)((hook->xmh_Link.ln_Type == NT_XMSAVER) ?
522                         &XModuleBase->xm_Savers : &XModuleBase->xm_Loaders),
523                         (struct Node *)hook);
524                 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
525         }
526
527         return hook;
528 }
529
530
531
532 /****** xmodule/xmRemHook **************************************************
533 *
534 *       NAME
535 *               xmRemHook -- Removes an XModule Hook
536 *
537 *       SYNOPSIS
538 *               xmRemHookA(xmHook)
539 *                          A0
540 *
541 *               void xmRemHook(struct XMHook *);
542 *
543 *       FUNCTION
544 *               Removes an XModule Hook, calls its RemoveHookFunc() and then
545 *               frees its memory.
546 *
547 *       INPUTS
548 *               xmHook - pointer to the hook to be removed.
549 *
550 *       SEE ALSO
551 *               xmAddHook()
552 *
553 ****************************************************************************
554 */
555 static LIBCALL void XModuleRemHook (
556         REG(a0, struct XMHook *hook),
557         REG(a6, struct XModuleBase *XModuleBase))
558 {
559         ObtainSemaphore (&XModuleBase->xm_BaseLock);
560         REMOVE ((struct Node *)hook);
561         ReleaseSemaphore (&XModuleBase->xm_BaseLock);
562
563         if (hook->xmh_RemoveHook)
564                 hook->xmh_RemoveHook (hook);
565
566         if (hook->xmh_LibraryBase) CloseLibrary (hook->xmh_LibraryBase);
567
568         FreePooled (XModuleBase->xm_Pool, hook, sizeof (struct XMHook));
569 }
570
571
572
573
574 /****** xmodule/xmCreateSong ***********************************************
575 *
576 *       NAME
577 *               xmCreateSongA -- Create and initialize a new SongInfo structure
578 *               xmCreateSong -- Varargs stub for xmCreateSongA
579 *
580 *       SYNOPSIS
581 *               songInfo = xmCreateSongA(tagList);
582 *               D0                      A0
583 *
584 *               struct SongInfo *xmCreateSongA(struct TagItem *);
585 *
586 *
587 *               songInfo = xmCreateSong(Tag1,...);
588 *
589 *               struct SongInfo *xmCreateSong(ULONG,...);
590 *
591 *       FUNCTION
592 *               Allocates and initializes a new SongInfo structure.  The song
593 *               can then be added to the song list via xmAddSongA(), in which
594 *               case, it is no longer required to free it with xmDeleteSong().
595 *
596 *       INPUTS
597 *               tagList - pointer to an optional tag list specifying how to
598 *                       initialize the SongInfo structure.
599 *
600 *       TAGS
601 *               SNGA_DefaultTracks - Sets the default number of pattern tracks for
602 *                       the song. Defaults to 4.
603 *
604 *               SNGA_DefaultPattLen - Sets the default number of pattern lines for
605 *                       the song. Defaults to 64.
606 *
607 *               SNGA_ReadyToUse - Adds one pattern and one position to the song.
608 *                       Defaults to FALSE.
609 *
610 *               XMSNG_AddToList - (struct SongInfo *) Add the song to the song list.
611 *                       When a song is being added to the list, a shared lock is
612 *                       obtained on it to prevent other tasks from modifying (or even
613 *                       remove) the song before you can do your job on it. IT IS YOUR
614 *                       DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
615 *                       Passing NULL adds the song on the head of the list, while -1
616 *                       (~0) will add it to the end of the SongList. Passing in a
617 *                       pointer to another song which is already in the list, will
618 *                       add the new song _after_ the latter.
619 *                       (Default is not adding the song).
620 *
621 *               XMSNG_Active - (BOOL) Makes this song the active one. This tag is
622 *                       considered only if the XMSNG_AddToList tag is also passed.
623 *
624 *       RESULT
625 *               songInfo - pointer to the newly allocated SongInfo structure, or
626 *                       NULL for failure.
627 *
628 *       SEE ALSO
629 *               xmDeleteSong(), xmAddSongA()
630 *
631 ****************************************************************************
632 */
633 static LIBCALL struct SongInfo * XModuleCreateSong (
634         REG(a0, struct TagItem *tags),
635         REG(a6, struct XModuleBase *XModuleBase))
636 {
637         struct SongInfo *si;
638         struct TagItem *tag;
639
640         if (si = NewObjectA (XModuleBase->xm_SongClass, NULL, tags))
641         {
642                 if (tag = FindTagItem (XMSNG_AddToList, tags))
643                 {
644                         ObtainSemaphore (&si->Lock);
645                         xmAddSongA (si, (struct SongInfo *)tag->ti_Data, tags);
646                 }
647         }
648
649         return si;
650 }
651
652
653
654 /****** xmodule/xmDeleteSong ***********************************************
655 *
656 *       NAME
657 *               xmDeleteSong -- Deletes a song
658 *
659 *       SYNOPSIS
660 *               xmDeleteSong(songInfo)
661 *                            A0
662 *
663 *               void xmDeleteSong(struct SongInfo *);
664 *
665 *       FUNCTION
666 *               Deletes a song and all the items attached to it (patterns,
667 *               instruments, etc.).  This call will also remove the song from
668 *               the song list if it had been added to it.
669 *
670 *       INPUTS
671 *               songInfo - pointer to the SongInfo structure to be deleted.
672 *                       Passing a NULL pointer is harmless.
673 *
674 *       NOTE
675 *               In order to delete a song which has been added to the public
676 *               song list, you must first obtain an exclusive lock on it to
677 *               prevent other tasks from walking on it while you are freeing
678 *               it's data structures.  The semaphore does NOT need to be
679 *               relinquished, because the SongInfo structure won't exist any
680 *               more when this call returns.
681 *
682 *       SEE ALSO
683 *               xmCreateSong(), xmRemSong()
684 *
685 ****************************************************************************
686 */
687 static LIBCALL void XModuleDeleteSong (
688         REG(a0, struct SongInfo *si),
689         REG(a6, struct XModuleBase *XModuleBase))
690 {
691         xmRemSong (si);
692         DisposeObject (si);
693 }
694
695
696
697 /****** xmodule/xmAddSongA *************************************************
698 *
699 *       NAME
700 *               xmAddSongA -- Add a song to the song list
701 *               xmAddSong -- Varargs stub for xmAddSongA
702 *
703 *       SYNOPSIS
704 *               success = xmAddSongA(songInfo,position,tagList);
705 *               D0                   A0       A1       A2
706 *
707 *               ULONG xmAddSongA(struct SongInfo *,struct SongInfo *,
708 *                       struct TagItem *);
709 *
710 *
711 *               success = xmAddSong(songInfo,position,tag1,...);
712 *
713 *               ULONG xmAddSong(struct SongInfo *,struct SongInfo *,
714 *                       LONG,...);
715 *
716 *       FUNCTION
717 *               Adds a song to the public song list. Trying to add a song
718 *               twice is harmless.
719 *
720 *       INPUTS
721 *               songInfo - song to be added.  Passing a NULL pointer is safe.
722 *               position - Position to add the song in.  Passing NULL adds the
723 *                       song in the head of the list, while ~0 adds it in the tail.
724 *                       passing a pointer to a SongInfo already in the list inserts
725 *                       the first song _after_ the other one.
726 *               tagList - pointer to a TagList for more arguments.
727 *
728 *       TAGS
729 *               XMSNG_Active - (BOOL) Makes this song the active one. The
730 *                       default is to just add the song without activating it.
731 *
732 *       RESULT
733 *               success - Will be 0 for failure, in which case the song will
734 *                       not be added to the songs list. Currently, xmAddSongA()
735 *                       will never fail.
736 *
737 *       NOTE
738 *               The song list is protected from multiple tasks access by a
739 *               SignalSemaphore in XModuleBase. This call will wait if the
740 *               song list has been locked by another task. Beware of deadlock
741 *               conditions!
742 *
743 *       SEE ALSO
744 *               xmRemSong()
745 *
746 ****************************************************************************
747 */
748 static LIBCALL ULONG XModuleAddSong (
749         REG(a0, struct SongInfo *si),
750         REG(a1, struct SongInfo *position),
751         REG(a2, struct TagItem *tags),
752         REG(a6, struct XModuleBase *XModuleBase))
753 {
754         if (!si) return FALSE;
755
756         if (!si->Link.ln_Type)  /* Is it already in the list? */
757         {
758                 ObtainSemaphore (&XModuleBase->xm_BaseLock);
759                 DetatchSongInfoList();
760
761                 if ((ULONG)position == ~0)
762                         ADDTAIL ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si);
763                 else
764                         Insert ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si, (struct Node *)position);
765
766                 si->Link.ln_Type = NT_XMSONG;           /* Mark this song */
767
768                 if (GetTagData (XMSNG_Active, FALSE, tags))
769                         xmActivateSong (si);
770
771                 UpdateSongInfoList();
772
773                 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
774         }
775
776         return TRUE;
777 }
778
779
780
781 /****** xmodule/xmRemSong **************************************************
782 *
783 *       NAME
784 *               xmRemSong -- Remove a song from the song list
785 *
786 *       SYNOPSIS
787 *               success = xmRemSong(songInfo);
788 *               D0                  A0
789 *
790 *               ULONG xmRemSong(struct SongInfo *);
791 *
792 *       FUNCTION
793 *               Removes a song from the public song list. It is safe to call this
794 *               function even if the song has not been added to the song list. If
795 *               the passed SongInfo is the active one, another song will be
796 *               selected automatically.
797 *
798 *       INPUTS
799 *               songInfo - song to be removed. If NULL, this function will take
800 *                       no action.
801 *
802 *       RESULT
803 *               success - Will be 0 for failure, in which case the song will
804 *                       not be removed from the song list. Currently,
805 *                       xmRemSong() will never fail.
806 *
807 *       NOTE
808 *               In order to remove a song, you must first obtain an exclusive
809 *               lock on it.
810 *
811 *               The song list is also protected from multiple task access by
812 *               a SignalSemaphore. This call will wait if the song list has
813 *               been locked by another task. Beware of deadlock conditions!
814 *
815 *       SEE ALSO
816 *               xmAddSongA()
817 *
818 ****************************************************************************
819 */
820 static LIBCALL ULONG XModuleRemSong (
821         REG(a0, struct SongInfo *si),
822         REG(a6, struct XModuleBase *XModuleBase))
823 {
824         if (!si) return FALSE;
825
826         if (!si->Link.ln_Type) return TRUE;     /* Not in the list */
827
828
829         /* Brutal but effective way to obtain two locks at the same time,
830          * avoiding the risk of deadlock conditions.  We can't just use
831          * ObtainSemaphoreList() here because the two semaphores can
832          * not be linked into a list.
833          */
834 /*      while (1)
835         {
836                 ObtainSemaphore (&XModuleBase->xm_BaseLock);
837
838                 if (AttemptSemaphore (&si->Lock))
839                         break;
840
841                 ReleaseSemaphore (&XModuleBase->xm_SongsLock);
842                 ObtainSemaphore (&si->Lock);
843
844                 if (AttemptSemaphore (&XModuleBase->xm_SongsLock))
845                         break;
846
847                 ReleaseSemaphore (&si->Lock);
848         }
849 */
850
851         ObtainSemaphore (&XModuleBase->xm_BaseLock);
852         DetatchSongInfoList();
853
854         REMOVE ((struct Node *)si);
855         si->Link.ln_Type = 0;   /* Mark song outside list */
856
857         if (XModuleBase->xm_CurrentSong == si)
858         {
859                 if (IsListEmpty ((struct List *)&XModuleBase->xm_Songs))
860                         XModuleBase->xm_CurrentSong = NULL;
861                 else
862                         XModuleBase->xm_CurrentSong = (struct SongInfo *)
863                                 XModuleBase->xm_Songs.mlh_TailPred;
864
865                 UpdateSongInfoList();
866                 UpdateSongInfo();
867         }
868         else UpdateSongInfoList();
869
870         ReleaseSemaphore (&XModuleBase->xm_BaseLock);
871         return TRUE;
872 }
873
874
875
876 /****** xmodule/xmActivateSong *********************************************
877 *
878 *       NAME
879 *               xmActivateSong -- Makes a song the active one
880 *
881 *       SYNOPSIS
882 *               success = xmActivateSong(songInfo);
883 *               D0                       A0
884 *
885 *               ULONG xmActivateSong(struct SongInfo *);
886 *
887 *       FUNCTION
888 *               Makes the passed song the currently active one.  It's pointer
889 *               will be stored in the public song list and most actions
890 *               will happen on this song by default.
891 *
892 *       INPUTS
893 *               songInfo - song to be activated. If NULL, this function will
894 *                       take no action.
895 *
896 *       RESULT
897 *               success - Will be 0 for failure, in which case the song will
898 *                       not be removed from the song list. Currently,
899 *                       xmActivateSong() will never fail.
900 *
901 *       NOTE
902 *               In order to activate a song, you must own a shared lock
903 *               on it.  Please do not hog this lock for a long time when
904 *               xmActivateSong() returns, because most internal routines
905 *               try to lock the current song before taking any action.
906 *
907 *       SEE ALSO
908 *
909 ****************************************************************************
910 */
911 static LIBCALL ULONG XModuleActivateSong (
912         REG(a0, struct SongInfo *si),
913         REG(a6, struct XModuleBase *XModuleBase))
914 {
915         if (!si) return FALSE;
916         if (!si->Link.ln_Type) return FALSE;    /* Not in the list */
917
918         ObtainSemaphore (&XModuleBase->xm_BaseLock);
919
920         if (XModuleBase->xm_CurrentSong != si)
921         {
922                 XModuleBase->xm_CurrentSong = si;
923                 UpdateSongInfo();       /**/
924         }
925
926         ReleaseSemaphore (&XModuleBase->xm_BaseLock);
927         return TRUE;
928 }
929
930
931
932 /****** xmodule/xmLockActiveSong *******************************************
933 *
934 *       NAME
935 *               xmLockActiveSong -- Obtains an lock on the active song
936 *
937 *       SYNOPSIS
938 *               song = xmLockActiveSong(mode);
939 *               D0                                              D0:16
940 *
941 *               struct SongInfo *xmActivateSong(UWORD);
942 *
943 *       FUNCTION
944 *               Obtains an exclusive or shared lock on the active song,
945 *               waiting if needed.  This call is a shortcut to:
946 *
947 *                       ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
948 *                       ObtainSemaphore[Shared] (&XModuleBase->xm_CurrentSong.Lock);
949 *                       ReleaseSemaphore (&XModuleBase->xm_BaseLock);
950 *
951 *               To unlock a song obtained in this way, you just need to
952 *               ReleaseSemaphore() it.
953 *
954 *               You MUST always lock a song before you even think to
955 *               read from -or write to- its data!
956 *
957 *       INPUTS
958 *               mode - one of SM_SHARED or SM_EXCLUSIVE.
959 *
960 *       RESULT
961 *               song - Pointer to the song which *was* active at the time
962 *                       you called xmLockActiveSong().  The song will be
963 *                       locked for you.  The result will be NULL if no song
964 *                       is active.
965 *
966 *       NOTE
967 *               Please be careful if you call this function while holding
968 *               locks on other songs.  Doing so, you run the risk of causing
969 *               deadlock condition.
970 *               This function does only lock the song; it is NOT guaranteed
971 *               that it will remain the active one.
972 *
973 *       SEE ALSO
974 *
975 ****************************************************************************
976 */
977 static LIBCALL struct SongInfo *XModuleLockActiveSong (
978         REG(d0, UWORD mode),
979         REG(a6, struct XModuleBase *XModuleBase))
980 {
981         struct SongInfo *si;
982
983
984 #ifdef OS30_ONLY
985         ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
986 #else
987         /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
988
989         /* Try to get the shared semaphore */
990         if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
991                 /* Check if we can get the exclusive version */
992                 if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
993                         /* Oh well, wait for the shared lock */
994                         ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
995 #endif /* OS30_ONLY */
996
997         if (si = XModuleBase->xm_CurrentSong)
998         {
999                 if (mode == SM_SHARED)
1000                 {
1001 #ifdef OS30_ONLY
1002                         ObtainSemaphoreShared (&si->Lock);
1003 #else
1004                         /* Try to get the shared semaphore */
1005                         if (!AttemptSemaphoreShared (&si->Lock))
1006                                 /* Check if we can get the exclusive version */
1007                                 if (!AttemptSemaphore (&si->Lock))
1008                                         /* Oh well, wait for the shared lock */
1009                                         ObtainSemaphoreShared (&si->Lock);
1010 #endif /* OS30_ONLY */
1011                 }
1012                 else ObtainSemaphore (&si->Lock);
1013         }
1014
1015         ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1016
1017         return si;
1018 }
1019
1020
1021
1022 /****** xmodule/xmLoadModuleA **********************************************
1023 *
1024 *       NAME
1025 *               xmLoadModuleA -- Loads a module and converts it in XModule format
1026 *               xmLoadModule -- Varargs stub for xmLoadModuleA
1027 *
1028 *       SYNOPSIS
1029 *               songInfo = xmLoadModuleA(fileName,tagList)
1030 *               D0                       A0       A1
1031 *
1032 *               struct SongInfo *xmLoadModuleA(CONST_STRPTR,struct TagItem *);
1033 *
1034 *
1035 *               songInfo = xmLoadModule(fileName,tag,...)
1036 *
1037 *               struct SongInfo *xmLoadModule(CONST_STRPTR,LONG,...);
1038 *
1039 *       FUNCTION
1040 *               Loads fileName using the correct module loader.
1041 *
1042 *       INPUTS
1043 *               fileName - File to read. Can be NULL if the XMSNG_FileHandle
1044 *                       tag is passed.
1045 *               tagList - Additional parameters (see below).  Can be NULL.
1046 *
1047 *       TAGS
1048 *               XMSNG_OldSong - ti_Data is the pointer to a SongInfo which will be
1049 *                       freed as soon as the module format has been determined.  This
1050 *                       is useful when the user wants to replace a song with another
1051 *                       one.  Passing NULL uses the currently active song.
1052 *
1053 *               XMSNG_AddToList - (struct SongInfo *) Add the song to the song list.
1054 *                       When a song is being added to the list, a shared lock is
1055 *                       obtained on it to prevent other tasks from modifying (or even
1056 *                       remove) the song before you can do your job on it. IT IS YOUR
1057 *                       DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
1058 *                       Passing NULL adds the song on the head of the list, while -1
1059 *                       (~0) will add it to the end of the SongList. Passing in a
1060 *                       pointer to another song which is already in the list, will
1061 *                       add the new song _after_ the latter.
1062 *                       (Default is not adding the song).
1063 *
1064 *               XMSNG_Loader - (struct XMHook *) Disables automatic format
1065 *                       checking and forces loading the module with the given
1066 *                       loader. (Defaults to NULL).
1067 *
1068 *               XMSNG_FileHandle - (BPTR) pointer to an open AmigaDOS
1069 *                       FileHandle to read the module from.  The file must
1070 *                       already be positioned over the beginning of the module data.
1071 *                       NOTE: Even if this tag is passed, the fileName parameter is
1072 *                               still used to fill in the SongName field in case it is
1073 *                               missing inside the module AND the filesystem does not
1074 *                               support the ACTION_EXAMINE_FH packet.
1075 *                       NOTE: Some loaders may require a fully seekable file, so be
1076 *                               careful when using pipes.
1077 *                       NOTE: automatic data decompression is not possible when
1078 *                               XMSNG_FileHandle is passed.
1079 *                       (Defaults to NULL).
1080 *
1081 *               XMSNG_IFFHandle - (BPTR) pointer to an already initialized
1082 *                       IFFHandle to read the module from.  The IFF must
1083 *                       already be positioned over the beginning of the module FORM.
1084 *                       Even if this tag is passed, the fileName parameter is still
1085 *                       used to fill in the SongName field in case it is missing
1086 *                       inside the module.
1087 *                       NOTE: The iffparse.library can deal with non-seekable files,
1088 *                               but some loaders may require a fully seekable file, so be
1089 *                               careful when using pipes.
1090 *                       NOTE: automatic file decompression is not possible when
1091 *                               XMSNG_IFFHandle is passed.
1092 *                       (Defaults to NULL).
1093 *
1094 *               XMSNG_Active - (BOOL) Makes this song the active one. This tag is
1095 *                       considered only if the XMSNG_AddToList tag is also passed.
1096 *
1097 *       RESULT
1098 *               songInfo - Pointer to the newly allocated SongInfo structure, or
1099 *                       NULL for failure.  This function will not fail if the module is
1100 *                       partially corrupted. Anyway, the returned SongInfo will always
1101 *                       be valid. You can check IoErr() to know if there were problems.
1102 *
1103 *       EXAMPLE
1104 *               \* Load a song and add it to the SongList *\
1105 *               si = xmLoadSongTags (file, NULL,
1106 *                       XMSNG_AddToList, (struct SongInfo *)~0,
1107 *                       TAG_DONE);
1108 *
1109 *               \* Check for errors even if si is not NULL *\
1110 *               error = IoErr();
1111 *
1112 *               \* Release Semaphore got by xmLoadSong() *\
1113 *               if (si) ReleaseSemaphore (&si->Lock);
1114 *
1115 *       SEE ALSO
1116 *               xmAddSongA(), xmIdentifyModule()
1117 *
1118 ****************************************************************************
1119 */
1120 static LIBCALL struct SongInfo * XModuleLoadModule (
1121         REG(a0, CONST_STRPTR filename),
1122         REG(a1, struct TagItem *tags),
1123         REG(a6, struct XModuleBase *XModuleBase))
1124 {
1125         struct XMHook   *loader;
1126         struct SongInfo *si = NULL;
1127         UBYTE                    fullpath[PATHNAME_MAX];
1128         ULONG                    err;
1129         BPTR                     fh, compressed = 0;
1130         UWORD                    type;
1131
1132
1133         loader = (struct XMHook *)GetTagData (XMSNG_Loader, NULL, tags);
1134
1135
1136         /* Get source file or open it */
1137
1138         if (fh = (BPTR) GetTagData (XMSNG_FileHandle, NULL, tags))
1139         {
1140                 /* Get the full pathname */
1141
1142                 if (!NameFromFH (fh, fullpath, PATHNAME_MAX))
1143                         fullpath[0] = '\0';
1144         }
1145         else
1146         {
1147                 if (!(fh = Open (filename, MODE_OLDFILE)))
1148                 {
1149                         UBYTE buf[FAULT_MAX];
1150
1151                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
1152                                 (APTR)MSG_ERR_LOAD, filename, buf);
1153                         return NULL;
1154                 }
1155
1156                 /* Optimize file buffering for faster I/O.
1157                  *
1158                  * NOTE: SetVBuf() was introduced in V39, but V37 has a
1159                  * no-op vector for it, so it is safe to call SetVBuf()
1160                  * without checking the OS version.
1161                  * NOTE: Due to a bug in dos.library V40, SetVBuf() works
1162                  * only when called right after Open() (before doing any I/O).
1163                  */
1164                 SetVBuf (fh, NULL, BUF_FULL, 16*1024);
1165
1166
1167                 /* Get the full pathname */
1168
1169                 if (!NameFromFH (fh, fullpath, PATHNAME_MAX))
1170                         fullpath[0] = '\0';
1171
1172
1173                 /* Check wether the file is compressed */
1174
1175                 if (type = CruncherType (fh))
1176                 {
1177                         Close (fh);
1178
1179                         if (compressed = DecompressFile (filename, type))
1180                         {
1181                                 if (!(fh = OpenFromLock (compressed)))
1182                                 {
1183                                         err = IoErr();
1184                                         UnLock (compressed);
1185                                         DecompressFileDone();
1186                                         SetIoErr (LastErr = err);
1187                                         return NULL;
1188                                 }
1189                         }
1190                         else return NULL;
1191                 }
1192         }
1193
1194
1195
1196         /* Lock loaders list */
1197
1198 #ifdef OS30_ONLY
1199         ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
1200 #else
1201         /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
1202
1203         /* Try to get the shared semaphore */
1204         if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
1205                 /* Check if we can get the exclusive version */
1206                 if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
1207                         /* Oh well, wait for the shared lock */
1208                         ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
1209 #endif /* OS30_ONLY */
1210
1211
1212         /* Find out what the heck this file format is */
1213
1214         if (!loader)
1215                 loader = xmIdentifyModule (fh, tags);
1216
1217         if (loader)
1218         {
1219                 if (loader->xmh_LoadMod)
1220                 {
1221                         Seek (fh, 0, OFFSET_BEGINNING);         /* Reset file */
1222
1223
1224                         /* Free old song */
1225                         {
1226                                 struct TagItem *tag;
1227
1228                                 if (tag = FindTagItem (XMSNG_OldSong, tags))
1229                                         xmDeleteSong (tag->ti_Data ?
1230                                                 (struct SongInfo *)tag->ti_Data : XModuleBase->xm_CurrentSong);
1231                         }
1232
1233
1234                         /* Create a new song and set its title and path.
1235                          * File name will be replaced by the real song name if the
1236                          * load format supports embedded song names (e.g.: SoundTracker).
1237                          */
1238                         if (si = xmCreateSong (
1239                                 SNGA_Path,      fullpath,
1240                                 SNGA_Title,     FilePart (filename),
1241                                 TAG_DONE))
1242                         {
1243                                 OpenProgressWindow();
1244
1245                                 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
1246                                         (APTR)MSG_READING_TYPE_MODULE, loader->xmh_Link.ln_Name);
1247
1248
1249                                 /* Call loader hook */
1250                                 err = loader->xmh_LoadMod (fh, si, loader, tags);
1251
1252                                 if (err == ERROR_IOERR)
1253                                 {
1254                                         if (err = IoErr())
1255                                                 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT | err,
1256                                                         (APTR)MSG_ERROR_READING, FilePart (filename));
1257                                         else
1258                                                 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_ERROR,
1259                                                         (APTR)MSG_UNESPECTED_EOF);
1260                                 }
1261
1262                                 FixSong (si);
1263
1264                                 CloseProgressWindow();
1265                         }
1266                         else err = ERROR_NO_FREE_STORE;
1267                 }
1268                 else err = ERROR_ACTION_NOT_KNOWN;
1269         }
1270         else err = ERROR_OBJECT_WRONG_TYPE;
1271
1272         ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1273
1274         Close (fh);
1275
1276         if (compressed) DecompressFileDone ();
1277
1278         if (!err)
1279                 xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG,
1280                         (APTR)MSG_MODULE_LOADED_OK, NULL);
1281         else
1282                 SetIoErr (LastErr = err);
1283
1284         if (si)
1285         {
1286                 struct TagItem *tag;
1287
1288                 if (tag = FindTagItem (XMSNG_AddToList, tags))
1289                 {
1290                         ObtainSemaphore (&si->Lock);
1291                         xmAddSongA (si, (struct SongInfo *)tag->ti_Data, NULL);
1292                         if (GetTagData (XMSNG_Active, FALSE, tags))
1293                                 xmActivateSong (si);
1294                 }
1295         }
1296
1297         return si;
1298 }
1299
1300
1301
1302 /****** xmodule/xmSaveModuleA **********************************************
1303 *
1304 *       NAME
1305 *               xmSaveModuleA -- Saves a module to the specified file format
1306 *               xmSaveModule -- Varargs stub for xmSaveModuleA
1307 *
1308 *       SYNOPSIS
1309 *               error = xmSaveModuleA(songInfo, fileName, saver, tagList)
1310 *               D0                    A0        A1        A2     A3
1311 *
1312 *               LONG xmSaveModuleA(struct SongInfo *,CONST_STRPTR,struct XMHook *,
1313 *                       struct TagItem *);
1314 *
1315 *
1316 *               error = xmSaveModule(songInfo, fileName, saver, tag1,...)
1317 *
1318 *               LONG xmSaveModule(struct SongInfo *,CONST_STRPTR,struct XMHook *,
1319 *                       LONG,...);
1320 *
1321 *       FUNCTION
1322 *               Saves songInfo to fileName using the specified saver.
1323 *
1324 *       INPUTS
1325 *               songInfo - Song to save.
1326 *               fileName - AmigaDOS filename to write the song to.
1327 *               saver - Pointer to the saver to use.  Pass NULL to use
1328 *                       the default saver.
1329 *
1330 *       TAGS
1331 *               No tags are defined for this call.
1332 *
1333 *       RESULT
1334 *               error - RETURN_OK for success, or any other AmigaDOS error code
1335 *                       to mean failure.  In particular, RETURN_WARN indicates that
1336 *                       something went wrong, but the song is still ok. The special
1337 *                       error code ERROR_IOERR means that some dos.library
1338 *                       call (e.g.: Read()) failed.  In the latter case, the
1339 *                       AmigaDOS IoErr() value will be set to explain the specific
1340 *                       cause of the problem.
1341 *
1342 *       SEE ALSO
1343 *
1344 ****************************************************************************
1345 */
1346 static LIBCALL LONG XModuleSaveModule (
1347         REG(a0, struct SongInfo *si),
1348         REG(a1, CONST_STRPTR filename),
1349         REG(a2, struct XMHook *saver),
1350         REG(a3, struct TagItem *tags),
1351         REG(a6, struct XModuleBase *XModuleBase))
1352 {
1353         LONG err;
1354         BPTR fh;
1355         BOOL releasebase = FALSE;
1356
1357
1358         /* Use the default saver if not specified */
1359         if (!saver)
1360         {
1361 #ifdef OS30_ONLY
1362                 ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
1363 #else
1364                 /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
1365
1366                 /* Try to get the shared semaphore */
1367                 if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
1368                         /* Check if we can get the exclusive version */
1369                         if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
1370                                 /* Oh well, wait for the shared lock */
1371                                 ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
1372 #endif /* OS30_ONLY */
1373
1374                 if (!(saver = XModuleBase->xm_DefaultSaver))
1375                 {
1376                         ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1377                         return ERROR_ACTION_NOT_KNOWN;
1378                 }
1379
1380                 releasebase = TRUE;
1381         }
1382
1383
1384         if (saver->xmh_SaveMod)
1385         {
1386                 ULONG i;
1387
1388                 /* Make some checks before actually saving */
1389
1390
1391                 if (si->LastInstrument > saver->xmh_MaxInstruments)
1392                 {
1393                         xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1394                                 (APTR)MSG_SONG_TOO_MANY_INSTRS, si->LastInstrument, saver->xmh_MaxInstruments);
1395
1396                         if (ShowRequest (MSG_TRY_REMAPPING_INSTRUMENTS, MSG_PROCEED_OR_CANCEL))
1397                                 xmProcessSong (si, NULL,
1398                                         XMSNG_RemapInstruments, TRUE,
1399                                         TAG_DONE);
1400                 }
1401
1402                 for (i = 1; i <= si->LastInstrument; i++)
1403                         if (si->Instr[i] && (si->Instr[i]->Length > saver->xmh_MaxSampleLen))
1404                         {
1405                                 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1406                                         (APTR)MSG_INSTR_TOO_LONG, i, saver->xmh_MaxSampleLen);
1407                         }
1408
1409                 for (i = 0; i < si->NumPatterns; i++)
1410                 {
1411                         if (si->Patt[i])
1412                                 if ((si->Patt[i]->Lines > saver->xmh_MaxPattLen) ||
1413                                         ( (saver->xmh_Flags & XMHF_FIXED_PATT_LEN) &&
1414                                         (si->Patt[i]->Lines != saver->xmh_MaxPattLen) ) )
1415                         {
1416                                 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1417                                         (APTR)MSG_PATT_LENGTH_INVALID, i, si->Patt[i]->Lines);
1418
1419                                 if (ShowRequest (MSG_WILL_MODIFY_SONG, MSG_PROCEED_OR_CANCEL))
1420                                 {
1421                                         xmProcessSong (si, NULL,
1422                                                 XMSNG_LimitPatterns, saver->xmh_MaxPattLen |
1423                                                 ((saver->xmh_Flags & XMHF_FIXED_PATT_LEN) ? (saver->xmh_MaxPattLen << 16) : 0),
1424                                                 TAG_DONE);
1425                                         break;
1426                                 }
1427                                 else
1428                                 {
1429                                         if (releasebase)
1430                                                 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1431
1432                                         return ERROR_BREAK;
1433                                 }
1434                         }
1435                 }
1436
1437                 if (si->MaxTracks > saver->xmh_MaxTracks)
1438                         xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1439                                 (APTR)MSG_SONG_TOO_MANY_TRACKS, saver->xmh_MaxTracks, si->MaxTracks);
1440
1441                 if (si->NumPatterns > saver->xmh_MaxPatterns)
1442                         xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1443                                 (APTR)MSG_SONG_TOO_MANY_PATTS, saver->xmh_MaxPatterns, si->NumPatterns);
1444
1445                 if (si->Length > saver->xmh_MaxLength)
1446                         xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1447                                 (APTR)MSG_SONG_TOO_MANY_POS, saver->xmh_MaxLength, si->Length);
1448
1449
1450                 /* Now make a backup of the old file before overwriting this */
1451
1452                 if (GuiSwitches.DoBackups)
1453                         BackupFile (filename, GuiSwitches.BackupTemplate, GuiSwitches.BackupVersions);
1454
1455
1456                 if (fh = Open (filename, MODE_NEWFILE))
1457                 {
1458                         OpenProgressWindow();
1459
1460                         /* Optimize file buffering for faster I/O.
1461                          *
1462                          * NOTE: SetVBuf() was introduced in V39, but V37 has a
1463                          * no-op vector for it, so it is safe to call SetVBuf()
1464                          * without checking the OS version.
1465                          * NOTE: Due to a bug in dos.library V40, SetVBuf() works
1466                          * only when called right after Open() (before doing any I/O).
1467                          */
1468                         SetVBuf (fh, NULL, BUF_FULL, 16*1024);
1469
1470
1471                         xmDisplayMessage (XMDMF_INFORMATION | XMDMF_USECATALOG,
1472                                 (APTR)MSG_SAVING_MODULE, saver->xmh_Link.ln_Name,
1473                                 si->Title ? si->Title : STR(MSG_SONG_UNTITLED));
1474
1475
1476                         err = saver->xmh_SaveMod (fh, si, saver, tags);
1477
1478                         Close (fh);
1479                         SetComment (filename, VERS " by Bernardo Innocenti");
1480
1481                         if (err)
1482                         {
1483                                 /* Delete incomplete file */
1484                                 DeleteFile (filename);
1485
1486                                 if (err == ERROR_IOERR)
1487                                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
1488                                                 (APTR)MSG_ERROR_WRITING, filename);
1489                         }
1490                         else
1491                         {
1492                                 if (SaveSwitches.SaveIcons)
1493                                         PutIcon ("def_Module", filename);
1494                                 xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG,
1495                                         (APTR)MSG_MODULE_SAVED_OK, NULL);
1496
1497                         }
1498
1499                         CloseProgressWindow();
1500                 }
1501                 else    /* Open failed */
1502                 {
1503                         err = IoErr();
1504                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
1505                                 (APTR)MSG_CANT_OPEN, filename);
1506                 }
1507         }
1508         else err = ERROR_ACTION_NOT_KNOWN;
1509
1510         if (releasebase)
1511                 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1512
1513         return err;
1514 }
1515
1516
1517
1518 /****** xmodule/xmIdentifyModule *******************************************
1519 *
1520 *       NAME
1521 *               xmIdentifyModule -- Returns the type of a module
1522 *
1523 *       SYNOPSIS
1524 *               loader = xmIdentifyModule(fh, tagList)
1525 *               D0                        D0  A0
1526 *
1527 *               struct XMHook *xmIdentifyModule(BPTR,struct TagItem *);
1528 *
1529 *       FUNCTION
1530 *               Finds out a loader which is able to read the given module.
1531 *
1532 *       INPUTS
1533 *               fh - AmigaDOS FileHandle to examine.
1534 *               tagList - Additional parameters. Leave it NULL for now.
1535 *
1536 *       RESULT
1537 *               loader - Pointer to the first loader which knows how to load this
1538 *                       module, or NULL otherwise.
1539 *
1540 *       NOTE
1541 *               Before you call this function, you must first obtain a lock on the
1542 *               loaders list to protect yourself from other tasks which might remove
1543 *               the returned loader before you can actually use it.
1544 *
1545 *       SEE ALSO
1546 *
1547 ****************************************************************************
1548 */
1549 static LIBCALL struct XMHook *XModuleIdentifyModule(
1550         REG(d0, BPTR fh),
1551         REG(a0, struct TagItem *tags),
1552         REG(a6, struct XModuleBase *XModuleBase))
1553 {
1554         struct XMHook *loader, *ret = NULL;
1555
1556         loader = (struct XMHook *) XModuleBase->xm_Loaders.mlh_Head;
1557
1558         while (loader->xmh_Link.ln_Succ)
1559         {
1560                 if (ret = loader->xmh_IdentifyMod (fh, loader, tags))
1561                         break;
1562
1563                 loader = (struct XMHook *)loader->xmh_Link.ln_Succ;
1564         }
1565
1566         Seek (fh, 0, OFFSET_BEGINNING);
1567
1568         return ret;
1569 }
1570
1571
1572
1573 /****** xmodule/xmSetSongLen ***********************************************
1574 *
1575 *       NAME
1576 *               xmSetSongLen -- Set the number of song positions
1577 *
1578 *       SYNOPSIS
1579 *               sequence = xmSetSongLen(songInfo, length)
1580 *               D0                      A0        D0:16
1581 *
1582 *               UWORD *xmSetSongLen(struct SongInfo *, UWORD);
1583 *
1584 *       FUNCTION
1585 *               Allocates space in the song for a sequence table of the given
1586 *               length. If no sequence exists yet, a new one is allocated.
1587 *               Increasing the song length may require the sequence table to be
1588 *               expanded, in which case it will be re-allocated and the old sequence
1589 *               entries will be copied. Decreasing the song length could also cause
1590 *               a reallocation of the sequence table if the size drops down enough.
1591 *               Setting the song length to 0 will free the sequence table and return
1592 *               NULL.
1593 *
1594 *       INPUTS
1595 *               songInfo - pointer to a SongInfo structure.
1596 *               length - new length of song sequence table.
1597 *
1598 *       NOTE
1599 *               This function will also adjust the CurrentPos field if it is found
1600 *               beyond the song end
1601 *
1602 *       BUGS
1603 *               This funtion is quite useless because all it does is setting the
1604 *               SNGA_Length attribute in the song object.  Setting the SNGA_Length
1605 *               attribute yourself is easier and faster if you are already setting
1606 *               other attributes in the same song.
1607 *
1608 *   RESULT
1609 *               Pointer to the newly allocated sequence table or NULL for failure,
1610 *               in which case the previous sequence table is left untouched.
1611 *
1612 ****************************************************************************
1613 */
1614 static LIBCALL UWORD *XModuleSetSongLen (
1615         REG(a0, struct SongInfo *si),
1616         REG(d0, UWORD len),
1617         REG(a6, struct XModuleBase *XModuleBase))
1618 {
1619         SetAttrs (si, SNGA_Length, len, TAG_DONE);
1620         return si->Sequence;
1621 }
1622
1623
1624
1625 /****** xmodule/xmAddPatternA **********************************************
1626 *
1627 *       NAME
1628 *               xmAddPatternA -- Adds a pattern to a song
1629 *               xmAddPattern -- Varargs stub for xmAddPatternA
1630 *
1631 *       SYNOPSIS
1632 *               pattern = xmAddPatternA(si, tagList)
1633 *               D0                      A0  A1
1634 *
1635 *               struct Pattern *xmAddPatternA(struct SongInfo *,struct TagItem *);
1636 *
1637 *
1638 *               pattern = xmAddPattern (si,tag1,...)
1639 *
1640 *               struct Pattern *xmAddPattern(struct SongInfo *,LONG Tag1,...);
1641 *
1642 *       FUNCTION
1643 *               Adds a pattern to a song by calling the SNGM_ADDPATTERN method.
1644 *
1645 *       INPUTS
1646 *               si - pointer to the song to which the pattern should be added.
1647 *               tagList - optional TagList.  See SNGM_ADDPATTERN for possible tags.
1648 *
1649 *       RESULT
1650 *               Pointer to the new pattern or NULL for failure.
1651 *
1652 *       NOTE
1653 *               In order to add patterns, you should have exclusive access to
1654 *               the song.  Always obtain a lock before you call this function on
1655 *               public songs.
1656 *
1657 *       SEE ALSO
1658 *               xmRemPattern(), songclass/SNGM_ADDPATTERN
1659 *
1660 ****************************************************************************
1661 */
1662 static LIBCALL void XModuleAddPattern (
1663         REG(a0, struct SongInfo *si),
1664         REG(a1, struct TagItem *tags),
1665         REG(a6, struct XModuleBase *XModuleBase))
1666 {
1667         DoMethod ((Object *)si, SNGM_ADDPATTERN, tags);
1668 }
1669
1670
1671
1672 /****** xmodule/xmSetPatternA **********************************************
1673 *
1674 *       NAME
1675 *               xmSetPatternA -- Sets pattern attributes
1676 *               xmSetPattern -- Varargs stub for xmSetPatternA
1677 *
1678 *       SYNOPSIS
1679 *               success = xmSetPatternA(si, pattNum, tagList)
1680 *               D0                      A0  D0       A1
1681 *
1682 *               ULONG xmSetPatternA(struct SongInfo *,ULONG,struct TagItem *);
1683 *
1684 *
1685 *               success = xmSetPattern (si,pattNum,tag1,...)
1686 *
1687 *               ULONG xmSetPattern(struct SongInfo *,ULONG,LONG Tag1,...);
1688 *
1689 *       FUNCTION
1690 *               Sets attributes of a pattern by calling the SNGM_SETPATTERN method.
1691 *
1692 *       INPUTS
1693 *               si - pointer to the song which contains the pattern to be set.
1694 *               tagList - list of attributes to set.  See SNGM_SETPATTERN for
1695 *                       possible tags.
1696 *
1697 *       RESULT
1698 *               Non zero for success
1699 *
1700 *       NOTE
1701 *               In order to set patterns attributes, you should have exclusive
1702 *               access to the song.  Always obtain a lock before you call this
1703 *               function on public songs.
1704 *
1705 *       SEE ALSO
1706 *               xmAddPattern(), songclass/SNGM_SETPATTERN
1707 *
1708 ****************************************************************************
1709 */
1710 static LIBCALL void XModuleSetPattern (
1711         REG(a0, struct SongInfo *si),
1712         REG(d0, ULONG pattNum),
1713         REG(a1, struct TagItem *tags),
1714         REG(a6, struct XModuleBase *XModuleBase))
1715 {
1716         DoMethod ((Object *)si, SNGM_SETPATTERN, pattNum, tags);
1717 }
1718
1719
1720
1721 /****** xmodule/xmRemPattern ***********************************************
1722 *
1723 *       NAME
1724 *               xmRemPattern -- Removes a pattern from a song
1725 *
1726 *       SYNOPSIS
1727 *               xmRemPattern(si, pattNum, replaceWith)
1728 *                            A0  D0,      D1
1729 *
1730 *               void xmRemPattern(struct SongInfo *,LONG,LONG);
1731 *
1732 *       FUNCTION
1733 *               Removes a pattern from a song by calling the SNGM_REMPATTERN method.
1734 *
1735 *       INPUTS
1736 *               si - pointer to the song to which the pattern should be removed.
1737 *               mum - Number of the pattern to be removed.
1738 *               replaceWith - What to put in the song sequence in place of the
1739 *                       deleted pattern.
1740 *
1741 *       NOTE
1742 *               In order to remove patterns, you should have exclusive access to
1743 *               the song.  Always obtain a lock before you call this function on
1744 *               public songs.
1745 *
1746 *       SEE ALSO
1747 *               xmAddPatternA(), songclass/SNGM_REMPATTERN
1748 *
1749 ****************************************************************************
1750 */
1751 static LIBCALL void XModuleRemPattern (
1752         REG(a0, struct SongInfo *si),
1753         REG(d0, ULONG pattNum),
1754         REG(d1, ULONG replaceWith))
1755 {
1756         DoMethod ((Object *)si, SNGM_REMPATTERN, pattNum, replaceWith);
1757 }
1758
1759
1760
1761 /****** xmodule/xmAddInstrumentA *******************************************
1762 *
1763 *       NAME
1764 *               xmAddInstrumentA -- Adds a instrument to a song
1765 *               xmAddInstrument -- Varargs stub for xmAddInstrumentA
1766 *
1767 *       SYNOPSIS
1768 *               instrument = xmAddInstrumentA(si, instrNum, tagList)
1769 *               D0                            A0  D0        A1
1770 *
1771 *               struct Instrument *xmAddInstrumentA(struct SongInfo *,LONG,
1772 *                       struct TagItem *);
1773 *
1774 *
1775 *               instrument = xmAddInstrument(si,instrNum,tag1,...)
1776 *
1777 *               struct Instrument *xmAddInstrument(struct SongInfo *,LONG,LONG,...);
1778 *
1779 *       FUNCTION
1780 *               Adds an instrument to a song by calling the SNGM_ADDINSTRUMENT method.
1781 *
1782 *       INPUTS
1783 *               si - pointer to the song to which the instrument should be added.
1784 *               tagList - optional TagList.  See SNGM_ADDINSTRUMENT for possible tags.
1785 *
1786 *       RESULT
1787 *               Pointer to the new instrument or NULL for failure.
1788 *
1789 *       NOTE
1790 *               In order to add instruments, you should have exclusive access to
1791 *               the song.  Always obtain a lock before you call this function on
1792 *               public songs.
1793 *
1794 *       SEE ALSO
1795 *               xmRemInstrument(), songclass/SNGM_REMINSTRUMENT
1796 *
1797 ****************************************************************************
1798 */
1799 static LIBCALL void XModuleAddInstrument (
1800         REG(a0, struct SongInfo *si),
1801         REG(d0, LONG num),
1802         REG(a1, struct TagItem *tags),
1803         REG(a6, struct XModuleBase *XModuleBase))
1804 {
1805         DoMethod ((Object *)si, SNGM_ADDINSTRUMENT, num, tags);
1806 }
1807
1808
1809
1810 /****** xmodule/xmSetInstrumentA *******************************************
1811 *
1812 *       NAME
1813 *               xmSetInstrumentA -- Sets an instrument's attributes
1814 *               xmSetInstrument -- Varargs stub for xmSetInstrumentA
1815 *
1816 *       SYNOPSIS
1817 *               success = xmSetInstrumentA(si, instrNum, tagList)
1818 *               D0                         A0  D0        A1
1819 *
1820 *               ULONG xmSetInstrumentA(struct SongInfo *,LONG,
1821 *                       struct TagItem *);
1822 *
1823 *
1824 *               success = xmSetInstrument(si,instrNum,tag1,...)
1825 *
1826 *               ULONG xmSetInstrument(struct SongInfo *,LONG,LONG,...);
1827 *
1828 *       FUNCTION
1829 *               Sets an instrument's attributes by calling the SNGM_SETINSTRUMENT
1830 *               method.
1831 *
1832 *       INPUTS
1833 *               si - pointer to the song which contains the instrument to be set.
1834 *               tagList - instrument attributes to set.  See SNGM_SETINSTRUMENT
1835 *                       for possible tags.
1836 *
1837 *       RESULT
1838 *               non zero for success.
1839 *
1840 *       NOTE
1841 *               In order to set instruments' attributes, you should have
1842 *               exclusive access to the song.  Always obtain a lock before
1843 *               you call this function on public songs.
1844 *
1845 *       SEE ALSO
1846 *               xmAddInstrument(), songclass/SNGM_SETINSTRUMENT
1847 *
1848 ****************************************************************************
1849 */
1850 static LIBCALL void XModuleSetInstrument (
1851         REG(a0, struct SongInfo *si),
1852         REG(d0, LONG num),
1853         REG(a1, struct TagItem *tags),
1854         REG(a6, struct XModuleBase *XModuleBase))
1855 {
1856         DoMethod ((Object *)si, SNGM_SETINSTRUMENT, num, tags);
1857 }
1858
1859
1860
1861 /****** xmodule/xmRemInstrument ***********************************************
1862 *
1863 *       NAME
1864 *               xmRemInstrument -- Removes an instrument from a song
1865 *
1866 *       SYNOPSIS
1867 *               xmRemInstrument(si, instrNum)
1868 *                               A0  D0
1869 *
1870 *               void xmRemInstrument(struct SongInfo *,LONG);
1871 *
1872 *       FUNCTION
1873 *               Removes an instrument from a song by calling the SNGM_REMINSTRUMENT
1874 *               method.
1875 *
1876 *       INPUTS
1877 *               si - pointer to the song to which the instrument should be removed.
1878 *               mum - Number of the instrument to be removed.
1879 *
1880 *       NOTE
1881 *               In order to remove instruments, you should have exclusive access to
1882 *               the song.  Always obtain a lock before you call this function on
1883 *               public songs.
1884 *
1885 *       SEE ALSO
1886 *               xmAddInstrumentA(), songclass/SNGM_REMINSTRUMENT
1887 *
1888 ****************************************************************************
1889 */
1890 static LIBCALL void XModuleRemInstrument (
1891         REG(a0, struct SongInfo *si),
1892         REG(d0, ULONG num),
1893         REG(a6, struct XModuleBase *XModuleBase))
1894 {
1895         DoMethod ((Object *)si, SNGM_REMINSTRUMENT, num);
1896 }
1897
1898
1899
1900 /****** xmodule/xmProcessSongA *********************************************
1901 *
1902 *       NAME
1903 *               xmProcessSongA -- Performs complex processing on a song
1904 *               xmProcessSong -- Varargs stub for xmProcessSongA()
1905 *
1906 *       SYNOPSIS
1907 *               result = xmProcessSongA(si, reserved, tagList)
1908 *                                       A0  A1,       A2
1909 *
1910 *               LONG xmProcessSongA(struct SongInfo *,void *,struct TagItem *);
1911 *
1912 *
1913 *               result = xmProcessSong(si, reserved, tag1, ...)
1914 *
1915 *               LONG xmProcessSongA(struct SongInfo *,void *,LONG,...);
1916 *
1917 *       FUNCTION
1918 *               Performs complex processing operations on a song.
1919 *
1920 *       INPUTS
1921 *               si - pointer to the song to be processed.
1922 *               reserved - Reserved for future use.
1923 *               tagList - List of arguments.  See below for possible tags.
1924 *
1925 *       RESULT
1926 *               The result depends on the kind of operation done.  For most
1927 *               operations, it's value will be 0 to indicate success and
1928 *               an AmigaDOS error code which indicates the cause for failure.
1929 *
1930 *       TAGS
1931 *               XMSNG_Optimize - (LONG).  Tries to reduce the song size by removing
1932 *                       all unused data.  Possible optimizations are:
1933 *                               - XMOF_REM_UNUSED_PATTERNS
1934 *                               - XMOF_REM_DUP_PATTERNS
1935 *                               - XMOF_CUT_PATTERNS
1936 *                               - XMOF_REM_UNUSED_INSTRS
1937 *                               - XMOF_CUT_INSTR_LOOPS
1938 *                               - XMOF_CUT_INSTR_TAILS
1939 *                               - XMOF_DEFAULT
1940 *                       XMOF_DEFAULT will select all the optimizations choosen by
1941 *                       the user in addition to the ones specified with the other flags.
1942 *
1943 *               XMSNG_RemapInstruments - (BOOL) Performs instruments remapping.
1944 *
1945 *               XMSNG_LimitPatterns - (UWORD,UWORD) Limits the length all the
1946 *                       patterns inside a minimum and maximum value.  The upper 16bits
1947 *                       of the argument are the minimum value, the lower ones are
1948 *                       the maximum value.  Patterns outside this limits will be grown
1949 *                       or split as appropriate.
1950 *
1951 *               XMSNG_Join - (struct SongInfo *) - Joins the song with another one.
1952 *
1953 *               XMSNG_Merge - (struct SongInfo *) - Merges the song with another one.
1954 *
1955 *       NOTE
1956 *               In order to process a song, you should have exclusive access to
1957 *               it.  Always obtain a lock before you call this function on
1958 *               public songs.
1959 *
1960 ****************************************************************************
1961 */
1962 static LIBCALL LONG XModuleProcessSong (
1963         REG(a0, struct SongInfo *si),
1964         REG(a1, void *reserved),
1965         REG(a2, struct TagItem *tags),
1966         REG(a6, struct XModuleBase *XModuleBase))
1967 {
1968         struct TagItem  *ti, *tstate = tags;
1969         LONG result = 0;
1970
1971         while (ti = NextTagItem(&tstate))
1972         {
1973                 switch (ti->ti_Tag)
1974                 {
1975                         case XMSNG_Optimize:
1976                         {
1977                                 ULONG flags = ti->ti_Data;
1978
1979                                 if (flags & XMOF_DEFAULT)
1980                                 {
1981                                         if (OptSwitches.RemPatts)               flags |= XMOF_REM_UNUSED_PATTERNS;
1982                                         if (OptSwitches.RemDupPatts)    flags |= XMOF_REM_DUP_PATTERNS;
1983                                         if (OptSwitches.RemInstr)               flags |= XMOF_REM_UNUSED_INSTRS;
1984                                         if (OptSwitches.RemDupInstr)    flags |= XMOF_REM_DUP_INSTRS;
1985                                         if (OptSwitches.CutAfterLoop)   flags |= XMOF_CUT_INSTR_LOOPS;
1986                                         if (OptSwitches.CutZeroTail)    flags |= XMOF_CUT_INSTR_TAILS;
1987                                         if (OptSwitches.CutPatts)               flags |= XMOF_CUT_PATTERNS;
1988                                         if (OptSwitches.RemapInstr)             flags |= XMOF_REMAP_INSTRS;
1989                                 }
1990
1991                                 if (flags & XMOF_REM_UNUSED_PATTERNS)           DiscardPatterns (si);
1992                                 if (flags & XMOF_REM_DUP_PATTERNS)                      RemDupPatterns (si);
1993                                 if (flags & XMOF_CUT_PATTERNS)                          CutPatterns (si);
1994                                 if (flags & XMOF_REM_UNUSED_INSTRS)                     RemUnusedInstruments (si);
1995                                 if (flags & XMOF_REM_DUP_INSTRS)                        RemDupInstruments (si);
1996                                 if (flags & XMOF_CUT_INSTR_LOOPS)                       OptimizeInstruments (si);
1997                                 if (flags & XMOF_CUT_INSTR_TAILS)                       OptimizeInstruments (si);
1998                                 if (flags & XMOF_REMAP_INSTRS)                          RemapInstruments (si);
1999
2000                                 break;
2001                         }
2002
2003                         case XMSNG_RemapInstruments:
2004                                 RemapInstruments (si);
2005                                 result = 0;
2006                                 break;
2007
2008                         case XMSNG_LimitPatterns:
2009                                 result = ResizePatterns (si, ti->ti_Data >> 16, ti->ti_Data & 0xFFFF);
2010                                 break;
2011
2012                         case XMSNG_Join:
2013                                 result = NULL;
2014                                 break;
2015
2016                         case XMSNG_Merge:
2017                                 result = NULL;
2018                                 break;
2019
2020                         default:
2021                                 break;
2022                 }
2023         }
2024
2025         return result;
2026 }
2027
2028
2029
2030 /****** xmodule/xmDisplayMessage *******************************************
2031 *
2032 *       NAME
2033 *               xmDisplayMessageA -- Displays a message to the user
2034 *               xmDisplayMessage -- Varargs stub for xmDisplayMessageA()
2035 *
2036 *       SYNOPSIS
2037 *               xmDisplayMessageA(level, message, args)
2038 *                                 DO     A0       A1
2039 *
2040 *               void xmDisplayMessageA(ULONG,APTR,LONG *);
2041 *
2042 *
2043 *               xmDisplayMessage(level, message, ...)
2044 *
2045 *               void xmDisplayMessage(ULONG,APTR,...);
2046 *
2047 *       FUNCTION
2048 *               Outputs a string in the XModule log window or in the 'action' field
2049 *               of the progress indicator.  The string is printf-formatted before
2050 *               being output.
2051 *
2052 *       INPUTS
2053 *               level - a number from 0 to 7 which indicates the importance of the
2054 *                       message.  7 is used for very annoying messages (eg: debug output),
2055 *                       0 for very important things (eg: disaster).
2056 *                       If you set the XMDMF_USECATALOG bit in the level parameter, you
2057 *                       can pass a catalog string number instead of a pointer to a string.
2058 *                       Specifying XMDMF_ACTION, the message will be put in the 'action'
2059 *                       field of the progress indicator.
2060 *                       If the flag XMDMF_DOSFAULT is specified, a Fault() output is
2061 *                       formatted using the message as an header.  In this case, the
2062 *                       level parameter takes another meaing: The lower word can contain
2063 *                       an AmigaDOS error code. If it is 0, IoErr() will be used to get
2064 *                       the error code.
2065 *               message - pointer to a string or catalog message number,
2066 *                       when the XMDMF_USECATALOG flag is set.
2067 *               args - arguments for formatting.
2068 *
2069 *       EXAMPLES
2070 *               xmDisplayMessage (XMDMF_ALERT,
2071 *                       "The application `%s' fucked up Windoze95 because %s.",
2072 *                       "(unknown name)", "an error occurred");
2073 *
2074 *               xmDisplayMessage (XMDMF_USE_CATALOG | XMDMF_COMMENT,
2075 *                       MSG_LIFE_UNIVERSE_AND_ALLTHAT, 42, "Fortytwo", "For tea too");
2076 *
2077 *               xmDisplayMessageA (XMDMF_ACTION | XMDMF_USECATALOG,
2078 *                       MSG_READING_COMICS, NULL);
2079 *
2080 *               xmDisplayMessage (XMDMF_DOSFAULT | XMDMF_USECATALOG,
2081 *                       MSG_CANT_LOAD_MODULE, ModuleName);
2082 *
2083 *       SEE ALSO
2084 *               xmDisplayProgress()
2085 *
2086 ****************************************************************************
2087 */
2088 static LIBCALL void XModuleDisplayMessage (
2089         REG(d0, ULONG level),
2090         REG(a0, CONST_STRPTR msg),
2091         REG(a1, LONG *args),
2092         REG(a6, struct XModuleBase *XModuleBase))
2093 {
2094         if (level & XMDMF_USECATALOG)
2095                 msg = STR((ULONG)msg);
2096
2097         if (level & XMDMF_DOSFAULT)
2098         {
2099                 UBYTE buf[FAULT_MAX + 100];
2100                 UBYTE buf2[100];
2101
2102                 if (GuiSwitches.LogLevel >= XMDMF_ERROR)
2103                 {
2104                         if (args) VSPrintf (buf2, msg, args);
2105
2106                         if (Fault ((level & XMDMF_DOSERRORMASK) ? (level & XMDMF_DOSERRORMASK) : IoErr(),
2107                                 args ? buf2 : msg, buf, FAULT_MAX + 100))
2108                         {
2109                                 if (level & XMDMF_USEREQUESTER) ShowRequestStr (buf, NULL, args);
2110                                 else ShowString (buf, args);
2111                         }
2112                 }
2113         }
2114         else if (level & XMDMF_ACTION)
2115         {
2116                 DisplayActionStr (msg);
2117                 if (GuiSwitches.LogLevel > XMDMF_INFORMATION)
2118                         ShowString (msg, args);
2119         }
2120         else if (level & XMDMF_USEREQUESTER)
2121                 ShowRequestStr (msg, NULL, args);
2122         else if (GuiSwitches.LogLevel > (level & XMDMF_LEVELMASK))
2123                 ShowString (msg, args);
2124 }
2125
2126
2127
2128 /****** xmodule/xmDisplayProgress ******************************************
2129 *
2130 *       NAME
2131 *               xmDisplayProgress -- Uptdates the progress bar indicator
2132 *
2133 *       SYNOPSIS
2134 *               abort = xmDisplayProgress(done, total)
2135 *                                         D0    D1
2136 *
2137 *               LONG xmDisplayMessageA(ULONG,ULONG);
2138 *
2139 *       FUNCTION
2140 *               Updates the position of the fuel gauge in the progress window to
2141 *               show the progress of an operation.  Additionally, it checks for
2142 *               user abort.
2143 *
2144 *       INPUTS
2145 *               done - a number which indicates how much of the work has
2146 *                       been already completed
2147 *               total - Total number of operations to do.
2148 *
2149 *       RESULT
2150 *               0 for success, ERROR_BREAK if user abort was detected.  You should
2151 *               always check this return code to abort what you were doing.
2152 *
2153 ****************************************************************************
2154 */
2155 static LIBCALL void XModuleDisplayProgress (
2156         REG(d0, ULONG done),
2157         REG(d1, ULONG total),
2158         REG(a6, struct XModuleBase *XModuleBase))
2159 {
2160         DisplayProgress (done, total);
2161 }