4 ** Copyright (C) 1995,96,97 Bernardo Innocenti
6 ** xmodule.library functions
9 #include <exec/libraries.h>
10 #include <exec/memory.h>
12 #include <dos/stdio.h>
13 #include <libraries/xmodule.h>
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>
23 #include "XModulePriv.h"
28 /****** xmodule/--background-- *********************************************
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.
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
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
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.
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.
65 * Actually, most modular programs call them `modules', but it would
66 * have created a lot of confusion with a program like XModule :-).
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
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.
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.
87 * songclass/--background--, exec/ObtainSemaphore()
89 ****************************************************************************
94 /* Library function prototypes */
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 (
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(
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),
156 REG(a1, struct TagItem *tags),
157 REG(a6, struct XModuleBase *XModuleBase));
158 static LIBCALL void XModuleSetInstrument (
159 REG(a0, struct SongInfo *si),
161 REG(a1, struct TagItem *tags),
162 REG(a6, struct XModuleBase *XModuleBase));
163 static LIBCALL void XModuleRemInstrument (
164 REG(a0, struct SongInfo *si),
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),
176 REG(a6, struct XModuleBase *XModuleBase));
177 static LIBCALL void XModuleDisplayProgress (
178 REG(d0, ULONG actual),
180 REG(a6, struct XModuleBase *XModuleBase));
184 /* Local function prototypes */
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);
194 /* Library vector table */
196 static APTR XModuleVectors[] =
212 XModuleLockActiveSong,
215 XModuleIdentifyModule,
222 XModuleAddInstrument,
223 XModuleSetInstrument,
224 XModuleRemInstrument,
226 XModuleDisplayMessage,
227 XModuleDisplayProgress,
234 GLOBALCALL ULONG MakeXModuleLibrary (void)
236 if (XModuleBase = (struct XModuleBase *)MakeLibrary (XModuleVectors,
237 NULL, NULL, sizeof (struct XModuleBase), NULL))
239 /* Clear base variables following the library node
240 * (InitSemaphore reqires a clean memory block!)
242 * GOOD NEWS: Clearing the library base is not required because
243 * MakeLibrary() allocates a MEMF_CLEAR memory block.
245 * memset (((UBYTE *)XModuleBase) + sizeof (struct Library), 0,
246 * sizeof (struct XModuleBase) - sizeof (struct Library));
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;
254 XModuleBase->xm_DOSBase = DOSBase;
255 XModuleBase->xm_UtilityBase = UtilityBase;
256 XModuleBase->xm_IFFParseBase = IFFParseBase;
257 XModuleBase->xm_IntuitionBase = IntuitionBase;
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);
264 /* Create global memory pool */
265 if (!(XModuleBase->xm_Pool = CreatePool (MEMF_ANY, 16*1024, 4*1024)))
266 return ERROR_NO_FREE_STORE;
268 if (!(XModuleBase->xm_SongClass = InitSongClass()))
269 return ERROR_NO_FREE_STORE;
274 return ERROR_NO_FREE_STORE;
279 GLOBALCALL void DisposeXModuleLibrary (void)
283 ObtainSemaphore (&XModuleBase->xm_BaseLock);
285 /* Free all songs in SongList */
286 while (!IsListEmpty ((struct List *)&XModuleBase->xm_Songs))
287 xmDeleteSong ((struct SongInfo *)XModuleBase->xm_Songs.mlh_Head);
289 /* Remove all loaders */
290 while (!IsListEmpty ((struct List *)&XModuleBase->xm_Loaders))
291 xmRemHook ((struct XMHook *)XModuleBase->xm_Loaders.mlh_Head);
293 /* Remove all savers */
294 while (!IsListEmpty ((struct List *)&XModuleBase->xm_Savers))
295 xmRemHook ((struct XMHook *)XModuleBase->xm_Savers.mlh_Head);
297 /* Free the songclass */
298 FreeSongClass (XModuleBase->xm_SongClass);
300 /* Dispose the global memory pool */
301 if (XModuleBase->xm_Pool) DeletePool (XModuleBase->xm_Pool);
303 FreeMem (((UBYTE *)XModuleBase) - XModuleBase->xm_Library.lib_NegSize,
304 XModuleBase->xm_Library.lib_NegSize + XModuleBase->xm_Library.lib_PosSize);
311 static LIBCALL void XModuleLibOpen (void)
317 static LIBCALL void XModuleLibClose (void)
323 static LIBCALL void XModuleLibExpunge (void)
329 static LIBCALL void XModuleLibExtFunc (void)
335 static LIBCALL LONG XModuleRexxServer (void)
342 /****** xmodule/xmAddHook **************************************************
345 * xmAddHookA -- Creates a new XModule Hook
346 * xmAddHook -- Varargs stub for xmAddHookA
349 * hook = xmAddHookA(tagList)
352 * struct XMHook *xmAddHookA(struct TagItem *);
355 * hook = xmAddHook(Tag1,...)
357 * struct XMHook *xmAddHook(ULONG,...);
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
365 * tagList - pointer to a tag list specifying how to initialize the
369 * XMHOOK_Type - (ULONG) Defines the pourpose of this hook. Possible
370 * values are currently NT_XMLOADER and NT_XMSAVER. (This
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).
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).
385 * XMHOOK_Descr - (STRPTR) Verbose description of the hook
386 * (without newlines). (Defaults to NULL).
388 * XMHOOK_Author - (STRPTR) Author's name. Please, just put
389 * your full name here; no greetings, copyright notices,
390 * etc. (Defaults to NULL).
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).
397 * XMHOOK_Flags - (ULONG) Sets miscellaneous flags for this hook.
398 * See xmodule.h for possible flags.
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).
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).
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:
418 * void RemoveHookFunc (struct XMHook *hook);
421 * XMHOOK_LoadModFunc - (APTR) Pointer to the hook function which
422 * loads a module. The template for the function is:
424 * LONG LoadModFunc (BPTR fileHandle, struct SongInfo *song,
426 * struct XMHook *loader);
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).
440 * XMHOOK_SaveModFunc - (APTR) Pointer to the hook function which
441 * saves a module. The template for the function is:
443 * LONG SaveModFunc (BPTR fileHandle, struct SongInfo *song,
445 * struct XMHook *saver);
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).
460 * XMHOOK_IdentifyModFunc - (APTR) Pointer to the hook function
461 * which identifies a module format. The template for the
464 * struct XMHook *IdentifyModFunc (BPTR fileHandle,
466 * struct XMHook *hook,struct TagItem *tagList);
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
478 * hook - Pointer to the newly allocated XMHook structure, or
482 * xmRemHook(), xmIdentifyModule(), xmLoadSong(), xmSaveSong()
484 ****************************************************************************
486 static LIBCALL struct XMHook *XModuleAddHook (
487 REG(a0, struct TagItem *tags),
488 REG(a6, struct XModuleBase *XModuleBase))
492 if (hook = AllocPooled (XModuleBase->xm_Pool, sizeof (struct XMHook)))
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;
516 if (hook->xmh_LibraryBase)
517 if (!(OpenLibrary (hook->xmh_LibraryBase->lib_Node.ln_Name, 0)))
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);
532 /****** xmodule/xmRemHook **************************************************
535 * xmRemHook -- Removes an XModule Hook
541 * void xmRemHook(struct XMHook *);
544 * Removes an XModule Hook, calls its RemoveHookFunc() and then
548 * xmHook - pointer to the hook to be removed.
553 ****************************************************************************
555 static LIBCALL void XModuleRemHook (
556 REG(a0, struct XMHook *hook),
557 REG(a6, struct XModuleBase *XModuleBase))
559 ObtainSemaphore (&XModuleBase->xm_BaseLock);
560 REMOVE ((struct Node *)hook);
561 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
563 if (hook->xmh_RemoveHook)
564 hook->xmh_RemoveHook (hook);
566 if (hook->xmh_LibraryBase) CloseLibrary (hook->xmh_LibraryBase);
568 FreePooled (XModuleBase->xm_Pool, hook, sizeof (struct XMHook));
574 /****** xmodule/xmCreateSong ***********************************************
577 * xmCreateSongA -- Create and initialize a new SongInfo structure
578 * xmCreateSong -- Varargs stub for xmCreateSongA
581 * songInfo = xmCreateSongA(tagList);
584 * struct SongInfo *xmCreateSongA(struct TagItem *);
587 * songInfo = xmCreateSong(Tag1,...);
589 * struct SongInfo *xmCreateSong(ULONG,...);
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().
597 * tagList - pointer to an optional tag list specifying how to
598 * initialize the SongInfo structure.
601 * SNGA_DefaultTracks - Sets the default number of pattern tracks for
602 * the song. Defaults to 4.
604 * SNGA_DefaultPattLen - Sets the default number of pattern lines for
605 * the song. Defaults to 64.
607 * SNGA_ReadyToUse - Adds one pattern and one position to the song.
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).
621 * XMSNG_Active - (BOOL) Makes this song the active one. This tag is
622 * considered only if the XMSNG_AddToList tag is also passed.
625 * songInfo - pointer to the newly allocated SongInfo structure, or
629 * xmDeleteSong(), xmAddSongA()
631 ****************************************************************************
633 static LIBCALL struct SongInfo * XModuleCreateSong (
634 REG(a0, struct TagItem *tags),
635 REG(a6, struct XModuleBase *XModuleBase))
640 if (si = NewObjectA (XModuleBase->xm_SongClass, NULL, tags))
642 if (tag = FindTagItem (XMSNG_AddToList, tags))
644 ObtainSemaphore (&si->Lock);
645 xmAddSongA (si, (struct SongInfo *)tag->ti_Data, tags);
654 /****** xmodule/xmDeleteSong ***********************************************
657 * xmDeleteSong -- Deletes a song
660 * xmDeleteSong(songInfo)
663 * void xmDeleteSong(struct SongInfo *);
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.
671 * songInfo - pointer to the SongInfo structure to be deleted.
672 * Passing a NULL pointer is harmless.
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.
683 * xmCreateSong(), xmRemSong()
685 ****************************************************************************
687 static LIBCALL void XModuleDeleteSong (
688 REG(a0, struct SongInfo *si),
689 REG(a6, struct XModuleBase *XModuleBase))
697 /****** xmodule/xmAddSongA *************************************************
700 * xmAddSongA -- Add a song to the song list
701 * xmAddSong -- Varargs stub for xmAddSongA
704 * success = xmAddSongA(songInfo,position,tagList);
707 * ULONG xmAddSongA(struct SongInfo *,struct SongInfo *,
711 * success = xmAddSong(songInfo,position,tag1,...);
713 * ULONG xmAddSong(struct SongInfo *,struct SongInfo *,
717 * Adds a song to the public song list. Trying to add a song
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.
729 * XMSNG_Active - (BOOL) Makes this song the active one. The
730 * default is to just add the song without activating it.
733 * success - Will be 0 for failure, in which case the song will
734 * not be added to the songs list. Currently, xmAddSongA()
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
746 ****************************************************************************
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))
754 if (!si) return FALSE;
756 if (!si->Link.ln_Type) /* Is it already in the list? */
758 ObtainSemaphore (&XModuleBase->xm_BaseLock);
759 DetatchSongInfoList();
761 if ((ULONG)position == ~0)
762 ADDTAIL ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si);
764 Insert ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si, (struct Node *)position);
766 si->Link.ln_Type = NT_XMSONG; /* Mark this song */
768 if (GetTagData (XMSNG_Active, FALSE, tags))
771 UpdateSongInfoList();
773 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
781 /****** xmodule/xmRemSong **************************************************
784 * xmRemSong -- Remove a song from the song list
787 * success = xmRemSong(songInfo);
790 * ULONG xmRemSong(struct SongInfo *);
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.
799 * songInfo - song to be removed. If NULL, this function will take
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.
808 * In order to remove a song, you must first obtain an exclusive
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!
818 ****************************************************************************
820 static LIBCALL ULONG XModuleRemSong (
821 REG(a0, struct SongInfo *si),
822 REG(a6, struct XModuleBase *XModuleBase))
824 if (!si) return FALSE;
826 if (!si->Link.ln_Type) return TRUE; /* Not in the list */
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.
836 ObtainSemaphore (&XModuleBase->xm_BaseLock);
838 if (AttemptSemaphore (&si->Lock))
841 ReleaseSemaphore (&XModuleBase->xm_SongsLock);
842 ObtainSemaphore (&si->Lock);
844 if (AttemptSemaphore (&XModuleBase->xm_SongsLock))
847 ReleaseSemaphore (&si->Lock);
851 ObtainSemaphore (&XModuleBase->xm_BaseLock);
852 DetatchSongInfoList();
854 REMOVE ((struct Node *)si);
855 si->Link.ln_Type = 0; /* Mark song outside list */
857 if (XModuleBase->xm_CurrentSong == si)
859 if (IsListEmpty ((struct List *)&XModuleBase->xm_Songs))
860 XModuleBase->xm_CurrentSong = NULL;
862 XModuleBase->xm_CurrentSong = (struct SongInfo *)
863 XModuleBase->xm_Songs.mlh_TailPred;
865 UpdateSongInfoList();
868 else UpdateSongInfoList();
870 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
876 /****** xmodule/xmActivateSong *********************************************
879 * xmActivateSong -- Makes a song the active one
882 * success = xmActivateSong(songInfo);
885 * ULONG xmActivateSong(struct SongInfo *);
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.
893 * songInfo - song to be activated. If NULL, this function will
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.
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.
909 ****************************************************************************
911 static LIBCALL ULONG XModuleActivateSong (
912 REG(a0, struct SongInfo *si),
913 REG(a6, struct XModuleBase *XModuleBase))
915 if (!si) return FALSE;
916 if (!si->Link.ln_Type) return FALSE; /* Not in the list */
918 ObtainSemaphore (&XModuleBase->xm_BaseLock);
920 if (XModuleBase->xm_CurrentSong != si)
922 XModuleBase->xm_CurrentSong = si;
923 UpdateSongInfo(); /**/
926 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
932 /****** xmodule/xmLockActiveSong *******************************************
935 * xmLockActiveSong -- Obtains an lock on the active song
938 * song = xmLockActiveSong(mode);
941 * struct SongInfo *xmActivateSong(UWORD);
944 * Obtains an exclusive or shared lock on the active song,
945 * waiting if needed. This call is a shortcut to:
947 * ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
948 * ObtainSemaphore[Shared] (&XModuleBase->xm_CurrentSong.Lock);
949 * ReleaseSemaphore (&XModuleBase->xm_BaseLock);
951 * To unlock a song obtained in this way, you just need to
952 * ReleaseSemaphore() it.
954 * You MUST always lock a song before you even think to
955 * read from -or write to- its data!
958 * mode - one of SM_SHARED or SM_EXCLUSIVE.
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
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.
975 ****************************************************************************
977 static LIBCALL struct SongInfo *XModuleLockActiveSong (
979 REG(a6, struct XModuleBase *XModuleBase))
985 ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
987 /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
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 */
997 if (si = XModuleBase->xm_CurrentSong)
999 if (mode == SM_SHARED)
1002 ObtainSemaphoreShared (&si->Lock);
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 */
1012 else ObtainSemaphore (&si->Lock);
1015 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1022 /****** xmodule/xmLoadModuleA **********************************************
1025 * xmLoadModuleA -- Loads a module and converts it in XModule format
1026 * xmLoadModule -- Varargs stub for xmLoadModuleA
1029 * songInfo = xmLoadModuleA(fileName,tagList)
1032 * struct SongInfo *xmLoadModuleA(CONST_STRPTR,struct TagItem *);
1035 * songInfo = xmLoadModule(fileName,tag,...)
1037 * struct SongInfo *xmLoadModule(CONST_STRPTR,LONG,...);
1040 * Loads fileName using the correct module loader.
1043 * fileName - File to read. Can be NULL if the XMSNG_FileHandle
1045 * tagList - Additional parameters (see below). Can be NULL.
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.
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).
1064 * XMSNG_Loader - (struct XMHook *) Disables automatic format
1065 * checking and forces loading the module with the given
1066 * loader. (Defaults to NULL).
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).
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).
1094 * XMSNG_Active - (BOOL) Makes this song the active one. This tag is
1095 * considered only if the XMSNG_AddToList tag is also passed.
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.
1104 * \* Load a song and add it to the SongList *\
1105 * si = xmLoadSongTags (file, NULL,
1106 * XMSNG_AddToList, (struct SongInfo *)~0,
1109 * \* Check for errors even if si is not NULL *\
1112 * \* Release Semaphore got by xmLoadSong() *\
1113 * if (si) ReleaseSemaphore (&si->Lock);
1116 * xmAddSongA(), xmIdentifyModule()
1118 ****************************************************************************
1120 static LIBCALL struct SongInfo * XModuleLoadModule (
1121 REG(a0, CONST_STRPTR filename),
1122 REG(a1, struct TagItem *tags),
1123 REG(a6, struct XModuleBase *XModuleBase))
1125 struct XMHook *loader;
1126 struct SongInfo *si = NULL;
1127 UBYTE fullpath[PATHNAME_MAX];
1129 BPTR fh, compressed = 0;
1133 loader = (struct XMHook *)GetTagData (XMSNG_Loader, NULL, tags);
1136 /* Get source file or open it */
1138 if (fh = (BPTR) GetTagData (XMSNG_FileHandle, NULL, tags))
1140 /* Get the full pathname */
1142 if (!NameFromFH (fh, fullpath, PATHNAME_MAX))
1147 if (!(fh = Open (filename, MODE_OLDFILE)))
1149 UBYTE buf[FAULT_MAX];
1151 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
1152 (APTR)MSG_ERR_LOAD, filename, buf);
1156 /* Optimize file buffering for faster I/O.
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).
1164 SetVBuf (fh, NULL, BUF_FULL, 16*1024);
1167 /* Get the full pathname */
1169 if (!NameFromFH (fh, fullpath, PATHNAME_MAX))
1173 /* Check wether the file is compressed */
1175 if (type = CruncherType (fh))
1179 if (compressed = DecompressFile (filename, type))
1181 if (!(fh = OpenFromLock (compressed)))
1184 UnLock (compressed);
1185 DecompressFileDone();
1186 SetIoErr (LastErr = err);
1196 /* Lock loaders list */
1199 ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
1201 /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
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 */
1212 /* Find out what the heck this file format is */
1215 loader = xmIdentifyModule (fh, tags);
1219 if (loader->xmh_LoadMod)
1221 Seek (fh, 0, OFFSET_BEGINNING); /* Reset file */
1226 struct TagItem *tag;
1228 if (tag = FindTagItem (XMSNG_OldSong, tags))
1229 xmDeleteSong (tag->ti_Data ?
1230 (struct SongInfo *)tag->ti_Data : XModuleBase->xm_CurrentSong);
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).
1238 if (si = xmCreateSong (
1239 SNGA_Path, fullpath,
1240 SNGA_Title, FilePart (filename),
1243 OpenProgressWindow();
1245 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
1246 (APTR)MSG_READING_TYPE_MODULE, loader->xmh_Link.ln_Name);
1249 /* Call loader hook */
1250 err = loader->xmh_LoadMod (fh, si, loader, tags);
1252 if (err == ERROR_IOERR)
1255 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT | err,
1256 (APTR)MSG_ERROR_READING, FilePart (filename));
1258 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_ERROR,
1259 (APTR)MSG_UNESPECTED_EOF);
1264 CloseProgressWindow();
1266 else err = ERROR_NO_FREE_STORE;
1268 else err = ERROR_ACTION_NOT_KNOWN;
1270 else err = ERROR_OBJECT_WRONG_TYPE;
1272 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1276 if (compressed) DecompressFileDone ();
1279 xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG,
1280 (APTR)MSG_MODULE_LOADED_OK, NULL);
1282 SetIoErr (LastErr = err);
1286 struct TagItem *tag;
1288 if (tag = FindTagItem (XMSNG_AddToList, tags))
1290 ObtainSemaphore (&si->Lock);
1291 xmAddSongA (si, (struct SongInfo *)tag->ti_Data, NULL);
1292 if (GetTagData (XMSNG_Active, FALSE, tags))
1293 xmActivateSong (si);
1302 /****** xmodule/xmSaveModuleA **********************************************
1305 * xmSaveModuleA -- Saves a module to the specified file format
1306 * xmSaveModule -- Varargs stub for xmSaveModuleA
1309 * error = xmSaveModuleA(songInfo, fileName, saver, tagList)
1312 * LONG xmSaveModuleA(struct SongInfo *,CONST_STRPTR,struct XMHook *,
1313 * struct TagItem *);
1316 * error = xmSaveModule(songInfo, fileName, saver, tag1,...)
1318 * LONG xmSaveModule(struct SongInfo *,CONST_STRPTR,struct XMHook *,
1322 * Saves songInfo to fileName using the specified saver.
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.
1331 * No tags are defined for this call.
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.
1344 ****************************************************************************
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))
1355 BOOL releasebase = FALSE;
1358 /* Use the default saver if not specified */
1362 ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
1364 /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
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 */
1374 if (!(saver = XModuleBase->xm_DefaultSaver))
1376 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1377 return ERROR_ACTION_NOT_KNOWN;
1384 if (saver->xmh_SaveMod)
1388 /* Make some checks before actually saving */
1391 if (si->LastInstrument > saver->xmh_MaxInstruments)
1393 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1394 (APTR)MSG_SONG_TOO_MANY_INSTRS, si->LastInstrument, saver->xmh_MaxInstruments);
1396 if (ShowRequest (MSG_TRY_REMAPPING_INSTRUMENTS, MSG_PROCEED_OR_CANCEL))
1397 xmProcessSong (si, NULL,
1398 XMSNG_RemapInstruments, TRUE,
1402 for (i = 1; i <= si->LastInstrument; i++)
1403 if (si->Instr[i] && (si->Instr[i]->Length > saver->xmh_MaxSampleLen))
1405 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1406 (APTR)MSG_INSTR_TOO_LONG, i, saver->xmh_MaxSampleLen);
1409 for (i = 0; i < si->NumPatterns; 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) ) )
1416 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
1417 (APTR)MSG_PATT_LENGTH_INVALID, i, si->Patt[i]->Lines);
1419 if (ShowRequest (MSG_WILL_MODIFY_SONG, MSG_PROCEED_OR_CANCEL))
1421 xmProcessSong (si, NULL,
1422 XMSNG_LimitPatterns, saver->xmh_MaxPattLen |
1423 ((saver->xmh_Flags & XMHF_FIXED_PATT_LEN) ? (saver->xmh_MaxPattLen << 16) : 0),
1430 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
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);
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);
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);
1450 /* Now make a backup of the old file before overwriting this */
1452 if (GuiSwitches.DoBackups)
1453 BackupFile (filename, GuiSwitches.BackupTemplate, GuiSwitches.BackupVersions);
1456 if (fh = Open (filename, MODE_NEWFILE))
1458 OpenProgressWindow();
1460 /* Optimize file buffering for faster I/O.
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).
1468 SetVBuf (fh, NULL, BUF_FULL, 16*1024);
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));
1476 err = saver->xmh_SaveMod (fh, si, saver, tags);
1479 SetComment (filename, VERS " by Bernardo Innocenti");
1483 /* Delete incomplete file */
1484 DeleteFile (filename);
1486 if (err == ERROR_IOERR)
1487 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
1488 (APTR)MSG_ERROR_WRITING, filename);
1492 if (SaveSwitches.SaveIcons)
1493 PutIcon ("def_Module", filename);
1494 xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG,
1495 (APTR)MSG_MODULE_SAVED_OK, NULL);
1499 CloseProgressWindow();
1501 else /* Open failed */
1504 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
1505 (APTR)MSG_CANT_OPEN, filename);
1508 else err = ERROR_ACTION_NOT_KNOWN;
1511 ReleaseSemaphore (&XModuleBase->xm_BaseLock);
1518 /****** xmodule/xmIdentifyModule *******************************************
1521 * xmIdentifyModule -- Returns the type of a module
1524 * loader = xmIdentifyModule(fh, tagList)
1527 * struct XMHook *xmIdentifyModule(BPTR,struct TagItem *);
1530 * Finds out a loader which is able to read the given module.
1533 * fh - AmigaDOS FileHandle to examine.
1534 * tagList - Additional parameters. Leave it NULL for now.
1537 * loader - Pointer to the first loader which knows how to load this
1538 * module, or NULL otherwise.
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.
1547 ****************************************************************************
1549 static LIBCALL struct XMHook *XModuleIdentifyModule(
1551 REG(a0, struct TagItem *tags),
1552 REG(a6, struct XModuleBase *XModuleBase))
1554 struct XMHook *loader, *ret = NULL;
1556 loader = (struct XMHook *) XModuleBase->xm_Loaders.mlh_Head;
1558 while (loader->xmh_Link.ln_Succ)
1560 if (ret = loader->xmh_IdentifyMod (fh, loader, tags))
1563 loader = (struct XMHook *)loader->xmh_Link.ln_Succ;
1566 Seek (fh, 0, OFFSET_BEGINNING);
1573 /****** xmodule/xmSetSongLen ***********************************************
1576 * xmSetSongLen -- Set the number of song positions
1579 * sequence = xmSetSongLen(songInfo, length)
1582 * UWORD *xmSetSongLen(struct SongInfo *, UWORD);
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
1595 * songInfo - pointer to a SongInfo structure.
1596 * length - new length of song sequence table.
1599 * This function will also adjust the CurrentPos field if it is found
1600 * beyond the song end
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.
1609 * Pointer to the newly allocated sequence table or NULL for failure,
1610 * in which case the previous sequence table is left untouched.
1612 ****************************************************************************
1614 static LIBCALL UWORD *XModuleSetSongLen (
1615 REG(a0, struct SongInfo *si),
1617 REG(a6, struct XModuleBase *XModuleBase))
1619 SetAttrs (si, SNGA_Length, len, TAG_DONE);
1620 return si->Sequence;
1625 /****** xmodule/xmAddPatternA **********************************************
1628 * xmAddPatternA -- Adds a pattern to a song
1629 * xmAddPattern -- Varargs stub for xmAddPatternA
1632 * pattern = xmAddPatternA(si, tagList)
1635 * struct Pattern *xmAddPatternA(struct SongInfo *,struct TagItem *);
1638 * pattern = xmAddPattern (si,tag1,...)
1640 * struct Pattern *xmAddPattern(struct SongInfo *,LONG Tag1,...);
1643 * Adds a pattern to a song by calling the SNGM_ADDPATTERN method.
1646 * si - pointer to the song to which the pattern should be added.
1647 * tagList - optional TagList. See SNGM_ADDPATTERN for possible tags.
1650 * Pointer to the new pattern or NULL for failure.
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
1658 * xmRemPattern(), songclass/SNGM_ADDPATTERN
1660 ****************************************************************************
1662 static LIBCALL void XModuleAddPattern (
1663 REG(a0, struct SongInfo *si),
1664 REG(a1, struct TagItem *tags),
1665 REG(a6, struct XModuleBase *XModuleBase))
1667 DoMethod ((Object *)si, SNGM_ADDPATTERN, tags);
1672 /****** xmodule/xmSetPatternA **********************************************
1675 * xmSetPatternA -- Sets pattern attributes
1676 * xmSetPattern -- Varargs stub for xmSetPatternA
1679 * success = xmSetPatternA(si, pattNum, tagList)
1682 * ULONG xmSetPatternA(struct SongInfo *,ULONG,struct TagItem *);
1685 * success = xmSetPattern (si,pattNum,tag1,...)
1687 * ULONG xmSetPattern(struct SongInfo *,ULONG,LONG Tag1,...);
1690 * Sets attributes of a pattern by calling the SNGM_SETPATTERN method.
1693 * si - pointer to the song which contains the pattern to be set.
1694 * tagList - list of attributes to set. See SNGM_SETPATTERN for
1698 * Non zero for success
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.
1706 * xmAddPattern(), songclass/SNGM_SETPATTERN
1708 ****************************************************************************
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))
1716 DoMethod ((Object *)si, SNGM_SETPATTERN, pattNum, tags);
1721 /****** xmodule/xmRemPattern ***********************************************
1724 * xmRemPattern -- Removes a pattern from a song
1727 * xmRemPattern(si, pattNum, replaceWith)
1730 * void xmRemPattern(struct SongInfo *,LONG,LONG);
1733 * Removes a pattern from a song by calling the SNGM_REMPATTERN method.
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
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
1747 * xmAddPatternA(), songclass/SNGM_REMPATTERN
1749 ****************************************************************************
1751 static LIBCALL void XModuleRemPattern (
1752 REG(a0, struct SongInfo *si),
1753 REG(d0, ULONG pattNum),
1754 REG(d1, ULONG replaceWith))
1756 DoMethod ((Object *)si, SNGM_REMPATTERN, pattNum, replaceWith);
1761 /****** xmodule/xmAddInstrumentA *******************************************
1764 * xmAddInstrumentA -- Adds a instrument to a song
1765 * xmAddInstrument -- Varargs stub for xmAddInstrumentA
1768 * instrument = xmAddInstrumentA(si, instrNum, tagList)
1771 * struct Instrument *xmAddInstrumentA(struct SongInfo *,LONG,
1772 * struct TagItem *);
1775 * instrument = xmAddInstrument(si,instrNum,tag1,...)
1777 * struct Instrument *xmAddInstrument(struct SongInfo *,LONG,LONG,...);
1780 * Adds an instrument to a song by calling the SNGM_ADDINSTRUMENT method.
1783 * si - pointer to the song to which the instrument should be added.
1784 * tagList - optional TagList. See SNGM_ADDINSTRUMENT for possible tags.
1787 * Pointer to the new instrument or NULL for failure.
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
1795 * xmRemInstrument(), songclass/SNGM_REMINSTRUMENT
1797 ****************************************************************************
1799 static LIBCALL void XModuleAddInstrument (
1800 REG(a0, struct SongInfo *si),
1802 REG(a1, struct TagItem *tags),
1803 REG(a6, struct XModuleBase *XModuleBase))
1805 DoMethod ((Object *)si, SNGM_ADDINSTRUMENT, num, tags);
1810 /****** xmodule/xmSetInstrumentA *******************************************
1813 * xmSetInstrumentA -- Sets an instrument's attributes
1814 * xmSetInstrument -- Varargs stub for xmSetInstrumentA
1817 * success = xmSetInstrumentA(si, instrNum, tagList)
1820 * ULONG xmSetInstrumentA(struct SongInfo *,LONG,
1821 * struct TagItem *);
1824 * success = xmSetInstrument(si,instrNum,tag1,...)
1826 * ULONG xmSetInstrument(struct SongInfo *,LONG,LONG,...);
1829 * Sets an instrument's attributes by calling the SNGM_SETINSTRUMENT
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.
1838 * non zero for success.
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.
1846 * xmAddInstrument(), songclass/SNGM_SETINSTRUMENT
1848 ****************************************************************************
1850 static LIBCALL void XModuleSetInstrument (
1851 REG(a0, struct SongInfo *si),
1853 REG(a1, struct TagItem *tags),
1854 REG(a6, struct XModuleBase *XModuleBase))
1856 DoMethod ((Object *)si, SNGM_SETINSTRUMENT, num, tags);
1861 /****** xmodule/xmRemInstrument ***********************************************
1864 * xmRemInstrument -- Removes an instrument from a song
1867 * xmRemInstrument(si, instrNum)
1870 * void xmRemInstrument(struct SongInfo *,LONG);
1873 * Removes an instrument from a song by calling the SNGM_REMINSTRUMENT
1877 * si - pointer to the song to which the instrument should be removed.
1878 * mum - Number of the instrument to be removed.
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
1886 * xmAddInstrumentA(), songclass/SNGM_REMINSTRUMENT
1888 ****************************************************************************
1890 static LIBCALL void XModuleRemInstrument (
1891 REG(a0, struct SongInfo *si),
1893 REG(a6, struct XModuleBase *XModuleBase))
1895 DoMethod ((Object *)si, SNGM_REMINSTRUMENT, num);
1900 /****** xmodule/xmProcessSongA *********************************************
1903 * xmProcessSongA -- Performs complex processing on a song
1904 * xmProcessSong -- Varargs stub for xmProcessSongA()
1907 * result = xmProcessSongA(si, reserved, tagList)
1910 * LONG xmProcessSongA(struct SongInfo *,void *,struct TagItem *);
1913 * result = xmProcessSong(si, reserved, tag1, ...)
1915 * LONG xmProcessSongA(struct SongInfo *,void *,LONG,...);
1918 * Performs complex processing operations on a song.
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.
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.
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
1940 * XMOF_DEFAULT will select all the optimizations choosen by
1941 * the user in addition to the ones specified with the other flags.
1943 * XMSNG_RemapInstruments - (BOOL) Performs instruments remapping.
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.
1951 * XMSNG_Join - (struct SongInfo *) - Joins the song with another one.
1953 * XMSNG_Merge - (struct SongInfo *) - Merges the song with another one.
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
1960 ****************************************************************************
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))
1968 struct TagItem *ti, *tstate = tags;
1971 while (ti = NextTagItem(&tstate))
1975 case XMSNG_Optimize:
1977 ULONG flags = ti->ti_Data;
1979 if (flags & XMOF_DEFAULT)
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;
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);
2003 case XMSNG_RemapInstruments:
2004 RemapInstruments (si);
2008 case XMSNG_LimitPatterns:
2009 result = ResizePatterns (si, ti->ti_Data >> 16, ti->ti_Data & 0xFFFF);
2030 /****** xmodule/xmDisplayMessage *******************************************
2033 * xmDisplayMessageA -- Displays a message to the user
2034 * xmDisplayMessage -- Varargs stub for xmDisplayMessageA()
2037 * xmDisplayMessageA(level, message, args)
2040 * void xmDisplayMessageA(ULONG,APTR,LONG *);
2043 * xmDisplayMessage(level, message, ...)
2045 * void xmDisplayMessage(ULONG,APTR,...);
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
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
2065 * message - pointer to a string or catalog message number,
2066 * when the XMDMF_USECATALOG flag is set.
2067 * args - arguments for formatting.
2070 * xmDisplayMessage (XMDMF_ALERT,
2071 * "The application `%s' fucked up Windoze95 because %s.",
2072 * "(unknown name)", "an error occurred");
2074 * xmDisplayMessage (XMDMF_USE_CATALOG | XMDMF_COMMENT,
2075 * MSG_LIFE_UNIVERSE_AND_ALLTHAT, 42, "Fortytwo", "For tea too");
2077 * xmDisplayMessageA (XMDMF_ACTION | XMDMF_USECATALOG,
2078 * MSG_READING_COMICS, NULL);
2080 * xmDisplayMessage (XMDMF_DOSFAULT | XMDMF_USECATALOG,
2081 * MSG_CANT_LOAD_MODULE, ModuleName);
2084 * xmDisplayProgress()
2086 ****************************************************************************
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))
2094 if (level & XMDMF_USECATALOG)
2095 msg = STR((ULONG)msg);
2097 if (level & XMDMF_DOSFAULT)
2099 UBYTE buf[FAULT_MAX + 100];
2102 if (GuiSwitches.LogLevel >= XMDMF_ERROR)
2104 if (args) VSPrintf (buf2, msg, args);
2106 if (Fault ((level & XMDMF_DOSERRORMASK) ? (level & XMDMF_DOSERRORMASK) : IoErr(),
2107 args ? buf2 : msg, buf, FAULT_MAX + 100))
2109 if (level & XMDMF_USEREQUESTER) ShowRequestStr (buf, NULL, args);
2110 else ShowString (buf, args);
2114 else if (level & XMDMF_ACTION)
2116 DisplayActionStr (msg);
2117 if (GuiSwitches.LogLevel > XMDMF_INFORMATION)
2118 ShowString (msg, args);
2120 else if (level & XMDMF_USEREQUESTER)
2121 ShowRequestStr (msg, NULL, args);
2122 else if (GuiSwitches.LogLevel > (level & XMDMF_LEVELMASK))
2123 ShowString (msg, args);
2128 /****** xmodule/xmDisplayProgress ******************************************
2131 * xmDisplayProgress -- Uptdates the progress bar indicator
2134 * abort = xmDisplayProgress(done, total)
2137 * LONG xmDisplayMessageA(ULONG,ULONG);
2140 * Updates the position of the fuel gauge in the progress window to
2141 * show the progress of an operation. Additionally, it checks for
2145 * done - a number which indicates how much of the work has
2146 * been already completed
2147 * total - Total number of operations to do.
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.
2153 ****************************************************************************
2155 static LIBCALL void XModuleDisplayProgress (
2156 REG(d0, ULONG done),
2157 REG(d1, ULONG total),
2158 REG(a6, struct XModuleBase *XModuleBase))
2160 DisplayProgress (done, total);