--- /dev/null
+/*
+** App.c
+**
+** Copyright (C) 1994,95,96 by Bernardo Innocenti
+**
+** Handle AppIcons & AppWindows
+*/
+
+#include <exec/memory.h>
+#include <workbench/workbench.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/wb.h>
+#include <proto/icon.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+XDEF ULONG AppSig = 0;
+XDEF LONG IconX = NO_ICON_POSITION;
+XDEF LONG IconY = NO_ICON_POSITION;
+XDEF UBYTE IconName[16];
+XDEF BOOL Iconified = FALSE;
+
+
+static struct MsgPort *AppPort = NULL;
+static struct AppIcon *MyAppIcon = NULL;
+static struct DiskObject *AppDObj = NULL;
+
+
+
+GLOBALCALL void HandleAppMessage (void)
+
+/* App Window event handler. Get Workbench message and call server */
+{
+ struct AppMessage *am;
+
+ while (am = (struct AppMessage *) GetMsg (AppPort))
+ {
+ switch (am->am_Type)
+ {
+ case AMTYPE_APPWINDOW:
+ (((struct WinUserData *) am->am_UserData)->DropIcon) (am);
+ break;
+
+ case AMTYPE_APPICON:
+ if (am->am_NumArgs == 0)
+ DeIconify();
+ else if (am->am_UserData)
+ ((void (*) (struct AppMessage *am))(am->am_UserData)) (am);
+
+ break;
+
+ default:
+ break;
+ }
+
+ ReplyMsg ((struct Message *) am);
+ }
+}
+
+
+
+GLOBALCALL void AddAppWin (struct WinUserData *wud)
+{
+ wud->AppWin = AddAppWindowA (0, (ULONG)wud, wud->Win, AppPort, NULL);
+}
+
+
+
+GLOBALCALL void RemAppWin (struct WinUserData *wud)
+{
+ struct Node *succ;
+ struct Message *msg;
+
+ RemoveAppWindow (wud->AppWin);
+ wud->AppWin = NULL;
+
+ /* Reply all pending messages for this window */
+
+ Forbid();
+
+ msg = (struct Message *) AppPort->mp_MsgList.lh_Head;
+
+ while (succ = msg->mn_Node.ln_Succ)
+ {
+ if ((struct WinUserData *)(((struct AppMessage *)msg)->am_UserData) == wud)
+ {
+ Remove ((struct Node *)msg);
+ ReplyMsg (msg);
+ }
+ msg = (struct Message *) succ;
+ }
+
+ Permit();
+}
+
+
+
+GLOBALCALL LONG CreateAppIcon (void (*handler) (struct AppMessage *am))
+{
+ if (!AppPort) return RETURN_FAIL;
+
+ if (MyAppIcon) return RETURN_OK;
+
+ /* Get icon */
+ if ( !(AppDObj = GetProgramIcon() ))
+ AppDObj = GetDefDiskObject (WBTOOL);
+
+ if (!AppDObj) return RETURN_FAIL;
+
+ /* Initialize AppIcon */
+ AppDObj->do_CurrentX = IconX;
+ AppDObj->do_CurrentY = IconY;
+
+ if (MyAppIcon = AddAppIconA (0, (ULONG)handler, IconName, AppPort, NULL, AppDObj, NULL))
+ return RETURN_OK;
+
+ FreeDiskObject (AppDObj); AppDObj = NULL;
+ return RETURN_FAIL;
+}
+
+
+
+GLOBALCALL void DeleteAppIcon (void)
+{
+ if (MyAppIcon)
+ {
+ RemoveAppIcon (MyAppIcon); MyAppIcon = NULL;
+ FreeDiskObject (AppDObj); AppDObj = NULL;
+ }
+}
+
+
+
+GLOBALCALL void Iconify (void)
+{
+ if (!CreateAppIcon (ToolBoxDropIcon))
+ {
+ CloseDownScreen();
+ Iconified = TRUE;
+ }
+}
+
+
+
+GLOBALCALL void DeIconify (void)
+{
+ if (!SetupScreen())
+ {
+ Iconified = FALSE;
+ if (!GuiSwitches.ShowAppIcon) DeleteAppIcon();
+ }
+}
+
+
+GLOBALCALL LONG SetupApp (void)
+{
+ if (!(AppPort = CreateMsgPort()))
+ return ERROR_NO_FREE_STORE;
+ AppSig = 1 << AppPort->mp_SigBit;
+ Signals |= AppSig;
+
+ if (GuiSwitches.ShowAppIcon)
+ CreateAppIcon (ToolBoxDropIcon);
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL void CleanupApp (void)
+{
+ if (AppPort)
+ {
+ DeleteAppIcon();
+
+ KillMsgPort (AppPort); AppPort = NULL;
+ Signals &= ~AppSig; AppSig = 0;
+ }
+}
--- /dev/null
+/*
+** $Id:$
+**
+** Copyright (C) 1993,94,95,96,98,99 by Bernardo Innocenti
+**
+** AppWindow support functions.
+*/
+
+GLOBALCALL void HandleAppMessage (void);
+GLOBALCALL void AddAppWin (struct WinUserData *wud);
+GLOBALCALL void RemAppWin (struct WinUserData *wud);
+GLOBALCALL LONG CreateAppIcon (void (*handler) (struct AppMessage *am));
+GLOBALCALL void DeleteAppIcon (void);
+GLOBALCALL void Iconify (void);
+GLOBALCALL void DeIconify (void);
+GLOBALCALL LONG SetupApp (void);
+GLOBALCALL void CleanupApp (void);
--- /dev/null
+/*
+** Audio.c
+**
+** Copyright (C) 1994,95 by Bernardo Innocenti
+**
+** audio.device interface routines.
+**
+**
+** A separate Process gets the AudioMsg messages from its pr_MsgPort
+** and dialogues with audio.device. Once executed, the AudioMsg's are
+** replied to the AudioReply port, and the main program frees them.
+** Each IOAudio request has a special USERDATA field which points back
+** to the original AudioMsg structure. This way it is possible to easly
+** track the status of each command being executed.
+**
+** +------------+ +-------------+ +------------+
+** |Main Process|<-->AudioMsg<-->|Audio Process|<-->IOAudio<-->|audio.device|
+** +------------+ +-------------+ +------------+
+*/
+
+#include <exec/memory.h>
+#include <devices/audio.h>
+#include <dos/dos.h>
+#include <dos/dostags.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* This is the (maximum) size of sample chunks sent to the audio.device */
+
+#define SAMPBUFSIZE 16768
+#define MAXAUDIOSIZE 65534
+
+/* Get/store pointer to original AudioMsg into an extended IOAudio structure */
+
+#define AUDIOUSERDATA(ioa) (*((struct AudioMsg **)(((struct IOAudio *)ioa) + 1)))
+
+
+struct AudioMsg
+{
+ struct Message am_Message;
+ void *am_Data;
+ UBYTE am_Command;
+ UBYTE am_Error;
+ UWORD am_Status;
+ ULONG am_Len;
+ ULONG am_Actual;
+ UWORD am_Per;
+ UWORD am_Vol;
+ BYTE *am_TmpBuf[2];
+ struct IOAudio *am_IOA[2];
+};
+
+
+/* Values for AudioMsg->am_Command */
+#define ACMD_PLAY_SAMPLE 1 /* am_Data points to 8 bit sample data */
+#define ACMD_PLAY_INSTR 1 /* am_Data points to an Instrument structure */
+
+
+
+/* Local functions prototypes */
+
+static void ReplyAudioMsg (struct AudioMsg *am);
+static void _PlaySample (struct AudioMsg *am);
+static UWORD FindFreeChannel (void);
+static ULONG AllocChannels (void);
+static void FreeChannels (void);
+
+
+
+/* Local data */
+
+static struct Process *AudioTask = NULL;
+/* Audio IO requests. Two for each channel (double buffer). */
+static struct IOAudio *AudioReq[4][2] = { 0 };
+static struct MsgPort *AudioReply = NULL;
+
+
+
+/* Global data */
+
+XDEF ULONG AudioSig = 0;
+
+
+
+HOOKCALL static void AudioProcess (void)
+{
+ ULONG audiosig, cmdsig, recsig, signals, err = 0;
+ LONG i, j;
+ struct IOAudio *ioa;
+ struct AudioMsg *am;
+ struct MsgPort *AudioPort; /* Audio reply port */
+
+
+ struct Process *thistask = (struct Process *)FindTask (NULL);
+
+ if (AudioPort = CreateMsgPort())
+ {
+ /* Create IOAudio requests */
+
+ for (i = 0; i < 4 ; i++)
+ for (j = 0; j < 2; j++)
+ if (!(AudioReq[i][j] = (struct IOAudio *)CreateIORequest (AudioPort, sizeof (struct IOAudio) + 4)))
+ err = ERROR_NO_FREE_STORE;
+
+ if (!err)
+ {
+ /* Open audio.device */
+ if (!(err = OpenDevice ("audio.device", 0, (struct IORequest *)AudioReq[0][0], 0)))
+ err = AllocChannels();
+ }
+ }
+ else err = ERROR_NO_FREE_STORE;
+
+
+ /* Wait for startup message */
+
+ WaitPort (&thistask->pr_MsgPort);
+ am = (struct AudioMsg *) GetMsg (&thistask->pr_MsgPort);
+
+ /* Reply startup message */
+ am->am_Error = err;
+ ReplyMsg ((struct Message *)am);
+
+ if (err)
+ {
+ Wait (SIGBREAKF_CTRL_C);
+ goto exit;
+ }
+
+ cmdsig = 1 << thistask->pr_MsgPort.mp_SigBit;
+ audiosig = 1 << AudioPort->mp_SigBit;
+
+ signals = cmdsig | audiosig | SIGBREAKF_CTRL_C;
+
+ do
+ {
+ recsig = Wait (signals);
+
+ if (recsig & audiosig)
+ {
+ /* Collect previously sent requests */
+ while (ioa = (struct IOAudio *) GetMsg (AudioPort))
+ {
+ if (am = AUDIOUSERDATA (ioa))
+ {
+ if (am->am_Actual < am->am_Len)
+ {
+ BYTE *samp;
+ ULONG len;
+
+ if (am->am_TmpBuf[am->am_Status])
+ {
+ len = min (SAMPBUFSIZE, am->am_Len - am->am_Actual);
+ samp = am->am_TmpBuf[am->am_Status];
+ CopyMem (((BYTE *)am->am_Data) + am->am_Actual, samp, len);
+ }
+ else
+ {
+ samp = ((BYTE *)am->am_Data) + am->am_Actual;
+ len = min (MAXAUDIOSIZE, am->am_Len - am->am_Actual);
+ }
+
+ /**/ioa->io_Command = CMD_WRITE;
+ /**/ioa->io_Flags = ADIOF_PERVOL;
+ ioa->ioa_Data = samp;
+ ioa->ioa_Length = len;
+ /**/ioa->ioa_Period = am->am_Per;
+ /**/ioa->ioa_Volume = am->am_Vol;
+ /**/ioa->ioa_Cycles = 1;
+
+ BeginIO ((struct IORequest *)ioa);
+ am->am_Actual += len;
+ am->am_Status ^= 1;
+ }
+ else
+ {
+ am = AUDIOUSERDATA(ioa);
+ AUDIOUSERDATA(am->am_IOA[0]) = NULL;
+ AUDIOUSERDATA(am->am_IOA[1]) = NULL;
+ ReplyAudioMsg (am);
+ }
+ }
+ }
+ }
+
+
+ if (recsig & cmdsig)
+ {
+ /* Get Command and execute it */
+ while (am = (struct AudioMsg *)GetMsg (&thistask->pr_MsgPort))
+ {
+ switch (am->am_Command)
+ {
+ case ACMD_PLAY_SAMPLE:
+ _PlaySample (am);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ while (!(recsig & SIGBREAKF_CTRL_C));
+
+
+
+exit:
+
+ if (AudioPort)
+ {
+ FreeChannels();
+
+ for (i = 3; i >= 0 ; i--)
+ for (j = 1; j >= 0; j--)
+ if (AudioReq[i][j])
+ {
+ if ((j == 0) && (AudioReq[i][j]->io_Device))
+ {
+ if (am = AUDIOUSERDATA(AudioReq[i][j]))
+ ReplyAudioMsg (am);
+
+ if (i == 0)
+ CloseDevice ((struct IORequest *)AudioReq[0][0]);
+ }
+ DeleteIORequest ((struct IORequest *)AudioReq[i][j]); AudioReq[i][j] = NULL;
+ }
+
+ DeleteMsgPort (AudioPort);
+ }
+
+ /* Signal that we are done.
+ * We Forbid() here to avoid being unloaded until exit.
+ */
+ Forbid();
+ Signal ((struct Task *)ThisTask, SIGF_SINGLE);
+}
+
+
+
+static void ReplyAudioMsg (struct AudioMsg *am)
+{
+ FreeVec (am->am_TmpBuf[0]);
+ FreeVec (am->am_TmpBuf[1]);
+ AUDIOUSERDATA(am->am_IOA[0]) = NULL;
+ AUDIOUSERDATA(am->am_IOA[1]) = NULL;
+ ReplyMsg ((struct Message *)am);
+}
+
+
+
+static void _PlaySample (struct AudioMsg *am)
+{
+ BYTE *samp = am->am_Data;
+ ULONG len = am->am_Len;
+ ULONG ch = FindFreeChannel ();
+ BOOL multi = FALSE, transfer = FALSE;
+
+ am->am_TmpBuf[0] = NULL;
+ am->am_TmpBuf[1] = NULL;
+ am->am_Status = 0;
+ am->am_IOA[0] = AudioReq[ch][0];
+ am->am_IOA[1] = AudioReq[ch][1];
+
+ if (!(TypeOfMem (samp) & MEMF_CHIP))
+ transfer = TRUE;
+
+ if (am->am_Len > MAXAUDIOSIZE)
+ {
+ multi = TRUE;
+ if (transfer)
+ len = SAMPBUFSIZE;
+ else
+ len = MAXAUDIOSIZE;
+ }
+
+ if (transfer)
+ if (am->am_TmpBuf[0] = AllocVec (len, MEMF_CHIP))
+ {
+ CopyMem (samp, am->am_TmpBuf[0], len);
+ samp = am->am_TmpBuf[0];
+ }
+ else return;
+
+ am->am_IOA[0]->io_Command = CMD_WRITE;
+ am->am_IOA[0]->io_Flags = ADIOF_PERVOL;
+ am->am_IOA[0]->ioa_Data = samp;
+ am->am_IOA[0]->ioa_Length = len;
+ am->am_IOA[0]->ioa_Period = am->am_Per;
+ am->am_IOA[0]->ioa_Volume = am->am_Vol;
+ am->am_IOA[0]->ioa_Cycles = 1;
+ AUDIOUSERDATA(am->am_IOA[0]) = am;
+
+ BeginIO ((struct IORequest *)am->am_IOA[0]);
+ am->am_Actual = len;
+
+ if (multi)
+ {
+ samp += len;
+
+ if (transfer)
+ {
+ len = min(SAMPBUFSIZE, am->am_Len - SAMPBUFSIZE);
+
+ if (am->am_TmpBuf[1] = AllocVec (len, MEMF_CHIP))
+ {
+ CopyMem (samp, am->am_TmpBuf[1], len);
+ samp = am->am_TmpBuf[1];
+ }
+ else return;
+ }
+ else len = min (MAXAUDIOSIZE, am->am_Len - MAXAUDIOSIZE);
+
+ am->am_IOA[1]->io_Command = CMD_WRITE;
+ am->am_IOA[1]->io_Flags = ADIOF_PERVOL;
+ am->am_IOA[1]->ioa_Data = samp;
+ am->am_IOA[1]->ioa_Length = len;
+ am->am_IOA[1]->ioa_Period = am->am_Per;
+ am->am_IOA[1]->ioa_Volume = am->am_Vol;
+ am->am_IOA[1]->ioa_Cycles = 1;
+ AUDIOUSERDATA(am->am_IOA[1]) = am;
+
+ BeginIO ((struct IORequest *)am->am_IOA[1]);
+ am->am_Actual += len;
+ }
+}
+
+
+
+GLOBALCALL void HandleAudio (void)
+{
+ struct Message *msg;
+
+ while (msg = GetMsg (AudioReply))
+ FreeMem (msg, sizeof (struct AudioMsg));
+}
+
+
+
+GLOBALCALL void PlaySample (BYTE *samp, ULONG len, UWORD vol, UWORD per)
+{
+ struct AudioMsg *am;
+
+ if (!samp) return;
+
+ if (!AudioTask)
+ if (SetupAudio()) return;
+
+ if (!(am = AllocMem (sizeof (struct AudioMsg), MEMF_PUBLIC)))
+ return;
+
+ am->am_Message.mn_ReplyPort = AudioReply;
+ am->am_Command = ACMD_PLAY_SAMPLE;
+
+ am->am_Data = samp;
+ am->am_Len = len;
+ am->am_Vol = vol;
+ am->am_Per = per;
+
+ PutMsg (&AudioTask->pr_MsgPort, (struct Message *)am);
+}
+
+
+
+static UWORD FindFreeChannel (void)
+{
+ UWORD ch;
+
+ for (ch = 0; ch < 4 ; ch++)
+ if (CheckIO ((struct IORequest *)AudioReq[ch][0]) &&
+ CheckIO ((struct IORequest *)AudioReq[ch][1]))
+ return ch;
+
+ {
+ struct AudioMsg *am;
+
+ AbortIO ((struct IORequest *)AudioReq[0][0]);
+ AbortIO ((struct IORequest *)AudioReq[0][1]);
+ WaitIO ((struct IORequest *)AudioReq[0][0]);
+ WaitIO ((struct IORequest *)AudioReq[0][1]);
+
+ if (am = AUDIOUSERDATA(AudioReq[0][0]))
+ {
+ ReplyAudioMsg (am);
+ AUDIOUSERDATA(AudioReq[0][0]) = NULL;
+ AUDIOUSERDATA(AudioReq[0][1]) = NULL;
+ }
+ }
+
+ return 0;
+}
+
+
+static ULONG AllocChannels (void)
+{
+ struct IOAudio *ioa;
+ ULONG i;
+
+ /* Allocate channels */
+
+ for (i = 0 ; i < 4; i++)
+ {
+ static UBYTE channels[] = {1, 2, 4, 8};
+
+ ioa = AudioReq[i][0];
+
+ ioa->ioa_Request.ln_Pri = 1;
+ ioa->io_Device = AudioReq[0][0]->io_Device;
+ ioa->io_Command = ADCMD_ALLOCATE;
+ ioa->io_Flags = ADIOF_NOWAIT | IOF_QUICK;
+ ioa->ioa_AllocKey = AudioReq[0][0]->ioa_AllocKey;
+ ioa->ioa_Data = channels;
+ ioa->ioa_Length = 4;
+
+ /* Using DoIO() here is not possible because the
+ * io_Flags field would be cleared.
+ */
+ BeginIO ((struct IORequest *)ioa);
+ WaitIO ((struct IORequest *)ioa);
+
+ /* Initailize other request */
+ CopyMem (ioa, AudioReq[i][1], sizeof (struct IOAudio));
+
+ if (ioa->io_Error)
+ return ioa->io_Error;
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static void FreeChannels (void)
+{
+ LONG i;
+
+ for (i = 3; i >= 0; i--)
+ {
+ if (AudioReq[i][0])
+ {
+ AbortIO ((struct IORequest *)AudioReq[i][0]);
+ WaitIO ((struct IORequest *)AudioReq[i][0]);
+
+ if (AudioReq[i][1])
+ {
+ AbortIO ((struct IORequest *)AudioReq[i][1]);
+ WaitIO ((struct IORequest *)AudioReq[i][1]);
+ }
+
+ AudioReq[i][0]->io_Command = ADCMD_FREE;
+ DoIO ((struct IORequest *)AudioReq[i][0]);
+ }
+ }
+}
+
+
+
+GLOBALCALL ULONG SetupAudio (void)
+{
+ struct AudioMsg audiomsg;
+
+ if (!(AudioReply = CreateMsgPort ()))
+ return ERROR_NO_FREE_STORE;
+
+ AudioSig = 1 << AudioReply->mp_SigBit;
+ Signals |= AudioSig;
+
+ /* Create Audio Process */
+ if (!(AudioTask = CreateNewProcTags (
+ NP_Entry, AudioProcess,
+ NP_Name, "XModule Audio Process",
+ NP_WindowPtr, ThisTask->pr_WindowPtr,
+ NP_Priority, 15,
+ NP_CopyVars, FALSE,
+ // NP_Input, NULL,
+ // NP_Output, NULL,
+ // NP_Error, NULL,
+ TAG_DONE)))
+ {
+ CleanupAudio();
+ return ERROR_NO_FREE_STORE;
+ }
+
+
+ /* Send Startup Message */
+
+ audiomsg.am_Message.mn_ReplyPort = AudioReply;
+ PutMsg (&(AudioTask->pr_MsgPort), (struct Message *)&audiomsg);
+ WaitPort (AudioReply);
+ GetMsg (AudioReply);
+
+ if (audiomsg.am_Error)
+ {
+ CleanupAudio();
+ return (audiomsg.am_Error);
+ }
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL void CleanupAudio (void)
+{
+ if (AudioTask)
+ {
+ /* Tell audio task to give up */
+ SetSignal (0, SIGF_SINGLE);
+ Signal ((struct Task *)AudioTask, SIGBREAKF_CTRL_C);
+
+ /* Wait until the audio task quits */
+ Wait (SIGF_SINGLE);
+ AudioTask = NULL;
+ }
+
+ if (AudioReply)
+ {
+ struct Message *msg;
+
+ while (msg = GetMsg (AudioReply))
+ FreeMem (msg, sizeof (struct AudioMsg));
+
+ DeleteMsgPort (AudioReply); AudioReply = NULL;
+ Signals &= ~AudioSig; AudioSig = 0;
+ }
+}
--- /dev/null
+@DATABASE "songclass"
+@MASTER "Work:SC/XM/Autodocs/songclass.doc"
+@REMARK This file was created by ADtoHT 2.1 on 08-Apr-97 12:17:15
+@REMARK Do not edit
+@REMARK ADtoHT is © 1993-1995 Christian Stieber
+
+@NODE MAIN "songclass.doc"
+
+ @{b}songclass@{ub}
+
+@{"background" LINK "background"}
+
+
+@ENDNODE
+@NODE "background" "songclass/background (information)"
+
+
+ NAME
+ songclass -- XModule 'boopsi'-oriented song implementation.
+
+ DESCRIPTION
+ The song class is an object oriented way to handle a song. The song
+ class handles all data storing mechanisms for you and adds a layer
+ of abstraction between the song internal data structures and the
+ application. The advantage is that the internal structures can be
+ changed while keeping compatibility with existing software.
+
+ Another great advantage of being a 'boopsi' class is that the song
+ can notify other boopsi objects whenever its attributes change.
+ This simplifies the task of keeping the user interface updated each
+ time the user (or an ARexx macro, or whatever) changes something.
+
+ For speed reasons, the song class does also allow 'white box'
+ istance access. This means that you can also directly access
+ the internal data structures of the song, without using standard
+ boopsi methods. You are ONLY allowed to READ public fields, but not
+ to write any of them. The main reason to forbid direct writing is
+ that the song class must send notifications to its targets, but it
+ does also allow the song implementation to change in future version
+ without breaking existing applications.
+
+ When you create a new istance of the song class, the object handle
+ you get is actually a SongInfo structure. This is only possible
+ because the song class is a subclass of the rootclass, whose istance
+ is placed at a negative offset in the object handle.
+ Future song class implementations could require to be subclasses
+ of other classes, such as the gadget class or even the datatypes
+ class. This problem will be probably got around by keeping the
+ root class as the real superclass of the song and creating an
+ istance of the other superclass which will be passed all the
+ methods which are not recognized by the song its-self. Call this
+ boopsi polymorphism, if you like to :-)
+
+ QUOTATION
+ Don't be a tuna head.
+
+@ENDNODE
--- /dev/null
+TABLE OF CONTENTS
+
+songclass/--background--
+\fsongclass/--background-- songclass/--background--
+
+ NAME
+ songclass -- XModule 'boopsi'-oriented song implementation.
+
+ DESCRIPTION
+ The song class is an object oriented way to handle a song. The song
+ class handles all data storing mechanisms for you and adds a layer
+ of abstraction between the song internal data structures and the
+ application. The advantage is that the internal structures can be
+ changed while keeping compatibility with existing software.
+
+ Another great advantage of being a 'boopsi' class is that the song
+ can notify other boopsi objects whenever its attributes change.
+ This simplifies the task of keeping the user interface updated each
+ time the user (or an ARexx macro, or whatever) changes something.
+
+ For speed reasons, the song class does also allow 'white box'
+ istance access. This means that you can also directly access
+ the internal data structures of the song, without using standard
+ boopsi methods. You are ONLY allowed to READ public fields, but not
+ to write any of them. The main reason to forbid direct writing is
+ that the song class must send notifications to its targets, but it
+ does also allow the song implementation to change in future version
+ without breaking existing applications.
+
+ When you create a new istance of the song class, the object handle
+ you get is actually a SongInfo structure. This is only possible
+ because the song class is a subclass of the rootclass, whose istance
+ is placed at a negative offset in the object handle.
+ Future song class implementations could require to be subclasses
+ of other classes, such as the gadget class or even the datatypes
+ class. This problem will be probably got around by keeping the
+ root class as the real superclass of the song and creating an
+ istance of the other superclass which will be passed all the
+ methods which are not recognized by the song its-self. Call this
+ boopsi polymorphism, if you like to :-)
+
+ QUOTATION
+ Don't be a tuna head.
+
+\f
\ No newline at end of file
--- /dev/null
+@DATABASE "xmodule"
+@MASTER "Work:SC/XM/Autodocs/xmodule.doc"
+@REMARK This file was created by ADtoHT 2.1 on 08-Apr-97 12:17:15
+@REMARK Do not edit
+@REMARK ADtoHT is © 1993-1995 Christian Stieber
+
+@NODE MAIN "xmodule.doc"
+
+ @{b}xmodule@{ub}
+
+@{"background" LINK "background"}
+
+@{"xmActivateSong()" LINK "xmActivateSong"} @{"xmAddHook()" LINK "xmAddHook"} @{"xmAddHookA()" LINK "xmAddHook"}
+@{"xmAddInstrument()" LINK "xmAddInstrumentA"} @{"xmAddInstrumentA()" LINK "xmAddInstrumentA"} @{"xmAddPattern()" LINK "xmAddPatternA"}
+@{"xmAddPatternA()" LINK "xmAddPatternA"} @{"xmAddSong()" LINK "xmAddSongA"} @{"xmAddSongA()" LINK "xmAddSongA"}
+@{"xmCreateSong()" LINK "xmCreateSong"} @{"xmCreateSongA()" LINK "xmCreateSong"} @{"xmDeleteSong()" LINK "xmDeleteSong"}
+@{"xmDisplayMessage()" LINK "xmDisplayMessage"} @{"xmDisplayMessageA()" LINK "xmDisplayMessage"} @{"xmDisplayProgress()" LINK "xmDisplayProgress"}
+@{"xmIdentifyModule()" LINK "xmIdentifyModule"} @{"xmLoadModule()" LINK "xmLoadModuleA"} @{"xmLoadModuleA()" LINK "xmLoadModuleA"}
+@{"xmLockActiveSong()" LINK "xmLockActiveSong"} @{"xmProcessSong()" LINK "xmProcessSongA"} @{"xmProcessSongA()" LINK "xmProcessSongA"}
+@{"xmRemHook()" LINK "xmRemHook"} @{"xmRemHookA()" LINK "xmRemHook"} @{"xmRemInstrument()" LINK "xmRemInstrument"}
+@{"xmRemPattern()" LINK "xmRemPattern"} @{"xmRemSong()" LINK "xmRemSong"} @{"xmSaveModule()" LINK "xmSaveModuleA"}
+@{"xmSaveModuleA()" LINK "xmSaveModuleA"} @{"xmSetInstrument()" LINK "xmSetInstrumentA"} @{"xmSetInstrumentA()" LINK "xmSetInstrumentA"}
+@{"xmSetPattern()" LINK "xmSetPatternA"} @{"xmSetPatternA()" LINK "xmSetPatternA"} @{"xmSetSongLen()" LINK "xmSetSongLen"}
+
+@ENDNODE
+@NODE "background" "xmodule/background (information)"
+
+
+ INTRODUCTION
+ The xmodule.library is an API that provides access to the XModule
+ song management engine, as well as other general pourpose
+ services. Hooks and external applications can use this library
+ to create, load, save and modify songs.
+
+ The xmodule.library does not exist as a stand alone shared libray.
+ Instead, it's code is contained inside the main XModule executable
+ (altrough it's logically independant from the rest of the XModule
+ code).
+
+ XModule adds the xmodule.library to the exec libray list at
+ startup time, unless it finds one already in the list (this might
+ happen when multiple copies of XModule are running at the same
+ time).
+
+ PUBLIC SONGS
+ The xmodule.library maintains a list of public songs which can be
+ used by all applications which opened the library. Each song in
+ the list is protected from multiple task access by a
+ SignalSemaphore, and whole list is also protected by another
+ semaphore. The locking mechanism is very simple: before you can
+ access a song you must obtain its semaphore, and you must release
+ it as soon as you are done with it. If you are locking a song to
+ just to read some information (i.e.: you don't want to modify
+ anything), you should obtain a shared lock instead of an exclusive
+ one. The list must be locked whenever you want to add or remove
+ a song, or when you scan it in any way.
+
+ Since public songs could be modified or even deleted by other
+ tasks, do not make your songs public unless your code is smart
+ enough to handle all possible situations.
+
+
+ HOOKS
+ Actually, most modular programs call them `modules', but it would
+ have created a lot of confusion with a program like XModule :-).
+
+ Hooks are programs that add some kind of functionality
+ to XModule. External hook files are standard shared libraries
+ which will open the xmodule.library when they are loaded and
+ call @{"xmAddHookA()" LINK "xmodule/xmAddHook"} to add themselves to a list of hooks maintained
+ by the library.
+
+ Currently, XModule supports two kinds of hooks: loaders and
+ savers. Loader hooks can also provide a function which
+ identifies a particular module format.
+
+ An external hook libray may also contain several hooks.
+ Putting a loader and a saver for one particolar format together
+ in one libray is generally a good idea, while making a hook
+ with hundereds of loaders and savers isn't a good move because
+ it makes the whole concept of external hooks quite useless.
+ Grouping different versions or variants of one format together
+ in one external hook is acceptable.
+
+ SEE ALSO
+ songclass/--background--, exec/ObtainSemaphore()
+
+@ENDNODE
+@NODE "xmActivateSong" "xmodule/xmActivateSong()"
+@{b}
+
+ NAME@{ub}
+ xmActivateSong -- Makes a song the active one@{b}
+
+ SYNOPSIS@{ub}
+ success = xmActivateSong(songInfo);
+ D0 A0
+
+ ULONG xmActivateSong(@{"struct SongInfo" LINK "songclass.h/File" 284} *);@{b}
+
+ FUNCTION@{ub}
+ Makes the passed song the currently active one. It's pointer
+ will be stored in the public song list and most actions
+ will happen on this song by default.@{b}
+
+ INPUTS@{ub}
+ songInfo - song to be activated. If NULL, this function will
+ take no action.@{b}
+
+ RESULT@{ub}
+ success - Will be 0 for failure, in which case the song will
+ not be removed from the song list. Currently,
+ xmActivateSong() will never fail.@{b}
+
+ NOTE@{ub}
+ In order to activate a song, you must own a shared lock
+ on it. Please do not hog this lock for a long time when
+ xmActivateSong() returns, because most internal routines
+ try to lock the current song before taking any action.@{b}
+
+ SEE ALSO@{ub}
+
+@ENDNODE
+@NODE "xmAddHook" "xmodule/xmAddHook()"
+@{b}
+
+ NAME@{ub}
+ xmAddHookA -- Creates a new XModule Hook
+ xmAddHook -- Varargs stub for xmAddHookA@{b}
+
+ SYNOPSIS@{ub}
+ hook = xmAddHookA(tagList)
+ D0 A0
+
+ @{"struct XMHook" LINK "xmodule.h/File" 92} *xmAddHookA(struct TagItem *);
+
+
+ hook = xmAddHook(Tag1,...)
+
+ @{"struct XMHook" LINK "xmodule.h/File" 92} *xmAddHook(ULONG,...);@{b}
+
+ FUNCTION@{ub}
+ Creates a new XMHook structure and fills it in with data supplied
+ with the TagList. Adds the newly created Hook to the appropriate
+ list.@{b}
+
+ INPUTS@{ub}
+ tagList - pointer to a tag list specifying how to initialize the
+ XMHook structure.@{b}
+
+ TAGS@{ub}
+ @{"XMHOOK_Type" LINK "xmodule.h/File" 235} - (ULONG) Defines the pourpose of this hook. Possible
+ values are currently @{"NT_XMLOADER" LINK "xmodule.h/File" 138} and @{"NT_XMSAVER" LINK "xmodule.h/File" 139}. (This
+ tag is REQUIRED).
+
+ @{"XMHOOK_Name" LINK "xmodule.h/File" 236} - (STRPTR) ti_Data contains a short name for the
+ hook (e.g: "SoundTracker"). This name will appear in the
+ Savers list if this hook is a saver and will be passed as an
+ argument for some ARexx commands, so please use a single
+ word name. (This tag is REQUIRED).
+
+ @{"XMHOOK_Priority" LINK "xmodule.h/File" 237} - (BYTE) Priority to give to this hook. Hooks
+ with higher priorities will be used before lower priority
+ ones and will come first in lists shown to the user. Valid
+ range is -128..+127, but please restrict to a -20..+20
+ interval for normal cases. (Defaults to 0).
+
+ @{"XMHOOK_Descr" LINK "xmodule.h/File" 238} - (STRPTR) Verbose description of the hook
+ (without newlines). (Defaults to NULL).
+
+ @{"XMHOOK_Author" LINK "xmodule.h/File" 239} - (STRPTR) Author's name. Please, just put
+ your full name here; no greetings, copyright notices,
+ etc. (Defaults to NULL).
+
+ @{"XMHOOK_ID" LINK "xmodule.h/File" 240} - (ULONG) This is a unique, IFF-style identifier for
+ the format. If the format is an IFF format, it must be the
+ same of the FORM ID. (Defaults to 0, this tag is required
+ for IFF loaders and savers).
+
+ @{"XMHOOK_Flags" LINK "xmodule.h/File" 241} - (ULONG) Sets miscellaneous flags for this hook.
+ See xmodule.h for possible flags.
+
+ @{"XMHOOK_LibraryBase" LINK "xmodule.h/File" 243} - (struct Library *) Pointer to the library
+ base for external hooks. This pointer will be used to open
+ the library when the hook is created and to close it when
+ the hook is deleted. If you do not pass this tag, you must
+ find some other way to keep your code in memory until one
+ or more of your hooks are active. XModule will close your
+ library just after calling the SetupXMHook() function.
+ (Defaults to NULL).
+
+ @{"XMHOOK_UserData" LINK "xmodule.h/File" 242} - (APTR) ti_Data will be stored in the
+ xmh_UserData field of the XMHook structure. This field can
+ come andy to store private data. (Defaults to NULL).
+
+ XMHOOK_RemoveHookFunc - (APTR) Pointer to a function which will be
+ called upon removing the hook. This function can be used to
+ free any private resources allocated by the hook when it was
+ created. The template for the function is:
+
+ void RemoveHookFunc (@{"struct XMHook" LINK "xmodule.h/File" 92} *hook);
+ A0
+
+ @{"XMHOOK_LoadModFunc" LINK "xmodule.h/File" 245} - (APTR) Pointer to the hook function which
+ loads a module. The template for the function is:
+
+ LONG LoadModFunc (BPTR fileHandle, @{"struct SongInfo" LINK "songclass.h/File" 284} *song,
+ D0 A0
+ @{"struct XMHook" LINK "xmodule.h/File" 92} *loader);
+ A1
+
+ `fileHandle' is an open file to load the module from. The
+ caller will take care to close it for you. The loader should
+ return RETURN_OK for success, or any other AmigaDOS error
+ code to mean failure. In particular, RETURN_WARN indicates
+ that something went wrong, but the song is ok. The special
+ error code @{"ERROR_IOERR" LINK "xmodule.h/File" 194} can be used when some dos.library
+ call (e.g.: Read()) failed. In the latter case, the
+ AmigaDOS IoErr value should be set to explain the specific
+ cause of the problem.
+ (This tag is required by all @{"NT_XMLOADER" LINK "xmodule.h/File" 138} type hooks).
+
+ @{"XMHOOK_SaveModFunc" LINK "xmodule.h/File" 246} - (APTR) Pointer to the hook function which
+ saves a module. The template for the function is:
+
+ LONG SaveModFunc (BPTR fileHandle, @{"struct SongInfo" LINK "songclass.h/File" 284} *song,
+ D0 A0
+ @{"struct XMHook" LINK "xmodule.h/File" 92} *saver);
+ A1
+
+ fileHandle is an open file to save the module to. The caller
+ will take care to close it for you. The saver should return
+ RETURN_OK for success, or any other AmigaDOS error code to
+ mean failure. In particular, RETURN_WARN indicates that
+ something went wrong, but the song is ok. The special
+ error code @{"ERROR_IOERR" LINK "xmodule.h/File" 194} can be used when some dos.library
+ call (e.g.: Read()) failed. In the latter case, the
+ AmigaDOS IoErr value should be set to explain the specific
+ cause of the problem.
+ (This tag is required by all @{"NT_XMSAVER" LINK "xmodule.h/File" 139} type hooks).
+
+
+ @{"XMHOOK_IdentifyModFunc" LINK "xmodule.h/File" 247} - (APTR) Pointer to the hook function
+ which identifies a module format. The template for the
+ function is:
+
+ @{"struct XMHook" LINK "xmodule.h/File" 92} *IdentifyModFunc (BPTR fileHandle,
+ D0
+ @{"struct XMHook" LINK "xmodule.h/File" 92} *hook,struct TagItem *tagList);
+ A0 A1
+
+ fileHandle is an open file to try the identification on. The
+ caller will take care to close it. NOTE: Do not make assumptions
+ on the initial file position (e.g: seek yourself to position 0
+ if you need to). This funtion should return a pointer to a valid
+ loader (usually the one passed in) if the format is recognized,
+ NULL otherwhise. (This tag is required by all @{"NT_XMLOADER" LINK "xmodule.h/File" 138} type
+ hooks).@{b}
+
+ RESULT@{ub}
+ hook - Pointer to the newly allocated XMHook structure, or
+ NULL for failure.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmRemHook()" LINK "xmRemHook"}, @{"xmIdentifyModule()" LINK "xmIdentifyModule"}, xmLoadSong(), xmSaveSong()
+
+@ENDNODE
+@NODE "xmAddInstrumentA" "xmodule/xmAddInstrumentA()"
+@{b}
+
+ NAME@{ub}
+ xmAddInstrumentA -- Adds a instrument to a song
+ xmAddInstrument -- Varargs stub for xmAddInstrumentA@{b}
+
+ SYNOPSIS@{ub}
+ instrument = xmAddInstrumentA(si, instrNum, tagList)
+ D0 A0 D0 A1
+
+ @{"struct Instrument" LINK "songclass.h/File" 183} *xmAddInstrumentA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,LONG,
+ struct TagItem *);
+
+
+ instrument = xmAddInstrument(si,instrNum,tag1,...)
+
+ @{"struct Instrument" LINK "songclass.h/File" 183} *xmAddInstrument(@{"struct SongInfo" LINK "songclass.h/File" 284} *,LONG,LONG,...);@{b}
+
+ FUNCTION@{ub}
+ Adds an instrument to a song by calling the @{"SNGM_ADDINSTRUMENT" LINK "songclass.h/File" 32} method .@{b}
+
+ INPUTS@{ub}
+ si - pointer to the song to which the instrument should be added.
+ tagList - optional TagList. See @{"SNGM_ADDINSTRUMENT" LINK "songclass.h/File" 32} for possible tags .@{b}
+
+ RESULT@{ub}
+ Pointer to the new instrument or NULL for failure.@{b}
+
+ NOTE@{ub}
+ In order to add instruments, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmRemInstrument()" LINK "xmRemInstrument"}, @{"songclass" LINK "songclass/MAIN"}/SNGM_REMINSTRUMENT
+
+@ENDNODE
+@NODE "xmAddPatternA" "xmodule/xmAddPatternA()"
+@{b}
+
+ NAME@{ub}
+ xmAddPatternA -- Adds a pattern to a song
+ xmAddPattern -- Varargs stub for xmAddPatternA@{b}
+
+ SYNOPSIS@{ub}
+ pattern = xmAddPatternA(si, tagList)
+ D0 A0 A1
+
+ @{"struct Pattern" LINK "songclass.h/File" 229} *xmAddPatternA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,struct TagItem *);
+
+
+ pattern = xmAddPattern (si,tag1,...)
+
+ @{"struct Pattern" LINK "songclass.h/File" 229} *xmAddPattern(@{"struct SongInfo" LINK "songclass.h/File" 284} *,LONG Tag1,...);@{b}
+
+ FUNCTION@{ub}
+ Adds a pattern to a song by calling the @{"SNGM_ADDPATTERN" LINK "songclass.h/File" 29} method.@{b}
+
+ INPUTS@{ub}
+ si - pointer to the song to which the pattern should be added.
+ tagList - optional TagList. See @{"SNGM_ADDPATTERN" LINK "songclass.h/File" 29} for possible tags.@{b}
+
+ RESULT@{ub}
+ Pointer to the new pattern or NULL for failure.@{b}
+
+ NOTE@{ub}
+ In order to add patterns, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmRemPattern()" LINK "xmRemPattern"}, @{"songclass" LINK "songclass/MAIN"}/SNGM_ADDPATTERN
+
+@ENDNODE
+@NODE "xmAddSongA" "xmodule/xmAddSongA()"
+@{b}
+
+ NAME@{ub}
+ xmAddSongA -- Add a song to the song list
+ xmAddSong -- Varargs stub for xmAddSongA@{b}
+
+ SYNOPSIS@{ub}
+ success = xmAddSongA(songInfo,position,tagList);
+ D0 A0 A1 A2
+
+ ULONG xmAddSongA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,@{"struct SongInfo" LINK "songclass.h/File" 284} *,
+ struct TagItem *);
+
+
+ success = xmAddSong(songInfo,position,tag1,...);
+
+ ULONG xmAddSong(@{"struct SongInfo" LINK "songclass.h/File" 284} *,@{"struct SongInfo" LINK "songclass.h/File" 284} *,
+ LONG,...);@{b}
+
+ FUNCTION@{ub}
+ Adds a song to the public song list. Trying to add a song
+ twice is harmless.@{b}
+
+ INPUTS@{ub}
+ songInfo - song to be added. Passing a NULL pointer is safe.
+ position - Position to add the song in. Passing NULL adds the
+ song in the head of the list, while ~0 adds it in the tail.
+ passing a pointer to a SongInfo already in the list inserts
+ the first song _after_ the other one.
+ tagList - pointer to a TagList for more arguments.@{b}
+
+ TAGS@{ub}
+ @{"XMSNG_Active" LINK "xmodule.h/File" 209} - (BOOL) Makes this song the active one. The
+ default is to just add the song without activating it.@{b}
+
+ RESULT@{ub}
+ success - Will be 0 for failure, in which case the song will
+ not be added to the songs list. Currently, xmAddSongA()
+ will never fail.@{b}
+
+ NOTE@{ub}
+ The song list is protected from multiple tasks access by a
+ SignalSemaphore in XModuleBase. This call will wait if the
+ song list has been locked by another task. Beware of deadlock
+ conditions!@{b}
+
+ SEE ALSO@{ub}
+ @{"xmRemSong()" LINK "xmRemSong"}
+
+@ENDNODE
+@NODE "xmCreateSong" "xmodule/xmCreateSong()"
+@{b}
+
+ NAME@{ub}
+ xmCreateSongA -- Create and initialize a new SongInfo structure
+ xmCreateSong -- Varargs stub for xmCreateSongA@{b}
+
+ SYNOPSIS@{ub}
+ songInfo = xmCreateSongA(tagList);
+ D0 A0
+
+ @{"struct SongInfo" LINK "songclass.h/File" 284} *xmCreateSongA(struct TagItem *);
+
+
+ songInfo = xmCreateSong(Tag1,...);
+
+ @{"struct SongInfo" LINK "songclass.h/File" 284} *xmCreateSong(ULONG,...);@{b}
+
+ FUNCTION@{ub}
+ Allocates and initializes a new SongInfo structure. The song
+ can then be added to the song list via @{"xmAddSongA()" LINK "xmAddSongA"}, in which
+ case, it is no longer required to free it with @{"xmDeleteSong()" LINK "xmDeleteSong"}.@{b}
+
+ INPUTS@{ub}
+ tagList - pointer to an optional tag list specifying how to
+ initialize the SongInfo structure.@{b}
+
+ TAGS@{ub}
+ @{"SNGA_DefaultTracks" LINK "songclass.h/File" 121} - Sets the default number of pattern tracks for
+ the song. Defaults to 4.
+
+ @{"SNGA_DefaultPattLen" LINK "songclass.h/File" 122} - Sets the default number of pattern lines for
+ the song. Defaults to 64.
+
+ @{"SNGA_ReadyToUse" LINK "songclass.h/File" 127} - Adds one pattern and one position to the song.
+ Defaults to FALSE.
+
+ @{"XMSNG_AddToList" LINK "xmodule.h/File" 205} - (@{"struct SongInfo" LINK "songclass.h/File" 284} *) Add the song to the song list.
+ When a song is being added to the list, a shared lock is
+ obtained on it to prevent other tasks from modifying (or even
+ remove) the song before you can do your job on it. IT IS YOUR
+ DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
+ Passing NULL adds the song on the head of the list, while -1
+ (~0) will add it to the end of the SongList. Passing in a
+ pointer to another song which is already in the list, will
+ add the new song _after_ the latter.
+ (Default is not adding the song).
+
+ @{"XMSNG_Active" LINK "xmodule.h/File" 209} - (BOOL) Makes this song the active one. This tag is
+ considered only if the @{"XMSNG_AddToList" LINK "xmodule.h/File" 205} tag is also passed.@{b}
+
+ RESULT@{ub}
+ songInfo - pointer to the newly allocated SongInfo structure, or
+ NULL for failure.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmDeleteSong()" LINK "xmDeleteSong"}, @{"xmAddSongA()" LINK "xmAddSongA"}
+
+@ENDNODE
+@NODE "xmDeleteSong" "xmodule/xmDeleteSong()"
+@{b}
+
+ NAME@{ub}
+ xmDeleteSong -- Deletes a song@{b}
+
+ SYNOPSIS@{ub}
+ xmDeleteSong(songInfo)
+ A0
+
+ void xmDeleteSong(@{"struct SongInfo" LINK "songclass.h/File" 284} *);@{b}
+
+ FUNCTION@{ub}
+ Deletes a song and all the items attached to it (patterns,
+ instruments, etc.). This call will also remove the song from
+ the song list if it had been added to it.@{b}
+
+ INPUTS@{ub}
+ songInfo - pointer to the SongInfo structure to be deleted.
+ Passing a NULL pointer is harmless.@{b}
+
+ NOTE@{ub}
+ In order to delete a song which has been added to the public
+ song list, you must first obtain an exclusive lock on it to
+ prevent other tasks from walking on it while you are freeing
+ it's data structures. The semaphore does NOT need to be
+ relinquished, because the SongInfo structure won't exist any
+ more when this call returns.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmCreateSong()" LINK "xmCreateSong"}, @{"xmRemSong()" LINK "xmRemSong"}
+
+@ENDNODE
+@NODE "xmDisplayMessage" "xmodule/xmDisplayMessage()"
+@{b}
+
+ NAME@{ub}
+ xmDisplayMessageA -- Displays a message to the user
+ xmDisplayMessage -- Varargs stub for xmDisplayMessageA()@{b}
+
+ SYNOPSIS@{ub}
+ xmDisplayMessageA(level, message, args)
+ DO A0 A1
+
+ void xmDisplayMessageA(ULONG,APTR,LONG *);
+
+
+ xmDisplayMessage(level, message, ...)
+
+ void xmDisplayMessage(ULONG,APTR,...);@{b}
+
+ FUNCTION@{ub}
+ Outputs a string in the XModule log window or in the 'action' field
+ of the progress indicator. The string is printf-formatted before
+ being output.@{b}
+
+ INPUTS@{ub}
+ level - a number from 0 to 7 which indicates the importance of the
+ message. 7 is used for very annoying messages (eg: debug output) ,
+ 0 for very important things (eg: disaster).
+ If you set the @{"XMDMF_USECATALOG" LINK "xmodule.h/File" 187} bit in the level parameter, you
+ can pass a catalog string number instead of a pointer to a string .
+ Specifying @{"XMDMF_ACTION" LINK "xmodule.h/File" 185}, the message will be put in the 'action'
+ field of the progress indicator.
+ If the flag @{"XMDMF_DOSFAULT" LINK "xmodule.h/File" 184} is specified, a Fault() output is
+ formatted using the message as an header. In this case, the
+ level parameter takes another meaing: The lower word can contain
+ an AmigaDOS error code. If it is 0, IoErr() will be used to get
+ the error code.
+ message - pointer to a string or catalog message number,
+ when the @{"XMDMF_USECATALOG" LINK "xmodule.h/File" 187} flag is set.
+ args - arguments for formatting.@{b}
+
+ EXAMPLES@{ub}
+ xmDisplayMessage (@{"XMDMF_ALERT" LINK "xmodule.h/File" 173},
+ "The application `%s' fucked up Windoze95 because %s.",
+ "(unknown name)", "an error occurred");
+
+ xmDisplayMessage (XMDMF_USE_CATALOG | @{"XMDMF_COMMENT" LINK "xmodule.h/File" 179},
+ MSG_LIFE_UNIVERSE_AND_ALLTHAT, 42, "Fortytwo", "For tea too");
+
+ xmDisplayMessageA (@{"XMDMF_ACTION" LINK "xmodule.h/File" 185} | @{"XMDMF_USECATALOG" LINK "xmodule.h/File" 187},
+ MSG_READING_COMICS, NULL);
+
+ xmDisplayMessage (@{"XMDMF_DOSFAULT" LINK "xmodule.h/File" 184} | @{"XMDMF_USECATALOG" LINK "xmodule.h/File" 187},
+ MSG_CANT_LOAD_MODULE, ModuleName);@{b}
+
+ SEE ALSO@{ub}
+ @{"xmDisplayProgress()" LINK "xmDisplayProgress"}
+
+@ENDNODE
+@NODE "xmDisplayProgress" "xmodule/xmDisplayProgress()"
+@{b}
+
+ NAME@{ub}
+ xmDisplayProgress -- Uptdates the progress bar indicator@{b}
+
+ SYNOPSIS@{ub}
+ abort = xmDisplayProgress(done, total)
+ D0 D1
+
+ LONG @{"xmDisplayMessageA" LINK "xmDisplayMessage"}(ULONG,ULONG);@{b}
+
+ FUNCTION@{ub}
+ Updates the position of the fuel gauge in the progress window to
+ show the progress of an operation. Additionally, it checks for
+ user abort.@{b}
+
+ INPUTS@{ub}
+ done - a number which indicates how much of the work has
+ been already completed
+ total - Total number of operations to do.@{b}
+
+ RESULT@{ub}
+ 0 for success, ERROR_BREAK if user abort was detected. You should
+ always check this return code to abort what you were doing.
+
+@ENDNODE
+@NODE "xmIdentifyModule" "xmodule/xmIdentifyModule()"
+@{b}
+
+ NAME@{ub}
+ xmIdentifyModule -- Returns the type of a module@{b}
+
+ SYNOPSIS@{ub}
+ loader = xmIdentifyModule(fh, tagList)
+ D0 D0 A0
+
+ @{"struct XMHook" LINK "xmodule.h/File" 92} *xmIdentifyModule(BPTR,struct TagItem *);@{b}
+
+ FUNCTION@{ub}
+ Finds out a loader which is able to read the given module.@{b}
+
+ INPUTS@{ub}
+ fh - AmigaDOS FileHandle to examine.
+ tagList - Additional parameters. Leave it NULL for now.@{b}
+
+ RESULT@{ub}
+ loader - Pointer to the first loader which knows how to load this
+ module, or NULL otherwise.@{b}
+
+ NOTE@{ub}
+ Before you call this function, you must first obtain a lock on the
+ loaders list to protect yourself from other tasks which might remove
+ the returned loader before you can actually use it.@{b}
+
+ SEE ALSO@{ub}
+
+@ENDNODE
+@NODE "xmLoadModuleA" "xmodule/xmLoadModuleA()"
+@{b}
+
+ NAME@{ub}
+ xmLoadModuleA -- Loads a module and converts it in XModule format
+ xmLoadModule -- Varargs stub for xmLoadModuleA@{b}
+
+ SYNOPSIS@{ub}
+ songInfo = xmLoadModuleA(fileName,tagList)
+ D0 A0 A1
+
+ @{"struct SongInfo" LINK "songclass.h/File" 284} *xmLoadModuleA(STRPTR,struct TagItem *);
+
+
+ songInfo = xmLoadModule(fileName,tag,...)
+
+ @{"struct SongInfo" LINK "songclass.h/File" 284} *xmLoadModule(STRPTR,LONG,...);@{b}
+
+ FUNCTION@{ub}
+ Loads fileName using the correct module loader.@{b}
+
+ INPUTS@{ub}
+ fileName - File to read. Can be NULL if the @{"XMSNG_FileHandle" LINK "xmodule.h/File" 208}
+ tag is passed.
+ tagList - Additional parameters (see below). Can be NULL.@{b}
+
+ TAGS@{ub}
+ @{"XMSNG_OldSong" LINK "xmodule.h/File" 206} - ti_Data is the pointer to a SongInfo which will be
+ freed as soon as the module format has been determined. This
+ is useful when the user wants to replace a song with another
+ one. Passing NULL uses the currently active song.
+
+ @{"XMSNG_AddToList" LINK "xmodule.h/File" 205} - (@{"struct SongInfo" LINK "songclass.h/File" 284} *) Add the song to the song list.
+ When a song is being added to the list, a shared lock is
+ obtained on it to prevent other tasks from modifying (or even
+ remove) the song before you can do your job on it. IT IS YOUR
+ DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
+ Passing NULL adds the song on the head of the list, while -1
+ (~0) will add it to the end of the SongList. Passing in a
+ pointer to another song which is already in the list, will
+ add the new song _after_ the latter.
+ (Default is not adding the song).
+
+ @{"XMSNG_Loader" LINK "xmodule.h/File" 207} - (@{"struct XMHook" LINK "xmodule.h/File" 92} *) Disables automatic format
+ checking and forces loading the module with the given
+ loader. (Defaults to NULL).
+
+ @{"XMSNG_FileHandle" LINK "xmodule.h/File" 208} - (BPTR) pointer to an open AmigaDOS
+ FileHandle to read the module from. The file must
+ already be positioned over the beginning of the module data.
+ NOTE: Even if this tag is passed, the fileName parameter is
+ still used to fill in the SongName field in case it is
+ missing inside the module AND the filesystem does not
+ support the ACTION_EXAMINE_FH packet.
+ NOTE: Some loaders may require a fully seekable file, so be
+ careful when using pipes.
+ NOTE: automatic data decompression is not possible when
+ @{"XMSNG_FileHandle" LINK "xmodule.h/File" 208} is passed.
+ (Defaults to NULL).
+
+ XMSNG_IFFHandle - (BPTR) pointer to an already initialized
+ IFFHandle to read the module from. The IFF must
+ already be positioned over the beginning of the module FORM.
+ Even if this tag is passed, the fileName parameter is still
+ used to fill in the SongName field in case it is missing
+ inside the module.
+ NOTE: The iffparse.library can deal with non-seekable files,
+ but some loaders may require a fully seekable file, so be
+ careful when using pipes.
+ NOTE: automatic file decompression is not possible when
+ XMSNG_IFFHandle is passed.
+ (Defaults to NULL).
+
+ @{"XMSNG_Active" LINK "xmodule.h/File" 209} - (BOOL) Makes this song the active one. This tag is
+ considered only if the @{"XMSNG_AddToList" LINK "xmodule.h/File" 205} tag is also passed.@{b}
+
+ RESULT@{ub}
+ songInfo - Pointer to the newly allocated SongInfo structure, or
+ NULL for failure. This function will not fail if the module is
+ partially corrupted. Anyway, the returned SongInfo will always
+ be valid. You can check IoErr() to know if there were problems.@{b}
+
+ EXAMPLE@{ub}
+ /* Load a song and add it to the SongList */
+ si = xmLoadSongTags (file, NULL,
+ @{"XMSNG_AddToList" LINK "xmodule.h/File" 205}, (@{"struct SongInfo" LINK "songclass.h/File" 284} *)~0,
+ TAG_DONE);
+
+ /* Check for errors even if si is not NULL */
+ error = IoErr();
+
+ /* Release Semaphore got by xmLoadSong() */
+ if (si) ReleaseSemaphore (&si->Lock);@{b}
+
+ SEE ALSO@{ub}
+ @{"xmAddSongA()" LINK "xmAddSongA"}, @{"xmIdentifyModule()" LINK "xmIdentifyModule"}
+
+@ENDNODE
+@NODE "xmLockActiveSong" "xmodule/xmLockActiveSong()"
+@{b}
+
+ NAME@{ub}
+ xmLockActiveSong -- Obtains an lock on the active song@{b}
+
+ SYNOPSIS@{ub}
+ song = xmLockActiveSong(mode);
+ D0 D0:16
+
+ @{"struct SongInfo" LINK "songclass.h/File" 284} *@{"xmActivateSong" LINK "xmActivateSong"}(UWORD);@{b}
+
+ FUNCTION@{ub}
+ Obtains an exclusive or shared lock on the active song,
+ waiting if needed. This call is a shortcut to:
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+ ObtainSemaphore[Shared] (&XModuleBase->xm_CurrentSong.Lock);
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ To unlock a song obtained in this way, you just need to
+ ReleaseSemaphore() it.
+
+ You MUST always lock a song before you even think to
+ read from -or write to- its data!@{b}
+
+ INPUTS@{ub}
+ mode - one of SM_SHARED or SM_EXCLUSIVE.@{b}
+
+ RESULT@{ub}
+ song - Pointer to the song which *was* active at the time
+ you called xmLockActiveSong(). The song will be
+ locked for you. The result will be NULL if no song
+ is active.@{b}
+
+ NOTE@{ub}
+ Please be careful if you call this function while holding
+ locks on other songs. Doing so, you run the risk of causing
+ deadlock condition.
+ This function does only lock the song; it is NOT guaranteed
+ that it will remain the active one.@{b}
+
+ SEE ALSO@{ub}
+
+@ENDNODE
+@NODE "xmProcessSongA" "xmodule/xmProcessSongA()"
+@{b}
+
+ NAME@{ub}
+ xmProcessSongA -- Performs complex processing on a song
+ xmProcessSong -- Varargs stub for xmProcessSongA()@{b}
+
+ SYNOPSIS@{ub}
+ result = xmProcessSongA(si, reserved, tagList)
+ A0 A1, A2
+
+ LONG xmProcessSongA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,void *,struct TagItem *);
+
+
+ result = xmProcessSong(si, reserved, tag1, ...)
+
+ LONG xmProcessSongA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,void *,LONG,...);@{b}
+
+ FUNCTION@{ub}
+ Performs complex processing operations on a song.@{b}
+
+ INPUTS@{ub}
+ si - pointer to the song to be processed.
+ reserved - Reserved for future use.
+ tagList - List of arguments. See below for possible tags.@{b}
+
+ RESULT@{ub}
+ The result depends on the kind of operation done. For most
+ operations, it's value will be 0 to indicate success and
+ an AmigaDOS error code which indicates the cause for failure.@{b}
+
+ TAGS@{ub}
+ @{"XMSNG_Optimize" LINK "xmodule.h/File" 213} - (LONG). Tries to reduce the song size by removing
+ all unused data. Possible optimizations are:
+ - @{"XMOF_REM_UNUSED_PATTERNS" LINK "xmodule.h/File" 221}
+ - @{"XMOF_REM_DUP_PATTERNS" LINK "xmodule.h/File" 222}
+ - @{"XMOF_CUT_PATTERNS" LINK "xmodule.h/File" 227}
+ - @{"XMOF_REM_UNUSED_INSTRS" LINK "xmodule.h/File" 223}
+ - @{"XMOF_CUT_INSTR_LOOPS" LINK "xmodule.h/File" 225}
+ - @{"XMOF_CUT_INSTR_TAILS" LINK "xmodule.h/File" 226}
+ - @{"XMOF_DEFAULT" LINK "xmodule.h/File" 220}
+ @{"XMOF_DEFAULT" LINK "xmodule.h/File" 220} will select all the optimizations choosen by
+ the user in addition to the ones specified with the other flags.
+
+ @{"XMSNG_RemapInstruments" LINK "xmodule.h/File" 214} - (BOOL) Performs instruments remapping.
+
+ @{"XMSNG_LimitPatterns" LINK "xmodule.h/File" 215} - (UWORD,UWORD) Limits the length all the
+ patterns inside a minimum and maximum value. The upper 16bits
+ of the argument are the minimum value, the lower ones are
+ the maximum value. Patterns outside this limits will be grown
+ or split as appropriate.
+
+ @{"XMSNG_Join" LINK "xmodule.h/File" 216} - (@{"struct SongInfo" LINK "songclass.h/File" 284} *) - Joins the song with another one.
+
+ @{"XMSNG_Merge" LINK "xmodule.h/File" 217} - (@{"struct SongInfo" LINK "songclass.h/File" 284} *) - Merges the song with another one.@{b}
+
+ NOTE@{ub}
+ In order to process a song, you should have exclusive access to
+ it. Always obtain a lock before you call this function on
+ public songs.
+
+@ENDNODE
+@NODE "xmRemHook" "xmodule/xmRemHook()"
+@{b}
+
+ NAME@{ub}
+ xmRemHook -- Removes an XModule Hook@{b}
+
+ SYNOPSIS@{ub}
+ xmRemHookA(xmHook)
+ A0
+
+ void xmRemHook(@{"struct XMHook" LINK "xmodule.h/File" 92} *);@{b}
+
+ FUNCTION@{ub}
+ Removes an XModule Hook, calls its RemoveHookFunc() and then
+ frees its memory.@{b}
+
+ INPUTS@{ub}
+ xmHook - pointer to the hook to be removed.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmAddHook()" LINK "xmAddHook"}
+
+@ENDNODE
+@NODE "xmRemInstrument" "xmodule/xmRemInstrument()"
+@{b}
+
+ NAME@{ub}
+ xmRemInstrument -- Removes an instrument from a song@{b}
+
+ SYNOPSIS@{ub}
+ xmRemInstrument(si, instrNum)
+ A0 D0
+
+ void xmRemInstrument(@{"struct SongInfo" LINK "songclass.h/File" 284} *,LONG);@{b}
+
+ FUNCTION@{ub}
+ Removes an instrument from a song by calling the @{"SNGM_REMINSTRUMENT" LINK "songclass.h/File" 34}
+ method.@{b}
+
+ INPUTS@{ub}
+ si - pointer to the song to which the instrument should be removed.
+ mum - Number of the instrument to be removed.@{b}
+
+ NOTE@{ub}
+ In order to remove instruments, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmAddInstrumentA()" LINK "xmAddInstrumentA"}, @{"songclass" LINK "songclass/MAIN"}/SNGM_REMINSTRUMENT
+
+@ENDNODE
+@NODE "xmRemPattern" "xmodule/xmRemPattern()"
+@{b}
+
+ NAME@{ub}
+ xmRemPattern -- Removes a pattern from a song@{b}
+
+ SYNOPSIS@{ub}
+ xmRemPattern(si, pattNum, replaceWith)
+ A0 D0, D1
+
+ void xmRemPattern(@{"struct SongInfo" LINK "songclass.h/File" 284} *,LONG,LONG);@{b}
+
+ FUNCTION@{ub}
+ Removes a pattern from a song by calling the @{"SNGM_REMPATTERN" LINK "songclass.h/File" 31} method.@{b}
+
+ INPUTS@{ub}
+ si - pointer to the song to which the pattern should be removed.
+ mum - Number of the pattern to be removed.
+ replaceWith - What to put in the song sequence in place of the
+ deleted pattern.@{b}
+
+ NOTE@{ub}
+ In order to remove patterns, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmAddPatternA()" LINK "xmAddPatternA"}, @{"songclass" LINK "songclass/MAIN"}/SNGM_REMPATTERN
+
+@ENDNODE
+@NODE "xmRemSong" "xmodule/xmRemSong()"
+@{b}
+
+ NAME@{ub}
+ xmRemSong -- Remove a song from the song list@{b}
+
+ SYNOPSIS@{ub}
+ success = xmRemSong(songInfo);
+ D0 A0
+
+ ULONG xmRemSong(@{"struct SongInfo" LINK "songclass.h/File" 284} *);@{b}
+
+ FUNCTION@{ub}
+ Removes a song from the public song list. It is safe to call this
+ function even if the song has not been added to the song list. If
+ the passed SongInfo is the active one, another song will be
+ selected automatically.@{b}
+
+ INPUTS@{ub}
+ songInfo - song to be removed. If NULL, this function will take
+ no action.@{b}
+
+ RESULT@{ub}
+ success - Will be 0 for failure, in which case the song will
+ not be removed from the song list. Currently,
+ xmRemSong() will never fail.@{b}
+
+ NOTE@{ub}
+ In order to remove a song, you must first obtain an exclusive
+ lock on it.
+
+ The song list is also protected from multiple task access by
+ a SignalSemaphore. This call will wait if the song list has
+ been locked by another task. Beware of deadlock conditions!@{b}
+
+ SEE ALSO@{ub}
+ @{"xmAddSongA()" LINK "xmAddSongA"}
+
+@ENDNODE
+@NODE "xmSaveModuleA" "xmodule/xmSaveModuleA()"
+@{b}
+
+ NAME@{ub}
+ xmSaveModuleA -- Saves a module to the specified file format
+ xmSaveModule -- Varargs stub for xmSaveModuleA@{b}
+
+ SYNOPSIS@{ub}
+ error = xmSaveModuleA(songInfo, fileName, saver, tagList)
+ D0 A0 A1 A2 A3
+
+ LONG xmSaveModuleA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,STRPTR,@{"struct XMHook" LINK "xmodule.h/File" 92} *,
+ struct TagItem *);
+
+
+ error = xmSaveModule(songInfo, fileName, saver, tag1,...)
+
+ LONG xmSaveModule(@{"struct SongInfo" LINK "songclass.h/File" 284} *,STRPTR,@{"struct XMHook" LINK "xmodule.h/File" 92} *,
+ LONG,...);@{b}
+
+ FUNCTION@{ub}
+ Saves songInfo to fileName using the specified saver.@{b}
+
+ INPUTS@{ub}
+ songInfo - Song to save.
+ fileName - AmigaDOS filename to write the song to.
+ saver - Pointer to the saver to use. Pass NULL to use
+ the default saver.@{b}
+
+ TAGS@{ub}
+ No tags are defined for this call.@{b}
+
+ RESULT@{ub}
+ error - RETURN_OK for success, or any other AmigaDOS error code
+ to mean failure. In particular, RETURN_WARN indicates that
+ something went wrong, but the song is still ok. The special
+ error code @{"ERROR_IOERR" LINK "xmodule.h/File" 194} means that some dos.library
+ call (e.g.: Read()) failed. In the latter case, the
+ AmigaDOS IoErr() value will be set to explain the specific
+ cause of the problem.@{b}
+
+ SEE ALSO@{ub}
+
+@ENDNODE
+@NODE "xmSetInstrumentA" "xmodule/xmSetInstrumentA()"
+@{b}
+
+ NAME@{ub}
+ xmSetInstrumentA -- Sets an instrument's attributes
+ xmSetInstrument -- Varargs stub for xmSetInstrumentA@{b}
+
+ SYNOPSIS@{ub}
+ success = xmSetInstrumentA(si, instrNum, tagList)
+ D0 A0 D0 A1
+
+ ULONG xmSetInstrumentA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,LONG,
+ struct TagItem *);
+
+
+ success = xmSetInstrument(si,instrNum,tag1,...)
+
+ ULONG xmSetInstrument(@{"struct SongInfo" LINK "songclass.h/File" 284} *,LONG,LONG,...);@{b}
+
+ FUNCTION@{ub}
+ Sets an instrument's attributes by calling the @{"SNGM_SETINSTRUMENT" LINK "songclass.h/File" 33}
+ method.@{b}
+
+ INPUTS@{ub}
+ si - pointer to the song which contains the instrument to be set.
+ tagList - instrument attributes to set. See @{"SNGM_SETINSTRUMENT" LINK "songclass.h/File" 33}
+ for possible tags.@{b}
+
+ RESULT@{ub}
+ non zero for success.@{b}
+
+ NOTE@{ub}
+ In order to set instruments' attributes, you should have
+ exclusive access to the song. Always obtain a lock before
+ you call this function on public songs.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmAddInstrument()" LINK "xmAddInstrumentA"}, @{"songclass" LINK "songclass/MAIN"}/SNGM_SETINSTRUMENT
+
+@ENDNODE
+@NODE "xmSetPatternA" "xmodule/xmSetPatternA()"
+@{b}
+
+ NAME@{ub}
+ xmSetPatternA -- Sets pattern attributes
+ xmSetPattern -- Varargs stub for xmSetPatternA@{b}
+
+ SYNOPSIS@{ub}
+ success = xmSetPatternA(si, pattNum, tagList)
+ D0 A0 D0 A1
+
+ ULONG xmSetPatternA(@{"struct SongInfo" LINK "songclass.h/File" 284} *,ULONG,struct TagItem *);
+
+
+ success = xmSetPattern (si,pattNum,tag1,...)
+
+ ULONG xmSetPattern(@{"struct SongInfo" LINK "songclass.h/File" 284} *,ULONG,LONG Tag1,...);@{b}
+
+ FUNCTION@{ub}
+ Sets attributes of a pattern by calling the @{"SNGM_SETPATTERN" LINK "songclass.h/File" 30} method.@{b}
+
+ INPUTS@{ub}
+ si - pointer to the song which contains the pattern to be set.
+ tagList - list of attributes to set. See @{"SNGM_SETPATTERN" LINK "songclass.h/File" 30} for
+ possible tags.@{b}
+
+ RESULT@{ub}
+ Non zero for success@{b}
+
+ NOTE@{ub}
+ In order to set patterns attributes, you should have exclusive
+ access to the song. Always obtain a lock before you call this
+ function on public songs.@{b}
+
+ SEE ALSO@{ub}
+ @{"xmAddPattern()" LINK "xmAddPatternA"}, @{"songclass" LINK "songclass/MAIN"}/SNGM_SETPATTERN
+
+@ENDNODE
+@NODE "xmSetSongLen" "xmodule/xmSetSongLen()"
+@{b}
+
+ NAME@{ub}
+ xmSetSongLen -- Set the number of song positions@{b}
+
+ SYNOPSIS@{ub}
+ sequence = xmSetSongLen(songInfo, length)
+ D0 A0 D0:16
+
+ UWORD *xmSetSongLen(@{"struct SongInfo" LINK "songclass.h/File" 284} *, UWORD);@{b}
+
+ FUNCTION@{ub}
+ Allocates space in the song for a sequence table of the given
+ length. If no sequence exists yet, a new one is allocated.
+ Increasing the song length may require the sequence table to be
+ expanded, in which case it will be re-allocated and the old sequence
+ entries will be copied. Decreasing the song length could also cause
+ a reallocation of the sequence table if the size drops down enough.
+ Setting the song length to 0 will free the sequence table and return
+ NULL.@{b}
+
+ INPUTS@{ub}
+ songInfo - pointer to a SongInfo structure.
+ length - new length of song sequence table.@{b}
+
+ NOTE@{ub}
+ This function will also adjust the CurrentPos field if it is found
+ beyond the song end@{b}
+
+ BUGS@{ub}
+ This funtion is quite useless because all it does is setting the
+ @{"SNGA_Length" LINK "songclass.h/File" 88} attribute in the song object. Setting the @{"SNGA_Length" LINK "songclass.h/File" 88}
+ attribute yourself is easier and faster if you are already setting
+ other attributes in the same song.@{b}
+
+ RESULT@{ub}
+ Pointer to the newly allocated sequence table or NULL for failure,
+ in which case the previous sequence table is left untouched.
+
+@ENDNODE
--- /dev/null
+TABLE OF CONTENTS
+
+xmodule/--background--
+xmodule/xmActivateSong
+xmodule/xmAddHook
+xmodule/xmAddInstrumentA
+xmodule/xmAddPatternA
+xmodule/xmAddSongA
+xmodule/xmCreateSong
+xmodule/xmDeleteSong
+xmodule/xmDisplayMessage
+xmodule/xmDisplayProgress
+xmodule/xmIdentifyModule
+xmodule/xmLoadModuleA
+xmodule/xmLockActiveSong
+xmodule/xmProcessSongA
+xmodule/xmRemHook
+xmodule/xmRemInstrument
+xmodule/xmRemPattern
+xmodule/xmRemSong
+xmodule/xmSaveModuleA
+xmodule/xmSetInstrumentA
+xmodule/xmSetPatternA
+xmodule/xmSetSongLen
+\fxmodule/--background-- xmodule/--background--
+
+ INTRODUCTION
+ The xmodule.library is an API that provides access to the XModule
+ song management engine, as well as other general pourpose
+ services. Hooks and external applications can use this library
+ to create, load, save and modify songs.
+
+ The xmodule.library does not exist as a stand alone shared libray.
+ Instead, it's code is contained inside the main XModule executable
+ (altrough it's logically independant from the rest of the XModule
+ code).
+
+ XModule adds the xmodule.library to the exec libray list at
+ startup time, unless it finds one already in the list (this might
+ happen when multiple copies of XModule are running at the same
+ time).
+
+ PUBLIC SONGS
+ The xmodule.library maintains a list of public songs which can be
+ used by all applications which opened the library. Each song in
+ the list is protected from multiple task access by a
+ SignalSemaphore, and whole list is also protected by another
+ semaphore. The locking mechanism is very simple: before you can
+ access a song you must obtain its semaphore, and you must release
+ it as soon as you are done with it. If you are locking a song to
+ just to read some information (i.e.: you don't want to modify
+ anything), you should obtain a shared lock instead of an exclusive
+ one. The list must be locked whenever you want to add or remove
+ a song, or when you scan it in any way.
+
+ Since public songs could be modified or even deleted by other
+ tasks, do not make your songs public unless your code is smart
+ enough to handle all possible situations.
+
+
+ HOOKS
+ Actually, most modular programs call them `modules', but it would
+ have created a lot of confusion with a program like XModule :-).
+
+ Hooks are programs that add some kind of functionality
+ to XModule. External hook files are standard shared libraries
+ which will open the xmodule.library when they are loaded and
+ call xmAddHookA() to add themselves to a list of hooks maintained
+ by the library.
+
+ Currently, XModule supports two kinds of hooks: loaders and
+ savers. Loader hooks can also provide a function which
+ identifies a particular module format.
+
+ An external hook libray may also contain several hooks.
+ Putting a loader and a saver for one particolar format together
+ in one libray is generally a good idea, while making a hook
+ with hundereds of loaders and savers isn't a good move because
+ it makes the whole concept of external hooks quite useless.
+ Grouping different versions or variants of one format together
+ in one external hook is acceptable.
+
+ SEE ALSO
+ songclass/--background--, exec/ObtainSemaphore()
+
+\fxmodule/xmActivateSong xmodule/xmActivateSong
+
+ NAME
+ xmActivateSong -- Makes a song the active one
+
+ SYNOPSIS
+ success = xmActivateSong(songInfo);
+ D0 A0
+
+ ULONG xmActivateSong(struct SongInfo *);
+
+ FUNCTION
+ Makes the passed song the currently active one. It's pointer
+ will be stored in the public song list and most actions
+ will happen on this song by default.
+
+ INPUTS
+ songInfo - song to be activated. If NULL, this function will
+ take no action.
+
+ RESULT
+ success - Will be 0 for failure, in which case the song will
+ not be removed from the song list. Currently,
+ xmActivateSong() will never fail.
+
+ NOTE
+ In order to activate a song, you must own a shared lock
+ on it. Please do not hog this lock for a long time when
+ xmActivateSong() returns, because most internal routines
+ try to lock the current song before taking any action.
+
+ SEE ALSO
+
+\fxmodule/xmAddHook xmodule/xmAddHook
+
+ NAME
+ xmAddHookA -- Creates a new XModule Hook
+ xmAddHook -- Varargs stub for xmAddHookA
+
+ SYNOPSIS
+ hook = xmAddHookA(tagList)
+ D0 A0
+
+ struct XMHook *xmAddHookA(struct TagItem *);
+
+
+ hook = xmAddHook(Tag1,...)
+
+ struct XMHook *xmAddHook(ULONG,...);
+
+ FUNCTION
+ Creates a new XMHook structure and fills it in with data supplied
+ with the TagList. Adds the newly created Hook to the appropriate
+ list.
+
+ INPUTS
+ tagList - pointer to a tag list specifying how to initialize the
+ XMHook structure.
+
+ TAGS
+ XMHOOK_Type - (ULONG) Defines the pourpose of this hook. Possible
+ values are currently NT_XMLOADER and NT_XMSAVER. (This
+ tag is REQUIRED).
+
+ XMHOOK_Name - (STRPTR) ti_Data contains a short name for the
+ hook (e.g: "SoundTracker"). This name will appear in the
+ Savers list if this hook is a saver and will be passed as an
+ argument for some ARexx commands, so please use a single
+ word name. (This tag is REQUIRED).
+
+ XMHOOK_Priority - (BYTE) Priority to give to this hook. Hooks
+ with higher priorities will be used before lower priority
+ ones and will come first in lists shown to the user. Valid
+ range is -128..+127, but please restrict to a -20..+20
+ interval for normal cases. (Defaults to 0).
+
+ XMHOOK_Descr - (STRPTR) Verbose description of the hook
+ (without newlines). (Defaults to NULL).
+
+ XMHOOK_Author - (STRPTR) Author's name. Please, just put
+ your full name here; no greetings, copyright notices,
+ etc. (Defaults to NULL).
+
+ XMHOOK_ID - (ULONG) This is a unique, IFF-style identifier for
+ the format. If the format is an IFF format, it must be the
+ same of the FORM ID. (Defaults to 0, this tag is required
+ for IFF loaders and savers).
+
+ XMHOOK_Flags - (ULONG) Sets miscellaneous flags for this hook.
+ See xmodule.h for possible flags.
+
+ XMHOOK_LibraryBase - (struct Library *) Pointer to the library
+ base for external hooks. This pointer will be used to open
+ the library when the hook is created and to close it when
+ the hook is deleted. If you do not pass this tag, you must
+ find some other way to keep your code in memory until one
+ or more of your hooks are active. XModule will close your
+ library just after calling the SetupXMHook() function.
+ (Defaults to NULL).
+
+ XMHOOK_UserData - (APTR) ti_Data will be stored in the
+ xmh_UserData field of the XMHook structure. This field can
+ come andy to store private data. (Defaults to NULL).
+
+ XMHOOK_RemoveHookFunc - (APTR) Pointer to a function which will be
+ called upon removing the hook. This function can be used to
+ free any private resources allocated by the hook when it was
+ created. The template for the function is:
+
+ void RemoveHookFunc (struct XMHook *hook);
+ A0
+
+ XMHOOK_LoadModFunc - (APTR) Pointer to the hook function which
+ loads a module. The template for the function is:
+
+ LONG LoadModFunc (BPTR fileHandle, struct SongInfo *song,
+ D0 A0
+ struct XMHook *loader);
+ A1
+
+ `fileHandle' is an open file to load the module from. The
+ caller will take care to close it for you. The loader should
+ return RETURN_OK for success, or any other AmigaDOS error
+ code to mean failure. In particular, RETURN_WARN indicates
+ that something went wrong, but the song is ok. The special
+ error code ERROR_IOERR can be used when some dos.library
+ call (e.g.: Read()) failed. In the latter case, the
+ AmigaDOS IoErr value should be set to explain the specific
+ cause of the problem.
+ (This tag is required by all NT_XMLOADER type hooks).
+
+ XMHOOK_SaveModFunc - (APTR) Pointer to the hook function which
+ saves a module. The template for the function is:
+
+ LONG SaveModFunc (BPTR fileHandle, struct SongInfo *song,
+ D0 A0
+ struct XMHook *saver);
+ A1
+
+ fileHandle is an open file to save the module to. The caller
+ will take care to close it for you. The saver should return
+ RETURN_OK for success, or any other AmigaDOS error code to
+ mean failure. In particular, RETURN_WARN indicates that
+ something went wrong, but the song is ok. The special
+ error code ERROR_IOERR can be used when some dos.library
+ call (e.g.: Read()) failed. In the latter case, the
+ AmigaDOS IoErr value should be set to explain the specific
+ cause of the problem.
+ (This tag is required by all NT_XMSAVER type hooks).
+
+
+ XMHOOK_IdentifyModFunc - (APTR) Pointer to the hook function
+ which identifies a module format. The template for the
+ function is:
+
+ struct XMHook *IdentifyModFunc (BPTR fileHandle,
+ D0
+ struct XMHook *hook,struct TagItem *tagList);
+ A0 A1
+
+ fileHandle is an open file to try the identification on. The
+ caller will take care to close it. NOTE: Do not make assumptions
+ on the initial file position (e.g: seek yourself to position 0
+ if you need to). This funtion should return a pointer to a valid
+ loader (usually the one passed in) if the format is recognized,
+ NULL otherwhise. (This tag is required by all NT_XMLOADER type
+ hooks).
+
+ RESULT
+ hook - Pointer to the newly allocated XMHook structure, or
+ NULL for failure.
+
+ SEE ALSO
+ xmRemHook(), xmIdentifyModule(), xmLoadSong(), xmSaveSong()
+
+\fxmodule/xmAddInstrumentA xmodule/xmAddInstrumentA
+
+ NAME
+ xmAddInstrumentA -- Adds a instrument to a song
+ xmAddInstrument -- Varargs stub for xmAddInstrumentA
+
+ SYNOPSIS
+ instrument = xmAddInstrumentA(si, instrNum, tagList)
+ D0 A0 D0 A1
+
+ struct Instrument *xmAddInstrumentA(struct SongInfo *,LONG,
+ struct TagItem *);
+
+
+ instrument = xmAddInstrument(si,instrNum,tag1,...)
+
+ struct Instrument *xmAddInstrument(struct SongInfo *,LONG,LONG,...);
+
+ FUNCTION
+ Adds an instrument to a song by calling the SNGM_ADDINSTRUMENT method
+.
+
+ INPUTS
+ si - pointer to the song to which the instrument should be added.
+ tagList - optional TagList. See SNGM_ADDINSTRUMENT for possible tags
+.
+
+ RESULT
+ Pointer to the new instrument or NULL for failure.
+
+ NOTE
+ In order to add instruments, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.
+
+ SEE ALSO
+ xmRemInstrument(), songclass/SNGM_REMINSTRUMENT
+
+\fxmodule/xmAddPatternA xmodule/xmAddPatternA
+
+ NAME
+ xmAddPatternA -- Adds a pattern to a song
+ xmAddPattern -- Varargs stub for xmAddPatternA
+
+ SYNOPSIS
+ pattern = xmAddPatternA(si, tagList)
+ D0 A0 A1
+
+ struct Pattern *xmAddPatternA(struct SongInfo *,struct TagItem *);
+
+
+ pattern = xmAddPattern (si,tag1,...)
+
+ struct Pattern *xmAddPattern(struct SongInfo *,LONG Tag1,...);
+
+ FUNCTION
+ Adds a pattern to a song by calling the SNGM_ADDPATTERN method.
+
+ INPUTS
+ si - pointer to the song to which the pattern should be added.
+ tagList - optional TagList. See SNGM_ADDPATTERN for possible tags.
+
+ RESULT
+ Pointer to the new pattern or NULL for failure.
+
+ NOTE
+ In order to add patterns, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.
+
+ SEE ALSO
+ xmRemPattern(), songclass/SNGM_ADDPATTERN
+
+\fxmodule/xmAddSongA xmodule/xmAddSongA
+
+ NAME
+ xmAddSongA -- Add a song to the song list
+ xmAddSong -- Varargs stub for xmAddSongA
+
+ SYNOPSIS
+ success = xmAddSongA(songInfo,position,tagList);
+ D0 A0 A1 A2
+
+ ULONG xmAddSongA(struct SongInfo *,struct SongInfo *,
+ struct TagItem *);
+
+
+ success = xmAddSong(songInfo,position,tag1,...);
+
+ ULONG xmAddSong(struct SongInfo *,struct SongInfo *,
+ LONG,...);
+
+ FUNCTION
+ Adds a song to the public song list. Trying to add a song
+ twice is harmless.
+
+ INPUTS
+ songInfo - song to be added. Passing a NULL pointer is safe.
+ position - Position to add the song in. Passing NULL adds the
+ song in the head of the list, while ~0 adds it in the tail.
+ passing a pointer to a SongInfo already in the list inserts
+ the first song _after_ the other one.
+ tagList - pointer to a TagList for more arguments.
+
+ TAGS
+ XMSNG_Active - (BOOL) Makes this song the active one. The
+ default is to just add the song without activating it.
+
+ RESULT
+ success - Will be 0 for failure, in which case the song will
+ not be added to the songs list. Currently, xmAddSongA()
+ will never fail.
+
+ NOTE
+ The song list is protected from multiple tasks access by a
+ SignalSemaphore in XModuleBase. This call will wait if the
+ song list has been locked by another task. Beware of deadlock
+ conditions!
+
+ SEE ALSO
+ xmRemSong()
+
+\fxmodule/xmCreateSong xmodule/xmCreateSong
+
+ NAME
+ xmCreateSongA -- Create and initialize a new SongInfo structure
+ xmCreateSong -- Varargs stub for xmCreateSongA
+
+ SYNOPSIS
+ songInfo = xmCreateSongA(tagList);
+ D0 A0
+
+ struct SongInfo *xmCreateSongA(struct TagItem *);
+
+
+ songInfo = xmCreateSong(Tag1,...);
+
+ struct SongInfo *xmCreateSong(ULONG,...);
+
+ FUNCTION
+ Allocates and initializes a new SongInfo structure. The song
+ can then be added to the song list via xmAddSongA(), in which
+ case, it is no longer required to free it with xmDeleteSong().
+
+ INPUTS
+ tagList - pointer to an optional tag list specifying how to
+ initialize the SongInfo structure.
+
+ TAGS
+ SNGA_DefaultTracks - Sets the default number of pattern tracks for
+ the song. Defaults to 4.
+
+ SNGA_DefaultPattLen - Sets the default number of pattern lines for
+ the song. Defaults to 64.
+
+ SNGA_ReadyToUse - Adds one pattern and one position to the song.
+ Defaults to FALSE.
+
+ XMSNG_AddToList - (struct SongInfo *) Add the song to the song list.
+ When a song is being added to the list, a shared lock is
+ obtained on it to prevent other tasks from modifying (or even
+ remove) the song before you can do your job on it. IT IS YOUR
+ DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
+ Passing NULL adds the song on the head of the list, while -1
+ (~0) will add it to the end of the SongList. Passing in a
+ pointer to another song which is already in the list, will
+ add the new song _after_ the latter.
+ (Default is not adding the song).
+
+ XMSNG_Active - (BOOL) Makes this song the active one. This tag is
+ considered only if the XMSNG_AddToList tag is also passed.
+
+ RESULT
+ songInfo - pointer to the newly allocated SongInfo structure, or
+ NULL for failure.
+
+ SEE ALSO
+ xmDeleteSong(), xmAddSongA()
+
+\fxmodule/xmDeleteSong xmodule/xmDeleteSong
+
+ NAME
+ xmDeleteSong -- Deletes a song
+
+ SYNOPSIS
+ xmDeleteSong(songInfo)
+ A0
+
+ void xmDeleteSong(struct SongInfo *);
+
+ FUNCTION
+ Deletes a song and all the items attached to it (patterns,
+ instruments, etc.). This call will also remove the song from
+ the song list if it had been added to it.
+
+ INPUTS
+ songInfo - pointer to the SongInfo structure to be deleted.
+ Passing a NULL pointer is harmless.
+
+ NOTE
+ In order to delete a song which has been added to the public
+ song list, you must first obtain an exclusive lock on it to
+ prevent other tasks from walking on it while you are freeing
+ it's data structures. The semaphore does NOT need to be
+ relinquished, because the SongInfo structure won't exist any
+ more when this call returns.
+
+ SEE ALSO
+ xmCreateSong(), xmRemSong()
+
+\fxmodule/xmDisplayMessage xmodule/xmDisplayMessage
+
+ NAME
+ xmDisplayMessageA -- Displays a message to the user
+ xmDisplayMessage -- Varargs stub for xmDisplayMessageA()
+
+ SYNOPSIS
+ xmDisplayMessageA(level, message, args)
+ DO A0 A1
+
+ void xmDisplayMessageA(ULONG,APTR,LONG *);
+
+
+ xmDisplayMessage(level, message, ...)
+
+ void xmDisplayMessage(ULONG,APTR,...);
+
+ FUNCTION
+ Outputs a string in the XModule log window or in the 'action' field
+ of the progress indicator. The string is printf-formatted before
+ being output.
+
+ INPUTS
+ level - a number from 0 to 7 which indicates the importance of the
+ message. 7 is used for very annoying messages (eg: debug output)
+,
+ 0 for very important things (eg: disaster).
+ If you set the XMDMF_USECATALOG bit in the level parameter, you
+ can pass a catalog string number instead of a pointer to a string
+.
+ Specifying XMDMF_ACTION, the message will be put in the 'action'
+ field of the progress indicator.
+ If the flag XMDMF_DOSFAULT is specified, a Fault() output is
+ formatted using the message as an header. In this case, the
+ level parameter takes another meaing: The lower word can contain
+ an AmigaDOS error code. If it is 0, IoErr() will be used to get
+ the error code.
+ message - pointer to a string or catalog message number,
+ when the XMDMF_USECATALOG flag is set.
+ args - arguments for formatting.
+
+ EXAMPLES
+ xmDisplayMessage (XMDMF_ALERT,
+ "The application `%s' fucked up Windoze95 because %s.",
+ "(unknown name)", "an error occurred");
+
+ xmDisplayMessage (XMDMF_USE_CATALOG | XMDMF_COMMENT,
+ MSG_LIFE_UNIVERSE_AND_ALLTHAT, 42, "Fortytwo", "For tea too");
+
+ xmDisplayMessageA (XMDMF_ACTION | XMDMF_USECATALOG,
+ MSG_READING_COMICS, NULL);
+
+ xmDisplayMessage (XMDMF_DOSFAULT | XMDMF_USECATALOG,
+ MSG_CANT_LOAD_MODULE, ModuleName);
+
+ SEE ALSO
+ xmDisplayProgress()
+
+\fxmodule/xmDisplayProgress xmodule/xmDisplayProgress
+
+ NAME
+ xmDisplayProgress -- Uptdates the progress bar indicator
+
+ SYNOPSIS
+ abort = xmDisplayProgress(done, total)
+ D0 D1
+
+ LONG xmDisplayMessageA(ULONG,ULONG);
+
+ FUNCTION
+ Updates the position of the fuel gauge in the progress window to
+ show the progress of an operation. Additionally, it checks for
+ user abort.
+
+ INPUTS
+ done - a number which indicates how much of the work has
+ been already completed
+ total - Total number of operations to do.
+
+ RESULT
+ 0 for success, ERROR_BREAK if user abort was detected. You should
+ always check this return code to abort what you were doing.
+
+\fxmodule/xmIdentifyModule xmodule/xmIdentifyModule
+
+ NAME
+ xmIdentifyModule -- Returns the type of a module
+
+ SYNOPSIS
+ loader = xmIdentifyModule(fh, tagList)
+ D0 D0 A0
+
+ struct XMHook *xmIdentifyModule(BPTR,struct TagItem *);
+
+ FUNCTION
+ Finds out a loader which is able to read the given module.
+
+ INPUTS
+ fh - AmigaDOS FileHandle to examine.
+ tagList - Additional parameters. Leave it NULL for now.
+
+ RESULT
+ loader - Pointer to the first loader which knows how to load this
+ module, or NULL otherwise.
+
+ NOTE
+ Before you call this function, you must first obtain a lock on the
+ loaders list to protect yourself from other tasks which might remove
+ the returned loader before you can actually use it.
+
+ SEE ALSO
+
+\fxmodule/xmLoadModuleA xmodule/xmLoadModuleA
+
+ NAME
+ xmLoadModuleA -- Loads a module and converts it in XModule format
+ xmLoadModule -- Varargs stub for xmLoadModuleA
+
+ SYNOPSIS
+ songInfo = xmLoadModuleA(fileName,tagList)
+ D0 A0 A1
+
+ struct SongInfo *xmLoadModuleA(STRPTR,struct TagItem *);
+
+
+ songInfo = xmLoadModule(fileName,tag,...)
+
+ struct SongInfo *xmLoadModule(STRPTR,LONG,...);
+
+ FUNCTION
+ Loads fileName using the correct module loader.
+
+ INPUTS
+ fileName - File to read. Can be NULL if the XMSNG_FileHandle
+ tag is passed.
+ tagList - Additional parameters (see below). Can be NULL.
+
+ TAGS
+ XMSNG_OldSong - ti_Data is the pointer to a SongInfo which will be
+ freed as soon as the module format has been determined. This
+ is useful when the user wants to replace a song with another
+ one. Passing NULL uses the currently active song.
+
+ XMSNG_AddToList - (struct SongInfo *) Add the song to the song list.
+ When a song is being added to the list, a shared lock is
+ obtained on it to prevent other tasks from modifying (or even
+ remove) the song before you can do your job on it. IT IS YOUR
+ DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
+ Passing NULL adds the song on the head of the list, while -1
+ (~0) will add it to the end of the SongList. Passing in a
+ pointer to another song which is already in the list, will
+ add the new song _after_ the latter.
+ (Default is not adding the song).
+
+ XMSNG_Loader - (struct XMHook *) Disables automatic format
+ checking and forces loading the module with the given
+ loader. (Defaults to NULL).
+
+ XMSNG_FileHandle - (BPTR) pointer to an open AmigaDOS
+ FileHandle to read the module from. The file must
+ already be positioned over the beginning of the module data.
+ NOTE: Even if this tag is passed, the fileName parameter is
+ still used to fill in the SongName field in case it is
+ missing inside the module AND the filesystem does not
+ support the ACTION_EXAMINE_FH packet.
+ NOTE: Some loaders may require a fully seekable file, so be
+ careful when using pipes.
+ NOTE: automatic data decompression is not possible when
+ XMSNG_FileHandle is passed.
+ (Defaults to NULL).
+
+ XMSNG_IFFHandle - (BPTR) pointer to an already initialized
+ IFFHandle to read the module from. The IFF must
+ already be positioned over the beginning of the module FORM.
+ Even if this tag is passed, the fileName parameter is still
+ used to fill in the SongName field in case it is missing
+ inside the module.
+ NOTE: The iffparse.library can deal with non-seekable files,
+ but some loaders may require a fully seekable file, so be
+ careful when using pipes.
+ NOTE: automatic file decompression is not possible when
+ XMSNG_IFFHandle is passed.
+ (Defaults to NULL).
+
+ XMSNG_Active - (BOOL) Makes this song the active one. This tag is
+ considered only if the XMSNG_AddToList tag is also passed.
+
+ RESULT
+ songInfo - Pointer to the newly allocated SongInfo structure, or
+ NULL for failure. This function will not fail if the module is
+ partially corrupted. Anyway, the returned SongInfo will always
+ be valid. You can check IoErr() to know if there were problems.
+
+ EXAMPLE
+ /* Load a song and add it to the SongList */
+ si = xmLoadSongTags (file, NULL,
+ XMSNG_AddToList, (struct SongInfo *)~0,
+ TAG_DONE);
+
+ /* Check for errors even if si is not NULL */
+ error = IoErr();
+
+ /* Release Semaphore got by xmLoadSong() */
+ if (si) ReleaseSemaphore (&si->Lock);
+
+ SEE ALSO
+ xmAddSongA(), xmIdentifyModule()
+
+\fxmodule/xmLockActiveSong xmodule/xmLockActiveSong
+
+ NAME
+ xmLockActiveSong -- Obtains an lock on the active song
+
+ SYNOPSIS
+ song = xmLockActiveSong(mode);
+ D0 D0:16
+
+ struct SongInfo *xmActivateSong(UWORD);
+
+ FUNCTION
+ Obtains an exclusive or shared lock on the active song,
+ waiting if needed. This call is a shortcut to:
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+ ObtainSemaphore[Shared] (&XModuleBase->xm_CurrentSong.Lock);
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ To unlock a song obtained in this way, you just need to
+ ReleaseSemaphore() it.
+
+ You MUST always lock a song before you even think to
+ read from -or write to- its data!
+
+ INPUTS
+ mode - one of SM_SHARED or SM_EXCLUSIVE.
+
+ RESULT
+ song - Pointer to the song which *was* active at the time
+ you called xmLockActiveSong(). The song will be
+ locked for you. The result will be NULL if no song
+ is active.
+
+ NOTE
+ Please be careful if you call this function while holding
+ locks on other songs. Doing so, you run the risk of causing
+ deadlock condition.
+ This function does only lock the song; it is NOT guaranteed
+ that it will remain the active one.
+
+ SEE ALSO
+
+\fxmodule/xmProcessSongA xmodule/xmProcessSongA
+
+ NAME
+ xmProcessSongA -- Performs complex processing on a song
+ xmProcessSong -- Varargs stub for xmProcessSongA()
+
+ SYNOPSIS
+ result = xmProcessSongA(si, reserved, tagList)
+ A0 A1, A2
+
+ LONG xmProcessSongA(struct SongInfo *,void *,struct TagItem *);
+
+
+ result = xmProcessSong(si, reserved, tag1, ...)
+
+ LONG xmProcessSongA(struct SongInfo *,void *,LONG,...);
+
+ FUNCTION
+ Performs complex processing operations on a song.
+
+ INPUTS
+ si - pointer to the song to be processed.
+ reserved - Reserved for future use.
+ tagList - List of arguments. See below for possible tags.
+
+ RESULT
+ The result depends on the kind of operation done. For most
+ operations, it's value will be 0 to indicate success and
+ an AmigaDOS error code which indicates the cause for failure.
+
+ TAGS
+ XMSNG_Optimize - (LONG). Tries to reduce the song size by removing
+ all unused data. Possible optimizations are:
+ - XMOF_REM_UNUSED_PATTERNS
+ - XMOF_REM_DUP_PATTERNS
+ - XMOF_CUT_PATTERNS
+ - XMOF_REM_UNUSED_INSTRS
+ - XMOF_CUT_INSTR_LOOPS
+ - XMOF_CUT_INSTR_TAILS
+ - XMOF_DEFAULT
+ XMOF_DEFAULT will select all the optimizations choosen by
+ the user in addition to the ones specified with the other flags.
+
+ XMSNG_RemapInstruments - (BOOL) Performs instruments remapping.
+
+ XMSNG_LimitPatterns - (UWORD,UWORD) Limits the length all the
+ patterns inside a minimum and maximum value. The upper 16bits
+ of the argument are the minimum value, the lower ones are
+ the maximum value. Patterns outside this limits will be grown
+ or split as appropriate.
+
+ XMSNG_Join - (struct SongInfo *) - Joins the song with another one.
+
+ XMSNG_Merge - (struct SongInfo *) - Merges the song with another one.
+
+ NOTE
+ In order to process a song, you should have exclusive access to
+ it. Always obtain a lock before you call this function on
+ public songs.
+
+\fxmodule/xmRemHook xmodule/xmRemHook
+
+ NAME
+ xmRemHook -- Removes an XModule Hook
+
+ SYNOPSIS
+ xmRemHookA(xmHook)
+ A0
+
+ void xmRemHook(struct XMHook *);
+
+ FUNCTION
+ Removes an XModule Hook, calls its RemoveHookFunc() and then
+ frees its memory.
+
+ INPUTS
+ xmHook - pointer to the hook to be removed.
+
+ SEE ALSO
+ xmAddHook()
+
+\fxmodule/xmRemInstrument xmodule/xmRemInstrument
+
+ NAME
+ xmRemInstrument -- Removes an instrument from a song
+
+ SYNOPSIS
+ xmRemInstrument(si, instrNum)
+ A0 D0
+
+ void xmRemInstrument(struct SongInfo *,LONG);
+
+ FUNCTION
+ Removes an instrument from a song by calling the SNGM_REMINSTRUMENT
+ method.
+
+ INPUTS
+ si - pointer to the song to which the instrument should be removed.
+ mum - Number of the instrument to be removed.
+
+ NOTE
+ In order to remove instruments, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.
+
+ SEE ALSO
+ xmAddInstrumentA(), songclass/SNGM_REMINSTRUMENT
+
+\fxmodule/xmRemPattern xmodule/xmRemPattern
+
+ NAME
+ xmRemPattern -- Removes a pattern from a song
+
+ SYNOPSIS
+ xmRemPattern(si, pattNum, replaceWith)
+ A0 D0, D1
+
+ void xmRemPattern(struct SongInfo *,LONG,LONG);
+
+ FUNCTION
+ Removes a pattern from a song by calling the SNGM_REMPATTERN method.
+
+ INPUTS
+ si - pointer to the song to which the pattern should be removed.
+ mum - Number of the pattern to be removed.
+ replaceWith - What to put in the song sequence in place of the
+ deleted pattern.
+
+ NOTE
+ In order to remove patterns, you should have exclusive access to
+ the song. Always obtain a lock before you call this function on
+ public songs.
+
+ SEE ALSO
+ xmAddPatternA(), songclass/SNGM_REMPATTERN
+
+\fxmodule/xmRemSong xmodule/xmRemSong
+
+ NAME
+ xmRemSong -- Remove a song from the song list
+
+ SYNOPSIS
+ success = xmRemSong(songInfo);
+ D0 A0
+
+ ULONG xmRemSong(struct SongInfo *);
+
+ FUNCTION
+ Removes a song from the public song list. It is safe to call this
+ function even if the song has not been added to the song list. If
+ the passed SongInfo is the active one, another song will be
+ selected automatically.
+
+ INPUTS
+ songInfo - song to be removed. If NULL, this function will take
+ no action.
+
+ RESULT
+ success - Will be 0 for failure, in which case the song will
+ not be removed from the song list. Currently,
+ xmRemSong() will never fail.
+
+ NOTE
+ In order to remove a song, you must first obtain an exclusive
+ lock on it.
+
+ The song list is also protected from multiple task access by
+ a SignalSemaphore. This call will wait if the song list has
+ been locked by another task. Beware of deadlock conditions!
+
+ SEE ALSO
+ xmAddSongA()
+
+\fxmodule/xmSaveModuleA xmodule/xmSaveModuleA
+
+ NAME
+ xmSaveModuleA -- Saves a module to the specified file format
+ xmSaveModule -- Varargs stub for xmSaveModuleA
+
+ SYNOPSIS
+ error = xmSaveModuleA(songInfo, fileName, saver, tagList)
+ D0 A0 A1 A2 A3
+
+ LONG xmSaveModuleA(struct SongInfo *,STRPTR,struct XMHook *,
+ struct TagItem *);
+
+
+ error = xmSaveModule(songInfo, fileName, saver, tag1,...)
+
+ LONG xmSaveModule(struct SongInfo *,STRPTR,struct XMHook *,
+ LONG,...);
+
+ FUNCTION
+ Saves songInfo to fileName using the specified saver.
+
+ INPUTS
+ songInfo - Song to save.
+ fileName - AmigaDOS filename to write the song to.
+ saver - Pointer to the saver to use. Pass NULL to use
+ the default saver.
+
+ TAGS
+ No tags are defined for this call.
+
+ RESULT
+ error - RETURN_OK for success, or any other AmigaDOS error code
+ to mean failure. In particular, RETURN_WARN indicates that
+ something went wrong, but the song is still ok. The special
+ error code ERROR_IOERR means that some dos.library
+ call (e.g.: Read()) failed. In the latter case, the
+ AmigaDOS IoErr() value will be set to explain the specific
+ cause of the problem.
+
+ SEE ALSO
+
+\fxmodule/xmSetInstrumentA xmodule/xmSetInstrumentA
+
+ NAME
+ xmSetInstrumentA -- Sets an instrument's attributes
+ xmSetInstrument -- Varargs stub for xmSetInstrumentA
+
+ SYNOPSIS
+ success = xmSetInstrumentA(si, instrNum, tagList)
+ D0 A0 D0 A1
+
+ ULONG xmSetInstrumentA(struct SongInfo *,LONG,
+ struct TagItem *);
+
+
+ success = xmSetInstrument(si,instrNum,tag1,...)
+
+ ULONG xmSetInstrument(struct SongInfo *,LONG,LONG,...);
+
+ FUNCTION
+ Sets an instrument's attributes by calling the SNGM_SETINSTRUMENT
+ method.
+
+ INPUTS
+ si - pointer to the song which contains the instrument to be set.
+ tagList - instrument attributes to set. See SNGM_SETINSTRUMENT
+ for possible tags.
+
+ RESULT
+ non zero for success.
+
+ NOTE
+ In order to set instruments' attributes, you should have
+ exclusive access to the song. Always obtain a lock before
+ you call this function on public songs.
+
+ SEE ALSO
+ xmAddInstrument(), songclass/SNGM_SETINSTRUMENT
+
+\fxmodule/xmSetPatternA xmodule/xmSetPatternA
+
+ NAME
+ xmSetPatternA -- Sets pattern attributes
+ xmSetPattern -- Varargs stub for xmSetPatternA
+
+ SYNOPSIS
+ success = xmSetPatternA(si, pattNum, tagList)
+ D0 A0 D0 A1
+
+ ULONG xmSetPatternA(struct SongInfo *,ULONG,struct TagItem *);
+
+
+ success = xmSetPattern (si,pattNum,tag1,...)
+
+ ULONG xmSetPattern(struct SongInfo *,ULONG,LONG Tag1,...);
+
+ FUNCTION
+ Sets attributes of a pattern by calling the SNGM_SETPATTERN method.
+
+ INPUTS
+ si - pointer to the song which contains the pattern to be set.
+ tagList - list of attributes to set. See SNGM_SETPATTERN for
+ possible tags.
+
+ RESULT
+ Non zero for success
+
+ NOTE
+ In order to set patterns attributes, you should have exclusive
+ access to the song. Always obtain a lock before you call this
+ function on public songs.
+
+ SEE ALSO
+ xmAddPattern(), songclass/SNGM_SETPATTERN
+
+\fxmodule/xmSetSongLen xmodule/xmSetSongLen
+
+ NAME
+ xmSetSongLen -- Set the number of song positions
+
+ SYNOPSIS
+ sequence = xmSetSongLen(songInfo, length)
+ D0 A0 D0:16
+
+ UWORD *xmSetSongLen(struct SongInfo *, UWORD);
+
+ FUNCTION
+ Allocates space in the song for a sequence table of the given
+ length. If no sequence exists yet, a new one is allocated.
+ Increasing the song length may require the sequence table to be
+ expanded, in which case it will be re-allocated and the old sequence
+ entries will be copied. Decreasing the song length could also cause
+ a reallocation of the sequence table if the size drops down enough.
+ Setting the song length to 0 will free the sequence table and return
+ NULL.
+
+ INPUTS
+ songInfo - pointer to a SongInfo structure.
+ length - new length of song sequence table.
+
+ NOTE
+ This function will also adjust the CurrentPos field if it is found
+ beyond the song end
+
+ BUGS
+ This funtion is quite useless because all it does is setting the
+ SNGA_Length attribute in the song object. Setting the SNGA_Length
+ attribute yourself is easier and faster if you are already setting
+ other attributes in the same song.
+
+ RESULT
+ Pointer to the newly allocated sequence table or NULL for failure,
+ in which case the previous sequence table is left untouched.
+
+\f
\ No newline at end of file
--- /dev/null
+#ifndef BOOPSISTUBS_H
+#define BOOPSISTUBS_H
+/*
+** $VER: BoopsiStubs.h 1.2 (1.9.97)
+**
+** Copyright (C) 1997 Bernardo Innocenti. All rights reserved.
+**
+** Use 4 chars wide TABs to read this file
+**
+** Using these inline versions of the amiga.lib boopsi support functions
+** results in faster and smaller code against their linked library
+** counterparts. When debug is active, these functions will also
+** validate the parameters you pass in.
+**
+*/
+
+#ifndef COMPILERSPECIFIC_H
+#include "CompilerSpecific.h"
+#endif /* COMPILERSPECIFIC_H */
+
+#ifndef DEBUG_H
+#include "Debug.h"
+#endif /* DEBUG_H */
+
+/* This definition will prevent the redefinition of the following stubs with
+ * their amiga.lib equivalents. This only works if you are using a patched
+ * version of <clib/alib_protos.h>
+ */
+#define USE_BOOPSI_STUBS
+
+
+
+/* the _HookPtr type is a shortcut for a pointer to a hook function */
+
+typedef ASMCALL ULONG (*_HookPtr)
+ (REG(a0, Class *), REG(a2, Object *), REG(a1, APTR));
+
+INLINE ULONG CoerceMethodA (struct IClass *cl, Object *o, Msg message)
+{
+ ASSERT_VALIDNO0(cl)
+ ASSERT_VALID(o)
+
+ return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message);
+}
+
+INLINE ULONG DoSuperMethodA (struct IClass *cl, Object *o, Msg message)
+{
+ ASSERT_VALIDNO0(cl)
+ ASSERT_VALID(o)
+
+ cl = cl->cl_Super;
+ return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message);
+}
+
+INLINE ULONG DoMethodA (Object *o, Msg message)
+{
+ Class *cl;
+ ASSERT_VALIDNO0(o)
+ cl = OCLASS (o);
+ ASSERT_VALIDNO0(cl)
+
+ return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message);
+}
+
+
+
+/* Var-args versions of the above functions. SAS/C is clever enough to inline these,
+ * while gcc and egcs refuse to inline a function with '...' (yikes!). The GCC
+ * versions of these functions are macro blocks similar to those used in the
+ * inline/#?.h headers.
+ */
+#if defined(__SASC) || defined (__STORM__)
+
+ INLINE ULONG CoerceMethod (struct IClass *cl, Object *o, ULONG MethodID, ...)
+ {
+ ASSERT_VALIDNO0(cl)
+ ASSERT_VALID(o)
+
+ return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID);
+ }
+
+ INLINE ULONG DoSuperMethod (struct IClass *cl, Object *o, ULONG MethodID, ...)
+ {
+ ASSERT_VALIDNO0(cl)
+ ASSERT_VALID(o)
+
+ cl = cl->cl_Super;
+ return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID);
+ }
+
+ INLINE ULONG DoMethod (Object *o, ULONG MethodID, ...)
+ {
+ Class *cl;
+
+ ASSERT_VALIDNO0(o)
+ cl = OCLASS (o);
+ ASSERT_VALIDNO0(cl)
+
+ return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID);
+ }
+
+ /* varargs stub for the OM_NOTIFY method */
+ INLINE void NotifyAttrs (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...)
+ {
+ ASSERT_VALIDNO0(o)
+ ASSERT_VALID(gi)
+
+ DoMethod (o, OM_NOTIFY, &attr1, gi, flags);
+ }
+
+ /* varargs stub for the OM_UPDATE method */
+ INLINE void UpdateAttrs (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...)
+ {
+ ASSERT_VALIDNO0(o)
+ ASSERT_VALID(gi)
+
+ DoMethod (o, OM_UPDATE, &attr1, gi, flags);
+ }
+
+#elif defined(__GNUC__)
+
+ #define CoerceMethod(cl, o, msg...) \
+ ({ \
+ ULONG _msg[] = { msg }; \
+ ASSERT_VALIDNO0(cl) \
+ ASSERT_VALID(o) \
+ ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)_msg); \
+ })
+
+ #define DoSuperMethod(cl, o, msg...) \
+ ({ \
+ Class *_cl; \
+ ULONG _msg[] = { msg }; \
+ ASSERT_VALID(o) \
+ ASSERT_VALIDNO0(cl) \
+ _cl = cl = cl->cl_Super; \
+ ASSERT_VALIDNO0(_cl) \
+ ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \
+ })
+
+ #define DoMethod(o, msg...) \
+ ({ \
+ Class *_cl; \
+ ULONG _msg[] = { msg }; \
+ ASSERT_VALIDNO0(o) \
+ _cl = OCLASS(o); \
+ ASSERT_VALIDNO0(_cl) \
+ ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \
+ })
+
+ /* Var-args stub for the OM_NOTIFY method */
+ #define NotifyAttrs(o, gi, flags, attrs...) \
+ ({ \
+ Class *_cl; \
+ ULONG _attrs[] = { attrs }; \
+ ULONG _msg[] = { OM_NOTIFY, (ULONG)_attrs, (ULONG)gi, flags }; \
+ ASSERT_VALIDNO0(o) \
+ _cl = OCLASS(o); \
+ ASSERT_VALIDNO0(_cl) \
+ ASSERT_VALID(gi) \
+ ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \
+ })
+
+ /* Var-args stub for the OM_UPDATE method */
+ #define UpdateAttrs(o, gi, flags, attrs...) \
+ ({ \
+ Class *_cl; \
+ ULONG _attrs[] = { attrs }; \
+ ULONG _msg[] = { OM_UPDATE, (ULONG)_attrs, (ULONG)gi, flags }; \
+ ASSERT_VALIDNO0(o) \
+ _cl = OCLASS(o); \
+ ASSERT_VALIDNO0(_cl) \
+ ASSERT_VALID(gi) \
+ ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \
+ })
+#endif
+
+/* Nobody else needs this anymore... */
+#undef _HookPtr
+
+#endif /* !BOOPSISTUBS_H */
--- /dev/null
+
+How to make a new catalog for XModule
+*************************************
+
+ This file contains all the strings used in XModule. Lines beginning with
+a semicolon (;) are comments and are ignored by the catalog generator.
+Every string is made up two lines:
+
+ MSG_READING_TYPE_MODULE_CHN (//)
+ Reading %s module with %ld tracks...
+
+ The first line is a symbol which identifies its meaning inside XModule.
+You do not need to modify it. The characters "(//)" are required by the
+catalog generator and shouldn't be modified.
+
+ The second line is the message itself. You should insert the
+translation in your own language here. In this example, the sequences
+"%s" and "%d" have a special meaning. At run time, they will be replaced
+by a value, let's say "TakeTracker" and "12", so that the resulting
+string would become "Reading TakeTracker module with 12 tracks...".
+
+ Your language might require a different ordering of the % symbols in the
+sentence, for instance "12 tracks TakeTracker module...". In this case,
+you can translate the string as follows:
+
+ %2$ld tracks %1$s module...
+
+ The template is %<position>$<sequence>, where <position> is the original
+position of the % command, <sequence> is the original command sequence.
+
+ For those amongst you who are not programmers, the meaning of some strings
+may be obscure. For instance, you might find a string like "Ok|Cancel",
+which describes the possible answers to a requesters. You should leave the
+'|' character alone and avoid inserting additional spaces before and after
+it.
+
+ Gadget labels have a "_GAD" postfix, while menu lables end with "_MEN".
+Some gadget labels will have underscore characters ('_') before their key
+shortcut. When you translate these labels, you shoud try to keep the same
+key (ie: "_Good Morning" -> "Buon _Giorno"). If it isn't possible,
+choose another meaningful letter, but avoid using the same key for two
+different gadgets in the same window, otherwise the user will get confused.
+
+ To help you with the translation, the template catalog "Empty.ct" is
+provided. This file has blank lines where the translated strings should
+be inserted, followed by comment lines with the original english string.
+Just rename it to <language>.ct and fill in all the empty lines.
+
+ When you are done with the translation, use a catalog generator like
+FlexCat, KitKat or CatComp to make the xxx.catalog file. Then, test the
+new catalog with XModule and check that every message is correct. To make
+a catalog with FlexCat, you can use the following command line:
+
+ FlexCat Catalogs/XModule.cd Catalogs/<language>.ct
+ CATALOG=Catalogs/<language>/XModule.catalog
+
+ Each time a new version of XModule gets released, the old catalogs will
+probably have to be updated to include all the new strings. In this case,
+I'll send you the new catalog translation file and ask you to update it.
+Of course, you are free to refuse if you do not want to do it, but, please,
+let me know this in time, so I can look around for another translator.
--- /dev/null
+##
+## Make the stuff in the catalogs/ subdirectory
+##
+## NOTE: FlexCat is a locale catalog generator made by Jochen Wiedmann.
+## It can be found on Aminet in the archive "dev/misc/FlexCat.lha".
+
+
+include /config.mk
+
+LANGUAGES = deutsch français italiano nederlands
+CATALOGS = $(LANGUAGES:%=%/$(PROJNAME).catalog)
+
+all: $(CATALOGS)
+
+clean:
+ -Delete $(CATALOGS)
+
+.PHONY: all clean NewCTFiles
+
+
+$(CATALOGS): %/$(PROJNAME).catalog : %.ct
+ FlexCat $(PROJNAME).cd $@ CATALOG=$<
+ @FileNote $< "$(@:%.ct=%) catalog for $(PROJNAME)"
+
+# All the catalogs depend on the master catalog descriptor
+$(CATALOGS): $(PROJNAME).cd
+
+
+# Make NewCTFiles to create updated CT files for all supported languages
+#
+NewCTFiles:
+ $(FLEXCAT) $(PROJNAME).cd italiano.ct NEWCTFILE italiano.ct
+ $(FLEXCAT) $(PROJNAME).cd deutsch.ct NEWCTFILE deutsch.ct
+ $(FLEXCAT) $(PROJNAME).cd français.ct NEWCTFILE français.ct
+ $(FLEXCAT) $(PROJNAME).cd nederlands.ct NEWCTFILE nederlands.ct
+ $(FLEXCAT) $(PROJNAME).cd NEWCTFILE empty.ct
+
+
+# old make rules, not used any more
+#
+#Catalogs/Italiano/XModule.catalog: Catalogs/italiano.ct
+# FlexCat Catalogs/XModule.cd Catalogs/italiano.ct CATALOG=Catalogs/Italiano/XModule.catalog
+# @FileNote Catalogs/Italiano/XModule.catalog "Italiano catalog for XModule translated by Steven Cantini"
+#
+#Catalogs/Deutsch/XModule.catalog: Catalogs/deutsch.ct
+# FlexCat Catalogs/XModule.cd Catalogs/deutsch.ct CATALOG=Catalogs/Deutsch/XModule.catalog
+# @FileNote Catalogs/Deutsch/XModule.catalog "Deutsch catalog for XModule translated by Michael Reichenbach"
+#
+#Catalogs/Français/XModule.catalog: Catalogs/français.ct
+# FlexCat Catalogs/XModule.cd Catalogs/français.ct CATALOG=Catalogs/Français/XModule.catalog
+# @FileNote Catalogs/Français/XModule.catalog "Français catalog for XModule translated by Julien Wilk"
+#
+#Catalogs/Nederlands/XModule.catalog: Catalogs/nederlands.ct
+# FlexCat Catalogs/XModule.cd Catalogs/nederlands.ct CATALOG=Catalogs/Nederlands/XModule.catalog
+# @FileNote Catalogs/Nederlands/XModule.catalog "Dutch catalog for XModule translated by Ji Yong Dijkhuis"
--- /dev/null
+## version $VER: Deutsch.ct 1.0 (12.6.95)
+## language deutsch
+## codeset 0
+;
+## chunk AUTH Deutsch catalog translation by Michael Reichenbach <htw564@htw-dresden.de>
+;**
+;** XModule.cd 3.9
+;**
+;** Copyright (C) 1995,96 Bernardo Innocenti
+;**
+;
+; **********************************
+; * Strings for all source modules *
+; **********************************
+;
+;
+MSG_NULL
+
+;
+; This string should remain blank.
+;
+MSG_OK
+Ok
+;Ok
+;
+MSG_UNDERSCORE_USE_GAD
+
+;_Use
+;
+MSG_UNDERSCORE_OK_GAD
+
+;_Ok
+;
+MSG_UNDERSCORE_CANCEL_GAD
+
+;_Cancel
+;
+MSG_YES_OR_NO
+Ja|Nein
+;Yes|No
+;
+MSG_RETRY_OR_CANCEL
+Nochmal versuchen|Abbrechen
+;Retry|Cancel
+;
+MSG_PROCEED_OR_CANCEL
+
+;Proceed|Cancel
+;
+MSG_CONTINUE
+Weiter
+;Continue
+;
+MSG_NO_FREE_STORE
+Nicht genug freier Speicher.
+;Insufficient memory.
+;
+MSG_BREAK
+Abgebrochen.
+;Aborted.
+;
+MSG_ERR_LOAD
+Kann \"%s\" nicht laden: %s.
+;Unable to load \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_CANT_OPEN
+Kann Datei \"%s\" nicht öffnen: %s.
+;Cannot open file \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_READING
+Fehler beim Lesen von \"%s\": %s.
+;Error reading \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_WRITING
+Fehler beim Schreiben von \"%s\": %s.
+;Error writing \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+;
+; *****************************
+; * Strings for Compression.c *
+; *****************************
+;
+MSG_DECRUNCHING
+Entpacke...
+;Decrunching...
+;
+MSG_NOTHING_IN_ARC
+Im Archiv \"%s\" nichts gefunden.
+;Nothing found in archive \"%s\".
+;
+MSG_CANT_LOAD_COMPRESSED
+Kann komprimierte Datei nicht laden.
+;Unable to load compressed file.
+;
+MSG_ERROR_DECOMPRESSING
+Fehler beim Entpacken der Datei \"%s\": %s.
+;Error decompressing file \"%s\": %s.
+;
+;
+; ********************
+; * Strings for Cx.c *
+; ********************
+;
+MSG_BAD_HOTKEY
+Falsche Commodity HotKey Beschreibung.
+;Bad Commodity HotKey description.
+;
+;
+; ***************************
+; * Strings for all loaders *
+; ***************************
+;
+MSG_READING_PATTS
+Lese Pattern...
+;Reading Patterns...
+;
+MSG_READING_INSTS_INFO
+Lese Informationen über Instrumente...
+;Reading Instruments Info...
+;
+MSG_READING_INSTS
+Lese Instrumente...
+;Reading Instruments...
+;
+MSG_ERR_CANT_LOAD_PATT
+FEHLER: Kann Pattern %ld nicht laden.
+;ERROR: Couldn't load pattern %ld.
+;
+MSG_ERR_CANT_LOAD_INST
+FEHLER: Kann Instrument %lx nicht laden.
+;ERROR: Couldn't load instrument %lx.
+;
+MSG_ERR_NO_MEM_FOR_INST
+FEHLER: Nicht genug freier Speicher für Instrument %lx.
+;ERROR: Not enough memory for instrument %lx.
+;
+MSG_ERR_NOT_A_SAMPLE
+FEHLER: Instrument %lx ist kein Sample.
+;ERROR: Instrument %lx is not a sample.
+;
+MSG_SONG_TOO_LONG
+WARNUNG: Songlänge überschreitet Maximum. Wird abgeschnitten.
+;WARNING: Song length exceeds maximum. Will be truncated.
+;
+MSG_SONG_HAS_TOO_MANY_PATT
+WARNUNG: Song überschreitet maximale Patternzahl.
+;WARNING: Song exceeds maximum number of patterns.
+;
+MSG_PATT_TOO_MANY_TRACKS
+WARNUNG: Pattern %ld hat zu viele Tracks. Verkürze auf %ld Tracks.
+;WARNING: Pattern %ld has too many tracks. Cropping to %ld tracks.
+;
+MSG_PATT_TOO_MANY_LINES
+WARNING: Pattern %ld hat zu viele Linien. Verkürze auf %ld Linien.
+;WARNING: Pattern %ld has too many lines. Cropping to %ld lines.
+;
+MSG_INVALID_NOTE
+WARNUNG: Ungültiger Ton %ld (Patt %ld Track %ld Linie %ld).
+;WARNING: Invalid note %ld (Patt %ld Track %ld Line %ld).
+;
+MSG_UNKNOWN_EFF
+Unbekannter Effekt: $%lx (Patt %ld Track %ld Linie %ld).
+;Unknown effect: $%lx (Patt %ld Track %ld Line %ld).
+;
+MSG_EXTRA_DATA_AFTER_MOD
+WARNUNG: Zusätzliche Daten hinter regulärem Modul: Werden ignoriert.
+;WARNING: Extra data found after valid module: Will be ignored.
+;
+MSG_WRITING_HEADER
+Schreibe Modul Header...
+;Writing Header...
+;
+MSG_WRITING_PATTS
+Schreibe Pattern...
+;Writing Patterns...
+;
+MSG_WRITING_INSTS
+Schreibe Instrumente...
+;Writing Instruments...
+;
+MSG_WRITING_INSTINFO
+Schreibe Informationen über Instrumente...
+;Writing Instruments Info...
+;
+MSG_WRITING_INSTDATA
+Schreibe Instrumentdaten...
+;Writing Instruments Data...
+;
+MSG_NOTE_TOO_LOW
+WARNUNG: Ton in Patt %ld Track %ld Linie %ld ist zu tief.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too low.
+;
+MSG_NOTE_TOO_HIGH
+WARNUNG: Ton in Patt %ld Track %ld Linie %ld ist zu hoch.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too high.
+;
+MSG_NO_MEM_TO_HALVE
+WARNUNG: Nicht genug freier Speicher, um Lautstärke des Instruments %lx zu halbieren.
+;WARNING: Not enough memory to halve volume of instrument %lx.
+;
+;
+; *****************************
+; * Strings for Get/SaveMED.c *
+; *****************************
+;
+MSG_READING_MMD
+Lade MMD%lc Modul...
+;Loading MMD%lc module...
+; The %lc is the MED format (0, 1, 2, ...)
+;
+MSG_UNSUPPORTED_MMD_FORMAT
+FEHLER: Nicht unterstütztes OctaMED Format.
+;ERROR: Unsupported OctaMED format.
+;
+MSG_WRONG_EFFECT_IN_MMD0
+WARNUNG: Der Effekt %lx wird im MMD0 Format nicht unterstützt. Benutze MMD1 oder höher.
+;WARNING: Effect %lx is not supported in MMD0 format. Use MMD1 or better.
+;
+;
+; ***********************************
+; * Strings for ScreamTrackerHook.c *
+; ***********************************
+;
+MSG_ADLIB_INSTR
+FEHLER: Instrument %lx ist ein ADLib %s.
+;ERROR: Instrument %lx is an ADLib %s.
+; %s is the name of the ADLib instrument (eg: Snare).
+;
+MSG_TRACK_OUT_OF_RANGE
+WARNUNG: Track %lx ist außerhalb des Speicherbereichs.
+;WARNING: Track %lx is out of range.
+;
+MSG_UNKNOWN_SAMPLE_COMPRESSION
+WARNUNG: Unbekannte Samplekompression des Instruments %lx.
+;WARNING: Unknown sample compression for instrument %lx.
+;
+MSG_INST_IS_STEREO
+WARNUNG: Instrument %lx ist ein Stereo Sample.
+;WARNING: Instrument %lx is a stereo sample.
+;
+MSG_INST_IS_16BIT
+WARNING: Instrument %lx ist 16bit: kann es nicht laden.
+;WARNING: Instrument %lx is 16bit: unable to load it.
+;
+;
+; *****************************
+; * Strings for TrackerHook.c *
+; *****************************
+;
+MSG_READING_TYPE_MODULE
+Lese %s Modul...
+;Reading %s module...
+; (%s is the module format)
+;
+MSG_EXCEEDS_64_PATTS
+WARNUNG: Modul überschreitet 64 Pattern. ProTracker 2.3 wird benötigt, um es abzuspielen.
+;NOTE: Module exceeds 64 patterns. You need ProTracker 2.3 to play it.
+;
+MSG_MODULE_ID
+
+;Tracker ID: \"%s\"
+;
+MSG_MODULE_HAS_N_CHN
+
+;Module has %lu tracks...
+;
+MSG_SONG_HAS_NO_INSTS
+Diese Datei ist ein Song und enthält keine Instrumente.
+;This file is a song and doesn't contain instruments.
+;
+MSG_EXCEEDS_MAXPAATTS
+WARNUNG: Modul überschreitet %ld Pattern.
+;WARNING: Module execeeds %ld patterns.
+;
+MSG_PATT_WILL_GROW
+Pattern %ld wird auf 64 Linien verlängert (war vorher %ld Linien lang).
+;Pattern %ld will grow to 64 lines (was %ld lines long).
+;
+MSG_SPLITTING_PATT
+Teile Pattern %ld (war vorher %ld Linien lang).
+;Splitting pattern %ld (was %ld lines long).
+;
+; **************************
+; * Strings for SaveMIDI.c *
+; **************************
+;
+MSG_CHOOSING_CHANNELS
+Wähle Kanäle...
+;Choosing Channels...
+;
+MSG_WRITING_MIDI_TRACKS
+Sichere MIDI Tracks...
+;Writing MIDI Tracks...
+;
+MSG_TOO_MANY_CHANNELS
+FEHLER: Song benötigt zu viele MIDI Kanäle.
+;ERROR: Song requires too many MIDI channels.
+;
+;
+; *********************
+; * Strings for Gui.c *
+; *********************
+;
+MSG_REALLY_QUIT_XMODULE
+XModule wirklich beenden?
+;Really Quit XModule?
+;
+MSG_CLOSE_ALL_WINDOWS
+Bitte alle fremden Fenster schließen\n\
+und dann `Weiter' wählen.
+;Please close all visitor windows\n\
+;and then select `Continue'.
+;
+;
+; ***********************
+; * Strings for Instr.c *
+; ***********************
+;
+MSG_UNKNOWN_IFF
+Unbekanntes IFF Format %s.
+;Unknown IFF format %s.
+;
+MSG_ILLEGAL_IFF_STRUCTURE
+Ungültige IFF Struktur.
+;Illegal IFF structure.
+;
+MSG_SELECT_RAW_MODE
+Instrument Format nicht erkannt.\n\
+Bitte RAW Modus wählen.
+;Unrecognized instrument format.\n\
+;Please select RAW mode.
+;
+MSG_RAW_MODES
+Signed 8bit|Unsigned 8bit|Abbrechen
+;Signed 8bit|Unsigned 8bit|Cancel
+;
+MSG_DATATYPES_ERROR
+DataType Fehler: %s.
+;DataTypes error: %s.
+; %s is a detailed description of the problem.
+;
+MSG_UNKNOWN_COMPRESSION
+Unbekannter Kompressionstyp.
+;Unknown compression type.
+;
+MSG_SAMPLE_WRONG_SIZE
+%lu Bit Samples werden nicht unterstützt.
+;%lu bit samples are not supported.
+; %lu is the number of bits per sample.
+;
+MSG_SAMPLE_NOT_MONO
+Andere Samples außer MONO werden nicht unterstützt.
+;Samples other than MONO are not supported.
+;
+MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS
+Samples mit %ld Kanälen werden nicht unterstützt.
+;Samples with %ld channels are not supported.
+;
+MSG_NO_MEMORY_TO_OPTIMIZE_INSTR
+WARNUNG: Nicht genug freier Speicher, um Instrument %lx zu optimieren.
+;WARNING: insufficient memory to optimize instrument %lx.
+;
+MSG_INSTR_WILL_SHRINK
+Instrument %lx wird von %ld auf %ld gekürzt.
+;Instrument %lx will shrink from %ld to %ld.
+;
+MSG_INSTR_DUPES_REMOVED
+Doppelte Instrumente gefunden und entfernt: %lx == %lx.
+;Duplicate instruments found and removed: %lx == %lx.
+;
+MSG_INSTR_UNUSED
+Instrument %lx wird nie benutzt und deshalb entfernt.
+;Instrument %lx was never used and it's being removed.
+;
+;
+; **********************
+; * Strings for Misc.c *
+; **********************
+;
+MSG_OPENLIB_VER_FAIL
+Konnte %s Version %ld oder höher nicht öffnen.
+;Couldn't open \"%s\" version %ld or greater.
+;
+MSG_OPENLIB_FAIL
+Konnte %s nicht öffnen.
+;Couldn't open \"%s\".
+;
+;
+; ***************************
+; * Strings for Operators.c *
+; ***************************
+;
+MSG_CANT_INSERT_PATT
+Kann Pattern nicht einfügen: Maximale Anzahl erreicht.
+;Unable to insert pattern: Maximum number of patterns reached.
+;
+MSG_PATT_UNUSED
+Pattern %ld wird nicht benutzt und deshalb gelöscht.
+;Pattern %ld is not used and is beeing deleted.
+;
+MSG_PATT_CUT
+Pattern %ld wird in Linie %ld abgeschnitten.
+;Pattern %ld will be cut at line %ld.
+;
+MSG_PATT_DUPE
+Doppelte Pattern gefunden und entfernt: %ld == %ld.
+;Duplicate patterns found and removed: %ld == %ld.
+; The two %ld are the pattern numbers.
+;
+MSG_SONG_LEN_DIFFERENT
+Songlängen sind verschieden. Benutze kürzere.
+;WARNING: Song lengths are different. Using shorter one.
+;
+MSG_PATT_LEN_DIFFERENT
+Verschiedene Patternlängen an Position %ld. Benutze kürzere.
+;WARNING: Different pattern lengths at position %ld. Using shorter one.
+;
+MSG_ERR_INSTR_OVERFLOW
+
+;ERROR: Instruments overflow. Try remapping the instruments.
+;
+;
+; ***********************
+; * Strings for Prefs.c *
+; ***********************
+;
+MSG_BAD_PREFS_VERSION
+Falsche Version der Voreinstellungsdatei.
+;Incorrect version of preferences file
+;
+;
+; ****************************
+; * Strings for Requesters.c *
+; ****************************
+;
+MSG_XMODULE_REQUEST
+XModule Anfrage
+;XModule Request
+; This is the title of requesters put out by XModule.
+;
+MSG_CLONE_WB
+Klone Workbench Bildschirm
+;Clone Workbench Screen
+;
+MSG_CLOSE_FILEREQUESTER
+Bitte Dateiauswahlfenster schließen\n und dann `Weiter' wählen.
+;Please close FileRequester\n and then select `Continue'.
+;
+MSG_SELECT_MODULES
+Modul(e) auswählen...
+;Select Module(s)...
+;
+MSG_SELECT_INSTRUMENTS
+Instrument(e) auswählen...
+;Select Instrument(s)...
+;
+MSG_SELECT_PATTERN
+Pattern auswählen...
+;Select Pattern...
+;
+MSG_SAVE_MODULE
+Sichere Modul...
+;Save Module...
+;
+MSG_SAVE_INSTRUMENT
+Sichere Instrument...
+;Save Instrument...
+;
+MSG_SAVE_PATTERN
+Sichere Pattern...
+;Save Pattern...
+;
+MSG_FILE_EXISTS
+Datei \"%s\"\nexistiert bereits.
+;File \"%s\"\nalready exists.
+;
+MSG_OVERWRITE
+Überschreiben|Andere auswählen|Abbrechen
+;Overwrite|Choose Another|Abort
+;
+;
+; **********************
+; * Strings for Song.c *
+; **********************
+;
+MSG_CLOSE_ALL_SONGS
+
+;Please close all open songs\n\
+;and then select `Continue'.
+;
+MSG_UNESPECTED_EOF
+Unerwartetes Dateiende.
+;Unespected end of file.
+;
+MSG_MODULE_LOADED_OK
+Modul geladen OK.
+;Module loaded OK.
+;
+MSG_MODULE_SAVED_OK
+Modul gesichert OK.
+;Module saved OK.
+;
+MSG_UNKNOWN_SAVE_FORMAT
+FEHLER: Unbekanntes Speicherformat.
+;ERROR: Unrecognized save format.
+;
+MSG_INVALID_LOOP_REMOVED
+Ungültige Schleife für Instrument %lx entfernt.
+;Removed invalid loop for instrument %lx.
+;
+MSG_INVALID_LOOP_FIXED
+Ungültige Schleife für Instrument %lx repariert.
+;Fixed invalid loop for instrument %lx.
+;
+MSG_SONG_HAS_NO_PATTS
+WARNUNG: Song hat keine Pattern.
+;WARNING: Song has no patterns.
+;
+MSG_SONG_HAS_NO_SEQ
+WARNUNG: Song hat keine Sequence.
+;WARNING: Song has no sequence.
+;
+MSG_INVALID_SONG_POS
+WARNUNG: Songposition %ld bezieht sich auf Pattern %ld, das nicht existiert.
+;WARNING: Song position %ld references pattern %ld, which doesn't exist.
+;
+MSG_UNKNOWN_MOD_FORMAT
+Kann Modul Format nicht identifizieren.\n\
+(Eine Datendatei als Modul zu laden ist unklug)
+;Unable to identify module format.\n\
+;(Loading a data file as a module is unwise)
+;
+MSG_SOUND_PRO_CANCEL
+SoundTracker 15|ProTracker|Abbrechen
+;SoundTracker 15|ProTracker|Cancel
+;
+MSG_AUTHOR_UNKNOWN
+Unbekannt
+;Unknown
+;
+MSG_SONG_UNTITLED
+Unbenannt
+;Untitled
+;
+;
+; **********************
+; * Strings for Help.c *
+; **********************
+;
+MSG_AMIGAGUIDE_ERROR
+AmigaGuide Fehler:
+;AmigaGuide error:
+; Note one blank space at the end of this line!
+;
+;
+; ***************************
+; * Strings for PattPrefs.c *
+; ***************************
+;
+MSG_PATTPREFS_TITLE
+
+;Pattern Editor Settings
+;
+;
+MSG_ADVANCE_TRACKS_GAD
+
+;Advance _Tracks
+;
+MSG_ADVANCE_LINES_GAD
+
+;Advance _Lines
+;
+MSG_MAX_UNDO_LEVELS_GAD
+
+;Max _Undo Levels
+;
+MSG_MAX_UNDO_MEM_GAD
+
+;Max Undo _Memory
+;
+MSG_CLIPBOARD_UNIT_GAD
+
+;Cli_pboard Unit
+;
+MSG_SCROLLER_POS_GAD
+
+;_Scroller Position
+;
+MSG_VERT_WRAP_GAD
+
+;_Vertical Wrap
+;
+MSG_HORIZ_WRAP_GAD
+
+;_Horizontal Wrap
+;
+MSG_HEX_LINE_NUMBERS_GAD
+
+;He_x Line Numbers
+;
+MSG_BLANK_ZERO_GAD
+
+;Blank _Zero Digits
+;
+MSG_BACKDROP_GAD
+
+;Backdrop _Window
+;
+MSG_HORIZ_SCROLLER_GAD
+
+;Ho_rizontal Scroller
+;
+MSG_DO_RULER_GAD
+
+;Display Cursor Ruler
+;
+MSG_DO_TINY_LINES_GAD
+
+;Display Tiny Lines
+;
+MSG_EDITOR_FONT_GAD
+
+;_Editor Font
+;
+MSG_BACKGROUND_PEN_GAD
+
+;Background
+;
+MSG_TEXT_PEN_GAD
+
+;T_ext
+;
+MSG_LINES_PEN_GAD
+
+;Li_nes
+;
+MSG_TINY_LINES_PEN_GAD
+
+;Tin_y Lines
+;
+MSG_RIGHT_GAD
+
+;Right
+;
+MSG_LEFT_GAD
+
+;Left
+;
+MSG_OFF_GAD
+
+;Off
+;
+MSG_PATT_TOO_LONG
+FEHLER: Pattern würde die maximale Linienanzahl überschreiten.
+;ERROR: Pattern would exceed the maximum number of lines.
+;
+;
+; *****************************
+; * Strings for SequenceWin.c *
+; *****************************
+;
+;
+MSG_SEQUENCE_TITLE
+
+;Sequence Editor
+;
+;
+MSG_SEQUENCE_GAD
+
+;Sequence
+;
+MSG_UNDERSCORE_ADD_GAD
+
+;_Add
+;
+MSG_UP_GAD
+
+;Up
+;
+MSG_DOWN_GAD
+
+;Down
+;
+MSG_UNDERSCORE_NAME_GAD
+
+;_Name
+;
+MSG_PATTERNS_GAD
+
+;Patterns
+;
+MSG_UNDERSCORE_UP_GAD
+
+;_Up
+;
+MSG_UNDERSCORE_DOWN_GAD
+
+;_Down
+;
+MSG_UNDERSCORE_INS_GAD
+
+;_Ins
+;
+;
+MSG_UNNAMED
+
+;-- unnamed --
+;
+;
+; ********************************
+; * Strings for InstrumentsWin.c *
+; ********************************
+;
+MSG_INSTRUMENTS_TITLE
+
+;Instruments
+;
+;
+MSG_VOLUME_GAD
+
+;_Volume
+;
+MSG_FINETUNE_GAD
+
+;_FineTune
+;
+MSG_LENGTH_GAD
+
+;Length
+;
+MSG_KIND_GAD
+
+;_Kind
+;
+MSG_EDIT_DOTS_GAD
+
+;_Edit...
+;
+MSG_SAMPLE_GAD
+
+;Sample
+;
+MSG_SYNTH_GAD
+
+;Synth
+;
+MSG_HYBRID_GAD
+
+;Hybrid
+;
+;
+MSG_INSTRUMENTS_MEN
+
+;Instruments
+;
+MSG_LOAD_MEN
+
+;Load...
+;
+MSG_REMAP_MEN
+
+;Remap
+;
+MSG_SAVE_COMPRESSED_MEN
+
+;Save Compressed
+;
+MSG_SAVE_RAW_MEN
+
+;Save Raw
+;
+;
+MSG_EMPTY
+
+;-- empty --
+;
+;
+; *************************
+; * Strings for PlayWin.c *
+; *************************
+;
+MSG_PLAY_TITLE
+
+;Play
+;
+;
+MSG_VOL_GAD
+
+;_Vol
+;
+MSG_POS_GAD
+
+;Pos
+;
+MSG_TIME_GAD
+
+;Time
+;
+MSG_RST_GAD
+
+;_Rst
+; Stands for Reset/Restart
+;
+MSG_PLAYER_INIT_ERR
+Abspielprogramm Initialisierungsfehler: %ld.
+;Player initialization error: %ld.
+;
+;
+; *****************************
+; * Strings for ProgressWin.c *
+; *****************************
+;
+MSG_PROGRESS_TITLE
+
+;XModule is working...
+;
+MSG_LOG_TITLE
+
+;XModule Log
+;
+;
+MSG_UNDERSCORE_ABORT_GAD
+
+;_Abort
+;
+;
+MSG_PERCENT_DONE
+%ld of %ld (%ld%% erledigt)
+;%ld of %ld (%ld%% done)
+; This string appears inside the progress window gauge.
+; eg: "5 of 20 (25% done)".
+; Leave that double percent (%%) alone! :-)
+;
+;
+; *****************************
+; * Strings for SongInfoWin.c *
+; *****************************
+;
+MSG_SONGINFO_TITLE
+
+;Song Information
+;
+;
+MSG_UNDERSCORE_NEW_GAD
+
+;Ne_w
+;
+MSG_OPEN_GAD
+
+;_Open...
+;
+MSG_SAVE_GAD
+
+;_Save
+;
+MSG_DEL_GAD
+
+;Del
+;
+MSG_SONG_NAME_GAD
+
+;Song _Name
+;
+MSG_AUTHOR_NAME_GAD
+
+;_Author
+;
+MSG_DEF_TEMPO_GAD
+
+;_Tempo
+;
+MSG_DEF_SPEED_GAD
+
+;S_peed
+;
+MSG_RESTART_GAD
+
+;_Restart
+;
+MSG_LENGHT_GAD
+
+;Length
+;
+MSG_NUM_PATTS_GAD
+
+;Num Patterns
+;
+MSG_NUM_TRACKS_GAD
+
+;Tracks
+;
+MSG_TOT_MOD_SIZE_GAD
+
+;Total Module Size
+;
+MSG_TOT_INST_SIZE_GAD
+
+;Total Instruments Size
+;
+;
+MSG_SONG_MEN
+
+;Song
+;
+MSG_MERGE_SONGS_MEN
+
+;Merge Songs
+;
+MSG_JOIN_SONGS_MEN
+
+;Join Songs
+;
+;
+MSG_JOIN_REQUIRES_TWO_SONGS
+Verbinden benötigt zwei Songs.
+;ERROR: Join requires two songs.
+;
+MSG_MERGE_REQUIRES_TWO_SONGS
+Verschmelzen benötigt zwei Songs.
+;ERROR: Merge requires two songs.
+;
+MSG_DISCARD_CURRENT_SONG
+Aktuellen Song verwerfen?
+;Discard current song?
+;
+;
+; ****************************
+; * Strings for ToolBoxWin.c *
+; ****************************
+;
+MSG_TOOLBOX_TITLE
+
+;ToolBox
+;
+;
+MSG_PLAY_GAD
+
+;Play...
+;
+MSG_SONGS_GAD
+
+;_Songs...
+;
+MSG_PATTERNS_DOTS_GAD
+
+;_Patterns...
+;
+MSG_INSTRUMENTS_GAD
+
+;_Instruments...
+;
+MSG_SEQUENCE_DOTS_GAD
+
+;Se_quence...
+;
+MSG_OPTIMIZATION_GAD
+
+;_Optimization...
+;
+MSG_PROJECT_MEN
+
+;Project
+;
+MSG_NEW_MEN
+
+;New
+;
+MSG_OPEN_MEN
+
+;Open...
+;
+MSG_OPEN_NEW_MEN
+
+;Open New...
+;
+MSG_SAVE_MEN
+
+;Save
+;
+MSG_SAVE_AS_MEN
+
+;Save As...
+;
+MSG_CLEAR_MEN
+
+;Clear...
+;
+MSG_ABOUT_MEN
+
+;About...
+;
+MSG_HELP_MEN
+
+;Help...
+;
+MSG_ICONIFY_MEN
+
+;Iconify...
+;
+MSG_QUIT_MEN
+
+;Quit
+;
+MSG_SETTINGS_MEN
+
+;Settings
+;
+MSG_SAVE_FORMAT_MEN
+
+;Save Format...
+;
+MSG_USER_INTERFACE_MEN
+
+;User Interface...
+;
+MSG_SAVE_ICONS_MEN
+
+;Save Icons
+;
+MSG_CONFIRM_OVERWRITE_MEN
+
+;Confirm Overwrite
+;
+MSG_CONFIRM_EXIT_MEN
+
+;Confirm Exit
+;
+MSG_VERBOSE_MEN
+
+;Verbose Log
+;
+MSG_OPEN_SETTINGS_MEN
+
+;Open Settings...
+;
+MSG_SAVE_SETTINGS_MEN
+
+;Save Settings
+;
+MSG_SAVE_SETTINGS_AS_MEN
+
+;Save Settings As...
+;
+;
+MSG_ABOUT_TEXT
+%s %s\n\
+Ein Programm zum Bearbeiten von Musikmodulen\n\n\
+\
+%s\n\
+Alle Rechte vorbehalten.\n\n\
+\
+Internet: bernie@shock.cosmos.it\n\n\
+Fidonet: 2:332/125.1\n\
+\
+Freier CHIP Speicher: %ldKB\n\
+Freier FAST Speicher: %ldKB\n\n\
+Öffentlicher Bildschirm: %s\n\
+ARexx Port: %s\n\
+CX HotKey: %s\n\
+Sprache: %s
+;%s\n\
+;A Music Module Processing Utility\n\n\
+;\
+;%s\n\
+;All rights reserved.\n\n\
+;\
+;Internet: bernie@shock.cosmos.it\n\n\
+;FidoNet: 2:332/125.1\n\
+;\
+;Free CHIP Memory: %ldKB\n\
+;Free FAST Memory: %ldKB\n\n\
+;Public Screen: %s\n\
+;ARexx Port: %s\n\
+;Cx HotKey: %s\n\
+;Language: %s
+;
+MSG_DEFAULT
+--Voreingestellt--
+;-- Default --
+;
+MSG_DISABLED
+--Gesperrt--
+;-- Disabled --
+;
+;
+; *********************************
+; * Strings for OptimizationWin.c *
+; *********************************
+;
+MSG_OPTIMIZATION_TITLE
+
+;Module Optimization
+;
+;
+MSG_OPTIMIZE_GAD
+
+;_Optimize
+;
+MSG_REM_UNUSED_PATTS_GAD
+
+;Remove Unused _Patterns
+;
+MSG_REM_DUPLICATE_PATTS_GAD
+
+;Remove _Duplicate Patterns
+;
+MSG_REM_UNUSED_INSTR_GAD
+
+;Remove Unused _Instruments
+;
+MSG_REM_DUP_INSTR_GAD
+
+;Remove Duplicate I_nstruments
+;
+MSG_CUT_AFTER_LOOP_GAD
+
+;Cut Instruments After _Loop
+;
+MSG_CUT_ZERO_TAILS_GAD
+
+;Cut Instrument _Zero Tails
+;
+MSG_CUT_PATTERNS_GAD
+
+;Cut _Breaked Patterns
+;
+MSG_REMAP_INSTRUMENTS_GAD
+
+;_Remap Instruments
+;
+;
+MSG_SAVED_X_BYTES
+%ld Bytes eingespart (%ld%%)
+;Saved %ld bytes (%ld%%)
+;
+;
+; **************************
+; * Strings for PrefsWin.c *
+; **************************
+;
+;
+MSG_PREFS_TITLE
+
+;User Interface Settings
+;
+;
+MSG_PUBLIC_SCREEN_GAD
+
+;_Public Screen
+;
+MSG_DISPLAY_MODE_GAD
+
+;Display _Mode
+;
+MSG_WINDOW_FONT_GAD
+
+;_Window Font
+;
+MSG_LISTVIEW_FONT_GAD
+
+;_ListView Font
+;
+MSG_REQUESTERS_GAD
+
+;_Requesters
+;
+MSG_USE_DATATYPES_GAD
+
+;Use _DataTypes
+;
+MSG_APPICON_GAD
+
+;Put App_Icon
+;
+MSG_REFRESH_GAD
+
+;Refres_h
+;
+MSG_LOG_TO_FILE_GAD
+
+;Log To File
+;
+MSG_LOG_LEVEL_GAD
+
+;_Log Level
+;
+MSG_ASK_AUTOSAVE_GAD
+
+;Confirm _Autosave
+;
+MSG_AUTOSAVE_TIME_GAD
+
+;Autosave Mi_nutes
+;
+MSG_DO_BACKUPS_GAD
+
+;Create _Backups
+;
+MSG_BACKUP_TEMPLATE_GAD
+
+;Bac_kup Template
+;
+MSG_BACKUP_VERSIONS_GAD
+
+;Backup _Versions
+;
+MSG_ASL_GAD
+
+;Asl
+;
+MSG_REQTOOLS_GAD
+
+;ReqTools
+;
+MSG_SMART_GAD
+
+;Smart
+;
+MSG_SIMPLE_GAD
+
+;Simple
+;
+MSG_CLONE_DEF_SCREEN
+
+;--Clone Default Screen--
+;
+;
+; ****************************
+; * Strings for PatternWin.c *
+; ****************************
+;
+MSG_PATTERN_TITLE
+
+;Pattern Editor
+;
+;
+MSG_PATTERNS_MEN
+
+;Patterns
+;
+MSG_SIZE_MEN
+
+;Size...
+;
+MSG_EDIT_MEN
+
+;Edit
+;
+MSG_MARK_MEN
+
+;Mark
+;
+MSG_CUT_MEN
+
+;Cut
+;
+MSG_COPY_MEN
+
+;Copy
+;
+MSG_PASTE_MEN
+
+;Paste
+;
+MSG_ERASE_MEN
+
+;Erase
+;
+MSG_UNDO_MEN
+
+;Undo
+;
+MSG_REDO_MEN
+
+;Redo
+;
+MSG_EDITOR_SETTINGS_MEN
+
+;Editor Settings...
+;
+;
+; ***************************
+; * Strings for SaversWin.c *
+; ***************************
+;
+MSG_SAVERS_TITLE
+
+;Savers
+;
+;
+MSG_SF_SEQUENCE_GAD
+
+;Se_quence
+;
+MSG_SF_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_SF_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_SF_NAMES_GAD
+
+;Names
+;
+MSG_ADD_ICON_GAD
+
+;Add I_con
+;
+MSG_MODE_GAD
+
+;_Mode
+;
+MSG_OPTIONS_DOTS_GAD
+
+;Options...
+;
+MSG_NONE_GAD
+
+;None
+;
+MSG_XPK_GAD
+
+;XPK
+;
+MSG_LHA_GAD
+
+;LhA
+;
+;
+MSG_DESCRIPTION
+
+;Description
+;
+MSG_AUTHOR
+
+;Author
+;
+MSG_MAXLENGTH
+
+;Max Length
+;
+MSG_MAXTRACKS
+
+;Max Tracks
+;
+MSG_MAXINSTRUMENTS
+
+;Max Instruments
+;
+MSG_MAXPATTERNS
+
+;Max Patterns
+;
+MSG_MAXPATTLEN
+
+;Max Pattern Length
+;
+MSG_MAXSAMPLELEN
+
+;Max Sample Length
+;
+;
+; **********************
+; * Strings ClearWin.c *
+; **********************
+;
+MSG_CLEAR_TITLE
+
+;Clear Module
+;
+;
+MSG_CLR_SEQUENCE_GAD
+
+;_Sequence
+;
+MSG_CLR_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_CLR_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_CLEARMOD_GAD
+
+;_Clear
+;
+;
+; ***************************
+; * Strings for SampleWin.c *
+; ***************************
+;
+MSG_SAMPLE_TITLE
+
+;Sample Editor
+;
+;
+MSG_RENDER_MEN
+
+;Render
+;
+MSG_POINTS_MEN
+
+;Points
+;
+MSG_LINES_MEN
+
+;Lines
+;
+MSG_FILLED_MEN
+
+;Filled
+;
+;
+; *****************************
+; * Strings for PattSizeWin.c *
+; *****************************
+;
+MSG_PATTSIZE_TITLE
+
+;Pattern Attributes
+;
+;
+MSG_LINES_GAD
+
+;_Lines
+;
+MSG_TRACKS_GAD
+
+;_Tracks
+;
+MSG_DOUBLE_GAD
+
+;_Double
+;
+MSG_HALVE_GAD
+
+;_Halve
+;
+;
+; *************************
+; * Strings for Library.c *
+; *************************
+;
+MSG_SONG_TOO_MANY_TRACKS
+
+;WARNING: The selected saver supports max %lu tracks, but the song has %lu tracks.
+;
+MSG_SONG_TOO_MANY_PATTS
+
+;WARNING: The selected saver supports max %lu patterns; the song has %lu patterns.
+;
+MSG_SONG_TOO_MANY_INSTRS
+
+;WARNING: The selected saver supports max %lu instruments, last instrument is %lu.
+;
+MSG_SONG_TOO_MANY_POS
+
+;WARNING: The selected saver supports max %lu positions, but the song has %lu positions.
+;
+MSG_PATT_LENGTH_INVALID
+
+;WARNING: The length of pattern %ld ($%lx lines) isn't allowed with the selected saver.
+;
+MSG_INSTR_TOO_LONG
+WARNUNG: Instrument %lx ist zu lang.
+;WARNING: Instrument %lx is too long and will be cropped to %lu bytes.
+;
+MSG_WILL_MODIFY_SONG
+
+;Some modifications need to be performed on this song\n\
+;in order to adapt it to the limitations of the\n\
+;destination format.
+;
+MSG_TRY_REMAPPING_INSTRUMENTS
+
+;Some instruments are placed beyond the limit for\n\
+;the selected format.\n\
+;Remapping the instruments now could help saving\n\
+;all them along with the selected format.
+;
+MSG_SAVING_MODULE
+
+;Saving %s module \"%s\"...
+; the first %s is the format name, the second %s is the song name.
--- /dev/null
+## version $VER: XX.catalog XX.XX (XX.XX.XX)
+## language X
+## codeset 0
+;
+;**
+;** XModule.cd 3.9
+;**
+;** Copyright (C) 1995,96 Bernardo Innocenti
+;**
+;
+; **********************************
+; * Strings for all source modules *
+; **********************************
+;
+;
+MSG_NULL
+
+;
+; This string should remain blank.
+;
+MSG_OK
+
+;Ok
+;
+MSG_UNDERSCORE_USE_GAD
+
+;_Use
+;
+MSG_UNDERSCORE_OK_GAD
+
+;_Ok
+;
+MSG_UNDERSCORE_CANCEL_GAD
+
+;_Cancel
+;
+MSG_YES_OR_NO
+
+;Yes|No
+;
+MSG_RETRY_OR_CANCEL
+
+;Retry|Cancel
+;
+MSG_PROCEED_OR_CANCEL
+
+;Proceed|Cancel
+;
+MSG_CONTINUE
+
+;Continue
+;
+MSG_NO_FREE_STORE
+
+;Insufficient memory.
+;
+MSG_BREAK
+
+;Aborted.
+;
+MSG_ERR_LOAD
+
+;Unable to load \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_CANT_OPEN
+
+;Cannot open file \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_READING
+
+;Error reading \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_WRITING
+
+;Error writing \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+;
+; *****************************
+; * Strings for Compression.c *
+; *****************************
+;
+MSG_DECRUNCHING
+
+;Decrunching...
+;
+MSG_NOTHING_IN_ARC
+
+;Nothing found in archive \"%s\".
+;
+MSG_CANT_LOAD_COMPRESSED
+
+;Unable to load compressed file.
+;
+MSG_ERROR_DECOMPRESSING
+
+;Error decompressing file \"%s\": %s.
+;
+;
+; ********************
+; * Strings for Cx.c *
+; ********************
+;
+MSG_BAD_HOTKEY
+
+;Bad Commodity HotKey description.
+;
+;
+; ***************************
+; * Strings for all loaders *
+; ***************************
+;
+MSG_READING_PATTS
+
+;Reading Patterns...
+;
+MSG_READING_INSTS_INFO
+
+;Reading Instruments Info...
+;
+MSG_READING_INSTS
+
+;Reading Instruments...
+;
+MSG_ERR_CANT_LOAD_PATT
+
+;ERROR: Couldn't load pattern %ld.
+;
+MSG_ERR_CANT_LOAD_INST
+
+;ERROR: Couldn't load instrument %lx.
+;
+MSG_ERR_NO_MEM_FOR_INST
+
+;ERROR: Not enough memory for instrument %lx.
+;
+MSG_ERR_NOT_A_SAMPLE
+
+;ERROR: Instrument %lx is not a sample.
+;
+MSG_SONG_TOO_LONG
+
+;WARNING: Song length exceeds maximum. Will be truncated.
+;
+MSG_SONG_HAS_TOO_MANY_PATT
+
+;WARNING: Song exceeds maximum number of patterns.
+;
+MSG_PATT_TOO_MANY_TRACKS
+
+;WARNING: Pattern %ld has too many tracks. Cropping to %ld tracks.
+;
+MSG_PATT_TOO_MANY_LINES
+
+;WARNING: Pattern %ld has too many lines. Cropping to %ld lines.
+;
+MSG_INVALID_NOTE
+
+;WARNING: Invalid note %ld (Patt %ld Track %ld Line %ld).
+;
+MSG_UNKNOWN_EFF
+
+;Unknown effect: $%lx (Patt %ld Track %ld Line %ld).
+;
+MSG_EXTRA_DATA_AFTER_MOD
+
+;WARNING: Extra data found after valid module: Will be ignored.
+;
+MSG_WRITING_HEADER
+
+;Writing Header...
+;
+MSG_WRITING_PATTS
+
+;Writing Patterns...
+;
+MSG_WRITING_INSTS
+
+;Writing Instruments...
+;
+MSG_WRITING_INSTINFO
+
+;Writing Instruments Info...
+;
+MSG_WRITING_INSTDATA
+
+;Writing Instruments Data...
+;
+MSG_NOTE_TOO_LOW
+
+;WARNING: Note at Patt %ld Track %ld Line %ld is too low.
+;
+MSG_NOTE_TOO_HIGH
+
+;WARNING: Note at Patt %ld Track %ld Line %ld is too high.
+;
+MSG_NO_MEM_TO_HALVE
+
+;WARNING: Not enough memory to halve volume of instrument %lx.
+;
+;
+; *****************************
+; * Strings for Get/SaveMED.c *
+; *****************************
+;
+MSG_READING_MMD
+
+;Loading MMD%lc module...
+; The %lc is the MED format (0, 1, 2, ...)
+;
+MSG_UNSUPPORTED_MMD_FORMAT
+
+;ERROR: Unsupported OctaMED format.
+;
+MSG_WRONG_EFFECT_IN_MMD0
+
+;WARNING: Effect %lx is not supported in MMD0 format. Use MMD1 or better.
+;
+;
+; ***********************************
+; * Strings for ScreamTrackerHook.c *
+; ***********************************
+;
+MSG_ADLIB_INSTR
+
+;ERROR: Instrument %lx is an ADLib %s.
+; %s is the name of the ADLib instrument (eg: Snare).
+;
+MSG_TRACK_OUT_OF_RANGE
+
+;WARNING: Track %lx is out of range.
+;
+MSG_UNKNOWN_SAMPLE_COMPRESSION
+
+;WARNING: Unknown sample compression for instrument %lx.
+;
+MSG_INST_IS_STEREO
+
+;WARNING: Instrument %lx is a stereo sample.
+;
+MSG_INST_IS_16BIT
+
+;WARNING: Instrument %lx is 16bit: unable to load it.
+;
+;
+; *****************************
+; * Strings for TrackerHook.c *
+; *****************************
+;
+MSG_READING_TYPE_MODULE
+
+;Reading %s module...
+; (%s is the module format)
+;
+MSG_EXCEEDS_64_PATTS
+
+;NOTE: Module exceeds 64 patterns. You need ProTracker 2.3 to play it.
+;
+MSG_MODULE_ID
+
+;Tracker ID: \"%s\"
+;
+MSG_MODULE_HAS_N_CHN
+
+;Module has %lu tracks...
+;
+MSG_SONG_HAS_NO_INSTS
+
+;This file is a song and doesn't contain instruments.
+;
+MSG_EXCEEDS_MAXPAATTS
+
+;WARNING: Module execeeds %ld patterns.
+;
+MSG_PATT_WILL_GROW
+
+;Pattern %ld will grow to 64 lines (was %ld lines long).
+;
+MSG_SPLITTING_PATT
+
+;Splitting pattern %ld (was %ld lines long).
+;
+; **************************
+; * Strings for SaveMIDI.c *
+; **************************
+;
+MSG_CHOOSING_CHANNELS
+
+;Choosing Channels...
+;
+MSG_WRITING_MIDI_TRACKS
+
+;Writing MIDI Tracks...
+;
+MSG_TOO_MANY_CHANNELS
+
+;ERROR: Song requires too many MIDI channels.
+;
+;
+; *********************
+; * Strings for Gui.c *
+; *********************
+;
+MSG_REALLY_QUIT_XMODULE
+
+;Really Quit XModule?
+;
+MSG_CLOSE_ALL_WINDOWS
+
+;Please close all visitor windows\n\
+;and then select `Continue'.
+;
+;
+; ***********************
+; * Strings for Instr.c *
+; ***********************
+;
+MSG_UNKNOWN_IFF
+
+;Unknown IFF format %s.
+;
+MSG_ILLEGAL_IFF_STRUCTURE
+
+;Illegal IFF structure.
+;
+MSG_SELECT_RAW_MODE
+
+;Unrecognized instrument format.\n\
+;Please select RAW mode.
+;
+MSG_RAW_MODES
+
+;Signed 8bit|Unsigned 8bit|Cancel
+;
+MSG_DATATYPES_ERROR
+
+;DataTypes error: %s.
+; %s is a detailed description of the problem.
+;
+MSG_UNKNOWN_COMPRESSION
+
+;Unknown compression type.
+;
+MSG_SAMPLE_WRONG_SIZE
+
+;%lu bit samples are not supported.
+; %lu is the number of bits per sample.
+;
+MSG_SAMPLE_NOT_MONO
+
+;Samples other than MONO are not supported.
+;
+MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS
+
+;Samples with %ld channels are not supported.
+;
+MSG_NO_MEMORY_TO_OPTIMIZE_INSTR
+
+;WARNING: insufficient memory to optimize instrument %lx.
+;
+MSG_INSTR_WILL_SHRINK
+
+;Instrument %lx will shrink from %ld to %ld.
+;
+MSG_INSTR_DUPES_REMOVED
+
+;Duplicate instruments found and removed: %lx == %lx.
+;
+MSG_INSTR_UNUSED
+
+;Instrument %lx was never used and it's being removed.
+;
+;
+; **********************
+; * Strings for Misc.c *
+; **********************
+;
+MSG_OPENLIB_VER_FAIL
+
+;Couldn't open \"%s\" version %ld or greater.
+;
+MSG_OPENLIB_FAIL
+
+;Couldn't open \"%s\".
+;
+;
+; ***************************
+; * Strings for Operators.c *
+; ***************************
+;
+MSG_CANT_INSERT_PATT
+
+;Unable to insert pattern: Maximum number of patterns reached.
+;
+MSG_PATT_UNUSED
+
+;Pattern %ld is not used and is beeing deleted.
+;
+MSG_PATT_CUT
+
+;Pattern %ld will be cut at line %ld.
+;
+MSG_PATT_DUPE
+
+;Duplicate patterns found and removed: %ld == %ld.
+; The two %ld are the pattern numbers.
+;
+MSG_SONG_LEN_DIFFERENT
+
+;WARNING: Song lengths are different. Using shorter one.
+;
+MSG_PATT_LEN_DIFFERENT
+
+;WARNING: Different pattern lengths at position %ld. Using shorter one.
+;
+MSG_ERR_INSTR_OVERFLOW
+
+;ERROR: Instruments overflow. Try remapping the instruments.
+;
+;
+; ***********************
+; * Strings for Prefs.c *
+; ***********************
+;
+MSG_BAD_PREFS_VERSION
+
+;Incorrect version of preferences file
+;
+;
+; ****************************
+; * Strings for Requesters.c *
+; ****************************
+;
+MSG_XMODULE_REQUEST
+
+;XModule Request
+; This is the title of requesters put out by XModule.
+;
+MSG_CLONE_WB
+
+;Clone Workbench Screen
+;
+MSG_CLOSE_FILEREQUESTER
+
+;Please close FileRequester\n and then select `Continue'.
+;
+MSG_SELECT_MODULES
+
+;Select Module(s)...
+;
+MSG_SELECT_INSTRUMENTS
+
+;Select Instrument(s)...
+;
+MSG_SELECT_PATTERN
+
+;Select Pattern...
+;
+MSG_SAVE_MODULE
+
+;Save Module...
+;
+MSG_SAVE_INSTRUMENT
+
+;Save Instrument...
+;
+MSG_SAVE_PATTERN
+
+;Save Pattern...
+;
+MSG_FILE_EXISTS
+
+;File \"%s\"\nalready exists.
+;
+MSG_OVERWRITE
+
+;Overwrite|Choose Another|Abort
+;
+;
+; **********************
+; * Strings for Song.c *
+; **********************
+;
+MSG_CLOSE_ALL_SONGS
+
+;Please close all open songs\n\
+;and then select `Continue'.
+;
+MSG_UNESPECTED_EOF
+
+;Unespected end of file.
+;
+MSG_MODULE_LOADED_OK
+
+;Module loaded OK.
+;
+MSG_MODULE_SAVED_OK
+
+;Module saved OK.
+;
+MSG_UNKNOWN_SAVE_FORMAT
+
+;ERROR: Unrecognized save format.
+;
+MSG_INVALID_LOOP_REMOVED
+
+;Removed invalid loop for instrument %lx.
+;
+MSG_INVALID_LOOP_FIXED
+
+;Fixed invalid loop for instrument %lx.
+;
+MSG_SONG_HAS_NO_PATTS
+
+;WARNING: Song has no patterns.
+;
+MSG_SONG_HAS_NO_SEQ
+
+;WARNING: Song has no sequence.
+;
+MSG_INVALID_SONG_POS
+
+;WARNING: Song position %ld references pattern %ld, which doesn't exist.
+;
+MSG_UNKNOWN_MOD_FORMAT
+
+;Unable to identify module format.\n\
+;(Loading a data file as a module is unwise)
+;
+MSG_SOUND_PRO_CANCEL
+
+;SoundTracker 15|ProTracker|Cancel
+;
+MSG_AUTHOR_UNKNOWN
+
+;Unknown
+;
+MSG_SONG_UNTITLED
+
+;Untitled
+;
+;
+; **********************
+; * Strings for Help.c *
+; **********************
+;
+MSG_AMIGAGUIDE_ERROR
+
+;AmigaGuide error:
+; Note one blank space at the end of this line!
+;
+;
+; ***************************
+; * Strings for PattPrefs.c *
+; ***************************
+;
+MSG_PATTPREFS_TITLE
+
+;Pattern Editor Settings
+;
+;
+MSG_ADVANCE_TRACKS_GAD
+
+;Advance _Tracks
+;
+MSG_ADVANCE_LINES_GAD
+
+;Advance _Lines
+;
+MSG_MAX_UNDO_LEVELS_GAD
+
+;Max _Undo Levels
+;
+MSG_MAX_UNDO_MEM_GAD
+
+;Max Undo _Memory
+;
+MSG_CLIPBOARD_UNIT_GAD
+
+;Cli_pboard Unit
+;
+MSG_SCROLLER_POS_GAD
+
+;_Scroller Position
+;
+MSG_VERT_WRAP_GAD
+
+;_Vertical Wrap
+;
+MSG_HORIZ_WRAP_GAD
+
+;_Horizontal Wrap
+;
+MSG_HEX_LINE_NUMBERS_GAD
+
+;He_x Line Numbers
+;
+MSG_BLANK_ZERO_GAD
+
+;Blank _Zero Digits
+;
+MSG_BACKDROP_GAD
+
+;Backdrop _Window
+;
+MSG_HORIZ_SCROLLER_GAD
+
+;Ho_rizontal Scroller
+;
+MSG_DO_RULER_GAD
+
+;Display Cursor Ruler
+;
+MSG_DO_TINY_LINES_GAD
+
+;Display Tiny Lines
+;
+MSG_EDITOR_FONT_GAD
+
+;_Editor Font
+;
+MSG_BACKGROUND_PEN_GAD
+
+;Background
+;
+MSG_TEXT_PEN_GAD
+
+;T_ext
+;
+MSG_LINES_PEN_GAD
+
+;Li_nes
+;
+MSG_TINY_LINES_PEN_GAD
+
+;Tin_y Lines
+;
+MSG_RIGHT_GAD
+
+;Right
+;
+MSG_LEFT_GAD
+
+;Left
+;
+MSG_OFF_GAD
+
+;Off
+;
+MSG_PATT_TOO_LONG
+
+;ERROR: Pattern would exceed the maximum number of lines.
+;
+;
+; *****************************
+; * Strings for SequenceWin.c *
+; *****************************
+;
+;
+MSG_SEQUENCE_TITLE
+
+;Sequence Editor
+;
+;
+MSG_SEQUENCE_GAD
+
+;Sequence
+;
+MSG_UNDERSCORE_ADD_GAD
+
+;_Add
+;
+MSG_UP_GAD
+
+;Up
+;
+MSG_DOWN_GAD
+
+;Down
+;
+MSG_UNDERSCORE_NAME_GAD
+
+;_Name
+;
+MSG_PATTERNS_GAD
+
+;Patterns
+;
+MSG_UNDERSCORE_UP_GAD
+
+;_Up
+;
+MSG_UNDERSCORE_DOWN_GAD
+
+;_Down
+;
+MSG_UNDERSCORE_INS_GAD
+
+;_Ins
+;
+;
+MSG_UNNAMED
+
+;-- unnamed --
+;
+;
+; ********************************
+; * Strings for InstrumentsWin.c *
+; ********************************
+;
+MSG_INSTRUMENTS_TITLE
+
+;Instruments
+;
+;
+MSG_VOLUME_GAD
+
+;_Volume
+;
+MSG_FINETUNE_GAD
+
+;_FineTune
+;
+MSG_LENGTH_GAD
+
+;Length
+;
+MSG_KIND_GAD
+
+;_Kind
+;
+MSG_EDIT_DOTS_GAD
+
+;_Edit...
+;
+MSG_SAMPLE_GAD
+
+;Sample
+;
+MSG_SYNTH_GAD
+
+;Synth
+;
+MSG_HYBRID_GAD
+
+;Hybrid
+;
+;
+MSG_INSTRUMENTS_MEN
+
+;Instruments
+;
+MSG_LOAD_MEN
+
+;Load...
+;
+MSG_REMAP_MEN
+
+;Remap
+;
+MSG_SAVE_COMPRESSED_MEN
+
+;Save Compressed
+;
+MSG_SAVE_RAW_MEN
+
+;Save Raw
+;
+;
+MSG_EMPTY
+
+;-- empty --
+;
+;
+; *************************
+; * Strings for PlayWin.c *
+; *************************
+;
+MSG_PLAY_TITLE
+
+;Play
+;
+;
+MSG_VOL_GAD
+
+;_Vol
+;
+MSG_POS_GAD
+
+;Pos
+;
+MSG_TIME_GAD
+
+;Time
+;
+MSG_RST_GAD
+
+;_Rst
+; Stands for Reset/Restart
+;
+MSG_PLAYER_INIT_ERR
+
+;Player initialization error: %ld.
+;
+;
+; *****************************
+; * Strings for ProgressWin.c *
+; *****************************
+;
+MSG_PROGRESS_TITLE
+
+;XModule is working...
+;
+MSG_LOG_TITLE
+
+;XModule Log
+;
+;
+MSG_UNDERSCORE_ABORT_GAD
+
+;_Abort
+;
+;
+MSG_PERCENT_DONE
+
+;%ld of %ld (%ld%% done)
+; This string appears inside the progress window gauge.
+; eg: "5 of 20 (25% done)".
+; Leave that double percent (%%) alone! :-)
+;
+;
+; *****************************
+; * Strings for SongInfoWin.c *
+; *****************************
+;
+MSG_SONGINFO_TITLE
+
+;Song Information
+;
+;
+MSG_UNDERSCORE_NEW_GAD
+
+;Ne_w
+;
+MSG_OPEN_GAD
+
+;_Open...
+;
+MSG_SAVE_GAD
+
+;_Save
+;
+MSG_DEL_GAD
+
+;Del
+;
+MSG_SONG_NAME_GAD
+
+;Song _Name
+;
+MSG_AUTHOR_NAME_GAD
+
+;_Author
+;
+MSG_DEF_TEMPO_GAD
+
+;_Tempo
+;
+MSG_DEF_SPEED_GAD
+
+;S_peed
+;
+MSG_RESTART_GAD
+
+;_Restart
+;
+MSG_LENGHT_GAD
+
+;Length
+;
+MSG_NUM_PATTS_GAD
+
+;Num Patterns
+;
+MSG_NUM_TRACKS_GAD
+
+;Tracks
+;
+MSG_TOT_MOD_SIZE_GAD
+
+;Total Module Size
+;
+MSG_TOT_INST_SIZE_GAD
+
+;Total Instruments Size
+;
+;
+MSG_SONG_MEN
+
+;Song
+;
+MSG_MERGE_SONGS_MEN
+
+;Merge Songs
+;
+MSG_JOIN_SONGS_MEN
+
+;Join Songs
+;
+;
+MSG_JOIN_REQUIRES_TWO_SONGS
+
+;ERROR: Join requires two songs.
+;
+MSG_MERGE_REQUIRES_TWO_SONGS
+
+;ERROR: Merge requires two songs.
+;
+MSG_DISCARD_CURRENT_SONG
+
+;Discard current song?
+;
+;
+; ****************************
+; * Strings for ToolBoxWin.c *
+; ****************************
+;
+MSG_TOOLBOX_TITLE
+
+;ToolBox
+;
+;
+MSG_PLAY_GAD
+
+;Play...
+;
+MSG_SONGS_GAD
+
+;_Songs...
+;
+MSG_PATTERNS_DOTS_GAD
+
+;_Patterns...
+;
+MSG_INSTRUMENTS_GAD
+
+;_Instruments...
+;
+MSG_SEQUENCE_DOTS_GAD
+
+;Se_quence...
+;
+MSG_OPTIMIZATION_GAD
+
+;_Optimization...
+;
+MSG_PROJECT_MEN
+
+;Project
+;
+MSG_NEW_MEN
+
+;New
+;
+MSG_OPEN_MEN
+
+;Open...
+;
+MSG_OPEN_NEW_MEN
+
+;Open New...
+;
+MSG_SAVE_MEN
+
+;Save
+;
+MSG_SAVE_AS_MEN
+
+;Save As...
+;
+MSG_CLEAR_MEN
+
+;Clear...
+;
+MSG_ABOUT_MEN
+
+;About...
+;
+MSG_HELP_MEN
+
+;Help...
+;
+MSG_ICONIFY_MEN
+
+;Iconify...
+;
+MSG_QUIT_MEN
+
+;Quit
+;
+MSG_SETTINGS_MEN
+
+;Settings
+;
+MSG_SAVE_FORMAT_MEN
+
+;Save Format...
+;
+MSG_USER_INTERFACE_MEN
+
+;User Interface...
+;
+MSG_SAVE_ICONS_MEN
+
+;Save Icons
+;
+MSG_CONFIRM_OVERWRITE_MEN
+
+;Confirm Overwrite
+;
+MSG_CONFIRM_EXIT_MEN
+
+;Confirm Exit
+;
+MSG_VERBOSE_MEN
+
+;Verbose Log
+;
+MSG_OPEN_SETTINGS_MEN
+
+;Open Settings...
+;
+MSG_SAVE_SETTINGS_MEN
+
+;Save Settings
+;
+MSG_SAVE_SETTINGS_AS_MEN
+
+;Save Settings As...
+;
+;
+MSG_ABOUT_TEXT
+
+;%s\n\
+;A Music Module Processing Utility\n\n\
+;\
+;%s\n\
+;All rights reserved.\n\n\
+;\
+;Internet: bernie@shock.cosmos.it\n\n\
+;FidoNet: 2:332/125.1\n\
+;\
+;Free CHIP Memory: %ldKB\n\
+;Free FAST Memory: %ldKB\n\n\
+;Public Screen: %s\n\
+;ARexx Port: %s\n\
+;Cx HotKey: %s\n\
+;Language: %s
+;
+MSG_DEFAULT
+
+;-- Default --
+;
+MSG_DISABLED
+
+;-- Disabled --
+;
+;
+; *********************************
+; * Strings for OptimizationWin.c *
+; *********************************
+;
+MSG_OPTIMIZATION_TITLE
+
+;Module Optimization
+;
+;
+MSG_OPTIMIZE_GAD
+
+;_Optimize
+;
+MSG_REM_UNUSED_PATTS_GAD
+
+;Remove Unused _Patterns
+;
+MSG_REM_DUPLICATE_PATTS_GAD
+
+;Remove _Duplicate Patterns
+;
+MSG_REM_UNUSED_INSTR_GAD
+
+;Remove Unused _Instruments
+;
+MSG_REM_DUP_INSTR_GAD
+
+;Remove Duplicate I_nstruments
+;
+MSG_CUT_AFTER_LOOP_GAD
+
+;Cut Instruments After _Loop
+;
+MSG_CUT_ZERO_TAILS_GAD
+
+;Cut Instrument _Zero Tails
+;
+MSG_CUT_PATTERNS_GAD
+
+;Cut _Breaked Patterns
+;
+MSG_REMAP_INSTRUMENTS_GAD
+
+;_Remap Instruments
+;
+;
+MSG_SAVED_X_BYTES
+
+;Saved %ld bytes (%ld%%)
+;
+;
+; **************************
+; * Strings for PrefsWin.c *
+; **************************
+;
+;
+MSG_PREFS_TITLE
+
+;User Interface Settings
+;
+;
+MSG_PUBLIC_SCREEN_GAD
+
+;_Public Screen
+;
+MSG_DISPLAY_MODE_GAD
+
+;Display _Mode
+;
+MSG_WINDOW_FONT_GAD
+
+;_Window Font
+;
+MSG_LISTVIEW_FONT_GAD
+
+;_ListView Font
+;
+MSG_REQUESTERS_GAD
+
+;_Requesters
+;
+MSG_USE_DATATYPES_GAD
+
+;Use _DataTypes
+;
+MSG_APPICON_GAD
+
+;Put App_Icon
+;
+MSG_REFRESH_GAD
+
+;Refres_h
+;
+MSG_LOG_TO_FILE_GAD
+
+;Log To File
+;
+MSG_LOG_LEVEL_GAD
+
+;_Log Level
+;
+MSG_ASK_AUTOSAVE_GAD
+
+;Confirm _Autosave
+;
+MSG_AUTOSAVE_TIME_GAD
+
+;Autosave Mi_nutes
+;
+MSG_DO_BACKUPS_GAD
+
+;Create _Backups
+;
+MSG_BACKUP_TEMPLATE_GAD
+
+;Bac_kup Template
+;
+MSG_BACKUP_VERSIONS_GAD
+
+;Backup _Versions
+;
+MSG_ASL_GAD
+
+;Asl
+;
+MSG_REQTOOLS_GAD
+
+;ReqTools
+;
+MSG_SMART_GAD
+
+;Smart
+;
+MSG_SIMPLE_GAD
+
+;Simple
+;
+MSG_CLONE_DEF_SCREEN
+
+;--Clone Default Screen--
+;
+;
+; ****************************
+; * Strings for PatternWin.c *
+; ****************************
+;
+MSG_PATTERN_TITLE
+
+;Pattern Editor
+;
+;
+MSG_PATTERNS_MEN
+
+;Patterns
+;
+MSG_SIZE_MEN
+
+;Size...
+;
+MSG_EDIT_MEN
+
+;Edit
+;
+MSG_MARK_MEN
+
+;Mark
+;
+MSG_CUT_MEN
+
+;Cut
+;
+MSG_COPY_MEN
+
+;Copy
+;
+MSG_PASTE_MEN
+
+;Paste
+;
+MSG_ERASE_MEN
+
+;Erase
+;
+MSG_UNDO_MEN
+
+;Undo
+;
+MSG_REDO_MEN
+
+;Redo
+;
+MSG_EDITOR_SETTINGS_MEN
+
+;Editor Settings...
+;
+;
+; ***************************
+; * Strings for SaversWin.c *
+; ***************************
+;
+MSG_SAVERS_TITLE
+
+;Savers
+;
+;
+MSG_SF_SEQUENCE_GAD
+
+;Se_quence
+;
+MSG_SF_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_SF_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_SF_NAMES_GAD
+
+;Names
+;
+MSG_ADD_ICON_GAD
+
+;Add I_con
+;
+MSG_MODE_GAD
+
+;_Mode
+;
+MSG_OPTIONS_DOTS_GAD
+
+;Options...
+;
+MSG_NONE_GAD
+
+;None
+;
+MSG_XPK_GAD
+
+;XPK
+;
+MSG_LHA_GAD
+
+;LhA
+;
+;
+MSG_DESCRIPTION
+
+;Description
+;
+MSG_AUTHOR
+
+;Author
+;
+MSG_MAXLENGTH
+
+;Max Length
+;
+MSG_MAXTRACKS
+
+;Max Tracks
+;
+MSG_MAXINSTRUMENTS
+
+;Max Instruments
+;
+MSG_MAXPATTERNS
+
+;Max Patterns
+;
+MSG_MAXPATTLEN
+
+;Max Pattern Length
+;
+MSG_MAXSAMPLELEN
+
+;Max Sample Length
+;
+;
+; **********************
+; * Strings ClearWin.c *
+; **********************
+;
+MSG_CLEAR_TITLE
+
+;Clear Module
+;
+;
+MSG_CLR_SEQUENCE_GAD
+
+;_Sequence
+;
+MSG_CLR_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_CLR_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_CLEARMOD_GAD
+
+;_Clear
+;
+;
+; ***************************
+; * Strings for SampleWin.c *
+; ***************************
+;
+MSG_SAMPLE_TITLE
+
+;Sample Editor
+;
+;
+MSG_RENDER_MEN
+
+;Render
+;
+MSG_POINTS_MEN
+
+;Points
+;
+MSG_LINES_MEN
+
+;Lines
+;
+MSG_FILLED_MEN
+
+;Filled
+;
+;
+; *****************************
+; * Strings for PattSizeWin.c *
+; *****************************
+;
+MSG_PATTSIZE_TITLE
+
+;Pattern Attributes
+;
+;
+MSG_LINES_GAD
+
+;_Lines
+;
+MSG_TRACKS_GAD
+
+;_Tracks
+;
+MSG_DOUBLE_GAD
+
+;_Double
+;
+MSG_HALVE_GAD
+
+;_Halve
+;
+;
+; *************************
+; * Strings for Library.c *
+; *************************
+;
+MSG_SONG_TOO_MANY_TRACKS
+
+;WARNING: The selected saver supports max %lu tracks, but the song has %lu tracks.
+;
+MSG_SONG_TOO_MANY_PATTS
+
+;WARNING: The selected saver supports max %lu patterns; the song has %lu patterns.
+;
+MSG_SONG_TOO_MANY_INSTRS
+
+;WARNING: The selected saver supports max %lu instruments, last instrument is %lu.
+;
+MSG_SONG_TOO_MANY_POS
+
+;WARNING: The selected saver supports max %lu positions, but the song has %lu positions.
+;
+MSG_PATT_LENGTH_INVALID
+
+;WARNING: The length of pattern %ld ($%lx lines) isn't allowed with the selected saver.
+;
+MSG_INSTR_TOO_LONG
+
+;WARNING: Instrument %lx is too long and will be cropped to %lu bytes.
+;
+MSG_WILL_MODIFY_SONG
+
+;Some modifications need to be performed on this song\n\
+;in order to adapt it to the limitations of the\n\
+;destination format.
+;
+MSG_TRY_REMAPPING_INSTRUMENTS
+
+;Some instruments are placed beyond the limit for\n\
+;the selected format.\n\
+;Remapping the instruments now could help saving\n\
+;all them along with the selected format.
+;
+MSG_SAVING_MODULE
+
+;Saving %s module \"%s\"...
+; the first %s is the format name, the second %s is the song name.
--- /dev/null
+## version $VER: Français.ct 1.0 (10.9.95)
+## language français
+## codeset 0
+;
+## chunk AUTH Français catalog translation by Julien Wilk <wilkj@esiee.fr>
+;**
+;** XModule.cd 3.9
+;**
+;** Copyright (C) 1995,96 Bernardo Innocenti
+;**
+;
+; **********************************
+; * Strings for all source modules *
+; **********************************
+;
+;
+MSG_NULL
+
+;
+; This string should remain blank.
+;
+MSG_OK
+Ok
+;Ok
+;
+MSG_UNDERSCORE_USE_GAD
+
+;_Use
+;
+MSG_UNDERSCORE_OK_GAD
+
+;_Ok
+;
+MSG_UNDERSCORE_CANCEL_GAD
+
+;_Cancel
+;
+MSG_YES_OR_NO
+Oui|Non
+;Yes|No
+;
+MSG_RETRY_OR_CANCEL
+Réessayer|Abandonner
+;Retry|Cancel
+;
+MSG_PROCEED_OR_CANCEL
+
+;Proceed|Cancel
+;
+MSG_CONTINUE
+Continuer
+;Continue
+;
+MSG_NO_FREE_STORE
+Mémoire insuffisante !
+;Insufficient memory.
+;
+MSG_BREAK
+Tache abandonnée.
+;Aborted.
+;
+MSG_ERR_LOAD
+Impossible de charger \"%s\": %s.
+;Unable to load \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_CANT_OPEN
+Impossible d'ouvrir le fichier \"%s\" %s.
+;Cannot open file \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_READING
+Erreur en lecture de \"%s\": %s.
+;Error reading \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_WRITING
+Erreur d'écriture de \"%s\" %s.
+;Error writing \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+;
+; *****************************
+; * Strings for Compression.c *
+; *****************************
+;
+MSG_DECRUNCHING
+Décompression...
+;Decrunching...
+;
+MSG_NOTHING_IN_ARC
+Aucun module trouvé dans l'archive \"%s\".
+;Nothing found in archive \"%s\".
+;
+MSG_CANT_LOAD_COMPRESSED
+Impossible de charger le fichier compressé.
+;Unable to load compressed file.
+;
+MSG_ERROR_DECOMPRESSING
+Erreur de décompression de \"%s\": %s.
+;Error decompressing file \"%s\": %s.
+;
+;
+; ********************
+; * Strings for Cx.c *
+; ********************
+;
+MSG_BAD_HOTKEY
+Mauvaise définition de la HotKey.
+;Bad Commodity HotKey description.
+;
+;
+; ***************************
+; * Strings for all loaders *
+; ***************************
+;
+MSG_READING_PATTS
+Lecture des Patterns...
+;Reading Patterns...
+;
+MSG_READING_INSTS_INFO
+Lecture des Info-Instruments...
+;Reading Instruments Info...
+;
+MSG_READING_INSTS
+Lecture des Instruments...
+;Reading Instruments...
+;
+MSG_ERR_CANT_LOAD_PATT
+ERREUR: Ne peut charger la pattern %ld.
+;ERROR: Couldn't load pattern %ld.
+;
+MSG_ERR_CANT_LOAD_INST
+ERREUR: Ne peut lire l'instrument %lx.
+;ERROR: Couldn't load instrument %lx.
+;
+MSG_ERR_NO_MEM_FOR_INST
+ERREUR: Pas assez de mémoire pour l'instrument %lx.
+;ERROR: Not enough memory for instrument %lx.
+;
+MSG_ERR_NOT_A_SAMPLE
+ERREUR: L'instrument %lx n'est pas un sample.
+;ERROR: Instrument %lx is not a sample.
+;
+MSG_SONG_TOO_LONG
+ATTENTION: Chanson trop longue. (excédent tronqué).
+;WARNING: Song length exceeds maximum. Will be truncated.
+;
+MSG_SONG_HAS_TOO_MANY_PATT
+ATTENTION: Trop de patterns.
+;WARNING: Song exceeds maximum number of patterns.
+;
+MSG_PATT_TOO_MANY_TRACKS
+ATENTION: La pattern %ld posséde trop de pistes. (Réduite à %ld pistes).
+;WARNING: Pattern %ld has too many tracks. Cropping to %ld tracks.
+;
+MSG_PATT_TOO_MANY_LINES
+ATTENTION: La pattern %ld posséde trop de lignes. (Réduite à %ld lignes).
+;WARNING: Pattern %ld has too many lines. Cropping to %ld lines.
+;
+MSG_INVALID_NOTE
+ATTENTION: Note %ld invalide (Pattern %ld Piste %ld Ligne %ld).
+;WARNING: Invalid note %ld (Patt %ld Track %ld Line %ld).
+;
+MSG_UNKNOWN_EFF
+Effet inconnu : $%lx (Pattern %ld Piste %ld Ligne %ld).
+;Unknown effect: $%lx (Patt %ld Track %ld Line %ld).
+;
+MSG_EXTRA_DATA_AFTER_MOD
+ATTENTION: Données supplémentaire après le module: Elles seront ignorées.
+;WARNING: Extra data found after valid module: Will be ignored.
+;
+MSG_WRITING_HEADER
+Ecriture de l'en-tête...
+;Writing Header...
+;
+MSG_WRITING_PATTS
+Ecriture des Patterns...
+;Writing Patterns...
+;
+MSG_WRITING_INSTS
+Ecriture des Instruments...
+;Writing Instruments...
+;
+MSG_WRITING_INSTINFO
+Ecriture des Info-Instruments...
+;Writing Instruments Info...
+;
+MSG_WRITING_INSTDATA
+Ecriture des Data-Instruments...
+;Writing Instruments Data...
+;
+MSG_NOTE_TOO_LOW
+ATTENTION: La note en Pattern %ld Piste %ld Ligne %ld est trop basse.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too low.
+;
+MSG_NOTE_TOO_HIGH
+ATTENTION: La note en Pattern %ld Piste %ld Ligne %ld est trop haute.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too high.
+;
+MSG_NO_MEM_TO_HALVE
+ATTENTION: Pas assez de mémoire pour réduire le volume de l'instrument %lx.
+;WARNING: Not enough memory to halve volume of instrument %lx.
+;
+;
+; *****************************
+; * Strings for Get/SaveMED.c *
+; *****************************
+;
+MSG_READING_MMD
+Chargement du module MMD%lc...
+;Loading MMD%lc module...
+; The %lc is the MED format (0, 1, 2, ...)
+;
+MSG_UNSUPPORTED_MMD_FORMAT
+ERREUR: Format OctaMED non reconnu.
+;ERROR: Unsupported OctaMED format.
+;
+MSG_WRONG_EFFECT_IN_MMD0
+ATTENTION: L'effet %lx n'est pas reconnu dans le format MMD0. Utilisez le MMD1 ou mieux.
+;WARNING: Effect %lx is not supported in MMD0 format. Use MMD1 or better.
+;
+;
+; ***********************************
+; * Strings for ScreamTrackerHook.c *
+; ***********************************
+;
+MSG_ADLIB_INSTR
+ERREUR: L'instrument %lx est un %s ADLIB.
+;ERROR: Instrument %lx is an ADLib %s.
+; %s is the name of the ADLib instrument (eg: Snare).
+;
+MSG_TRACK_OUT_OF_RANGE
+ATTENTION: La piste %lx est hors de portée.
+;WARNING: Track %lx is out of range.
+;
+MSG_UNKNOWN_SAMPLE_COMPRESSION
+ATTENTION: Type de compression inconnue pour l'intrument %lx.
+;WARNING: Unknown sample compression for instrument %lx.
+;
+MSG_INST_IS_STEREO
+ATTENTION: L'instrument %lx est un sample stéréo.
+;WARNING: Instrument %lx is a stereo sample.
+;
+MSG_INST_IS_16BIT
+ATTENTION: L'instrument %lx est en 16 bits: impossible de le charger.
+;WARNING: Instrument %lx is 16bit: unable to load it.
+;
+;
+; *****************************
+; * Strings for TrackerHook.c *
+; *****************************
+;
+MSG_READING_TYPE_MODULE
+Chargement du module en %s...
+;Reading %s module...
+; (%s is the module format)
+;
+MSG_EXCEEDS_64_PATTS
+ATTENTION: Le module contient plus de 64 patterns. Il nécessite au moins ProTracker 2.3.
+;NOTE: Module exceeds 64 patterns. You need ProTracker 2.3 to play it.
+;
+MSG_MODULE_ID
+
+;Tracker ID: \"%s\"
+;
+MSG_MODULE_HAS_N_CHN
+
+;Module has %lu tracks...
+;
+MSG_SONG_HAS_NO_INSTS
+Ce fichier est une "song" et ne contient aucun instruments.
+;This file is a song and doesn't contain instruments.
+;
+MSG_EXCEEDS_MAXPAATTS
+ATTENTION: Le module contient plus de %ld patterns.
+;WARNING: Module execeeds %ld patterns.
+;
+MSG_PATT_WILL_GROW
+La pattern %ld est rallongée à 64 lignes. Elle n'était que de %ld lignes.
+;Pattern %ld will grow to 64 lines (was %ld lines long).
+;
+MSG_SPLITTING_PATT
+Morcelage de la pattern %ld (longue de %ld lignes).
+;Splitting pattern %ld (was %ld lines long).
+;
+; **************************
+; * Strings for SaveMIDI.c *
+; **************************
+;
+MSG_CHOOSING_CHANNELS
+Choix des voix...
+;Choosing Channels...
+;
+MSG_WRITING_MIDI_TRACKS
+Ecriture de pistes MIDI...
+;Writing MIDI Tracks...
+;
+MSG_TOO_MANY_CHANNELS
+ERREUR: Cette "song" requiert trop de voix MIDI.
+;ERROR: Song requires too many MIDI channels.
+;
+;
+; *********************
+; * Strings for Gui.c *
+; *********************
+;
+MSG_REALLY_QUIT_XMODULE
+Vous voulez VRAIMENT quitter XModule ?
+;Really Quit XModule?
+;
+MSG_CLOSE_ALL_WINDOWS
+Veuillez fermer toutes les fenêtres\n\
+puis cliquer sur 'Continuer'.
+;Please close all visitor windows\n\
+;and then select `Continue'.
+;
+;
+; ***********************
+; * Strings for Instr.c *
+; ***********************
+;
+MSG_UNKNOWN_IFF
+Format IFF inconnu pour %s.
+;Unknown IFF format %s.
+;
+MSG_ILLEGAL_IFF_STRUCTURE
+Structure IFF incohérente.
+;Illegal IFF structure.
+;
+MSG_SELECT_RAW_MODE
+Format d'instrument inconnu.\n\
+Veuillez sélectionner le mode RAW.
+;Unrecognized instrument format.\n\
+;Please select RAW mode.
+;
+MSG_RAW_MODES
+8 bits signé|8 bits non signés|Abandonner
+;Signed 8bit|Unsigned 8bit|Cancel
+;
+MSG_DATATYPES_ERROR
+Erreur de DataTypes: %s.
+;DataTypes error: %s.
+; %s is a detailed description of the problem.
+;
+MSG_UNKNOWN_COMPRESSION
+Type de compression inconnu.
+;Unknown compression type.
+;
+MSG_SAMPLE_WRONG_SIZE
+Les samples de %lu bits ne sont pas acceptés.
+;%lu bit samples are not supported.
+; %lu is the number of bits per sample.
+;
+MSG_SAMPLE_NOT_MONO
+Les samples autres que MONO ne sont pas acceptés.
+;Samples other than MONO are not supported.
+;
+MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS
+Les samples de %ld voix ne sont pas acceptés.
+;Samples with %ld channels are not supported.
+;
+MSG_NO_MEMORY_TO_OPTIMIZE_INSTR
+ATTENTION: Mémoire insuffisante pour optimiser l'instrument %lx.
+;WARNING: insufficient memory to optimize instrument %lx.
+;
+MSG_INSTR_WILL_SHRINK
+L'instrument %lx sera réduit de %ld à %ld.
+;Instrument %lx will shrink from %ld to %ld.
+;
+MSG_INSTR_DUPES_REMOVED
+Instrument redondant décelé et retiré: %lx == %lx.
+;Duplicate instruments found and removed: %lx == %lx.
+;
+MSG_INSTR_UNUSED
+L'instrument %lx n'étant jamais utilisé, il est retiré.
+;Instrument %lx was never used and it's being removed.
+;
+;
+; **********************
+; * Strings for Misc.c *
+; **********************
+;
+MSG_OPENLIB_VER_FAIL
+
+;Couldn't open \"%s\" version %ld or greater.
+;
+MSG_OPENLIB_FAIL
+Impossible d'ouvrir %s version %ld ou supérieur.
+;Couldn't open \"%s\".
+;
+;
+; ***************************
+; * Strings for Operators.c *
+; ***************************
+;
+MSG_CANT_INSERT_PATT
+Impossible d'insérer une autre pattern: La limite a déjà été atteinte.
+;Unable to insert pattern: Maximum number of patterns reached.
+;
+MSG_PATT_UNUSED
+La pattern %ld n'étant jamais utilisée, elle est retirée.
+;Pattern %ld is not used and is beeing deleted.
+;
+MSG_PATT_CUT
+La pattern %ld est coupée à la ligne %ld.
+;Pattern %ld will be cut at line %ld.
+;
+MSG_PATT_DUPE
+Pattern redondante décelée et retirée: %ld == %ld.
+;Duplicate patterns found and removed: %ld == %ld.
+; The two %ld are the pattern numbers.
+;
+MSG_SONG_LEN_DIFFERENT
+ATTENTION: Longueurs des "song" différentes. La plus petite est conservée.
+;WARNING: Song lengths are different. Using shorter one.
+;
+MSG_PATT_LEN_DIFFERENT
+ATTENTION: Patterns de longueurs différentes en %ld. La plus petite est conservée.
+;WARNING: Different pattern lengths at position %ld. Using shorter one.
+;
+MSG_ERR_INSTR_OVERFLOW
+
+;ERROR: Instruments overflow. Try remapping the instruments.
+;
+;
+; ***********************
+; * Strings for Prefs.c *
+; ***********************
+;
+MSG_BAD_PREFS_VERSION
+Version incorrecte du fichier de préférences.
+;Incorrect version of preferences file
+;
+;
+; ****************************
+; * Strings for Requesters.c *
+; ****************************
+;
+MSG_XMODULE_REQUEST
+Requête de XModule
+;XModule Request
+; This is the title of requesters put out by XModule.
+;
+MSG_CLONE_WB
+Clône de l'écran Workbench
+;Clone Workbench Screen
+;
+MSG_CLOSE_FILEREQUESTER
+Veuillez fermer le Requester de fichier\n et sélectionner 'Continuer'.
+;Please close FileRequester\n and then select `Continue'.
+;
+MSG_SELECT_MODULES
+Sélectionnez le(s) Module(s)...
+;Select Module(s)...
+;
+MSG_SELECT_INSTRUMENTS
+Sélectionnez l(es) Instrument(s)...
+;Select Instrument(s)...
+;
+MSG_SELECT_PATTERN
+sélectionnez la Pattern...
+;Select Pattern...
+;
+MSG_SAVE_MODULE
+Ecriture du module...
+;Save Module...
+;
+MSG_SAVE_INSTRUMENT
+Ecriture de l'instrument...
+;Save Instrument...
+;
+MSG_SAVE_PATTERN
+Ecriture de la pattern...
+;Save Pattern...
+;
+MSG_FILE_EXISTS
+Le fichier \"%s\"\n existe déjà.
+;File \"%s\"\nalready exists.
+;
+MSG_OVERWRITE
+Réécrire|Choisir un autre|Abandonner
+;Overwrite|Choose Another|Abort
+;
+;
+; **********************
+; * Strings for Song.c *
+; **********************
+;
+MSG_CLOSE_ALL_SONGS
+
+;Please close all open songs\n\
+;and then select `Continue'.
+;
+MSG_UNESPECTED_EOF
+Mauvaise fin de fichier.
+;Unespected end of file.
+;
+MSG_MODULE_LOADED_OK
+Module chargé OK.
+;Module loaded OK.
+;
+MSG_MODULE_SAVED_OK
+Module sauvagardé OK.
+;Module saved OK.
+;
+MSG_UNKNOWN_SAVE_FORMAT
+ERREUR: Format de sauvegarde inconnu.
+;ERROR: Unrecognized save format.
+;
+MSG_INVALID_LOOP_REMOVED
+Loop invalide de l'instrument %lx retiré.
+;Removed invalid loop for instrument %lx.
+;
+MSG_INVALID_LOOP_FIXED
+Loop invalide de l'instrument %lx corrigé.
+;Fixed invalid loop for instrument %lx.
+;
+MSG_SONG_HAS_NO_PATTS
+ATTENTION: La "song" ne contient aucune pattern.
+;WARNING: Song has no patterns.
+;
+MSG_SONG_HAS_NO_SEQ
+ATTENTION: La "song" ne contient aucune séquence.
+;WARNING: Song has no sequence.
+;
+MSG_INVALID_SONG_POS
+ATTENTION: La "song" en %ld référencie la pattern %ld qui n'existe pas.
+;WARNING: Song position %ld references pattern %ld, which doesn't exist.
+;
+MSG_UNKNOWN_MOD_FORMAT
+Impossible d'identifier le format de ce module.\n\
+(Charger un ficher de datas comme un module n'est pas très judicieux)
+;Unable to identify module format.\n\
+;(Loading a data file as a module is unwise)
+;
+MSG_SOUND_PRO_CANCEL
+SoundTracker 15|ProTracker|Abandonner
+;SoundTracker 15|ProTracker|Cancel
+;
+MSG_AUTHOR_UNKNOWN
+Inconnu
+;Unknown
+;
+MSG_SONG_UNTITLED
+Pas de titre
+;Untitled
+;
+;
+; **********************
+; * Strings for Help.c *
+; **********************
+;
+MSG_AMIGAGUIDE_ERROR
+Erreur AmigaGuide:
+;AmigaGuide error:
+; Note one blank space at the end of this line!
+;
+;
+; ***************************
+; * Strings for PattPrefs.c *
+; ***************************
+;
+MSG_PATTPREFS_TITLE
+
+;Pattern Editor Settings
+;
+;
+MSG_ADVANCE_TRACKS_GAD
+
+;Advance _Tracks
+;
+MSG_ADVANCE_LINES_GAD
+
+;Advance _Lines
+;
+MSG_MAX_UNDO_LEVELS_GAD
+
+;Max _Undo Levels
+;
+MSG_MAX_UNDO_MEM_GAD
+
+;Max Undo _Memory
+;
+MSG_CLIPBOARD_UNIT_GAD
+
+;Cli_pboard Unit
+;
+MSG_SCROLLER_POS_GAD
+
+;_Scroller Position
+;
+MSG_VERT_WRAP_GAD
+
+;_Vertical Wrap
+;
+MSG_HORIZ_WRAP_GAD
+
+;_Horizontal Wrap
+;
+MSG_HEX_LINE_NUMBERS_GAD
+
+;He_x Line Numbers
+;
+MSG_BLANK_ZERO_GAD
+
+;Blank _Zero Digits
+;
+MSG_BACKDROP_GAD
+
+;Backdrop _Window
+;
+MSG_HORIZ_SCROLLER_GAD
+
+;Ho_rizontal Scroller
+;
+MSG_DO_RULER_GAD
+
+;Display Cursor Ruler
+;
+MSG_DO_TINY_LINES_GAD
+
+;Display Tiny Lines
+;
+MSG_EDITOR_FONT_GAD
+
+;_Editor Font
+;
+MSG_BACKGROUND_PEN_GAD
+
+;Background
+;
+MSG_TEXT_PEN_GAD
+
+;T_ext
+;
+MSG_LINES_PEN_GAD
+
+;Li_nes
+;
+MSG_TINY_LINES_PEN_GAD
+
+;Tin_y Lines
+;
+MSG_RIGHT_GAD
+
+;Right
+;
+MSG_LEFT_GAD
+
+;Left
+;
+MSG_OFF_GAD
+
+;Off
+;
+MSG_PATT_TOO_LONG
+ERREUR: La pattern dépasse le nombre maximum de lignes.
+;ERROR: Pattern would exceed the maximum number of lines.
+;
+;
+; *****************************
+; * Strings for SequenceWin.c *
+; *****************************
+;
+;
+MSG_SEQUENCE_TITLE
+
+;Sequence Editor
+;
+;
+MSG_SEQUENCE_GAD
+
+;Sequence
+;
+MSG_UNDERSCORE_ADD_GAD
+
+;_Add
+;
+MSG_UP_GAD
+
+;Up
+;
+MSG_DOWN_GAD
+
+;Down
+;
+MSG_UNDERSCORE_NAME_GAD
+
+;_Name
+;
+MSG_PATTERNS_GAD
+
+;Patterns
+;
+MSG_UNDERSCORE_UP_GAD
+
+;_Up
+;
+MSG_UNDERSCORE_DOWN_GAD
+
+;_Down
+;
+MSG_UNDERSCORE_INS_GAD
+
+;_Ins
+;
+;
+MSG_UNNAMED
+
+;-- unnamed --
+;
+;
+; ********************************
+; * Strings for InstrumentsWin.c *
+; ********************************
+;
+MSG_INSTRUMENTS_TITLE
+
+;Instruments
+;
+;
+MSG_VOLUME_GAD
+
+;_Volume
+;
+MSG_FINETUNE_GAD
+
+;_FineTune
+;
+MSG_LENGTH_GAD
+
+;Length
+;
+MSG_KIND_GAD
+
+;_Kind
+;
+MSG_EDIT_DOTS_GAD
+
+;_Edit...
+;
+MSG_SAMPLE_GAD
+
+;Sample
+;
+MSG_SYNTH_GAD
+
+;Synth
+;
+MSG_HYBRID_GAD
+
+;Hybrid
+;
+;
+MSG_INSTRUMENTS_MEN
+
+;Instruments
+;
+MSG_LOAD_MEN
+
+;Load...
+;
+MSG_REMAP_MEN
+
+;Remap
+;
+MSG_SAVE_COMPRESSED_MEN
+
+;Save Compressed
+;
+MSG_SAVE_RAW_MEN
+
+;Save Raw
+;
+;
+MSG_EMPTY
+
+;-- empty --
+;
+;
+; *************************
+; * Strings for PlayWin.c *
+; *************************
+;
+MSG_PLAY_TITLE
+
+;Play
+;
+;
+MSG_VOL_GAD
+
+;_Vol
+;
+MSG_POS_GAD
+
+;Pos
+;
+MSG_TIME_GAD
+
+;Time
+;
+MSG_RST_GAD
+
+;_Rst
+; Stands for Reset/Restart
+;
+MSG_PLAYER_INIT_ERR
+Erreur d'initialisation du Player: %ld.
+;Player initialization error: %ld.
+;
+;
+; *****************************
+; * Strings for ProgressWin.c *
+; *****************************
+;
+MSG_PROGRESS_TITLE
+
+;XModule is working...
+;
+MSG_LOG_TITLE
+
+;XModule Log
+;
+;
+MSG_UNDERSCORE_ABORT_GAD
+
+;_Abort
+;
+;
+MSG_PERCENT_DONE
+%ld de %ld (%ld%% réalisés).
+;%ld of %ld (%ld%% done)
+; This string appears inside the progress window gauge.
+; eg: "5 of 20 (25% done)".
+; Leave that double percent (%%) alone! :-)
+;
+;
+; *****************************
+; * Strings for SongInfoWin.c *
+; *****************************
+;
+MSG_SONGINFO_TITLE
+
+;Song Information
+;
+;
+MSG_UNDERSCORE_NEW_GAD
+
+;Ne_w
+;
+MSG_OPEN_GAD
+
+;_Open...
+;
+MSG_SAVE_GAD
+
+;_Save
+;
+MSG_DEL_GAD
+
+;Del
+;
+MSG_SONG_NAME_GAD
+
+;Song _Name
+;
+MSG_AUTHOR_NAME_GAD
+
+;_Author
+;
+MSG_DEF_TEMPO_GAD
+
+;_Tempo
+;
+MSG_DEF_SPEED_GAD
+
+;S_peed
+;
+MSG_RESTART_GAD
+
+;_Restart
+;
+MSG_LENGHT_GAD
+
+;Length
+;
+MSG_NUM_PATTS_GAD
+
+;Num Patterns
+;
+MSG_NUM_TRACKS_GAD
+
+;Tracks
+;
+MSG_TOT_MOD_SIZE_GAD
+
+;Total Module Size
+;
+MSG_TOT_INST_SIZE_GAD
+
+;Total Instruments Size
+;
+;
+MSG_SONG_MEN
+
+;Song
+;
+MSG_MERGE_SONGS_MEN
+
+;Merge Songs
+;
+MSG_JOIN_SONGS_MEN
+
+;Join Songs
+;
+;
+MSG_JOIN_REQUIRES_TWO_SONGS
+ERREUR: Un ajout nécessite deux "song".
+;ERROR: Join requires two songs.
+;
+MSG_MERGE_REQUIRES_TWO_SONGS
+ERREUR: Une fusion nécessite deux "song".
+;ERROR: Merge requires two songs.
+;
+MSG_DISCARD_CURRENT_SONG
+Abandonner le traitement de cette "song"?
+;Discard current song?
+;
+;
+; ****************************
+; * Strings for ToolBoxWin.c *
+; ****************************
+;
+MSG_TOOLBOX_TITLE
+
+;ToolBox
+;
+;
+MSG_PLAY_GAD
+
+;Play...
+;
+MSG_SONGS_GAD
+
+;_Songs...
+;
+MSG_PATTERNS_DOTS_GAD
+
+;_Patterns...
+;
+MSG_INSTRUMENTS_GAD
+
+;_Instruments...
+;
+MSG_SEQUENCE_DOTS_GAD
+
+;Se_quence...
+;
+MSG_OPTIMIZATION_GAD
+
+;_Optimization...
+;
+MSG_PROJECT_MEN
+
+;Project
+;
+MSG_NEW_MEN
+
+;New
+;
+MSG_OPEN_MEN
+
+;Open...
+;
+MSG_OPEN_NEW_MEN
+
+;Open New...
+;
+MSG_SAVE_MEN
+
+;Save
+;
+MSG_SAVE_AS_MEN
+
+;Save As...
+;
+MSG_CLEAR_MEN
+
+;Clear...
+;
+MSG_ABOUT_MEN
+
+;About...
+;
+MSG_HELP_MEN
+
+;Help...
+;
+MSG_ICONIFY_MEN
+
+;Iconify...
+;
+MSG_QUIT_MEN
+
+;Quit
+;
+MSG_SETTINGS_MEN
+
+;Settings
+;
+MSG_SAVE_FORMAT_MEN
+
+;Save Format...
+;
+MSG_USER_INTERFACE_MEN
+
+;User Interface...
+;
+MSG_SAVE_ICONS_MEN
+
+;Save Icons
+;
+MSG_CONFIRM_OVERWRITE_MEN
+
+;Confirm Overwrite
+;
+MSG_CONFIRM_EXIT_MEN
+
+;Confirm Exit
+;
+MSG_VERBOSE_MEN
+
+;Verbose Log
+;
+MSG_OPEN_SETTINGS_MEN
+
+;Open Settings...
+;
+MSG_SAVE_SETTINGS_MEN
+
+;Save Settings
+;
+MSG_SAVE_SETTINGS_AS_MEN
+
+;Save Settings As...
+;
+;
+MSG_ABOUT_TEXT
+%s %s\n\
+Programme de Traitement de Modules\n\n\
+\
+%s\n\
+Tous droits réservés.\n\n\
+\
+Internet: bernie@shock.cosmos.it\n\n\
+FidoNet: 2:332/125.1\n\
+\
+Mémoire CHIP libre: %ld Ko\n\
+Mémoire FAST libre: %ld Ko\n\n\
+Ecran Public: %s\n\
+Port ARexx: %s\n\
+Cx_HotKey: %s\n\
+Language: %s
+;%s\n\
+;A Music Module Processing Utility\n\n\
+;\
+;%s\n\
+;All rights reserved.\n\n\
+;\
+;Internet: bernie@shock.cosmos.it\n\n\
+;FidoNet: 2:332/125.1\n\
+;\
+;Free CHIP Memory: %ldKB\n\
+;Free FAST Memory: %ldKB\n\n\
+;Public Screen: %s\n\
+;ARexx Port: %s\n\
+;Cx HotKey: %s\n\
+;Language: %s
+;
+MSG_DEFAULT
+--Défaut--
+;-- Default --
+;
+MSG_DISABLED
+--Désactivé--
+;-- Disabled --
+;
+;
+; *********************************
+; * Strings for OptimizationWin.c *
+; *********************************
+;
+MSG_OPTIMIZATION_TITLE
+
+;Module Optimization
+;
+;
+MSG_OPTIMIZE_GAD
+
+;_Optimize
+;
+MSG_REM_UNUSED_PATTS_GAD
+
+;Remove Unused _Patterns
+;
+MSG_REM_DUPLICATE_PATTS_GAD
+
+;Remove _Duplicate Patterns
+;
+MSG_REM_UNUSED_INSTR_GAD
+
+;Remove Unused _Instruments
+;
+MSG_REM_DUP_INSTR_GAD
+
+;Remove Duplicate I_nstruments
+;
+MSG_CUT_AFTER_LOOP_GAD
+
+;Cut Instruments After _Loop
+;
+MSG_CUT_ZERO_TAILS_GAD
+
+;Cut Instrument _Zero Tails
+;
+MSG_CUT_PATTERNS_GAD
+
+;Cut _Breaked Patterns
+;
+MSG_REMAP_INSTRUMENTS_GAD
+
+;_Remap Instruments
+;
+;
+MSG_SAVED_X_BYTES
+%ld octets gagnés (%ld%%)
+;Saved %ld bytes (%ld%%)
+;
+;
+; **************************
+; * Strings for PrefsWin.c *
+; **************************
+;
+;
+MSG_PREFS_TITLE
+
+;User Interface Settings
+;
+;
+MSG_PUBLIC_SCREEN_GAD
+
+;_Public Screen
+;
+MSG_DISPLAY_MODE_GAD
+
+;Display _Mode
+;
+MSG_WINDOW_FONT_GAD
+
+;_Window Font
+;
+MSG_LISTVIEW_FONT_GAD
+
+;_ListView Font
+;
+MSG_REQUESTERS_GAD
+
+;_Requesters
+;
+MSG_USE_DATATYPES_GAD
+
+;Use _DataTypes
+;
+MSG_APPICON_GAD
+
+;Put App_Icon
+;
+MSG_REFRESH_GAD
+
+;Refres_h
+;
+MSG_LOG_TO_FILE_GAD
+
+;Log To File
+;
+MSG_LOG_LEVEL_GAD
+
+;_Log Level
+;
+MSG_ASK_AUTOSAVE_GAD
+
+;Confirm _Autosave
+;
+MSG_AUTOSAVE_TIME_GAD
+
+;Autosave Mi_nutes
+;
+MSG_DO_BACKUPS_GAD
+
+;Create _Backups
+;
+MSG_BACKUP_TEMPLATE_GAD
+
+;Bac_kup Template
+;
+MSG_BACKUP_VERSIONS_GAD
+
+;Backup _Versions
+;
+MSG_ASL_GAD
+
+;Asl
+;
+MSG_REQTOOLS_GAD
+
+;ReqTools
+;
+MSG_SMART_GAD
+
+;Smart
+;
+MSG_SIMPLE_GAD
+
+;Simple
+;
+MSG_CLONE_DEF_SCREEN
+
+;--Clone Default Screen--
+;
+;
+; ****************************
+; * Strings for PatternWin.c *
+; ****************************
+;
+MSG_PATTERN_TITLE
+
+;Pattern Editor
+;
+;
+MSG_PATTERNS_MEN
+
+;Patterns
+;
+MSG_SIZE_MEN
+
+;Size...
+;
+MSG_EDIT_MEN
+
+;Edit
+;
+MSG_MARK_MEN
+
+;Mark
+;
+MSG_CUT_MEN
+
+;Cut
+;
+MSG_COPY_MEN
+
+;Copy
+;
+MSG_PASTE_MEN
+
+;Paste
+;
+MSG_ERASE_MEN
+
+;Erase
+;
+MSG_UNDO_MEN
+
+;Undo
+;
+MSG_REDO_MEN
+
+;Redo
+;
+MSG_EDITOR_SETTINGS_MEN
+
+;Editor Settings...
+;
+;
+; ***************************
+; * Strings for SaversWin.c *
+; ***************************
+;
+MSG_SAVERS_TITLE
+
+;Savers
+;
+;
+MSG_SF_SEQUENCE_GAD
+
+;Se_quence
+;
+MSG_SF_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_SF_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_SF_NAMES_GAD
+
+;Names
+;
+MSG_ADD_ICON_GAD
+
+;Add I_con
+;
+MSG_MODE_GAD
+
+;_Mode
+;
+MSG_OPTIONS_DOTS_GAD
+
+;Options...
+;
+MSG_NONE_GAD
+
+;None
+;
+MSG_XPK_GAD
+
+;XPK
+;
+MSG_LHA_GAD
+
+;LhA
+;
+;
+MSG_DESCRIPTION
+
+;Description
+;
+MSG_AUTHOR
+
+;Author
+;
+MSG_MAXLENGTH
+
+;Max Length
+;
+MSG_MAXTRACKS
+
+;Max Tracks
+;
+MSG_MAXINSTRUMENTS
+
+;Max Instruments
+;
+MSG_MAXPATTERNS
+
+;Max Patterns
+;
+MSG_MAXPATTLEN
+
+;Max Pattern Length
+;
+MSG_MAXSAMPLELEN
+
+;Max Sample Length
+;
+;
+; **********************
+; * Strings ClearWin.c *
+; **********************
+;
+MSG_CLEAR_TITLE
+
+;Clear Module
+;
+;
+MSG_CLR_SEQUENCE_GAD
+
+;_Sequence
+;
+MSG_CLR_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_CLR_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_CLEARMOD_GAD
+
+;_Clear
+;
+;
+; ***************************
+; * Strings for SampleWin.c *
+; ***************************
+;
+MSG_SAMPLE_TITLE
+
+;Sample Editor
+;
+;
+MSG_RENDER_MEN
+
+;Render
+;
+MSG_POINTS_MEN
+
+;Points
+;
+MSG_LINES_MEN
+
+;Lines
+;
+MSG_FILLED_MEN
+
+;Filled
+;
+;
+; *****************************
+; * Strings for PattSizeWin.c *
+; *****************************
+;
+MSG_PATTSIZE_TITLE
+
+;Pattern Attributes
+;
+;
+MSG_LINES_GAD
+
+;_Lines
+;
+MSG_TRACKS_GAD
+
+;_Tracks
+;
+MSG_DOUBLE_GAD
+
+;_Double
+;
+MSG_HALVE_GAD
+
+;_Halve
+;
+;
+; *************************
+; * Strings for Library.c *
+; *************************
+;
+MSG_SONG_TOO_MANY_TRACKS
+
+;WARNING: The selected saver supports max %lu tracks, but the song has %lu tracks.
+;
+MSG_SONG_TOO_MANY_PATTS
+
+;WARNING: The selected saver supports max %lu patterns; the song has %lu patterns.
+;
+MSG_SONG_TOO_MANY_INSTRS
+
+;WARNING: The selected saver supports max %lu instruments, last instrument is %lu.
+;
+MSG_SONG_TOO_MANY_POS
+
+;WARNING: The selected saver supports max %lu positions, but the song has %lu positions.
+;
+MSG_PATT_LENGTH_INVALID
+
+;WARNING: The length of pattern %ld ($%lx lines) isn't allowed with the selected saver.
+;
+MSG_INSTR_TOO_LONG
+ATTENTION: L'instrument %lx est trop long.
+;WARNING: Instrument %lx is too long and will be cropped to %lu bytes.
+;
+MSG_WILL_MODIFY_SONG
+
+;Some modifications need to be performed on this song\n\
+;in order to adapt it to the limitations of the\n\
+;destination format.
+;
+MSG_TRY_REMAPPING_INSTRUMENTS
+
+;Some instruments are placed beyond the limit for\n\
+;the selected format.\n\
+;Remapping the instruments now could help saving\n\
+;all them along with the selected format.
+;
+MSG_SAVING_MODULE
+
+;Saving %s module \"%s\"...
+; the first %s is the format name, the second %s is the song name.
--- /dev/null
+## version $VER: Italiano.catalog 1.1 ($TODAY)
+## language italiano
+## codeset 0
+;
+## chunk AUTH Italian catalog translation by Steven Cantini
+;**
+;** XModule.cd 3.9
+;**
+;** Copyright (C) 1995,96 Bernardo Innocenti
+;**
+;
+; **********************************
+; * Strings for all source modules *
+; **********************************
+;
+;
+MSG_NULL
+
+;
+; This string should remain blank.
+;
+MSG_OK
+Ok
+;Ok
+;
+MSG_UNDERSCORE_USE_GAD
+_Usa
+;_Use
+;
+MSG_UNDERSCORE_OK_GAD
+
+;_Ok
+;
+MSG_UNDERSCORE_CANCEL_GAD
+_Annulla
+;_Cancel
+;
+MSG_YES_OR_NO
+Sì|No
+;Yes|No
+;
+MSG_RETRY_OR_CANCEL
+Riprova|Annulla
+;Retry|Cancel
+;
+MSG_PROCEED_OR_CANCEL
+
+;Proceed|Cancel
+;
+MSG_CONTINUE
+Continua
+;Continue
+;
+MSG_NO_FREE_STORE
+Memoria insufficiente.
+;Insufficient memory.
+;
+MSG_BREAK
+Annullato.
+;Aborted.
+;
+MSG_ERR_LOAD
+Non posso caricare \"%s\": %s.
+;Unable to load \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_CANT_OPEN
+Non posso aprire il file \"%s\": %s.
+;Cannot open file \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_READING
+Errore di lettura nel file \"%s\": %s.
+;Error reading \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_WRITING
+Errore scrivendo \"%s\": %s.
+;Error writing \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+;
+; *****************************
+; * Strings for Compression.c *
+; *****************************
+;
+MSG_DECRUNCHING
+Decompressione...
+;Decrunching...
+;
+MSG_NOTHING_IN_ARC
+L'archivio \"%s\" è vuoto.
+;Nothing found in archive \"%s\".
+;
+MSG_CANT_LOAD_COMPRESSED
+Non posso caricare il file compresso.
+;Unable to load compressed file.
+;
+MSG_ERROR_DECOMPRESSING
+Errore di decompressione sul file \"%s\": %s.
+;Error decompressing file \"%s\": %s.
+;
+;
+; ********************
+; * Strings for Cx.c *
+; ********************
+;
+MSG_BAD_HOTKEY
+Descrizione della Commodity HotKey errata.
+;Bad Commodity HotKey description.
+;
+;
+; ***************************
+; * Strings for all loaders *
+; ***************************
+;
+MSG_READING_PATTS
+Lettura Patterns...
+;Reading Patterns...
+;
+MSG_READING_INSTS_INFO
+Lettura Info degli Strumenti...
+;Reading Instruments Info...
+;
+MSG_READING_INSTS
+Lettura Strumenti...
+;Reading Instruments...
+;
+MSG_ERR_CANT_LOAD_PATT
+ERRORE: Non ho potuto caricare il pattern %ld.
+;ERROR: Couldn't load pattern %ld.
+;
+MSG_ERR_CANT_LOAD_INST
+ERRORE: Non ho potuto caricare lo strumento %lx.
+;ERROR: Couldn't load instrument %lx.
+;
+MSG_ERR_NO_MEM_FOR_INST
+ERRORE: Memoria insufficiente per lo strumento %lx.
+;ERROR: Not enough memory for instrument %lx.
+;
+MSG_ERR_NOT_A_SAMPLE
+ERRORE: Lo strumento %lx non è un sample.
+;ERROR: Instrument %lx is not a sample.
+;
+MSG_SONG_TOO_LONG
+ATTENZIONE: La song è troppo grande. Verra' troncata.
+;WARNING: Song length exceeds maximum. Will be truncated.
+;
+MSG_SONG_HAS_TOO_MANY_PATT
+ATTENZIONE: La song ha troppi pattern.
+;WARNING: Song exceeds maximum number of patterns.
+;
+MSG_PATT_TOO_MANY_TRACKS
+ATTENZIONE: Il pattern %ld ha troppe tracce. Lo riduco a %ld tracce.
+;WARNING: Pattern %ld has too many tracks. Cropping to %ld tracks.
+;
+MSG_PATT_TOO_MANY_LINES
+ATTENZIONE: Il pattern %ld ha troppe linee. Lo riduco a %ld linee.
+;WARNING: Pattern %ld has too many lines. Cropping to %ld lines.
+;
+MSG_INVALID_NOTE
+ATTENZIONE: Nota %ld non valida (Patt %ld Traccia %ld Linea %ld).
+;WARNING: Invalid note %ld (Patt %ld Track %ld Line %ld).
+;
+MSG_UNKNOWN_EFF
+Effetto sconosciuto: $%lx (Patt %ld Traccia %ld Linea %ld).
+;Unknown effect: $%lx (Patt %ld Track %ld Line %ld).
+;
+MSG_EXTRA_DATA_AFTER_MOD
+ATTENZIONE: Ulteriori dati trovati dopo un modulo valido: li ignoro.
+;WARNING: Extra data found after valid module: Will be ignored.
+;
+MSG_WRITING_HEADER
+Scrittura dell'Header...
+;Writing Header...
+;
+MSG_WRITING_PATTS
+Scrittura dei Pattern...
+;Writing Patterns...
+;
+MSG_WRITING_INSTS
+Scrittura degli Strumenti...
+;Writing Instruments...
+;
+MSG_WRITING_INSTINFO
+Scrittura Info degli Strumenti...
+;Writing Instruments Info...
+;
+MSG_WRITING_INSTDATA
+Scrittura dei Dati degli Strumenti...
+;Writing Instruments Data...
+;
+MSG_NOTE_TOO_LOW
+ATTENZIONE: La nota al Patt %ld Traccia %ld Linea %ld è troppo bassa.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too low.
+;
+MSG_NOTE_TOO_HIGH
+ATTENZIONE: La nota al Patt %ld Traccia %ld Linea %ld è troppo alta.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too high.
+;
+MSG_NO_MEM_TO_HALVE
+ATTENZIONE: Memoria insuff. per dimezzare il volume dello strumento %lx.
+;WARNING: Not enough memory to halve volume of instrument %lx.
+;
+;
+; *****************************
+; * Strings for Get/SaveMED.c *
+; *****************************
+;
+MSG_READING_MMD
+Lettura modulo MMD%lc...
+;Loading MMD%lc module...
+; The %lc is the MED format (0, 1, 2, ...)
+;
+MSG_UNSUPPORTED_MMD_FORMAT
+ERRORE: Formato OctaMED non supportato.
+;ERROR: Unsupported OctaMED format.
+;
+MSG_WRONG_EFFECT_IN_MMD0
+ATTENZIONE: L'effetto %lx non è supportato nell'MMD0. Usa almeno l'MMD1.
+;WARNING: Effect %lx is not supported in MMD0 format. Use MMD1 or better.
+;
+;
+; ***********************************
+; * Strings for ScreamTrackerHook.c *
+; ***********************************
+;
+MSG_ADLIB_INSTR
+ERRORE: Lo strumento %lx è un %s ADLib.
+;ERROR: Instrument %lx is an ADLib %s.
+; %s is the name of the ADLib instrument (eg: Snare).
+;
+MSG_TRACK_OUT_OF_RANGE
+ATTENZIONE: La traccia %lx è fuori dal limite.
+;WARNING: Track %lx is out of range.
+;
+MSG_UNKNOWN_SAMPLE_COMPRESSION
+ATTENZIONE: Compressione del sample sconosciuta per lo strumento %lx.
+;WARNING: Unknown sample compression for instrument %lx.
+;
+MSG_INST_IS_STEREO
+ATTENZIONE: Lo strumento %lx e' stereo.
+;WARNING: Instrument %lx is a stereo sample.
+;
+MSG_INST_IS_16BIT
+ATTENZIONE: Lo strumento %lx e' a 16bit: non puo' essere caricato.
+;WARNING: Instrument %lx is 16bit: unable to load it.
+;
+;
+; *****************************
+; * Strings for TrackerHook.c *
+; *****************************
+;
+MSG_READING_TYPE_MODULE
+Lettura modulo %s...
+;Reading %s module...
+; (%s is the module format)
+;
+MSG_EXCEEDS_64_PATTS
+ATTENZIONE: Il modulo ha più di 64 pattern. Ti serve il ProTracker 2.3 per sentirlo.
+;NOTE: Module exceeds 64 patterns. You need ProTracker 2.3 to play it.
+;
+MSG_MODULE_ID
+
+;Tracker ID: \"%s\"
+;
+MSG_MODULE_HAS_N_CHN
+
+;Module has %lu tracks...
+;
+MSG_SONG_HAS_NO_INSTS
+Questo file è una song e non contiene strumenti.
+;This file is a song and doesn't contain instruments.
+;
+MSG_EXCEEDS_MAXPAATTS
+ATTENZIONE: Il modulo supera %ld pattern.
+;WARNING: Module execeeds %ld patterns.
+;
+MSG_PATT_WILL_GROW
+Il pattern %ld sarà portato a 64 linee (era lungo %ld).
+;Pattern %ld will grow to 64 lines (was %ld lines long).
+;
+MSG_SPLITTING_PATT
+Il pattern %ld verrà spezzato (era lungo %ld linee).
+;Splitting pattern %ld (was %ld lines long).
+;
+; **************************
+; * Strings for SaveMIDI.c *
+; **************************
+;
+MSG_CHOOSING_CHANNELS
+Scelta dei Canali...
+;Choosing Channels...
+;
+MSG_WRITING_MIDI_TRACKS
+Scrittura tracce MIDI...
+;Writing MIDI Tracks...
+;
+MSG_TOO_MANY_CHANNELS
+ERRORE: La song richiede troppi canali MIDI.
+;ERROR: Song requires too many MIDI channels.
+;
+;
+; *********************
+; * Strings for Gui.c *
+; *********************
+;
+MSG_REALLY_QUIT_XMODULE
+Si vuole veramente uscire da XModule?
+;Really Quit XModule?
+;
+MSG_CLOSE_ALL_WINDOWS
+Chiudi tutte le finestre in questo schermo\ne poi seleziona `Continua'.
+;Please close all visitor windows\n\
+;and then select `Continue'.
+;
+;
+; ***********************
+; * Strings for Instr.c *
+; ***********************
+;
+MSG_UNKNOWN_IFF
+Formato IFF %s sconosciuto.
+;Unknown IFF format %s.
+;
+MSG_ILLEGAL_IFF_STRUCTURE
+Struttura IFF illegale.
+;Illegal IFF structure.
+;
+MSG_SELECT_RAW_MODE
+Formato dello strumento sconosciuto.\nPer favore scegli il modo RAW.
+;Unrecognized instrument format.\n\
+;Please select RAW mode.
+;
+MSG_RAW_MODES
+8bit con segno|8bit senza segno|Annulla
+;Signed 8bit|Unsigned 8bit|Cancel
+;
+MSG_DATATYPES_ERROR
+Errore DataTypes: %s.
+;DataTypes error: %s.
+; %s is a detailed description of the problem.
+;
+MSG_UNKNOWN_COMPRESSION
+Tipo di compressione sconosciuta.
+;Unknown compression type.
+;
+MSG_SAMPLE_WRONG_SIZE
+I campioni a %lu bit non sono supportati.
+;%lu bit samples are not supported.
+; %lu is the number of bits per sample.
+;
+MSG_SAMPLE_NOT_MONO
+I campioni non MONO non sono supportati.
+;Samples other than MONO are not supported.
+;
+MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS
+I campioni con %ld canali non sono supportati.
+;Samples with %ld channels are not supported.
+;
+MSG_NO_MEMORY_TO_OPTIMIZE_INSTR
+ATTENZIONE: Memoria insufficiente per ottimizzare lo strumento %lx.
+;WARNING: insufficient memory to optimize instrument %lx.
+;
+MSG_INSTR_WILL_SHRINK
+Accorcio lo strumento %lx da %ld a %ld bytes.
+;Instrument %lx will shrink from %ld to %ld.
+;
+MSG_INSTR_DUPES_REMOVED
+Ho trovato e rimosso due strumenti uguali: %lx == %lx.
+;Duplicate instruments found and removed: %lx == %lx.
+;
+MSG_INSTR_UNUSED
+Lo strumento %lx non è mai usato. Lo rimuovo.
+;Instrument %lx was never used and it's being removed.
+;
+;
+; **********************
+; * Strings for Misc.c *
+; **********************
+;
+MSG_OPENLIB_VER_FAIL
+Non riesco ad aprire \"%s\" versione %ld o superiore.
+;Couldn't open \"%s\" version %ld or greater.
+;
+MSG_OPENLIB_FAIL
+Non riesco ad aprire \"%s\".
+;Couldn't open \"%s\".
+;
+;
+; ***************************
+; * Strings for Operators.c *
+; ***************************
+;
+MSG_CANT_INSERT_PATT
+Non posso inserire il pattern: è stato raggiunto il numero massimo di pattern.
+;Unable to insert pattern: Maximum number of patterns reached.
+;
+MSG_PATT_UNUSED
+Il pattern %ld non è usato e verrà cancellato.
+;Pattern %ld is not used and is beeing deleted.
+;
+MSG_PATT_CUT
+Il pattern %ld sarà tagliato alla linea %ld.
+;Pattern %ld will be cut at line %ld.
+;
+MSG_PATT_DUPE
+Ho trovato e rimosso due pattern uguali: %ld == %ld.
+;Duplicate patterns found and removed: %ld == %ld.
+; The two %ld are the pattern numbers.
+;
+MSG_SONG_LEN_DIFFERENT
+ATTENZIONE: Le song hanno lunghezza diversa. Uso quella piu piccola.
+;WARNING: Song lengths are different. Using shorter one.
+;
+MSG_PATT_LEN_DIFFERENT
+ATTENZIONE: Lunghezze di pattern diverse alla posizione %ld. Uso la più piccola.
+;WARNING: Different pattern lengths at position %ld. Using shorter one.
+;
+MSG_ERR_INSTR_OVERFLOW
+
+;ERROR: Instruments overflow. Try remapping the instruments.
+;
+;
+; ***********************
+; * Strings for Prefs.c *
+; ***********************
+;
+MSG_BAD_PREFS_VERSION
+Versione errata del file di preferenze
+;Incorrect version of preferences file
+;
+;
+; ****************************
+; * Strings for Requesters.c *
+; ****************************
+;
+MSG_XMODULE_REQUEST
+Richiesta di XModule
+;XModule Request
+; This is the title of requesters put out by XModule.
+;
+MSG_CLONE_WB
+Clona lo schermo Workbench
+;Clone Workbench Screen
+;
+MSG_CLOSE_FILEREQUESTER
+Per favore chiudi il FileRequester\ne poi seleziona `Continua'.
+;Please close FileRequester\n and then select `Continue'.
+;
+MSG_SELECT_MODULES
+Seleziona uno o più moduli...
+;Select Module(s)...
+;
+MSG_SELECT_INSTRUMENTS
+Seleziona uno o più strumenti...
+;Select Instrument(s)...
+;
+MSG_SELECT_PATTERN
+Seleziona un pattern...
+;Select Pattern...
+;
+MSG_SAVE_MODULE
+Salva il modulo...
+;Save Module...
+;
+MSG_SAVE_INSTRUMENT
+Salva lo strumento...
+;Save Instrument...
+;
+MSG_SAVE_PATTERN
+Salva il pattern...
+;Save Pattern...
+;
+MSG_FILE_EXISTS
+Il file \"%s\"\nesiste già.
+;File \"%s\"\nalready exists.
+;
+MSG_OVERWRITE
+Sovrascrivi|Cambia Nome|Annulla
+;Overwrite|Choose Another|Abort
+;
+;
+; **********************
+; * Strings for Song.c *
+; **********************
+;
+MSG_CLOSE_ALL_SONGS
+
+;Please close all open songs\n\
+;and then select `Continue'.
+;
+MSG_UNESPECTED_EOF
+Fine prematura del file.
+;Unespected end of file.
+;
+MSG_MODULE_LOADED_OK
+Modulo caricato correttamente.
+;Module loaded OK.
+;
+MSG_MODULE_SAVED_OK
+Modulo salvato correttamente.
+;Module saved OK.
+;
+MSG_UNKNOWN_SAVE_FORMAT
+ERRORE: Formato di save sconosciuto.
+;ERROR: Unrecognized save format.
+;
+MSG_INVALID_LOOP_REMOVED
+Ho rimosso un loop non valido per lo strumento %lx.
+;Removed invalid loop for instrument %lx.
+;
+MSG_INVALID_LOOP_FIXED
+Ho riparato un loop non valido per lo strumento %lx.
+;Fixed invalid loop for instrument %lx.
+;
+MSG_SONG_HAS_NO_PATTS
+ATTENZIONE: La song non ha pattern.
+;WARNING: Song has no patterns.
+;
+MSG_SONG_HAS_NO_SEQ
+ATTENZIONE: La song non ha una sequenza.
+;WARNING: Song has no sequence.
+;
+MSG_INVALID_SONG_POS
+ATTENZIONE: La posizione %ld rimanda al pattern %ld, che non esiste.
+;WARNING: Song position %ld references pattern %ld, which doesn't exist.
+;
+MSG_UNKNOWN_MOD_FORMAT
+Non riesco ad identificare il formato.\n(Caricare un file di dati come modulo è pericoloso)
+;Unable to identify module format.\n\
+;(Loading a data file as a module is unwise)
+;
+MSG_SOUND_PRO_CANCEL
+SoundTracker 15|ProTracker|Annulla
+;SoundTracker 15|ProTracker|Cancel
+;
+MSG_AUTHOR_UNKNOWN
+Sconosciuto
+;Unknown
+;
+MSG_SONG_UNTITLED
+Senza titolo
+;Untitled
+;
+;
+; **********************
+; * Strings for Help.c *
+; **********************
+;
+MSG_AMIGAGUIDE_ERROR
+Errore AmigaGuide:
+;AmigaGuide error:
+; Note one blank space at the end of this line!
+;
+;
+; ***************************
+; * Strings for PattPrefs.c *
+; ***************************
+;
+MSG_PATTPREFS_TITLE
+Parametri Editor Pattern
+;Pattern Editor Settings
+;
+;
+MSG_ADVANCE_TRACKS_GAD
+
+;Advance _Tracks
+;
+MSG_ADVANCE_LINES_GAD
+
+;Advance _Lines
+;
+MSG_MAX_UNDO_LEVELS_GAD
+
+;Max _Undo Levels
+;
+MSG_MAX_UNDO_MEM_GAD
+
+;Max Undo _Memory
+;
+MSG_CLIPBOARD_UNIT_GAD
+
+;Cli_pboard Unit
+;
+MSG_SCROLLER_POS_GAD
+
+;_Scroller Position
+;
+MSG_VERT_WRAP_GAD
+
+;_Vertical Wrap
+;
+MSG_HORIZ_WRAP_GAD
+
+;_Horizontal Wrap
+;
+MSG_HEX_LINE_NUMBERS_GAD
+
+;He_x Line Numbers
+;
+MSG_BLANK_ZERO_GAD
+
+;Blank _Zero Digits
+;
+MSG_BACKDROP_GAD
+
+;Backdrop _Window
+;
+MSG_HORIZ_SCROLLER_GAD
+
+;Ho_rizontal Scroller
+;
+MSG_DO_RULER_GAD
+
+;Display Cursor Ruler
+;
+MSG_DO_TINY_LINES_GAD
+
+;Display Tiny Lines
+;
+MSG_EDITOR_FONT_GAD
+
+;_Editor Font
+;
+MSG_BACKGROUND_PEN_GAD
+
+;Background
+;
+MSG_TEXT_PEN_GAD
+
+;T_ext
+;
+MSG_LINES_PEN_GAD
+
+;Li_nes
+;
+MSG_TINY_LINES_PEN_GAD
+
+;Tin_y Lines
+;
+MSG_RIGHT_GAD
+
+;Right
+;
+MSG_LEFT_GAD
+
+;Left
+;
+MSG_OFF_GAD
+
+;Off
+;
+MSG_PATT_TOO_LONG
+ERRORE: Il pattern crescerebbe oltre il massimo numero di linee.
+;ERROR: Pattern would exceed the maximum number of lines.
+;
+;
+; *****************************
+; * Strings for SequenceWin.c *
+; *****************************
+;
+;
+MSG_SEQUENCE_TITLE
+Editor Sequenza
+;Sequence Editor
+;
+;
+MSG_SEQUENCE_GAD
+Sequenza
+;Sequence
+;
+MSG_UNDERSCORE_ADD_GAD
+_Aggiungi
+;_Add
+;
+MSG_UP_GAD
+Su
+;Up
+;
+MSG_DOWN_GAD
+Giu`
+;Down
+;
+MSG_UNDERSCORE_NAME_GAD
+_Nome
+;_Name
+;
+MSG_PATTERNS_GAD
+Patterns
+;Patterns
+;
+MSG_UNDERSCORE_UP_GAD
+_Su
+;_Up
+;
+MSG_UNDERSCORE_DOWN_GAD
+_Giu`
+;_Down
+;
+MSG_UNDERSCORE_INS_GAD
+_Ins
+;_Ins
+;
+;
+MSG_UNNAMED
+--senza nome--
+;-- unnamed --
+;
+;
+; ********************************
+; * Strings for InstrumentsWin.c *
+; ********************************
+;
+MSG_INSTRUMENTS_TITLE
+Strumenti
+;Instruments
+;
+;
+MSG_VOLUME_GAD
+_Volume
+;_Volume
+;
+MSG_FINETUNE_GAD
+_FineTune
+;_FineTune
+;
+MSG_LENGTH_GAD
+Lunghezza
+;Length
+;
+MSG_KIND_GAD
+_Tipo
+;_Kind
+;
+MSG_EDIT_DOTS_GAD
+_Edita...
+;_Edit...
+;
+MSG_SAMPLE_GAD
+Campione
+;Sample
+;
+MSG_SYNTH_GAD
+Synth
+;Synth
+;
+MSG_HYBRID_GAD
+Ibrido
+;Hybrid
+;
+;
+MSG_INSTRUMENTS_MEN
+Strumenti
+;Instruments
+;
+MSG_LOAD_MEN
+Carica...
+;Load...
+;
+MSG_REMAP_MEN
+Rimappa
+;Remap
+;
+MSG_SAVE_COMPRESSED_MEN
+Salva Compresso
+;Save Compressed
+;
+MSG_SAVE_RAW_MEN
+Salva Grezzo
+;Save Raw
+;
+;
+MSG_EMPTY
+--vuoto--
+;-- empty --
+;
+;
+; *************************
+; * Strings for PlayWin.c *
+; *************************
+;
+MSG_PLAY_TITLE
+Riproduzione
+;Play
+;
+;
+MSG_VOL_GAD
+
+;_Vol
+;
+MSG_POS_GAD
+Pos
+;Pos
+;
+MSG_TIME_GAD
+Tempo
+;Time
+;
+MSG_RST_GAD
+Rst
+;_Rst
+; Stands for Reset/Restart
+;
+MSG_PLAYER_INIT_ERR
+Errore di inizializzazione del player: %ld.
+;Player initialization error: %ld.
+;
+;
+; *****************************
+; * Strings for ProgressWin.c *
+; *****************************
+;
+MSG_PROGRESS_TITLE
+
+;XModule is working...
+;
+MSG_LOG_TITLE
+
+;XModule Log
+;
+;
+MSG_UNDERSCORE_ABORT_GAD
+
+;_Abort
+;
+;
+MSG_PERCENT_DONE
+%ld di %ld (%ld%% fatto)
+;%ld of %ld (%ld%% done)
+; This string appears inside the progress window gauge.
+; eg: "5 of 20 (25% done)".
+; Leave that double percent (%%) alone! :-)
+;
+;
+; *****************************
+; * Strings for SongInfoWin.c *
+; *****************************
+;
+MSG_SONGINFO_TITLE
+Informazioni Song
+;Song Information
+;
+;
+MSG_UNDERSCORE_NEW_GAD
+
+;Ne_w
+;
+MSG_OPEN_GAD
+_Carica...
+;_Open...
+;
+MSG_SAVE_GAD
+_Salva
+;_Save
+;
+MSG_DEL_GAD
+Del
+;Del
+;
+MSG_SONG_NAME_GAD
+_Nome Song
+;Song _Name
+;
+MSG_AUTHOR_NAME_GAD
+_Autore
+;_Author
+;
+MSG_DEF_TEMPO_GAD
+_Tempo
+;_Tempo
+;
+MSG_DEF_SPEED_GAD
+_Velocita`
+;S_peed
+;
+MSG_RESTART_GAD
+_Riparti
+;_Restart
+;
+MSG_LENGHT_GAD
+Lunghezza
+;Length
+;
+MSG_NUM_PATTS_GAD
+Num Patterns
+;Num Patterns
+;
+MSG_NUM_TRACKS_GAD
+Tracce
+;Tracks
+;
+MSG_TOT_MOD_SIZE_GAD
+Lunghezza Totale Modulo
+;Total Module Size
+;
+MSG_TOT_INST_SIZE_GAD
+Lunghezza Totale Strumenti
+;Total Instruments Size
+;
+;
+MSG_SONG_MEN
+Song
+;Song
+;
+MSG_MERGE_SONGS_MEN
+Fusione Song
+;Merge Songs
+;
+MSG_JOIN_SONGS_MEN
+Unione Song
+;Join Songs
+;
+;
+MSG_JOIN_REQUIRES_TWO_SONGS
+ERRORE: L'unione richiede due song.
+;ERROR: Join requires two songs.
+;
+MSG_MERGE_REQUIRES_TWO_SONGS
+ERRORE: La fusione richiede due song.
+;ERROR: Merge requires two songs.
+;
+MSG_DISCARD_CURRENT_SONG
+Scarto la song corrente?
+;Discard current song?
+;
+;
+; ****************************
+; * Strings for ToolBoxWin.c *
+; ****************************
+;
+MSG_TOOLBOX_TITLE
+Barra Strumenti
+;ToolBox
+;
+;
+MSG_PLAY_GAD
+Play...
+;Play...
+;
+MSG_SONGS_GAD
+_Songs...
+;_Songs...
+;
+MSG_PATTERNS_DOTS_GAD
+_Patterns...
+;_Patterns...
+;
+MSG_INSTRUMENTS_GAD
+S_trumenti...
+;_Instruments...
+;
+MSG_SEQUENCE_DOTS_GAD
+Se_quenza...
+;Se_quence...
+;
+MSG_OPTIMIZATION_GAD
+_Ottimizzazione...
+;_Optimization...
+;
+MSG_PROJECT_MEN
+Progetto
+;Project
+;
+MSG_NEW_MEN
+Nuovo
+;New
+;
+MSG_OPEN_MEN
+Carica...
+;Open...
+;
+MSG_OPEN_NEW_MEN
+Carica Un Altro...
+;Open New...
+;
+MSG_SAVE_MEN
+Salva
+;Save
+;
+MSG_SAVE_AS_MEN
+Salva Con Nome...
+;Save As...
+;
+MSG_CLEAR_MEN
+Cancella...
+;Clear...
+;
+MSG_ABOUT_MEN
+Informazioni...
+;About...
+;
+MSG_HELP_MEN
+Aiuto...
+;Help...
+;
+MSG_ICONIFY_MEN
+Iconifica...
+;Iconify...
+;
+MSG_QUIT_MEN
+Fine
+;Quit
+;
+MSG_SETTINGS_MEN
+Opzioni
+;Settings
+;
+MSG_SAVE_FORMAT_MEN
+Formato Di Uscita...
+;Save Format...
+;
+MSG_USER_INTERFACE_MEN
+Interfaccia Utente...
+;User Interface...
+;
+MSG_SAVE_ICONS_MEN
+Genera Icone
+;Save Icons
+;
+MSG_CONFIRM_OVERWRITE_MEN
+Conferma Sovrascrittura
+;Confirm Overwrite
+;
+MSG_CONFIRM_EXIT_MEN
+Conferma Uscita
+;Confirm Exit
+;
+MSG_VERBOSE_MEN
+Rapporto Dettagliato
+;Verbose Log
+;
+MSG_OPEN_SETTINGS_MEN
+Carica Configurazione...
+;Open Settings...
+;
+MSG_SAVE_SETTINGS_MEN
+Salva Configurazione...
+;Save Settings
+;
+MSG_SAVE_SETTINGS_AS_MEN
+Salva Configurazione Con Nome...
+;Save Settings As...
+;
+;
+MSG_ABOUT_TEXT
+%s %s\n\
+Programma Di Elaborarazione Moduli Musicali\n\n\
+\
+%s\n\
+Tutti i diritti riservati.\n\n\
+\
+Internet: bernie@shock.cosmos.it\n\n\
+FidoNet: 2:332/125.1\n\
+\
+Memoria CHIP Libera: %ldKB\n\
+Memoria FAST Libera: %ldKB\n\n\
+Schermo Pubblico: %s\n\
+Porta ARexx: %s\n\
+Cx HotKey: %s\n\
+Lingua: %s
+;%s\n\
+;A Music Module Processing Utility\n\n\
+;\
+;%s\n\
+;All rights reserved.\n\n\
+;\
+;Internet: bernie@shock.cosmos.it\n\n\
+;FidoNet: 2:332/125.1\n\
+;\
+;Free CHIP Memory: %ldKB\n\
+;Free FAST Memory: %ldKB\n\n\
+;Public Screen: %s\n\
+;ARexx Port: %s\n\
+;Cx HotKey: %s\n\
+;Language: %s
+;
+MSG_DEFAULT
+--Default--
+;-- Default --
+;
+MSG_DISABLED
+--Disabilitato--
+;-- Disabled --
+;
+;
+; *********************************
+; * Strings for OptimizationWin.c *
+; *********************************
+;
+MSG_OPTIMIZATION_TITLE
+Ottimizzazione Modulo
+;Module Optimization
+;
+;
+MSG_OPTIMIZE_GAD
+_Ottimizzare
+;_Optimize
+;
+MSG_REM_UNUSED_PATTS_GAD
+Rimuovi _Pattern Non Usati
+;Remove Unused _Patterns
+;
+MSG_REM_DUPLICATE_PATTS_GAD
+Rimuovi Patterns _Doppi
+;Remove _Duplicate Patterns
+;
+MSG_REM_UNUSED_INSTR_GAD
+Rimuovi _Strumenti Non Usati
+;Remove Unused _Instruments
+;
+MSG_REM_DUP_INSTR_GAD
+
+;Remove Duplicate I_nstruments
+;
+MSG_CUT_AFTER_LOOP_GAD
+Taglia Strumenti Dopo Il Loop
+;Cut Instruments After _Loop
+;
+MSG_CUT_ZERO_TAILS_GAD
+Taglia Code di _Zeri Degli Strumenti
+;Cut Instrument _Zero Tails
+;
+MSG_CUT_PATTERNS_GAD
+
+;Cut _Breaked Patterns
+;
+MSG_REMAP_INSTRUMENTS_GAD
+
+;_Remap Instruments
+;
+;
+MSG_SAVED_X_BYTES
+Guadagnati %ld bytes (%ld%%)
+;Saved %ld bytes (%ld%%)
+;
+;
+; **************************
+; * Strings for PrefsWin.c *
+; **************************
+;
+;
+MSG_PREFS_TITLE
+Settaggi dell'interfaccia utente
+;User Interface Settings
+;
+;
+MSG_PUBLIC_SCREEN_GAD
+_Schermo Pubblico
+;_Public Screen
+;
+MSG_DISPLAY_MODE_GAD
+_Modo Video
+;Display _Mode
+;
+MSG_WINDOW_FONT_GAD
+Font della _Finestra
+;_Window Font
+;
+MSG_LISTVIEW_FONT_GAD
+
+;_ListView Font
+;
+MSG_REQUESTERS_GAD
+_Requesters
+;_Requesters
+;
+MSG_USE_DATATYPES_GAD
+
+;Use _DataTypes
+;
+MSG_APPICON_GAD
+Metti l'App_Icon
+;Put App_Icon
+;
+MSG_REFRESH_GAD
+
+;Refres_h
+;
+MSG_LOG_TO_FILE_GAD
+
+;Log To File
+;
+MSG_LOG_LEVEL_GAD
+
+;_Log Level
+;
+MSG_ASK_AUTOSAVE_GAD
+
+;Confirm _Autosave
+;
+MSG_AUTOSAVE_TIME_GAD
+
+;Autosave Mi_nutes
+;
+MSG_DO_BACKUPS_GAD
+
+;Create _Backups
+;
+MSG_BACKUP_TEMPLATE_GAD
+
+;Bac_kup Template
+;
+MSG_BACKUP_VERSIONS_GAD
+
+;Backup _Versions
+;
+MSG_ASL_GAD
+Asl
+;Asl
+;
+MSG_REQTOOLS_GAD
+ReqTools
+;ReqTools
+;
+MSG_SMART_GAD
+Smart
+;Smart
+;
+MSG_SIMPLE_GAD
+Simple
+;Simple
+;
+MSG_CLONE_DEF_SCREEN
+
+;--Clone Default Screen--
+;
+;
+; ****************************
+; * Strings for PatternWin.c *
+; ****************************
+;
+MSG_PATTERN_TITLE
+Editor Pattern
+;Pattern Editor
+;
+;
+MSG_PATTERNS_MEN
+Patterns
+;Patterns
+;
+MSG_SIZE_MEN
+Dimensione...
+;Size...
+;
+MSG_EDIT_MEN
+Editor
+;Edit
+;
+MSG_MARK_MEN
+Marca
+;Mark
+;
+MSG_CUT_MEN
+Taglia
+;Cut
+;
+MSG_COPY_MEN
+Copia
+;Copy
+;
+MSG_PASTE_MEN
+Incolla
+;Paste
+;
+MSG_ERASE_MEN
+Cancella
+;Erase
+;
+MSG_UNDO_MEN
+Undo
+;Undo
+;
+MSG_REDO_MEN
+Redo
+;Redo
+;
+MSG_EDITOR_SETTINGS_MEN
+Opzioni Editor...
+;Editor Settings...
+;
+;
+; ***************************
+; * Strings for SaversWin.c *
+; ***************************
+;
+MSG_SAVERS_TITLE
+
+;Savers
+;
+;
+MSG_SF_SEQUENCE_GAD
+
+;Se_quence
+;
+MSG_SF_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_SF_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_SF_NAMES_GAD
+
+;Names
+;
+MSG_ADD_ICON_GAD
+
+;Add I_con
+;
+MSG_MODE_GAD
+
+;_Mode
+;
+MSG_OPTIONS_DOTS_GAD
+
+;Options...
+;
+MSG_NONE_GAD
+
+;None
+;
+MSG_XPK_GAD
+
+;XPK
+;
+MSG_LHA_GAD
+
+;LhA
+;
+;
+MSG_DESCRIPTION
+
+;Description
+;
+MSG_AUTHOR
+
+;Author
+;
+MSG_MAXLENGTH
+
+;Max Length
+;
+MSG_MAXTRACKS
+
+;Max Tracks
+;
+MSG_MAXINSTRUMENTS
+
+;Max Instruments
+;
+MSG_MAXPATTERNS
+
+;Max Patterns
+;
+MSG_MAXPATTLEN
+
+;Max Pattern Length
+;
+MSG_MAXSAMPLELEN
+
+;Max Sample Length
+;
+;
+; **********************
+; * Strings ClearWin.c *
+; **********************
+;
+MSG_CLEAR_TITLE
+Cancellazione Modulo
+;Clear Module
+;
+;
+MSG_CLR_SEQUENCE_GAD
+
+;_Sequence
+;
+MSG_CLR_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_CLR_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_CLEARMOD_GAD
+
+;_Clear
+;
+;
+; ***************************
+; * Strings for SampleWin.c *
+; ***************************
+;
+MSG_SAMPLE_TITLE
+Editor Campionamenti
+;Sample Editor
+;
+;
+MSG_RENDER_MEN
+Grafico
+;Render
+;
+MSG_POINTS_MEN
+A Punti
+;Points
+;
+MSG_LINES_MEN
+A Linee
+;Lines
+;
+MSG_FILLED_MEN
+Riempito
+;Filled
+;
+;
+; *****************************
+; * Strings for PattSizeWin.c *
+; *****************************
+;
+MSG_PATTSIZE_TITLE
+Attributi Pattern
+;Pattern Attributes
+;
+;
+MSG_LINES_GAD
+
+;_Lines
+;
+MSG_TRACKS_GAD
+
+;_Tracks
+;
+MSG_DOUBLE_GAD
+
+;_Double
+;
+MSG_HALVE_GAD
+
+;_Halve
+;
+;
+; *************************
+; * Strings for Library.c *
+; *************************
+;
+MSG_SONG_TOO_MANY_TRACKS
+
+;WARNING: The selected saver supports max %lu tracks, but the song has %lu tracks.
+;
+MSG_SONG_TOO_MANY_PATTS
+
+;WARNING: The selected saver supports max %lu patterns; the song has %lu patterns.
+;
+MSG_SONG_TOO_MANY_INSTRS
+
+;WARNING: The selected saver supports max %lu instruments, last instrument is %lu.
+;
+MSG_SONG_TOO_MANY_POS
+
+;WARNING: The selected saver supports max %lu positions, but the song has %lu positions.
+;
+MSG_PATT_LENGTH_INVALID
+
+;WARNING: The length of pattern %ld ($%lx lines) isn't allowed with the selected saver.
+;
+MSG_INSTR_TOO_LONG
+ATTENZIONE: Lo strumento %lx è troppo lungo.
+;WARNING: Instrument %lx is too long and will be cropped to %lu bytes.
+;
+MSG_WILL_MODIFY_SONG
+
+;Some modifications need to be performed on this song\n\
+;in order to adapt it to the limitations of the\n\
+;destination format.
+;
+MSG_TRY_REMAPPING_INSTRUMENTS
+
+;Some instruments are placed beyond the limit for\n\
+;the selected format.\n\
+;Remapping the instruments now could help saving\n\
+;all them along with the selected format.
+;
+MSG_SAVING_MODULE
+
+;Saving %s module \"%s\"...
+; the first %s is the format name, the second %s is the song name.
--- /dev/null
+## version $VER: Nederlands.ct 1.0 (15.12.95)
+## language nederlands
+## codeset 0
+;
+## chunk AUTH Dutch catalog translation by Ji Yong Dijkhuis <0dijkhuis01@lelystad.flnet.nl>
+;**
+;** XModule.cd 3.9
+;**
+;** Copyright (C) 1995,96 Bernardo Innocenti
+;**
+;
+; **********************************
+; * Strings for all source modules *
+; **********************************
+;
+;
+MSG_NULL
+
+;
+; This string should remain blank.
+;
+MSG_OK
+Ok
+;Ok
+;
+MSG_UNDERSCORE_USE_GAD
+
+;_Use
+;
+MSG_UNDERSCORE_OK_GAD
+
+;_Ok
+;
+MSG_UNDERSCORE_CANCEL_GAD
+
+;_Cancel
+;
+MSG_YES_OR_NO
+Ja|Nee
+;Yes|No
+;
+MSG_RETRY_OR_CANCEL
+Nogmaals|Annuleer
+;Retry|Cancel
+;
+MSG_PROCEED_OR_CANCEL
+
+;Proceed|Cancel
+;
+MSG_CONTINUE
+Doorgaan
+;Continue
+;
+MSG_NO_FREE_STORE
+Niet genoeg geheugen
+;Insufficient memory.
+;
+MSG_BREAK
+Afgebroken
+;Aborted.
+;
+MSG_ERR_LOAD
+Kan \"%s\" niet laden: %s.
+;Unable to load \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_CANT_OPEN
+Kan \"%s\" niet openen: %s.
+;Cannot open file \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_READING
+Fout bij lezen \"%s\": %s.
+;Error reading \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_WRITING
+Fout bij bewaren \"%s\": %s.
+;Error writing \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+;
+; *****************************
+; * Strings for Compression.c *
+; *****************************
+;
+MSG_DECRUNCHING
+Uitpakken...
+;Decrunching...
+;
+MSG_NOTHING_IN_ARC
+Niets gevonden in archief \"%s\".
+;Nothing found in archive \"%s\".
+;
+MSG_CANT_LOAD_COMPRESSED
+Niet mogelijk om gecomprimeerd bestand te laden.
+;Unable to load compressed file.
+;
+MSG_ERROR_DECOMPRESSING
+Fout bij uitpakken bestand \"%s\": %s
+;Error decompressing file \"%s\": %s.
+;
+;
+; ********************
+; * Strings for Cx.c *
+; ********************
+;
+MSG_BAD_HOTKEY
+Slechte Commodity Hotkey beschrijving.
+;Bad Commodity HotKey description.
+;
+;
+; ***************************
+; * Strings for all loaders *
+; ***************************
+;
+MSG_READING_PATTS
+Lezen Patronen...
+;Reading Patterns...
+;
+MSG_READING_INSTS_INFO
+Lezen Instrumenten Info...
+;Reading Instruments Info...
+;
+MSG_READING_INSTS
+Lezen Instrumenten...
+;Reading Instruments...
+;
+MSG_ERR_CANT_LOAD_PATT
+FOUT: Kon patroon %ld niet laden.
+;ERROR: Couldn't load pattern %ld.
+;
+MSG_ERR_CANT_LOAD_INST
+FOUT: Kon instrument %lx niet laden.
+;ERROR: Couldn't load instrument %lx.
+;
+MSG_ERR_NO_MEM_FOR_INST
+FOUT: Niet genoeg geheugen voor instrument %lx.
+;ERROR: Not enough memory for instrument %lx.
+;
+MSG_ERR_NOT_A_SAMPLE
+FOUT: Instrument %lx is geen sample.
+;ERROR: Instrument %lx is not a sample.
+;
+MSG_SONG_TOO_LONG
+WAASCHUWING: Song lengte overschrijdt maximum. Zal worden ingekort.
+;WARNING: Song length exceeds maximum. Will be truncated.
+;
+MSG_SONG_HAS_TOO_MANY_PATT
+WAARSCHUWING: Song overschrijdt maximaal aantal patronen.
+;WARNING: Song exceeds maximum number of patterns.
+;
+MSG_PATT_TOO_MANY_TRACKS
+WAARSCHUWING: Patroon %ld heeft te veel sporen. Afkappen naar %ld
+;WARNING: Pattern %ld has too many tracks. Cropping to %ld tracks.
+;
+MSG_PATT_TOO_MANY_LINES
+Patroon %ld heeft te veel lijnen. Afkappen naar %ld lijnen.
+;WARNING: Pattern %ld has too many lines. Cropping to %ld lines.
+;
+MSG_INVALID_NOTE
+WAARSCHUWING: Ongeldige noot %ld (Pat %ld Spoor %ld Lijn %ld).
+;WARNING: Invalid note %ld (Patt %ld Track %ld Line %ld).
+;
+MSG_UNKNOWN_EFF
+Onbekend effect: $%lx (Pat %ld Spoor %ld Lijn %ld).
+;Unknown effect: $%lx (Patt %ld Track %ld Line %ld).
+;
+MSG_EXTRA_DATA_AFTER_MOD
+WAARSCHUWING: Extra data gevonden achter module: Zal worden
+;WARNING: Extra data found after valid module: Will be ignored.
+;
+MSG_WRITING_HEADER
+Bewaren Kop...
+;Writing Header...
+;
+MSG_WRITING_PATTS
+Bewaren Patroon...
+;Writing Patterns...
+;
+MSG_WRITING_INSTS
+Bewaren Instrumenten...
+;Writing Instruments...
+;
+MSG_WRITING_INSTINFO
+Bewaren Instrumenten Info...
+;Writing Instruments Info...
+;
+MSG_WRITING_INSTDATA
+Bewaren Instrumenten Data...
+;Writing Instruments Data...
+;
+MSG_NOTE_TOO_LOW
+WAARSCHUWING: Noot op Pat %ld Spoor %ld Lijn %ld is te laag.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too low.
+;
+MSG_NOTE_TOO_HIGH
+WAARSCHUWING: Noot op Pat %ld Spoor %ld Lijn %ld is te hoog.
+;WARNING: Note at Patt %ld Track %ld Line %ld is too high.
+;
+MSG_NO_MEM_TO_HALVE
+WAARSCHUWING: Niet genoeg geheugen om volume van instrument %lx te
+;WARNING: Not enough memory to halve volume of instrument %lx.
+;
+;
+; *****************************
+; * Strings for Get/SaveMED.c *
+; *****************************
+;
+MSG_READING_MMD
+Laden MMD%lc module...
+;Loading MMD%lc module...
+; The %lc is the MED format (0, 1, 2, ...)
+;
+MSG_UNSUPPORTED_MMD_FORMAT
+FOUT: Geen ondersteund OctaMED formaat.
+;ERROR: Unsupported OctaMED format.
+;
+MSG_WRONG_EFFECT_IN_MMD0
+WAARSCHUWING: Effect %lx wordt niet ondersteund in MMD0 formaat.
+;WARNING: Effect %lx is not supported in MMD0 format. Use MMD1 or better.
+;
+;
+; ***********************************
+; * Strings for ScreamTrackerHook.c *
+; ***********************************
+;
+MSG_ADLIB_INSTR
+FOUT: Instrument %lx is een ADLib %s.
+;ERROR: Instrument %lx is an ADLib %s.
+; %s is the name of the ADLib instrument (eg: Snare).
+;
+MSG_TRACK_OUT_OF_RANGE
+WAARSCHUWING: Spoor %lx is buiten bereik.
+;WARNING: Track %lx is out of range.
+;
+MSG_UNKNOWN_SAMPLE_COMPRESSION
+WAARSCHUWING: Onbekende sample compressie voor instrument %lx.
+;WARNING: Unknown sample compression for instrument %lx.
+;
+MSG_INST_IS_STEREO
+WAARSCHUWING: Instrument %lx is een stereo sample.
+;WARNING: Instrument %lx is a stereo sample.
+;
+MSG_INST_IS_16BIT
+WAARSCHUWING: Instrument %lx is 16bit: onmogelijk om te laden.
+;WARNING: Instrument %lx is 16bit: unable to load it.
+;
+;
+; *****************************
+; * Strings for TrackerHook.c *
+; *****************************
+;
+MSG_READING_TYPE_MODULE
+Lezen %s module
+;Reading %s module...
+; (%s is the module format)
+;
+MSG_EXCEEDS_64_PATTS
+WAARSCHUWING: Module overschrijdt 64 patronen. ProTracker 2.3 benodigd om module te spelen.
+;NOTE: Module exceeds 64 patterns. You need ProTracker 2.3 to play it.
+;
+MSG_MODULE_ID
+
+;Tracker ID: \"%s\"
+;
+MSG_MODULE_HAS_N_CHN
+
+;Module has %lu tracks...
+;
+MSG_SONG_HAS_NO_INSTS
+Dit bestand is een song en heeft geen instrumenten.
+;This file is a song and doesn't contain instruments.
+;
+MSG_EXCEEDS_MAXPAATTS
+WAARSCHUWING: Module overschreidt %ld patronen.
+;WARNING: Module execeeds %ld patterns.
+;
+MSG_PATT_WILL_GROW
+Patroon %ld wordt verlengd naar 64 lijnen (was %ld lijnen lang).
+;Pattern %ld will grow to 64 lines (was %ld lines long).
+;
+MSG_SPLITTING_PATT
+Splitsen patroon %ld (was %ld lijnen lang).
+;Splitting pattern %ld (was %ld lines long).
+;
+; **************************
+; * Strings for SaveMIDI.c *
+; **************************
+;
+MSG_CHOOSING_CHANNELS
+Kiezen Kanalen...
+;Choosing Channels...
+;
+MSG_WRITING_MIDI_TRACKS
+Bewaren MIDI Sporen...
+;Writing MIDI Tracks...
+;
+MSG_TOO_MANY_CHANNELS
+FOUT: Song heeft te veel MIDI kanalen nodig.
+;ERROR: Song requires too many MIDI channels.
+;
+;
+; *********************
+; * Strings for Gui.c *
+; *********************
+;
+MSG_REALLY_QUIT_XMODULE
+Wekelijke beeindigen XModule?
+;Really Quit XModule?
+;
+MSG_CLOSE_ALL_WINDOWS
+Sluit alle vreemde vensters\n\
+a.u.b en selecteer dan `Doorgaan'.
+;Please close all visitor windows\n\
+;and then select `Continue'.
+;
+;
+; ***********************
+; * Strings for Instr.c *
+; ***********************
+;
+MSG_UNKNOWN_IFF
+Onbekend IFF formaat %s.
+;Unknown IFF format %s.
+;
+MSG_ILLEGAL_IFF_STRUCTURE
+Foutieve IFF structuur.
+;Illegal IFF structure.
+;
+MSG_SELECT_RAW_MODE
+Onbekend instrument formaat.\n\
+;Unrecognized instrument format.\n\
+;Please select RAW mode.
+;Unrecognized instrument format.\n\
+;Please select RAW mode.
+;
+MSG_RAW_MODES
+Signed 8bit|Unsigned 8bit|Annuleer
+;Signed 8bit|Unsigned 8bit|Cancel
+;
+MSG_DATATYPES_ERROR
+DataTypes fout: %s.
+;DataTypes error: %s.
+; %s is a detailed description of the problem.
+;
+MSG_UNKNOWN_COMPRESSION
+Onbekend compressie type.
+;Unknown compression type.
+;
+MSG_SAMPLE_WRONG_SIZE
+%lu bit samples worden niet ondersteund.
+;%lu bit samples are not supported.
+; %lu is the number of bits per sample.
+;
+MSG_SAMPLE_NOT_MONO
+Samples anders dan MONO worden niet ondersteund.
+;Samples other than MONO are not supported.
+;
+MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS
+Samples met %ld kanalen worden niet ondersteund.
+;Samples with %ld channels are not supported.
+;
+MSG_NO_MEMORY_TO_OPTIMIZE_INSTR
+WAARSCHUWING: onvoldoende geheugen om instrument %lx te optimalizeren.
+;WARNING: insufficient memory to optimize instrument %lx.
+;
+MSG_INSTR_WILL_SHRINK
+Instrument %lx zal krimpen van %ld naar %ld.
+;Instrument %lx will shrink from %ld to %ld.
+;
+MSG_INSTR_DUPES_REMOVED
+Dubbele instrumenten gevonden en verwijderd: %lx == %lx.
+;Duplicate instruments found and removed: %lx == %lx.
+;
+MSG_INSTR_UNUSED
+Instrument %lx is niet gebruikt en wordt verwijderd.
+;Instrument %lx was never used and it's being removed.
+;
+;
+; **********************
+; * Strings for Misc.c *
+; **********************
+;
+MSG_OPENLIB_VER_FAIL
+
+;Couldn't open \"%s\" version %ld or greater.
+;
+MSG_OPENLIB_FAIL
+Kon %s versie %ld of hoger niet openen.
+;Couldn't open \"%s\".
+;
+;
+; ***************************
+; * Strings for Operators.c *
+; ***************************
+;
+MSG_CANT_INSERT_PATT
+Invoegen patroon niet mogelijk: Maximaal aantal patronen bereikt.
+;Unable to insert pattern: Maximum number of patterns reached.
+;
+MSG_PATT_UNUSED
+Patroon %ld is niet gebruikt en wordt verwijdert.
+;Pattern %ld is not used and is beeing deleted.
+;
+MSG_PATT_CUT
+Patroon %ld wordt afgekapt op lijn %ld.
+;Pattern %ld will be cut at line %ld.
+;
+MSG_PATT_DUPE
+Dubbele patronen gevonden en verwijderd: %ld == %ld.
+;Duplicate patterns found and removed: %ld == %ld.
+; The two %ld are the pattern numbers.
+;
+MSG_SONG_LEN_DIFFERENT
+WAARSCHUWING: Song lengtes zijn verschillend. De kortere wordt gebruikt.
+;WARNING: Song lengths are different. Using shorter one.
+;
+MSG_PATT_LEN_DIFFERENT
+WAARSCHUWING: Verschillende patroon lengtes op positie %ld. De kortere wordt gebruikt.
+;WARNING: Different pattern lengths at position %ld. Using shorter one.
+;
+MSG_ERR_INSTR_OVERFLOW
+
+;ERROR: Instruments overflow. Try remapping the instruments.
+;
+;
+; ***********************
+; * Strings for Prefs.c *
+; ***********************
+;
+MSG_BAD_PREFS_VERSION
+Foutieve versie van instellingen bestand.
+;Incorrect version of preferences file
+;
+;
+; ****************************
+; * Strings for Requesters.c *
+; ****************************
+;
+MSG_XMODULE_REQUEST
+XModule Aanvraag Venster
+;XModule Request
+; This is the title of requesters put out by XModule.
+;
+MSG_CLONE_WB
+Namaken Workbench Scherm
+;Clone Workbench Screen
+;
+MSG_CLOSE_FILEREQUESTER
+Bestandsaanvraag venster\n sluiten a.u.b en selecteer dan `Doorgaan'.
+;Please close FileRequester\n and then select `Continue'.
+;
+MSG_SELECT_MODULES
+Selecteer Module(s)...
+;Select Module(s)...
+;
+MSG_SELECT_INSTRUMENTS
+Selecteer Instrument(en)...
+;Select Instrument(s)...
+;
+MSG_SELECT_PATTERN
+Selecteer Patroon...
+;Select Pattern...
+;
+MSG_SAVE_MODULE
+Bewaren Module...
+;Save Module...
+;
+MSG_SAVE_INSTRUMENT
+Bewaren Instrument...
+;Save Instrument...
+;
+MSG_SAVE_PATTERN
+Bewaren Patroon...
+;Save Pattern...
+;
+MSG_FILE_EXISTS
+Bestand \"%s\"\n bestaat reeds.
+;File \"%s\"\nalready exists.
+;
+MSG_OVERWRITE
+Overschrijven|Kies Andere|Annuleer
+;Overwrite|Choose Another|Abort
+;
+;
+; **********************
+; * Strings for Song.c *
+; **********************
+;
+MSG_CLOSE_ALL_SONGS
+
+;Please close all open songs\n\
+;and then select `Continue'.
+;
+MSG_UNESPECTED_EOF
+Onverwacht einde van bestand.
+;Unespected end of file.
+;
+MSG_MODULE_LOADED_OK
+Module goed ingeladen.
+;Module loaded OK.
+;
+MSG_MODULE_SAVED_OK
+Module goed weggeschreven.
+;Module saved OK.
+;
+MSG_UNKNOWN_SAVE_FORMAT
+FOUT: Onbekend bewaar formaat.
+;ERROR: Unrecognized save format.
+;
+MSG_INVALID_LOOP_REMOVED
+Foutieve loop voor instrument %lx verwijderd.
+;Removed invalid loop for instrument %lx.
+;
+MSG_INVALID_LOOP_FIXED
+Foutieve loop voor instrument %lx hersteld.
+;Fixed invalid loop for instrument %lx.
+;
+MSG_SONG_HAS_NO_PATTS
+WAARSCHUWING: Song heeft geen patronen.
+;WARNING: Song has no patterns.
+;
+MSG_SONG_HAS_NO_SEQ
+WAARSCHUWING: Song heeft geen volgreeks.
+;WARNING: Song has no sequence.
+;
+MSG_INVALID_SONG_POS
+WAARSCHUWING: Song positie %ld refereert aan patroon %ld, deze bestaat niet.
+;WARNING: Song position %ld references pattern %ld, which doesn't exist.
+;
+MSG_UNKNOWN_MOD_FORMAT
+Kon module formaat niet identificeren.\n\
+(Een data bestand als mudule laden is onverstandig)
+;Unable to identify module format.\n\
+;(Loading a data file as a module is unwise)
+;
+MSG_SOUND_PRO_CANCEL
+Soundtracker 15|ProTracker|Annuleer
+;SoundTracker 15|ProTracker|Cancel
+;
+MSG_AUTHOR_UNKNOWN
+Onbekend
+;Unknown
+;
+MSG_SONG_UNTITLED
+Ongetiteld
+;Untitled
+;
+;
+; **********************
+; * Strings for Help.c *
+; **********************
+;
+MSG_AMIGAGUIDE_ERROR
+AmigaGuide fout:
+;AmigaGuide error:
+; Note one blank space at the end of this line!
+;
+;
+; ***************************
+; * Strings for PattPrefs.c *
+; ***************************
+;
+MSG_PATTPREFS_TITLE
+
+;Pattern Editor Settings
+;
+;
+MSG_ADVANCE_TRACKS_GAD
+
+;Advance _Tracks
+;
+MSG_ADVANCE_LINES_GAD
+
+;Advance _Lines
+;
+MSG_MAX_UNDO_LEVELS_GAD
+
+;Max _Undo Levels
+;
+MSG_MAX_UNDO_MEM_GAD
+
+;Max Undo _Memory
+;
+MSG_CLIPBOARD_UNIT_GAD
+
+;Cli_pboard Unit
+;
+MSG_SCROLLER_POS_GAD
+
+;_Scroller Position
+;
+MSG_VERT_WRAP_GAD
+
+;_Vertical Wrap
+;
+MSG_HORIZ_WRAP_GAD
+
+;_Horizontal Wrap
+;
+MSG_HEX_LINE_NUMBERS_GAD
+
+;He_x Line Numbers
+;
+MSG_BLANK_ZERO_GAD
+
+;Blank _Zero Digits
+;
+MSG_BACKDROP_GAD
+
+;Backdrop _Window
+;
+MSG_HORIZ_SCROLLER_GAD
+
+;Ho_rizontal Scroller
+;
+MSG_DO_RULER_GAD
+
+;Display Cursor Ruler
+;
+MSG_DO_TINY_LINES_GAD
+
+;Display Tiny Lines
+;
+MSG_EDITOR_FONT_GAD
+
+;_Editor Font
+;
+MSG_BACKGROUND_PEN_GAD
+
+;Background
+;
+MSG_TEXT_PEN_GAD
+
+;T_ext
+;
+MSG_LINES_PEN_GAD
+
+;Li_nes
+;
+MSG_TINY_LINES_PEN_GAD
+
+;Tin_y Lines
+;
+MSG_RIGHT_GAD
+
+;Right
+;
+MSG_LEFT_GAD
+
+;Left
+;
+MSG_OFF_GAD
+
+;Off
+;
+MSG_PATT_TOO_LONG
+FOUT: Patroon zou het maximum aantal lijnen overschrijden.
+;ERROR: Pattern would exceed the maximum number of lines.
+;
+;
+; *****************************
+; * Strings for SequenceWin.c *
+; *****************************
+;
+;
+MSG_SEQUENCE_TITLE
+
+;Sequence Editor
+;
+;
+MSG_SEQUENCE_GAD
+
+;Sequence
+;
+MSG_UNDERSCORE_ADD_GAD
+
+;_Add
+;
+MSG_UP_GAD
+
+;Up
+;
+MSG_DOWN_GAD
+
+;Down
+;
+MSG_UNDERSCORE_NAME_GAD
+
+;_Name
+;
+MSG_PATTERNS_GAD
+
+;Patterns
+;
+MSG_UNDERSCORE_UP_GAD
+
+;_Up
+;
+MSG_UNDERSCORE_DOWN_GAD
+
+;_Down
+;
+MSG_UNDERSCORE_INS_GAD
+
+;_Ins
+;
+;
+MSG_UNNAMED
+
+;-- unnamed --
+;
+;
+; ********************************
+; * Strings for InstrumentsWin.c *
+; ********************************
+;
+MSG_INSTRUMENTS_TITLE
+
+;Instruments
+;
+;
+MSG_VOLUME_GAD
+
+;_Volume
+;
+MSG_FINETUNE_GAD
+
+;_FineTune
+;
+MSG_LENGTH_GAD
+
+;Length
+;
+MSG_KIND_GAD
+
+;_Kind
+;
+MSG_EDIT_DOTS_GAD
+
+;_Edit...
+;
+MSG_SAMPLE_GAD
+
+;Sample
+;
+MSG_SYNTH_GAD
+
+;Synth
+;
+MSG_HYBRID_GAD
+
+;Hybrid
+;
+;
+MSG_INSTRUMENTS_MEN
+
+;Instruments
+;
+MSG_LOAD_MEN
+
+;Load...
+;
+MSG_REMAP_MEN
+
+;Remap
+;
+MSG_SAVE_COMPRESSED_MEN
+
+;Save Compressed
+;
+MSG_SAVE_RAW_MEN
+
+;Save Raw
+;
+;
+MSG_EMPTY
+
+;-- empty --
+;
+;
+; *************************
+; * Strings for PlayWin.c *
+; *************************
+;
+MSG_PLAY_TITLE
+
+;Play
+;
+;
+MSG_VOL_GAD
+
+;_Vol
+;
+MSG_POS_GAD
+
+;Pos
+;
+MSG_TIME_GAD
+
+;Time
+;
+MSG_RST_GAD
+
+;_Rst
+; Stands for Reset/Restart
+;
+MSG_PLAYER_INIT_ERR
+Afspeler initialisatie fout: %ld.
+;Player initialization error: %ld.
+;
+;
+; *****************************
+; * Strings for ProgressWin.c *
+; *****************************
+;
+MSG_PROGRESS_TITLE
+
+;XModule is working...
+;
+MSG_LOG_TITLE
+
+;XModule Log
+;
+;
+MSG_UNDERSCORE_ABORT_GAD
+
+;_Abort
+;
+;
+MSG_PERCENT_DONE
+%ld van %ld (%ld%% gedaan)
+;%ld of %ld (%ld%% done)
+; This string appears inside the progress window gauge.
+; eg: "5 of 20 (25% done)".
+; Leave that double percent (%%) alone! :-)
+;
+;
+; *****************************
+; * Strings for SongInfoWin.c *
+; *****************************
+;
+MSG_SONGINFO_TITLE
+
+;Song Information
+;
+;
+MSG_UNDERSCORE_NEW_GAD
+
+;Ne_w
+;
+MSG_OPEN_GAD
+
+;_Open...
+;
+MSG_SAVE_GAD
+
+;_Save
+;
+MSG_DEL_GAD
+
+;Del
+;
+MSG_SONG_NAME_GAD
+
+;Song _Name
+;
+MSG_AUTHOR_NAME_GAD
+
+;_Author
+;
+MSG_DEF_TEMPO_GAD
+
+;_Tempo
+;
+MSG_DEF_SPEED_GAD
+
+;S_peed
+;
+MSG_RESTART_GAD
+
+;_Restart
+;
+MSG_LENGHT_GAD
+
+;Length
+;
+MSG_NUM_PATTS_GAD
+
+;Num Patterns
+;
+MSG_NUM_TRACKS_GAD
+
+;Tracks
+;
+MSG_TOT_MOD_SIZE_GAD
+
+;Total Module Size
+;
+MSG_TOT_INST_SIZE_GAD
+
+;Total Instruments Size
+;
+;
+MSG_SONG_MEN
+
+;Song
+;
+MSG_MERGE_SONGS_MEN
+
+;Merge Songs
+;
+MSG_JOIN_SONGS_MEN
+
+;Join Songs
+;
+;
+MSG_JOIN_REQUIRES_TWO_SONGS
+FOUT: Toevoegen vereist twee songs.
+;ERROR: Join requires two songs.
+;
+MSG_MERGE_REQUIRES_TWO_SONGS
+FOUT: Samenvoegen vereist twee songs.
+;ERROR: Merge requires two songs.
+;
+MSG_DISCARD_CURRENT_SONG
+Verwijderen huidige song?
+;Discard current song?
+;
+;
+; ****************************
+; * Strings for ToolBoxWin.c *
+; ****************************
+;
+MSG_TOOLBOX_TITLE
+
+;ToolBox
+;
+;
+MSG_PLAY_GAD
+
+;Play...
+;
+MSG_SONGS_GAD
+
+;_Songs...
+;
+MSG_PATTERNS_DOTS_GAD
+
+;_Patterns...
+;
+MSG_INSTRUMENTS_GAD
+
+;_Instruments...
+;
+MSG_SEQUENCE_DOTS_GAD
+
+;Se_quence...
+;
+MSG_OPTIMIZATION_GAD
+
+;_Optimization...
+;
+MSG_PROJECT_MEN
+
+;Project
+;
+MSG_NEW_MEN
+
+;New
+;
+MSG_OPEN_MEN
+
+;Open...
+;
+MSG_OPEN_NEW_MEN
+
+;Open New...
+;
+MSG_SAVE_MEN
+
+;Save
+;
+MSG_SAVE_AS_MEN
+
+;Save As...
+;
+MSG_CLEAR_MEN
+
+;Clear...
+;
+MSG_ABOUT_MEN
+
+;About...
+;
+MSG_HELP_MEN
+
+;Help...
+;
+MSG_ICONIFY_MEN
+
+;Iconify...
+;
+MSG_QUIT_MEN
+
+;Quit
+;
+MSG_SETTINGS_MEN
+
+;Settings
+;
+MSG_SAVE_FORMAT_MEN
+
+;Save Format...
+;
+MSG_USER_INTERFACE_MEN
+
+;User Interface...
+;
+MSG_SAVE_ICONS_MEN
+
+;Save Icons
+;
+MSG_CONFIRM_OVERWRITE_MEN
+
+;Confirm Overwrite
+;
+MSG_CONFIRM_EXIT_MEN
+
+;Confirm Exit
+;
+MSG_VERBOSE_MEN
+
+;Verbose Log
+;
+MSG_OPEN_SETTINGS_MEN
+
+;Open Settings...
+;
+MSG_SAVE_SETTINGS_MEN
+
+;Save Settings
+;
+MSG_SAVE_SETTINGS_AS_MEN
+
+;Save Settings As...
+;
+;
+MSG_ABOUT_TEXT
+%s %s\n\
+Een Muziek Module Bewerkings Utility\n\n
+;%s\n\
+;A Music Module Processing Utility\n\n\
+;\
+;%s\n\
+;All rights reserved.\n\n\
+;\
+;Internet: bernie@shock.cosmos.it\n\n\
+;FidoNet: 2:332/125.1\n\
+;\
+;Free CHIP Memory: %ldKB\n\
+;Free FAST Memory: %ldKB\n\n\
+;Public Screen: %s\n\
+;ARexx Port: %s\n\
+;Cx HotKey: %s\n\
+;Language: %s
+;
+MSG_DEFAULT
+--Standaard--
+;-- Default --
+;
+MSG_DISABLED
+--Uitgeschakeld--
+;-- Disabled --
+;
+;
+; *********************************
+; * Strings for OptimizationWin.c *
+; *********************************
+;
+MSG_OPTIMIZATION_TITLE
+
+;Module Optimization
+;
+;
+MSG_OPTIMIZE_GAD
+
+;_Optimize
+;
+MSG_REM_UNUSED_PATTS_GAD
+
+;Remove Unused _Patterns
+;
+MSG_REM_DUPLICATE_PATTS_GAD
+
+;Remove _Duplicate Patterns
+;
+MSG_REM_UNUSED_INSTR_GAD
+
+;Remove Unused _Instruments
+;
+MSG_REM_DUP_INSTR_GAD
+
+;Remove Duplicate I_nstruments
+;
+MSG_CUT_AFTER_LOOP_GAD
+
+;Cut Instruments After _Loop
+;
+MSG_CUT_ZERO_TAILS_GAD
+
+;Cut Instrument _Zero Tails
+;
+MSG_CUT_PATTERNS_GAD
+
+;Cut _Breaked Patterns
+;
+MSG_REMAP_INSTRUMENTS_GAD
+
+;_Remap Instruments
+;
+;
+MSG_SAVED_X_BYTES
+Bewaard %ld bytes (%ld%%)
+;Saved %ld bytes (%ld%%)
+;
+;
+; **************************
+; * Strings for PrefsWin.c *
+; **************************
+;
+;
+MSG_PREFS_TITLE
+
+;User Interface Settings
+;
+;
+MSG_PUBLIC_SCREEN_GAD
+
+;_Public Screen
+;
+MSG_DISPLAY_MODE_GAD
+
+;Display _Mode
+;
+MSG_WINDOW_FONT_GAD
+
+;_Window Font
+;
+MSG_LISTVIEW_FONT_GAD
+
+;_ListView Font
+;
+MSG_REQUESTERS_GAD
+
+;_Requesters
+;
+MSG_USE_DATATYPES_GAD
+
+;Use _DataTypes
+;
+MSG_APPICON_GAD
+
+;Put App_Icon
+;
+MSG_REFRESH_GAD
+
+;Refres_h
+;
+MSG_LOG_TO_FILE_GAD
+
+;Log To File
+;
+MSG_LOG_LEVEL_GAD
+
+;_Log Level
+;
+MSG_ASK_AUTOSAVE_GAD
+
+;Confirm _Autosave
+;
+MSG_AUTOSAVE_TIME_GAD
+
+;Autosave Mi_nutes
+;
+MSG_DO_BACKUPS_GAD
+
+;Create _Backups
+;
+MSG_BACKUP_TEMPLATE_GAD
+
+;Bac_kup Template
+;
+MSG_BACKUP_VERSIONS_GAD
+
+;Backup _Versions
+;
+MSG_ASL_GAD
+
+;Asl
+;
+MSG_REQTOOLS_GAD
+
+;ReqTools
+;
+MSG_SMART_GAD
+
+;Smart
+;
+MSG_SIMPLE_GAD
+
+;Simple
+;
+MSG_CLONE_DEF_SCREEN
+
+;--Clone Default Screen--
+;
+;
+; ****************************
+; * Strings for PatternWin.c *
+; ****************************
+;
+MSG_PATTERN_TITLE
+
+;Pattern Editor
+;
+;
+MSG_PATTERNS_MEN
+
+;Patterns
+;
+MSG_SIZE_MEN
+
+;Size...
+;
+MSG_EDIT_MEN
+
+;Edit
+;
+MSG_MARK_MEN
+
+;Mark
+;
+MSG_CUT_MEN
+
+;Cut
+;
+MSG_COPY_MEN
+
+;Copy
+;
+MSG_PASTE_MEN
+
+;Paste
+;
+MSG_ERASE_MEN
+
+;Erase
+;
+MSG_UNDO_MEN
+
+;Undo
+;
+MSG_REDO_MEN
+
+;Redo
+;
+MSG_EDITOR_SETTINGS_MEN
+
+;Editor Settings...
+;
+;
+; ***************************
+; * Strings for SaversWin.c *
+; ***************************
+;
+MSG_SAVERS_TITLE
+
+;Savers
+;
+;
+MSG_SF_SEQUENCE_GAD
+
+;Se_quence
+;
+MSG_SF_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_SF_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_SF_NAMES_GAD
+
+;Names
+;
+MSG_ADD_ICON_GAD
+
+;Add I_con
+;
+MSG_MODE_GAD
+
+;_Mode
+;
+MSG_OPTIONS_DOTS_GAD
+
+;Options...
+;
+MSG_NONE_GAD
+
+;None
+;
+MSG_XPK_GAD
+
+;XPK
+;
+MSG_LHA_GAD
+
+;LhA
+;
+;
+MSG_DESCRIPTION
+
+;Description
+;
+MSG_AUTHOR
+
+;Author
+;
+MSG_MAXLENGTH
+
+;Max Length
+;
+MSG_MAXTRACKS
+
+;Max Tracks
+;
+MSG_MAXINSTRUMENTS
+
+;Max Instruments
+;
+MSG_MAXPATTERNS
+
+;Max Patterns
+;
+MSG_MAXPATTLEN
+
+;Max Pattern Length
+;
+MSG_MAXSAMPLELEN
+
+;Max Sample Length
+;
+;
+; **********************
+; * Strings ClearWin.c *
+; **********************
+;
+MSG_CLEAR_TITLE
+
+;Clear Module
+;
+;
+MSG_CLR_SEQUENCE_GAD
+
+;_Sequence
+;
+MSG_CLR_INSTRUMENTS_GAD
+
+;_Instruments
+;
+MSG_CLR_PATTERNS_GAD
+
+;_Patterns
+;
+MSG_CLEARMOD_GAD
+
+;_Clear
+;
+;
+; ***************************
+; * Strings for SampleWin.c *
+; ***************************
+;
+MSG_SAMPLE_TITLE
+
+;Sample Editor
+;
+;
+MSG_RENDER_MEN
+
+;Render
+;
+MSG_POINTS_MEN
+
+;Points
+;
+MSG_LINES_MEN
+
+;Lines
+;
+MSG_FILLED_MEN
+
+;Filled
+;
+;
+; *****************************
+; * Strings for PattSizeWin.c *
+; *****************************
+;
+MSG_PATTSIZE_TITLE
+
+;Pattern Attributes
+;
+;
+MSG_LINES_GAD
+
+;_Lines
+;
+MSG_TRACKS_GAD
+
+;_Tracks
+;
+MSG_DOUBLE_GAD
+
+;_Double
+;
+MSG_HALVE_GAD
+
+;_Halve
+;
+;
+; *************************
+; * Strings for Library.c *
+; *************************
+;
+MSG_SONG_TOO_MANY_TRACKS
+
+;WARNING: The selected saver supports max %lu tracks, but the song has %lu tracks.
+;
+MSG_SONG_TOO_MANY_PATTS
+
+;WARNING: The selected saver supports max %lu patterns; the song has %lu patterns.
+;
+MSG_SONG_TOO_MANY_INSTRS
+
+;WARNING: The selected saver supports max %lu instruments, last instrument is %lu.
+;
+MSG_SONG_TOO_MANY_POS
+
+;WARNING: The selected saver supports max %lu positions, but the song has %lu positions.
+;
+MSG_PATT_LENGTH_INVALID
+
+;WARNING: The length of pattern %ld ($%lx lines) isn't allowed with the selected saver.
+;
+MSG_INSTR_TOO_LONG
+WAARSCHUWING: Instrument %lx is te lang.
+;WARNING: Instrument %lx is too long and will be cropped to %lu bytes.
+;
+MSG_WILL_MODIFY_SONG
+
+;Some modifications need to be performed on this song\n\
+;in order to adapt it to the limitations of the\n\
+;destination format.
+;
+MSG_TRY_REMAPPING_INSTRUMENTS
+
+;Some instruments are placed beyond the limit for\n\
+;the selected format.\n\
+;Remapping the instruments now could help saving\n\
+;all them along with the selected format.
+;
+MSG_SAVING_MODULE
+
+;Saving %s module \"%s\"...
+; the first %s is the format name, the second %s is the song name.
--- /dev/null
+#language english
+#version 1
+;**
+;** XModule.cd 3.9
+;**
+;** Copyright (C) 1995,96 Bernardo Innocenti
+;**
+;
+; **********************************
+; * Strings for all source modules *
+; **********************************
+;
+;
+MSG_NULL (0/0/0)
+
+; This string should remain blank.
+;
+MSG_OK (//)
+Ok
+;
+MSG_UNDERSCORE_USE_GAD (//)
+_Use
+;
+MSG_UNDERSCORE_OK_GAD (//)
+_Ok
+;
+MSG_UNDERSCORE_CANCEL_GAD (//)
+_Cancel
+;
+MSG_YES_OR_NO (//)
+Yes|No
+;
+MSG_RETRY_OR_CANCEL (//)
+Retry|Cancel
+;
+MSG_PROCEED_OR_CANCEL (//)
+Proceed|Cancel
+;
+MSG_CONTINUE (//)
+Continue
+;
+MSG_NO_FREE_STORE (//)
+Insufficient memory.
+;
+MSG_BREAK (//)
+Aborted.
+;
+MSG_ERR_LOAD (//)
+Unable to load \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_CANT_OPEN (//)
+Cannot open file \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_READING (//)
+Error reading \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+MSG_ERROR_WRITING (//)
+Error writing \"%s\"
+; %s is the filename. A colon ':' and the explanation of failure will be postpended.
+;
+;
+; *****************************
+; * Strings for Compression.c *
+; *****************************
+;
+MSG_DECRUNCHING (//)
+Decrunching...
+;
+MSG_NOTHING_IN_ARC (//)
+Nothing found in archive \"%s\".
+;
+MSG_CANT_LOAD_COMPRESSED (//)
+Unable to load compressed file.
+;
+MSG_ERROR_DECOMPRESSING (//)
+Error decompressing file \"%s\": %s.
+;
+;
+; ********************
+; * Strings for Cx.c *
+; ********************
+;
+MSG_BAD_HOTKEY (//)
+Bad Commodity HotKey description.
+;
+;
+; ***************************
+; * Strings for all loaders *
+; ***************************
+;
+MSG_READING_PATTS (//)
+Reading Patterns...
+;
+MSG_READING_INSTS_INFO (//)
+Reading Instruments Info...
+;
+MSG_READING_INSTS (//)
+Reading Instruments...
+;
+MSG_ERR_CANT_LOAD_PATT (//)
+ERROR: Couldn't load pattern %ld.
+;
+MSG_ERR_CANT_LOAD_INST (//)
+ERROR: Couldn't load instrument %lx.
+;
+MSG_ERR_NO_MEM_FOR_INST (//)
+ERROR: Not enough memory for instrument %lx.
+;
+MSG_ERR_NOT_A_SAMPLE (//)
+ERROR: Instrument %lx is not a sample.
+;
+MSG_SONG_TOO_LONG (//)
+WARNING: Song length exceeds maximum. Will be truncated.
+;
+MSG_SONG_HAS_TOO_MANY_PATT (//)
+WARNING: Song exceeds maximum number of patterns.
+;
+MSG_PATT_TOO_MANY_TRACKS (//)
+WARNING: Pattern %ld has too many tracks. Cropping to %ld tracks.
+;
+MSG_PATT_TOO_MANY_LINES (//)
+WARNING: Pattern %ld has too many lines. Cropping to %ld lines.
+;
+MSG_INVALID_NOTE (//)
+WARNING: Invalid note %ld (Patt %ld Track %ld Line %ld).
+;
+MSG_UNKNOWN_EFF (//)
+Unknown effect: $%lx (Patt %ld Track %ld Line %ld).
+;
+MSG_EXTRA_DATA_AFTER_MOD (//)
+WARNING: Extra data found after valid module: Will be ignored.
+;
+MSG_WRITING_HEADER (//)
+Writing Header...
+;
+MSG_WRITING_PATTS (//)
+Writing Patterns...
+;
+MSG_WRITING_INSTS (//)
+Writing Instruments...
+;
+MSG_WRITING_INSTINFO (//)
+Writing Instruments Info...
+;
+MSG_WRITING_INSTDATA (//)
+Writing Instruments Data...
+;
+MSG_NOTE_TOO_LOW (//)
+WARNING: Note at Patt %ld Track %ld Line %ld is too low.
+;
+MSG_NOTE_TOO_HIGH (//)
+WARNING: Note at Patt %ld Track %ld Line %ld is too high.
+;
+MSG_NO_MEM_TO_HALVE (//)
+WARNING: Not enough memory to halve volume of instrument %lx.
+;
+;
+; *****************************
+; * Strings for Get/SaveMED.c *
+; *****************************
+;
+MSG_READING_MMD (//)
+Loading MMD%lc module...
+; The %lc is the MED format (0, 1, 2, ...)
+;
+MSG_UNSUPPORTED_MMD_FORMAT (//)
+ERROR: Unsupported OctaMED format.
+;
+MSG_WRONG_EFFECT_IN_MMD0 (//)
+WARNING: Effect %lx is not supported in MMD0 format. Use MMD1 or better.
+;
+;
+; ***********************************
+; * Strings for ScreamTrackerHook.c *
+; ***********************************
+;
+MSG_ADLIB_INSTR (//)
+ERROR: Instrument %lx is an ADLib %s.
+; %s is the name of the ADLib instrument (eg: Snare).
+;
+MSG_TRACK_OUT_OF_RANGE (//)
+WARNING: Track %lx is out of range.
+;
+MSG_UNKNOWN_SAMPLE_COMPRESSION (//)
+WARNING: Unknown sample compression for instrument %lx.
+;
+MSG_INST_IS_STEREO (//)
+WARNING: Instrument %lx is a stereo sample.
+;
+MSG_INST_IS_16BIT (//)
+WARNING: Instrument %lx is 16bit: unable to load it.
+;
+;
+; *****************************
+; * Strings for TrackerHook.c *
+; *****************************
+;
+MSG_READING_TYPE_MODULE (//)
+Reading %s module...
+; (%s is the module format)
+;
+MSG_EXCEEDS_64_PATTS (//)
+NOTE: Module exceeds 64 patterns. You need ProTracker 2.3 to play it.
+;
+MSG_MODULE_ID (//)
+Tracker ID: \"%s\"
+;
+MSG_MODULE_HAS_N_CHN (//)
+Module has %lu tracks...
+;
+MSG_SONG_HAS_NO_INSTS (//)
+This file is a song and doesn't contain instruments.
+;
+MSG_EXCEEDS_MAXPAATTS (//)
+WARNING: Module execeeds %ld patterns.
+;
+MSG_PATT_WILL_GROW (//)
+Pattern %ld will grow to 64 lines (was %ld lines long).
+;
+MSG_SPLITTING_PATT (//)
+Splitting pattern %ld (was %ld lines long).
+;
+; **************************
+; * Strings for SaveMIDI.c *
+; **************************
+;
+MSG_CHOOSING_CHANNELS (//)
+Choosing Channels...
+;
+MSG_WRITING_MIDI_TRACKS (//)
+Writing MIDI Tracks...
+;
+MSG_TOO_MANY_CHANNELS (//)
+ERROR: Song requires too many MIDI channels.
+;
+;
+; *********************
+; * Strings for Gui.c *
+; *********************
+;
+MSG_REALLY_QUIT_XMODULE (//)
+Really Quit XModule?
+;
+MSG_CLOSE_ALL_WINDOWS (//)
+Please close all visitor windows\n\
+and then select `Continue'.
+;
+;
+; ***********************
+; * Strings for Instr.c *
+; ***********************
+;
+MSG_UNKNOWN_IFF (//)
+Unknown IFF format %s.
+;
+MSG_ILLEGAL_IFF_STRUCTURE (//)
+Illegal IFF structure.
+;
+MSG_SELECT_RAW_MODE (//)
+Unrecognized instrument format.\n\
+Please select RAW mode.
+;
+MSG_RAW_MODES (//)
+Signed 8bit|Unsigned 8bit|Cancel
+;
+MSG_DATATYPES_ERROR (//)
+DataTypes error: %s.
+; %s is a detailed description of the problem.
+;
+MSG_UNKNOWN_COMPRESSION (//)
+Unknown compression type.
+;
+MSG_SAMPLE_WRONG_SIZE (//)
+%lu bit samples are not supported.
+; %lu is the number of bits per sample.
+;
+MSG_SAMPLE_NOT_MONO (//)
+Samples other than MONO are not supported.
+;
+MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS (//)
+Samples with %ld channels are not supported.
+;
+MSG_NO_MEMORY_TO_OPTIMIZE_INSTR (//)
+WARNING: insufficient memory to optimize instrument %lx.
+;
+MSG_INSTR_WILL_SHRINK (//)
+Instrument %lx will shrink from %ld to %ld.
+;
+MSG_INSTR_DUPES_REMOVED (//)
+Duplicate instruments found and removed: %lx == %lx.
+;
+MSG_INSTR_UNUSED (//)
+Instrument %lx was never used and it's being removed.
+;
+;
+; **********************
+; * Strings for Misc.c *
+; **********************
+;
+MSG_OPENLIB_VER_FAIL (//)
+Couldn't open \"%s\" version %ld or greater.
+;
+MSG_OPENLIB_FAIL (//)
+Couldn't open \"%s\".
+;
+;
+; ***************************
+; * Strings for Operators.c *
+; ***************************
+;
+MSG_CANT_INSERT_PATT (//)
+Unable to insert pattern: Maximum number of patterns reached.
+;
+MSG_PATT_UNUSED (//)
+Pattern %ld is not used and is beeing deleted.
+;
+MSG_PATT_CUT (//)
+Pattern %ld will be cut at line %ld.
+;
+MSG_PATT_DUPE (//)
+Duplicate patterns found and removed: %ld == %ld.
+; The two %ld are the pattern numbers.
+;
+MSG_SONG_LEN_DIFFERENT (//)
+WARNING: Song lengths are different. Using shorter one.
+;
+MSG_PATT_LEN_DIFFERENT (//)
+WARNING: Different pattern lengths at position %ld. Using shorter one.
+;
+MSG_ERR_INSTR_OVERFLOW (//)
+ERROR: Instruments overflow. Try remapping the instruments.
+;
+;
+; ***********************
+; * Strings for Prefs.c *
+; ***********************
+;
+MSG_BAD_PREFS_VERSION (//)
+Incorrect version of preferences file
+;
+;
+; ****************************
+; * Strings for Requesters.c *
+; ****************************
+;
+MSG_XMODULE_REQUEST (//)
+XModule Request
+; This is the title of requesters put out by XModule.
+;
+MSG_CLONE_WB (//)
+Clone Workbench Screen
+;
+MSG_CLOSE_FILEREQUESTER (//)
+Please close FileRequester\n and then select `Continue'.
+;
+MSG_SELECT_MODULES (//)
+Select Module(s)...
+;
+MSG_SELECT_INSTRUMENTS (//)
+Select Instrument(s)...
+;
+MSG_SELECT_PATTERN (//)
+Select Pattern...
+;
+MSG_SAVE_MODULE (//)
+Save Module...
+;
+MSG_SAVE_INSTRUMENT (//)
+Save Instrument...
+;
+MSG_SAVE_PATTERN (//)
+Save Pattern...
+;
+MSG_FILE_EXISTS (//)
+File \"%s\"\nalready exists.
+;
+MSG_OVERWRITE (//)
+Overwrite|Choose Another|Abort
+;
+;
+; **********************
+; * Strings for Song.c *
+; **********************
+;
+MSG_CLOSE_ALL_SONGS (//)
+Please close all open songs\n\
+and then select `Continue'.
+;
+MSG_UNESPECTED_EOF (//)
+Unespected end of file.
+;
+MSG_MODULE_LOADED_OK (//)
+Module loaded OK.
+;
+MSG_MODULE_SAVED_OK (//)
+Module saved OK.
+;
+MSG_UNKNOWN_SAVE_FORMAT (//)
+ERROR: Unrecognized save format.
+;
+MSG_INVALID_LOOP_REMOVED (//)
+Removed invalid loop for instrument %lx.
+;
+MSG_INVALID_LOOP_FIXED (//)
+Fixed invalid loop for instrument %lx.
+;
+MSG_SONG_HAS_NO_PATTS (//)
+WARNING: Song has no patterns.
+;
+MSG_SONG_HAS_NO_SEQ (//)
+WARNING: Song has no sequence.
+;
+MSG_INVALID_SONG_POS (//)
+WARNING: Song position %ld references pattern %ld, which doesn't exist.
+;
+MSG_UNKNOWN_MOD_FORMAT (//)
+Unable to identify module format.\n\
+(Loading a data file as a module is unwise)
+;
+MSG_SOUND_PRO_CANCEL (//)
+SoundTracker 15|ProTracker|Cancel
+;
+MSG_AUTHOR_UNKNOWN (//)
+Unknown
+;
+MSG_SONG_UNTITLED (//)
+Untitled
+;
+;
+; **********************
+; * Strings for Help.c *
+; **********************
+;
+MSG_AMIGAGUIDE_ERROR (//)
+AmigaGuide error:
+; Note one blank space at the end of this line!
+;
+;
+; ***************************
+; * Strings for PattPrefs.c *
+; ***************************
+;
+MSG_PATTPREFS_TITLE (//)
+Pattern Editor Settings
+;
+;
+MSG_ADVANCE_TRACKS_GAD (//)
+Advance _Tracks
+;
+MSG_ADVANCE_LINES_GAD (//)
+Advance _Lines
+;
+MSG_MAX_UNDO_LEVELS_GAD (//)
+Max _Undo Levels
+;
+MSG_MAX_UNDO_MEM_GAD (//)
+Max Undo _Memory
+;
+MSG_CLIPBOARD_UNIT_GAD (//)
+Cli_pboard Unit
+;
+MSG_SCROLLER_POS_GAD (//)
+_Scroller Position
+;
+MSG_VERT_WRAP_GAD (//)
+_Vertical Wrap
+;
+MSG_HORIZ_WRAP_GAD (//)
+_Horizontal Wrap
+;
+MSG_HEX_LINE_NUMBERS_GAD (//)
+He_x Line Numbers
+;
+MSG_BLANK_ZERO_GAD (//)
+Blank _Zero Digits
+;
+MSG_BACKDROP_GAD (//)
+Backdrop _Window
+;
+MSG_HORIZ_SCROLLER_GAD (//)
+Ho_rizontal Scroller
+;
+MSG_DO_RULER_GAD (//)
+Display Cursor Ruler
+;
+MSG_DO_TINY_LINES_GAD (//)
+Display Tiny Lines
+;
+MSG_EDITOR_FONT_GAD (//)
+_Editor Font
+;
+MSG_BACKGROUND_PEN_GAD (//)
+Background
+;
+MSG_TEXT_PEN_GAD (//)
+T_ext
+;
+MSG_LINES_PEN_GAD (//)
+Li_nes
+;
+MSG_TINY_LINES_PEN_GAD (//)
+Tin_y Lines
+;
+MSG_RIGHT_GAD (//)
+Right
+;
+MSG_LEFT_GAD (//)
+Left
+;
+MSG_OFF_GAD (//)
+Off
+;
+MSG_PATT_TOO_LONG (//)
+ERROR: Pattern would exceed the maximum number of lines.
+;
+;
+; *****************************
+; * Strings for SequenceWin.c *
+; *****************************
+;
+;
+MSG_SEQUENCE_TITLE (//)
+Sequence Editor
+;
+;
+MSG_SEQUENCE_GAD (//)
+Sequence
+;
+MSG_UNDERSCORE_ADD_GAD (//)
+_Add
+;
+MSG_UP_GAD (//)
+Up
+;
+MSG_DOWN_GAD (//)
+Down
+;
+MSG_UNDERSCORE_NAME_GAD (//)
+_Name
+;
+MSG_PATTERNS_GAD (//)
+Patterns
+;
+MSG_UNDERSCORE_UP_GAD (//)
+_Up
+;
+MSG_UNDERSCORE_DOWN_GAD (//)
+_Down
+;
+MSG_UNDERSCORE_INS_GAD (//)
+_Ins
+;
+;
+MSG_UNNAMED (//)
+-- unnamed --
+;
+;
+; ********************************
+; * Strings for InstrumentsWin.c *
+; ********************************
+;
+MSG_INSTRUMENTS_TITLE (//)
+Instruments
+;
+;
+MSG_VOLUME_GAD (//)
+_Volume
+;
+MSG_FINETUNE_GAD (//)
+_FineTune
+;
+MSG_LENGTH_GAD (//)
+Length
+;
+MSG_KIND_GAD (//)
+_Kind
+;
+MSG_EDIT_DOTS_GAD (//)
+_Edit...
+;
+MSG_SAMPLE_GAD (//)
+Sample
+;
+MSG_SYNTH_GAD (//)
+Synth
+;
+MSG_HYBRID_GAD (//)
+Hybrid
+;
+;
+MSG_INSTRUMENTS_MEN (//)
+Instruments
+;
+MSG_LOAD_MEN (//)
+Load...
+;
+MSG_REMAP_MEN (//)
+Remap
+;
+MSG_SAVE_COMPRESSED_MEN (//)
+Save Compressed
+;
+MSG_SAVE_RAW_MEN (//)
+Save Raw
+;
+;
+MSG_EMPTY (//)
+-- empty --
+;
+;
+; *************************
+; * Strings for PlayWin.c *
+; *************************
+;
+MSG_PLAY_TITLE (//)
+Play
+;
+;
+MSG_VOL_GAD (//)
+_Vol
+;
+MSG_POS_GAD (//)
+Pos
+;
+MSG_TIME_GAD (//)
+Time
+;
+MSG_RST_GAD (//)
+_Rst
+; Stands for Reset/Restart
+;
+MSG_PLAYER_INIT_ERR (//)
+Player initialization error: %ld.
+;
+;
+; *****************************
+; * Strings for ProgressWin.c *
+; *****************************
+;
+MSG_PROGRESS_TITLE (//)
+XModule is working...
+;
+MSG_LOG_TITLE (//)
+XModule Log
+;
+;
+MSG_UNDERSCORE_ABORT_GAD (//)
+_Abort
+;
+;
+MSG_PERCENT_DONE (//30)
+%ld of %ld (%ld%% done)
+; This string appears inside the progress window gauge.
+; eg: "5 of 20 (25% done)".
+; Leave that double percent (%%) alone! :-)
+;
+;
+; *****************************
+; * Strings for SongInfoWin.c *
+; *****************************
+;
+MSG_SONGINFO_TITLE (//)
+Song Information
+;
+;
+MSG_UNDERSCORE_NEW_GAD (//)
+Ne_w
+;
+MSG_OPEN_GAD (//)
+_Open...
+;
+MSG_SAVE_GAD (//)
+_Save
+;
+MSG_DEL_GAD (//)
+Del
+;
+MSG_SONG_NAME_GAD (//)
+Song _Name
+;
+MSG_AUTHOR_NAME_GAD (//)
+_Author
+;
+MSG_DEF_TEMPO_GAD (//)
+_Tempo
+;
+MSG_DEF_SPEED_GAD (//)
+S_peed
+;
+MSG_RESTART_GAD (//)
+_Restart
+;
+MSG_LENGHT_GAD (//)
+Length
+;
+MSG_NUM_PATTS_GAD (//)
+Num Patterns
+;
+MSG_NUM_TRACKS_GAD (//)
+Tracks
+;
+MSG_TOT_MOD_SIZE_GAD (//)
+Total Module Size
+;
+MSG_TOT_INST_SIZE_GAD (//)
+Total Instruments Size
+;
+;
+MSG_SONG_MEN (//)
+Song
+;
+MSG_MERGE_SONGS_MEN (//)
+Merge Songs
+;
+MSG_JOIN_SONGS_MEN (//)
+Join Songs
+;
+;
+MSG_JOIN_REQUIRES_TWO_SONGS (//)
+ERROR: Join requires two songs.
+;
+MSG_MERGE_REQUIRES_TWO_SONGS (//)
+ERROR: Merge requires two songs.
+;
+MSG_DISCARD_CURRENT_SONG (//)
+Discard current song?
+;
+;
+; ****************************
+; * Strings for ToolBoxWin.c *
+; ****************************
+;
+MSG_TOOLBOX_TITLE (//)
+ToolBox
+;
+;
+MSG_PLAY_GAD (//)
+Play...
+;
+MSG_SONGS_GAD (//)
+_Songs...
+;
+MSG_PATTERNS_DOTS_GAD (//)
+_Patterns...
+;
+MSG_INSTRUMENTS_GAD (//)
+_Instruments...
+;
+MSG_SEQUENCE_DOTS_GAD (//)
+Se_quence...
+;
+MSG_OPTIMIZATION_GAD (//)
+_Optimization...
+;
+MSG_PROJECT_MEN (//)
+Project
+;
+MSG_NEW_MEN (//)
+New
+;
+MSG_OPEN_MEN (//)
+Open...
+;
+MSG_OPEN_NEW_MEN (//)
+Open New...
+;
+MSG_SAVE_MEN (//)
+Save
+;
+MSG_SAVE_AS_MEN (//)
+Save As...
+;
+MSG_CLEAR_MEN (//)
+Clear...
+;
+MSG_ABOUT_MEN (//)
+About...
+;
+MSG_HELP_MEN (//)
+Help...
+;
+MSG_ICONIFY_MEN (//)
+Iconify...
+;
+MSG_QUIT_MEN (//)
+Quit
+;
+MSG_SETTINGS_MEN (//)
+Settings
+;
+MSG_SAVE_FORMAT_MEN (//)
+Save Format...
+;
+MSG_USER_INTERFACE_MEN (//)
+User Interface...
+;
+MSG_SAVE_ICONS_MEN (//)
+Save Icons
+;
+MSG_CONFIRM_OVERWRITE_MEN (//)
+Confirm Overwrite
+;
+MSG_CONFIRM_EXIT_MEN (//)
+Confirm Exit
+;
+MSG_VERBOSE_MEN (//)
+Verbose Log
+;
+MSG_OPEN_SETTINGS_MEN (//)
+Open Settings...
+;
+MSG_SAVE_SETTINGS_MEN (//)
+Save Settings
+;
+MSG_SAVE_SETTINGS_AS_MEN (//)
+Save Settings As...
+;
+;
+MSG_ABOUT_TEXT (//)
+%s\n\
+A Music Module Processing Utility\n\n\
+\
+%s\n\
+All rights reserved.\n\n\
+\
+Internet: bernie@shock.cosmos.it\n\n\
+FidoNet: 2:332/118.4\n\
+\
+Free CHIP Memory: %ldKB\n\
+Free FAST Memory: %ldKB\n\n\
+Public Screen: %s\n\
+ARexx Port: %s\n\
+Cx HotKey: %s\n\
+Language: %s
+;
+MSG_DEFAULT (//)
+-- Default --
+;
+MSG_DISABLED (//)
+-- Disabled --
+;
+;
+; *********************************
+; * Strings for OptimizationWin.c *
+; *********************************
+;
+MSG_OPTIMIZATION_TITLE (//)
+Module Optimization
+;
+;
+MSG_OPTIMIZE_GAD (//)
+_Optimize
+;
+MSG_REM_UNUSED_PATTS_GAD (//)
+Remove Unused _Patterns
+;
+MSG_REM_DUPLICATE_PATTS_GAD (//)
+Remove _Duplicate Patterns
+;
+MSG_REM_UNUSED_INSTR_GAD (//)
+Remove Unused _Instruments
+;
+MSG_REM_DUP_INSTR_GAD (//)
+Remove Duplicate I_nstruments
+;
+MSG_CUT_AFTER_LOOP_GAD (//)
+Cut Instruments After _Loop
+;
+MSG_CUT_ZERO_TAILS_GAD (//)
+Cut Instrument _Zero Tails
+;
+MSG_CUT_PATTERNS_GAD (//)
+Cut _Breaked Patterns
+;
+MSG_REMAP_INSTRUMENTS_GAD (//)
+_Remap Instruments
+;
+;
+MSG_SAVED_X_BYTES (//)
+Saved %ld bytes (%ld%%)
+;
+;
+; **************************
+; * Strings for PrefsWin.c *
+; **************************
+;
+;
+MSG_PREFS_TITLE (//)
+User Interface Settings
+;
+;
+MSG_PUBLIC_SCREEN_GAD (//)
+_Public Screen
+;
+MSG_DISPLAY_MODE_GAD (//)
+Display _Mode
+;
+MSG_WINDOW_FONT_GAD (//)
+_Window Font
+;
+MSG_LISTVIEW_FONT_GAD (//)
+_ListView Font
+;
+MSG_REQUESTERS_GAD (//)
+_Requesters
+;
+MSG_USE_DATATYPES_GAD (//)
+Use _DataTypes
+;
+MSG_APPICON_GAD (//)
+Put App_Icon
+;
+MSG_REFRESH_GAD (//)
+Refres_h
+;
+MSG_LOG_TO_FILE_GAD (//)
+Log To File
+;
+MSG_LOG_LEVEL_GAD (//)
+_Log Level
+;
+MSG_ASK_AUTOSAVE_GAD (//)
+Confirm _Autosave
+;
+MSG_AUTOSAVE_TIME_GAD (//)
+Autosave Mi_nutes
+;
+MSG_DO_BACKUPS_GAD (//)
+Create _Backups
+;
+MSG_BACKUP_TEMPLATE_GAD (//)
+Bac_kup Template
+;
+MSG_BACKUP_VERSIONS_GAD (//)
+Backup _Versions
+;
+MSG_ASL_GAD (//)
+Asl
+;
+MSG_REQTOOLS_GAD (//)
+ReqTools
+;
+MSG_SMART_GAD (//)
+Smart
+;
+MSG_SIMPLE_GAD (//)
+Simple
+;
+MSG_CLONE_DEF_SCREEN (//)
+--Clone Default Screen--
+;
+;
+; ****************************
+; * Strings for PatternWin.c *
+; ****************************
+;
+MSG_PATTERN_TITLE (//)
+Pattern Editor
+;
+;
+MSG_PATTERNS_MEN (//)
+Patterns
+;
+MSG_SIZE_MEN (//)
+Size...
+;
+MSG_EDIT_MEN (//)
+Edit
+;
+MSG_MARK_MEN (//)
+Mark
+;
+MSG_CUT_MEN (//)
+Cut
+;
+MSG_COPY_MEN (//)
+Copy
+;
+MSG_PASTE_MEN (//)
+Paste
+;
+MSG_ERASE_MEN (//)
+Erase
+;
+MSG_UNDO_MEN (//)
+Undo
+;
+MSG_REDO_MEN (//)
+Redo
+;
+MSG_EDITOR_SETTINGS_MEN (//)
+Editor Settings...
+;
+;
+; ***************************
+; * Strings for SaversWin.c *
+; ***************************
+;
+MSG_SAVERS_TITLE (//)
+Savers
+;
+;
+MSG_SF_SEQUENCE_GAD (//)
+Se_quence
+;
+MSG_SF_INSTRUMENTS_GAD (//)
+_Instruments
+;
+MSG_SF_PATTERNS_GAD (//)
+_Patterns
+;
+MSG_SF_NAMES_GAD (//)
+Names
+;
+MSG_ADD_ICON_GAD (//)
+Add I_con
+;
+MSG_MODE_GAD (//)
+_Mode
+;
+MSG_OPTIONS_DOTS_GAD (//)
+Options...
+;
+MSG_NONE_GAD (//)
+None
+;
+MSG_XPK_GAD (//)
+XPK
+;
+MSG_LHA_GAD (//)
+LhA
+;
+;
+MSG_DESCRIPTION (//)
+Description
+;
+MSG_AUTHOR (//)
+Author
+;
+MSG_MAXLENGTH (//)
+Max Length
+;
+MSG_MAXTRACKS (//)
+Max Tracks
+;
+MSG_MAXINSTRUMENTS (//)
+Max Instruments
+;
+MSG_MAXPATTERNS (//)
+Max Patterns
+;
+MSG_MAXPATTLEN (//)
+Max Pattern Length
+;
+MSG_MAXSAMPLELEN (//)
+Max Sample Length
+;
+;
+; **********************
+; * Strings ClearWin.c *
+; **********************
+;
+MSG_CLEAR_TITLE (//)
+Clear Module
+;
+;
+MSG_CLR_SEQUENCE_GAD (//)
+_Sequence
+;
+MSG_CLR_INSTRUMENTS_GAD (//)
+_Instruments
+;
+MSG_CLR_PATTERNS_GAD (//)
+_Patterns
+;
+MSG_CLEARMOD_GAD (//)
+_Clear
+;
+;
+; ***************************
+; * Strings for SampleWin.c *
+; ***************************
+;
+MSG_SAMPLE_TITLE (//)
+Sample Editor
+;
+;
+MSG_RENDER_MEN (//)
+Render
+;
+MSG_POINTS_MEN (//)
+Points
+;
+MSG_LINES_MEN (//)
+Lines
+;
+MSG_FILLED_MEN (//)
+Filled
+;
+;
+; *****************************
+; * Strings for PattSizeWin.c *
+; *****************************
+;
+MSG_PATTSIZE_TITLE (//)
+Pattern Attributes
+;
+;
+MSG_LINES_GAD (//)
+_Lines
+;
+MSG_TRACKS_GAD (//)
+_Tracks
+;
+MSG_DOUBLE_GAD (//)
+_Double
+;
+MSG_HALVE_GAD (//)
+_Halve
+;
+;
+; *************************
+; * Strings for Library.c *
+; *************************
+;
+MSG_SONG_TOO_MANY_TRACKS (//)
+WARNING: The selected saver supports max %lu tracks, but the song has %lu tracks.
+;
+MSG_SONG_TOO_MANY_PATTS (//)
+WARNING: The selected saver supports max %lu patterns; the song has %lu patterns.
+;
+MSG_SONG_TOO_MANY_INSTRS (//)
+WARNING: The selected saver supports max %lu instruments, last instrument is %lu.
+;
+MSG_SONG_TOO_MANY_POS (//)
+WARNING: The selected saver supports max %lu positions, but the song has %lu positions.
+;
+MSG_PATT_LENGTH_INVALID (//)
+WARNING: The length of pattern %ld ($%lx lines) isn't allowed with the selected saver.
+;
+MSG_INSTR_TOO_LONG (//)
+WARNING: Instrument %lx is too long and will be cropped to %lu bytes.
+;
+MSG_WILL_MODIFY_SONG (//)
+Some modifications need to be performed on this song\n\
+in order to adapt it to the limitations of the\n\
+destination format.
+;
+MSG_TRY_REMAPPING_INSTRUMENTS (//)
+Some instruments are placed beyond the limit for\n\
+the selected format.\n\
+Remapping the instruments now could help saving\n\
+all them along with the selected format.
+;
+MSG_SAVING_MODULE (//)
+Saving %s module \"%s\"...
+; the first %s is the format name, the second %s is the song name.
--- /dev/null
+/*
+** ClearWin.c
+**
+** Copyright (C) 1994,95,96 Bernardo Innocenti
+**
+** Clear panel handling functions.
+*/
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Gadgets IDs */
+
+enum {
+ GD_ClearGroup0,
+ GD_ClearGroup1,
+ GD_ClearSequence,
+ GD_ClearInstruments,
+ GD_ClearPatterns,
+ GD_ClearPerform,
+
+ Clear_CNT
+};
+
+
+/*****************************/
+/* Local function prototypes */
+/*****************************/
+
+static void ClearPerformClicked (struct WinUserData *wud);
+
+
+
+
+XDEF struct ClearSwitches ClearSwitches = { 1, 1, 1 };
+
+
+
+static LONG ClearArgs[] =
+{
+ VGROUP_KIND, BBFT_RIDGE,
+ CHECKBOX_KIND, NULL, MSG_CLR_SEQUENCE_GAD, (LONG)&ClearSwitches.ClearSeq, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_CLR_INSTRUMENTS_GAD, (LONG)&ClearSwitches.ClearInstr, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_CLR_PATTERNS_GAD, (LONG)&ClearSwitches.ClearPatt, TAG_DONE,
+ ENDGROUP_KIND,
+ BUTTON_KIND, (LONG)ClearPerformClicked, MSG_CLEARMOD_GAD, TAG_DONE,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG ClearWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)ClearArgs,
+ XMWIN_GCount, Clear_CNT,
+ XMWIN_Title, MSG_CLEAR_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, CHECKBOXIDCMP | BUTTONIDCMP | IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW,
+ XMWIN_HelpNode, (LONG)"Clear",
+ TAG_DONE
+};
+
+
+
+GLOBALCALL void UpdateClearSwitches (void)
+{
+ struct WinUserData *wud = WDescr[WID_CLEAR].Wud;
+
+ if (wud && wud->Win)
+ SetGadgets (wud,
+ GD_ClearSequence, ClearSwitches.ClearSeq,
+ GD_ClearInstruments, ClearSwitches.ClearInstr,
+ GD_ClearPatterns, ClearSwitches.ClearPatt,
+ -1);
+}
+
+
+/*****************/
+/* Clear Gadgets */
+/*****************/
+
+static void ClearPerformClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+ LONG i;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (ClearSwitches.ClearPatt)
+ {
+ for (i = si->NumPatterns - 1 ; i >= 0 ; i--)
+ xmRemPattern (si, i, 0);
+
+ xmAddPatternA (si, NULL);
+ }
+
+ if (ClearSwitches.ClearInstr)
+ {
+ for (i = 1 ; i <= si->LastInstrument ; i++)
+ xmRemInstrument (si, i);
+ }
+
+ if (ClearSwitches.ClearSeq)
+ {
+ xmSetSongLen (si, 1);
+ si->Sequence[0] = 0;
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSongInfo();
+ MyCloseWindow (wud);
+}
--- /dev/null
+#ifndef COMPILERSPECIFIC_H
+#define COMPILERSPECIFIC_H
+/*
+** $VER: CompilerSpecific.h 2.3 (26.10.97)
+**
+** Copyright (C) 1997 Bernardo Innocenti. All rights reserved.
+**
+** Compiler specific definitions is here. You can add support
+** for other compilers in this header. Please return any changes
+** you make to me, so I can add them to my personal copy of this file.
+**
+** Here is a short description of the macros defined below:
+**
+** LIBCALL
+** Shared library entry point, with register args
+**
+** HOOKCALL
+** Hook or boopsi dispatcher entry point with arguments
+** passed in registers
+**
+** GLOBALCALL
+** Attribute for functions to be exported to other modules for
+** global access within the same executable file.
+** Usually defined to "extern", but can be overridden for special
+** needs, such as compiling all modules together in a single
+** object module to optimize code better.
+**
+** XDEF
+** Attribute for symbols to be exported to other modules for
+** global access within the same executable file.
+** Usually defined to an empty value.
+**
+** XREF
+** Attribute for symbols to be imported from other modules
+** within the same executable file.
+** Usually defined to "extern".
+**
+** INLINE
+** Please put function body inline to the calling code
+**
+** STDARGS
+** Function uses standard C conventions for arguments
+**
+** ASMCALL
+** Function takes arguments in the specified 68K registers
+**
+** REGCALL
+** Function takes arguments in registers choosen by the compiler
+**
+** CONSTCALL
+** Function does not modify any global variable
+**
+** FORMATCALL(archetype,string_index,first_to_check)
+** Function uses printf or scanf-like formatting
+**
+** SAVEDS
+** Function needs to reload context for small data model
+**
+** INTERRUPT
+** Function will be called from within an interrupt
+**
+** NORETURN
+** Function does never return
+**
+** ALIGNED
+** Variable must be aligned to longword boundaries
+**
+** CHIP
+** Variable must be stored in CHIP RAM
+**
+** REG(reg,arg)
+** Put argument <arg> in 68K register <reg>
+**
+** min(a,b)
+** Return the minimum between <a> and <b>
+**
+** max(a,b)
+** Return the maximum between <a> and <b>
+**
+** abs(a)
+** Return the absolute value of <a>
+**
+** _COMPILED_WITH
+** A string containing the name of the compiler
+*/
+
+#ifdef __SASC
+ /* SAS/C 6.58 or better */
+
+ #define INLINE static __inline
+ #define STDARGS __stdargs
+ #define ASMCALL __asm
+ #define REGCALL __regcall
+ #define CONSTCALL /* unsupported */
+ #define FORMATCALL /* unsupported */
+ #define SAVEDS __saveds
+ #define INTERRUPT __interrupt
+ #define NORETURN /* unsupported */
+ #define ALIGNED __aligned
+ #define CHIP __chip
+ #define REG(reg,arg) register __##reg arg
+ #define _COMPILED_WITH "SAS/C"
+
+ /* For min(), max() and abs() */
+ #define USE_BUILTIN_MATH
+ #include <string.h>
+#else
+#ifdef __GNUC__
+ /* GeekGadgets GCC 2.7.2.1 or better */
+
+ #define INLINE static inline
+ #define STDARGS __attribute__((stkparm))
+ #define ASMCALL /* nothing */
+ #define REGCALL /* nothing */
+ #define CONSTCALL __attribute__((const))
+ #define FORMATCALL(a,s,f) __attribute__((format(a,s,f)))
+ #define SAVEDS __attribute__((saveds))
+ #define INTERRUPT __attribute__((interrupt))
+ #define NORETURN __attribute__((noreturn))
+ #define ALIGNED __attribute__((aligned(4)))
+ #define REG(reg,arg) arg __asm(#reg)
+ #define _COMPILED_WITH "GCC"
+
+ #define min(a,b) (((a)<(b))?(a):(b))
+ #define max(a,b) (((a)>(b))?(a):(b))
+ #define abs(a) (((a)>0)?(a):-(a))
+
+ /* Inline versions of some str*() and mem*() functions
+ */
+ #define _ANSI_SOURCE
+ #include <string.h>
+ #undef _ANSI_SOURCE
+
+ INLINE STDARGS size_t strlen (const char *src)
+ { const char *tmp = src; while (*tmp) tmp++; return tmp - src; }
+
+ INLINE STDARGS int memcmp (const void *src, const void *dest, size_t n)
+ {
+ while (n--)
+ {
+ if (*((char *)src) != *((char *)dest))
+ return (*((char *)src) - *((char *)dest));
+ ((char *)src)++;
+ ((char *)dest)++;
+ }
+ return 0;
+ }
+
+ INLINE STDARGS void * memset (void *m, int c, size_t n)
+ {
+ void *r = m;
+ n++;
+ while (--n > 0)
+ *(((unsigned char *) m)++) = (unsigned char) c;
+ return r;
+ }
+
+ /* GCC produces code which calls these two functions
+ * to initialize and copy structures and arrays.
+ *
+ * INLINE STDARGS void bzero (void *buf, size_t len)
+ * { while (len--) *((char *)buf)++ = 0; }
+ *
+ * INLINE STDARGS void bcopy (const void *src, void *dest, size_t len)
+ * { while (len--) *((char *)dest)++ = *((const char *)src)++; }
+ */
+
+#else
+#ifdef __STORM__
+ /* StormC 2.00.23 or better */
+ #define INLINE __inline
+ #define STDARGS /* nothing */
+ #define ASMCALL /* nothing */
+ #define REGCALL register
+ #define CONSTCALL /* unsupported */
+ #define FORMATCALL /* unsupported */
+ #define SAVEDS __saveds
+ #define INTERRUPT __interrupt
+ #define NORETURN /* unsupported */
+ #define ALIGNED /* unsupported */
+ #define CHIP __chip
+ #define REG(reg,arg) register __##reg arg
+ #define _COMPILED_WITH "StormC"
+
+ #define min(a,b) (((a)<(b))?(a):(b))
+ #define max(a,b) (((a)>(b))?(a):(b))
+ #define abs(a) (((a)>0)?(a):-(a))
+
+ #define _INLINE_INCLUDES
+ #include <string.h>
+#else
+#ifdef __MAXON__
+ /* Maxon C/C++ 3.0 */
+
+ #define INLINE static inline
+ #define STDARGS /* ? */
+ #define ASMCALL /* ? */
+ #define REGCALL /* ? */
+ #define CONSTCALL /* unsupported */
+ #define FORMATCALL /* unsupported */
+ #define SAVEDS /* unsupported */
+ #define INTERRUPT /* unsupported */
+ #define NORETURN /* unsupported */
+ #define ALIGNED /* unsupported */
+ #define REG(reg,arg) register __##reg arg
+ #define _COMPILED_WITH "Maxon C"
+
+ /* For min(), max() and abs() */
+ #define USE_BUILTIN_MATH
+ #include <string.h>
+
+ #error Maxon C compiler support is untested. Please check all the above definitions
+#else
+#ifdef _DCC
+ /* DICE C 3.15 */
+
+ #define INLINE static __inline
+ #define STDARGS __stdargs
+ #define ASMCALL /* nothing */
+ #define REGCALL /* ? */
+ #define CONSTCALL /* unsupported */
+ #define FORMATCALL /* unsupported */
+ #define SAVEDS __geta4
+ #define INTERRUPT /* unsupported */
+ #define NORETURN /* unsupported */
+ #define ALIGNED __aligned
+ #define REG(reg,arg) __##reg arg
+ #define _COMPILED_WITH "DICE"
+
+ #define min(a,b) (((a)<(b))?(a):(b))
+ #define max(a,b) (((a)>(b))?(a):(b))
+ #define abs(a) (((a)>0)?(a):-(a))
+
+ #error DICE compiler support is untested. Please check all the above definitions
+#else
+#ifdef AZTEC_C
+ /* Aztec/Manx C */
+
+ #define INLINE static
+ #define STDARGS /* ? */
+ #define ASMCALL /* ? */
+ #define REGCALL /* ? */
+ #define CONSTCALL /* unsupported */
+ #define FORMATCALL /* unsupported */
+ #define SAVEDS __geta4
+ #define INTERRUPT /* unsupported */
+ #define NORETURN /* unsupported */
+ #define ALIGNED __aligned
+ #define REG(reg,arg) __##reg arg
+ #define _COMPILED_WITH "Manx C"
+
+ #define min(a,b) (((a)<(b))?(a):(b))
+ #define max(a,b) (((a)>(b))?(a):(b))
+ #define abs(a) (((a)>0)?(a):-(a))
+
+ #error Aztec/Manx C compiler support is untested. Please check all the above definitions
+#else
+ #error Please add compiler specific definitions for your compiler
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+
+/* CONST_STRPTR is a typedef made by a patched version of <exec/types.h>.
+ * Passing "const char *" parameters will only work if the OS protos are
+ * patched accordingly, otherwise you will get a lot of compiler warnings
+ * for const to volatile conversions.
+ *
+ * Using "const" where it is appropriate helps the compiler optimizing
+ * code better, so this mess is probably worth it. I wish one day the OS
+ * headers will come with support for the const keyword.
+ */
+#ifndef _CONST_STRPTR_DEFINED
+typedef char *CONST_STRPTR;
+#endif
+
+
+/* Special function attributes */
+
+#define LIBCALL ASMCALL SAVEDS
+#define HOOKCALL ASMCALL SAVEDS
+#ifdef __cplusplus
+ #define GLOBALCALL extern "C"
+#else
+ #define GLOBALCALL
+#endif
+
+/* special variable attributes */
+#define XDEF
+#define XREF extern
+
+
+/* AROS Compatibility: IPTR is a type which can store a pointer
+ * as well as a long integer.
+ */
+#ifndef IPTR
+#define IPTR LONG
+#endif /* IPTR */
+
+
+#endif /* !COMPILERSPECIFIC_H */
--- /dev/null
+/*
+** Compress.c
+**
+** Copyright (C) 1994,96,97 Bernardo Innocenti
+**
+** Compression/Decompression handling functions.
+*/
+
+#include <exec/memory.h>
+#include <utility/hooks.h>
+#include <xpk/xpk.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/powerpacker.h>
+#include <proto/xpkmaster.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+#define CTYPE_LHA 1
+#define CTYPE_XPK 2
+#define CTYPE_POWERPACKER 3
+
+
+/* static struct Library *PPBase = NULL; */
+static struct Library *XpkBase = NULL;
+
+
+XDEF UBYTE LhACommand[64] = "LhA >NIL: e -x0 -q \"%s\"";
+XDEF UBYTE TmpDir[PATHNAME_MAX] = "T:XModuleTmp";
+XDEF UBYTE LhAFilter[64] = "~(#?readme#?|#?txt#?|#?display#?|#?fileid#?)";
+
+
+
+static LONG HOOKCALL XPKProgressFunc (REG(a0,struct Hook *hook), REG(a1,struct XpkProgress *pr))
+{
+ return (DisplayProgress (pr->xp_UCur, pr->xp_ULen));
+}
+
+
+
+static struct Hook XPKProgressHook =
+{
+ { NULL, NULL },
+ XPKProgressFunc,
+ NULL,
+ 0
+};
+
+
+
+GLOBALCALL BPTR DecompressFile (CONST_STRPTR name, UWORD type)
+
+/* This function will try to decompress the given file and store
+ * it in TmpDir. If TmpDir does not exist, it will be created.
+ * The decompressed file is then locked and returned.
+ * A return value of NULL means failure. Call DecompressFileDone()
+ * when you are done with the decompressed file.
+ */
+{
+ struct AnchorPath *ap;
+ BPTR ret = 0;
+ BPTR dir, olddir;
+ LONG err = 0;
+ UBYTE FullName[PATHNAME_MAX];
+
+
+ OpenProgressWindow();
+
+ DisplayAction (MSG_DECRUNCHING);
+
+ /* Find the full path name of the given file */
+ {
+ BPTR lock;
+
+ if (lock = Lock (name, ACCESS_READ))
+ {
+ if (!NameFromLock (lock, FullName, PATHNAME_MAX))
+ err = IoErr();
+ UnLock (lock);
+ }
+ else err = IoErr();
+ }
+
+ if (!err)
+ {
+ /* Try to lock or create TmpDir */
+
+ if (!(dir = Lock (TmpDir, ACCESS_READ)))
+ {
+ if (dir = CreateDir (TmpDir))
+ if (!(ChangeMode (CHANGE_LOCK, dir, ACCESS_READ)))
+ {
+ UnLock (dir);
+ dir = NULL;
+ }
+ }
+
+ if (dir)
+ {
+ olddir = CurrentDir (dir);
+
+ switch (type)
+ {
+ case CTYPE_LHA:
+ {
+ UBYTE buf[64+PATHNAME_MAX];
+
+ SPrintf (buf, LhACommand, FullName);
+ if (!SystemTagList (buf, NULL))
+ {
+ if (ap = AllocMem (sizeof (struct AnchorPath) + PATHNAME_MAX, MEMF_CLEAR))
+ {
+ ap->ap_Strlen = PATHNAME_MAX;
+
+ if (!(err = MatchFirst (LhAFilter, ap)))
+ {
+ if (!(ret = Lock (ap->ap_Buf, ACCESS_READ)))
+ err = IoErr();
+ }
+
+ MatchEnd (ap);
+ FreeMem (ap, sizeof (struct AnchorPath) + PATHNAME_MAX);
+ }
+ else err = ERROR_NO_FREE_STORE; /* Fail AllocMem() */
+
+ }
+ else err = IoErr(); /* Fail SystemTagList() */
+
+ break;
+ }
+
+ case CTYPE_XPK:
+ {
+ UBYTE dest[PATHNAME_MAX];
+ UBYTE errstring[XPKERRMSGSIZE];
+
+ if (!(XpkBase = OpenLibrary ("xpkmaster.library", 2L)))
+ {
+ CantOpenLib ("xpkmaster.library", 2L);
+ CloseProgressWindow();
+ return 0;
+ }
+
+ strcpy (dest, TmpDir);
+ if (AddPart (dest, "XPKTmp", PATHNAME_MAX))
+ {
+ if (XpkUnpackTags (
+ XPK_InName, FullName,
+ XPK_OutName, dest,
+ XPK_GetError, errstring,
+ XPK_ChunkHook, &XPKProgressHook,
+ // XPK_TaskPri, ThisTask->pr_Task.tc_Node.ln_Pri-1,
+ TAG_DONE))
+ {
+ xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_ERROR_DECOMPRESSING, FilePart (FullName),
+ errstring);
+ }
+ else ret = Lock (dest, ACCESS_READ);
+ }
+
+ CloseLibrary (XpkBase); XpkBase = NULL;
+ break;
+ }
+
+/* case CTYPE_POWERPACKER:
+
+ if (!(PPBase = OpenLibrary ("powerpacker.library", 0L)))
+ {
+ CantOpenLib ("powerpacker.library", 0L);
+ CloseProgressWindow();
+ return 0;
+ }
+
+ xmDisplayMessageA (XMDMF_INFORMATION,
+ "PowerPacker compressed files are not supported yet.", NULL);
+
+ CloseLibrary (PPBase); PPBase = NULL;
+*/
+ default:
+ break;
+ }
+
+ CurrentDir (olddir);
+ UnLock (dir);
+ }
+ else err = IoErr(); /* Fail CreateDir() */
+ }
+
+ /* Report error */
+
+ if (err)
+ {
+ if (err == ERROR_NO_MORE_ENTRIES)
+ xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_NOTHING_IN_ARC, name);
+ else
+ xmDisplayMessageA (XMDMF_DOSFAULT | XMDMF_USECATALOG,
+ (APTR)MSG_CANT_LOAD_COMPRESSED, NULL);
+ }
+
+ if (!ret) DecompressFileDone();
+
+ CloseProgressWindow();
+
+ return ret;
+}
+
+
+
+GLOBALCALL void DecompressFileDone (void)
+
+/* This call releases all resources got by DecompressFile(). */
+{
+ BPTR dir, olddir;
+ struct FileInfoBlock *fib;
+
+ if (dir = Lock (TmpDir, ACCESS_READ))
+ {
+ olddir = CurrentDir (dir);
+
+ if (fib = AllocDosObject (DOS_FIB, NULL))
+ {
+ if (Examine (dir, fib))
+ {
+ /* Delete all files in the temp directory */
+ while (ExNext (dir, fib))
+ DeleteFile (fib->fib_FileName);
+ }
+
+ FreeDosObject (DOS_FIB, fib);
+ }
+
+ CurrentDir (olddir);
+ UnLock (dir);
+ }
+
+ DeleteFile (TmpDir);
+}
+
+
+
+GLOBALCALL LONG CruncherType (BPTR file)
+{
+ union
+ {
+ LONG fileid;
+ struct
+ {
+ UWORD dummy;
+ ULONG prefix;
+ } lha;
+ } id;
+
+ if (Read (file, &id, sizeof (id)) != sizeof (id))
+ return 0;
+
+ if ((id.lha.prefix >> 8) == '-lh')
+ return CTYPE_LHA;
+
+ if (id.fileid == 'XPKF' || id.fileid == 'PP20' || id.fileid == 'PX20')
+ return CTYPE_XPK;
+
+ return 0;
+}
--- /dev/null
+/*
+** CustomClasses.c
+**
+** Copyright (C) 1995,96,97 Bernardo Innocenti
+**
+** Special custom BOOPSI classes.
+*/
+
+#include <exec/memory.h>
+#include <utility/tagitem.h>
+#include <graphics/gfxbase.h>
+#include <devices/inputevent.h>
+
+#include <intuition/intuition.h>
+#include <intuition/classes.h>
+#include <intuition/classusr.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/icclass.h>
+#include <intuition/imageclass.h>
+
+#include <proto/exec.h>
+#include <proto/graphics.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+
+#include "CustomClasses.h"
+#include "XModulePriv.h"
+#include "Gui.h"
+
+/* Per object instance data */
+struct ScrollButtonData
+{
+ /* The number of ticks we still have to wait
+ * before sending any notification.
+ */
+ ULONG TickCounter;
+};
+
+
+
+/* Function prototypes */
+
+static LIBCALL ULONG ScrollButtonDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct Gadget *g),
+ REG(a1, struct gpInput *gpi));
+
+static ULONG HOOKCALL VImageDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct Image *im),
+ REG(a1, struct opSet *ops));
+
+static void NotifyAttrChanges (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...);
+static void DrawPlayImage (struct RastPort *rp, UWORD width, UWORD heigth);
+static void DrawStopImage (struct RastPort *rp, UWORD width, UWORD heigth);
+static void DrawForwardImage (struct RastPort *rp, UWORD width, UWORD heigth);
+static void DrawRewindImage (struct RastPort *rp, UWORD width, UWORD heigth);
+static void DrawPickImage (struct RastPort *rp, UWORD width, UWORD height);
+static void DrawVImage (struct impDraw *imp, struct Image *im, struct BitMap *bm);
+
+
+
+/* tagcall stub for OM_NOTIFY */
+static void NotifyAttrChanges (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...)
+{
+ DoSuperMethod (OCLASS(o), o, OM_NOTIFY, &attr1, gi, flags);
+}
+
+
+/*
+** ScrollButtonClass
+**
+** Parts of the code have been inspired by ScrollerWindow 0.3 demo
+** Copyright © 1994 Christoph Feck, TowerSystems.
+**
+** Subclass of buttongclass. The ROM class has two problems, which make
+** it not quite usable for scrollarrows. The first problem is the missing
+** delay. Once the next INTUITICK gets send by input.device, the ROM
+** class already sends a notification. The other problem is that it also
+** notifies us, when the button finally gets released (which is necessary
+** for command buttons).
+**
+** We define a new class with the GM_GOACTIVE and GM_HANDLEINPUT method
+** overloaded to work around these problems.
+*/
+
+static LIBCALL ULONG ScrollButtonDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct Gadget *g),
+ REG(a1, struct gpInput *gpi))
+
+/* ScrollButton Class Dispatcher entrypoint.
+ * Handle BOOPSI messages.
+ */
+{
+ struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
+
+ switch (gpi->MethodID)
+ {
+ case GM_GOACTIVE:
+ /* May define an attribute to make delay configurable */
+ bd->TickCounter = 3;
+
+ /* Notify our target that we have initially hit. */
+ NotifyAttrChanges ((Object *)g, gpi->gpi_GInfo, 0,
+ GA_ID, g->GadgetID,
+ TAG_DONE);
+
+ /* Send more input */
+ return GMR_MEACTIVE;
+
+ case GM_HANDLEINPUT:
+ {
+ struct RastPort *rp;
+ ULONG retval = GMR_MEACTIVE;
+ UWORD selected = 0;
+
+ /* This also works with classic (non-boopsi) images. */
+ if (PointInImage ((gpi->gpi_Mouse.X << 16) + (gpi->gpi_Mouse.Y), g->GadgetRender))
+ {
+ /* We are hit */
+ selected = GFLG_SELECTED;
+ }
+
+ if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE && gpi->gpi_IEvent->ie_Code == SELECTUP)
+ {
+ /* Gadgetup, time to go */
+ retval = GMR_NOREUSE;
+ /* Unselect the gadget on our way out... */
+ selected = 0;
+ }
+ else if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER)
+ {
+ /* We got a tick. Decrement counter, and if 0, send notify. */
+
+ if (bd->TickCounter) bd->TickCounter--;
+ else if (selected)
+ {
+ NotifyAttrChanges ((Object *) g, gpi->gpi_GInfo, 0,
+ GA_ID, g->GadgetID,
+ TAG_DONE);
+ }
+ }
+
+ if ((g->Flags & GFLG_SELECTED) != selected)
+ {
+ /* Update changes in gadget render */
+ g->Flags ^= GFLG_SELECTED;
+ if (rp = ObtainGIRPort (gpi->gpi_GInfo))
+ {
+ DoMethod ((Object *) g, GM_RENDER, gpi->gpi_GInfo, rp, GREDRAW_UPDATE);
+ ReleaseGIRPort (rp);
+ }
+ }
+ return retval;
+ }
+
+ default:
+ /* Super class handles everything else */
+ return (DoSuperMethodA (cl, (Object *)g, (Msg) gpi));
+ }
+}
+
+
+
+GLOBALCALL Class *InitScrollButtonClass (void)
+{
+ Class *class;
+
+ if (class = MakeClass (NULL, BUTTONGCLASS, NULL, sizeof(struct ScrollButtonData), 0))
+ class->cl_Dispatcher.h_Entry = (ULONG (*)()) ScrollButtonDispatcher;
+
+ return class;
+}
+
+
+
+GLOBALCALL BOOL FreeScrollButtonClass (Class *cl)
+{
+ return (FreeClass (cl));
+}
+
+
+
+static ULONG HOOKCALL VImageDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct Image *im),
+ REG(a1, struct opSet *ops))
+
+/* VImage Class Dispatcher entrypoint.
+ * Handle BOOPSI messages.
+ */
+{
+ switch (ops->MethodID)
+ {
+ case OM_NEW:
+
+ /* Create the image structure */
+ if (im = (struct Image *)DoSuperMethodA (cl, (Object *)im, (Msg) ops))
+ {
+ ULONG which;
+ struct RastPort rp;
+ /* struct DrawInfo *dri = (struct DrawInfo *)GetTagData (GA_DrawInfo, NULL, ops->ops_AttrList);
+ */
+ which = GetTagData (SYSIA_Which, 0, ops->ops_AttrList);
+
+ InitRastPort (&rp);
+
+#ifndef OS30_ONLY
+ if (GfxBase->LibNode.lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ rp.BitMap = AllocBitMap (im->Width, im->Height, 1, BMF_CLEAR, NULL);
+#ifndef OS30_ONLY
+ else
+ {
+ if (rp.BitMap = AllocMem (sizeof (struct BitMap), MEMF_PUBLIC))
+ {
+ InitBitMap (rp.BitMap, 1, im->Width, im->Height);
+ if (!(rp.BitMap->Planes[0] = AllocMem (RASSIZE(im->Width, im->Height), MEMF_CHIP | MEMF_CLEAR)))
+ {
+ FreeMem (rp.BitMap, sizeof (struct BitMap));
+ rp.BitMap = NULL;
+ }
+ }
+ }
+#endif /* !OS30_ONLY */
+
+ if (rp.BitMap)
+ {
+ PLANEPTR planeptr;
+ struct TmpRas tmpras;
+ struct AreaInfo areainfo;
+ WORD areabuffer[(5 * 10 + 1) / 2];
+
+ if (planeptr = AllocRaster (im->Width, im->Height))
+ {
+ InitTmpRas (&tmpras, planeptr, RASSIZE(im->Width, im->Height));
+ InitArea (&areainfo, areabuffer, 10);
+ SetAPen (&rp, 1);
+ rp.TmpRas = &tmpras;
+ rp.AreaInfo = &areainfo;
+
+ switch (which)
+ {
+ case IM_PLAY:
+ DrawPlayImage (&rp, im->Width, im->Height);
+ break;
+
+ case IM_STOP:
+ DrawStopImage (&rp, im->Width, im->Height);
+ break;
+
+ case IM_FWD:
+ DrawForwardImage (&rp, im->Width, im->Height);
+ break;
+
+ case IM_REW:
+ DrawRewindImage (&rp, im->Width, im->Height);
+ break;
+
+ case IM_PICK:
+ DrawPickImage (&rp, im->Width, im->Height);
+ break;
+ }
+
+ /* Just to be sure... */
+ rp.TmpRas = NULL;
+ rp.AreaInfo = NULL;
+
+ FreeRaster (planeptr, im->Width, im->Height);
+ }
+
+ /* This way our image will complement better */
+ rp.BitMap->Planes[1] = (UBYTE *)0;
+ rp.BitMap->Depth = 2;
+
+
+ /* Failing to allocate the TmpRas will cause the
+ * image to be blank, but no error will be
+ * reported.
+ */
+
+ /* Store the BitMap pointer here for later usage */
+ im->ImageData = (UWORD *)rp.BitMap;
+
+ return (ULONG)im; /* Return new image object */
+ }
+
+ DisposeObject (im);
+ }
+
+ return NULL;
+
+ case IM_DRAW:
+ case IM_DRAWFRAME:
+ DrawVImage ((struct impDraw *)ops, im, (struct BitMap *)im->ImageData);
+ break;
+
+ case OM_DISPOSE:
+
+ /* Restore original depth! */
+ ((struct BitMap *)im->ImageData)->Depth = 1;
+
+#ifndef OS30_ONLY
+ if (GfxBase->LibNode.lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ FreeBitMap ((struct BitMap *)im->ImageData);
+#ifndef OS30_ONLY
+ else
+ {
+ FreeMem (((struct BitMap *)im->ImageData)->Planes[0], RASSIZE(im->Width, im->Height));
+ FreeMem (((struct BitMap *)im->ImageData), sizeof (struct BitMap));
+ }
+#endif /* !OS30_ONLY */
+
+ /* Now let our superclass free it's istance */
+ /* Note: I'm falling through here! */
+
+ default:
+ /* Our Super class handles everything else */
+ return (DoSuperMethodA (cl, (Object *)im, (Msg) ops));
+ }
+}
+
+
+
+GLOBALCALL Class *InitVImageClass (void)
+{
+ Class *class;
+
+ if (class = MakeClass (NULL, IMAGECLASS, NULL, 0, 0))
+ class->cl_Dispatcher.h_Entry = (ULONG (*)()) VImageDispatcher;
+
+ return class;
+}
+
+
+
+GLOBALCALL BOOL FreeVImageClass (Class *cl)
+{
+ return (FreeClass (cl));
+}
+
+
+
+static void DrawPlayImage (struct RastPort *rp, UWORD width, UWORD height)
+{
+ UWORD ymin = height / 4,
+ ymax = (height * 3) / 4,
+ ymid;
+
+ ymin -= (ymax - ymin) & 1; /* Force odd heigth for better arrow aspect */
+ ymid = (ymin + ymax) / 2;
+
+ RectFill (rp, 1, ymin, (width / 4) - 1, ymax);
+
+ AreaMove (rp, width / 3, ymin);
+ AreaDraw (rp, width - 2, ymid);
+ AreaDraw (rp, width / 3, ymax);
+
+ AreaEnd (rp);
+}
+
+
+
+static void DrawStopImage (struct RastPort *rp, UWORD width, UWORD height)
+{
+ RectFill (rp, width / 4, height / 4, (width * 3) / 4, (height * 3) / 4);
+}
+
+
+
+static void DrawForwardImage (struct RastPort *rp, UWORD width, UWORD height)
+{
+ UWORD ymin = height / 4,
+ ymax = (height * 3) / 4,
+ ymid;
+
+ ymin -= (ymax - ymin) & 1; /* Force odd heigth for better arrow aspect */
+ ymid = (ymin + ymax) / 2;
+
+ AreaMove (rp, 1, ymin);
+ AreaDraw (rp, width / 2, ymid);
+ AreaDraw (rp, 1, ymax);
+
+ AreaMove (rp, width / 2, ymin);
+ AreaDraw (rp, width - 2, ymid);
+ AreaDraw (rp, width / 2, ymax);
+
+ AreaEnd (rp);
+}
+
+
+
+static void DrawRewindImage (struct RastPort *rp, UWORD width, UWORD height)
+{
+ UWORD ymin = height / 4,
+ ymax = (height * 3) / 4,
+ ymid;
+
+ ymin -= (ymax - ymin) & 1; /* Force odd heigth for better arrow aspect */
+ ymid = (ymin + ymax) / 2;
+
+ AreaMove (rp, width - 2, ymin);
+ AreaDraw (rp, width / 2, ymid);
+ AreaDraw (rp, width - 2, ymax);
+
+ AreaMove (rp, width / 2 - 1, ymin);
+ AreaDraw (rp, 1, ymid);
+ AreaDraw (rp, width / 2 - 1, ymax);
+
+ AreaEnd (rp);
+}
+
+
+static void DrawPickImage (struct RastPort *rp, UWORD width, UWORD height)
+/*
+ * arrowxmin
+ * | tailxmin
+ * | | tailxmax
+ * | | |
+ * | v v
+ * | ###<----tailymin
+ * v ###
+ * #######<--arrowymin
+ * #####
+ * ###
+ * #<-----arrowymax
+ * #######<--arrowymax+1
+ */
+{
+ UWORD tailymin = height / 6,
+ tailxmin = (width * 2) / 5,
+ tailxmax = (width * 3) / 5,
+ arrowymin = (height * 2) / 5,
+ arrowymax = (height * 4) / 5,
+ arrowxmin = width / 5,
+ arrowxmax = (width * 4) / 5;
+
+ AreaMove (rp, tailxmin, tailymin);
+ AreaDraw (rp, tailxmax, tailymin);
+ AreaDraw (rp, tailxmax, arrowymin);
+ AreaDraw (rp, arrowxmax, arrowymin);
+ AreaDraw (rp, (arrowxmin + arrowxmax) / 2, arrowymax);
+ AreaDraw (rp, arrowxmin, arrowymin);
+ AreaDraw (rp, tailxmin, arrowymin);
+ AreaEnd (rp);
+
+ if (arrowymax < height - 1) arrowymax++;
+
+ Move (rp, arrowxmin, arrowymax);
+ Draw (rp, arrowxmax, arrowymax);
+}
+
+
+
+static void DrawVImage (struct impDraw *imp, struct Image *im, struct BitMap *bm)
+{
+ if (bm)
+ BltBitMapRastPort (bm, 0, 0, imp->imp_RPort,
+ imp->imp_Offset.X, imp->imp_Offset.Y, im->Width, im->Height,
+ (imp->imp_State == IDS_SELECTED) ? 0x030 : 0x0C0);
+}
+
+
+
+GLOBALCALL struct Gadget *CreateUpButton (ULONG id, struct Gadget *target, LONG *map, LONG Place)
+{
+ struct Gadget *UpButton;
+ struct Image *UpImage;
+
+ if (!(UpImage = NewImageObject (UPIMAGE)))
+ return NULL;
+
+ if (!(UpButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
+ GA_ID, id,
+ GA_RelBottom, - (UpImage->Height * 2) - SizeHeight + 1,
+ (Place == SCROLLERPLACE_LEFT) ? GA_Left : GA_RelRight,
+ (Place == SCROLLERPLACE_LEFT) ? 0 : (- SizeWidth + 1),
+ /* ^--- perhaps using UpImage->Width would be better... */
+
+ (Place == SCROLLERPLACE_LEFT) ? GA_LeftBorder : GA_RightBorder, TRUE,
+
+ /* No need for GA_Width/Height. buttongclass is smart :) */
+ GA_Image, UpImage,
+ ICA_TARGET, target,
+ ICA_MAP, map,
+ TAG_DONE)))
+ DisposeObject (UpImage);
+
+ return UpButton;
+}
+
+
+
+GLOBALCALL struct Gadget *CreateDnButton (ULONG id, struct Gadget *target, LONG *map, LONG Place)
+{
+ struct Gadget *DnButton;
+ struct Image *DnImage;
+
+
+ if (!(DnImage = NewImageObject (DOWNIMAGE)))
+ return NULL;
+
+ if (!(DnButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
+ GA_ID, id,
+ GA_RelBottom, - DnImage->Height - SizeHeight + 1,
+ (Place == SCROLLERPLACE_LEFT) ? GA_Left : GA_RelRight,
+ (Place == SCROLLERPLACE_LEFT) ? 0 : (- SizeWidth + 1),
+ (Place == SCROLLERPLACE_LEFT) ? GA_LeftBorder : GA_RightBorder, TRUE,
+ /* No need for GA_Width/Height. buttongclass is smart :) */
+ GA_Image, DnImage,
+ ICA_TARGET, target,
+ ICA_MAP, map,
+ TAG_DONE)))
+ DisposeObject (DnImage);
+
+ return DnButton;
+}
+
+
+
+GLOBALCALL struct Gadget *CreateSxButton (ULONG id, struct Gadget *target, LONG *map)
+{
+ struct Gadget *SxButton;
+ struct Image *SxImage;
+
+
+ if (!(SxImage = NewImageObject (LEFTIMAGE)))
+ return NULL;
+
+ if (!(SxButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
+ GA_ID, id,
+ GA_RelBottom, - SxImage->Height + 1,
+ GA_RelRight, - SizeWidth - (SxImage->Width * 2) + 1,
+ GA_BottomBorder, TRUE,
+ /* No need for GA_Width/Height. buttongclass is smart :) */
+ GA_Image, SxImage,
+ ICA_TARGET, target,
+ ICA_MAP, map,
+ TAG_DONE)))
+ DisposeObject (SxImage);
+
+ return SxButton;
+}
+
+
+
+GLOBALCALL struct Gadget *CreateDxButton (ULONG id, struct Gadget *target, LONG *map)
+{
+ struct Gadget *DxButton;
+ struct Image *DxImage;
+
+
+ if (!(DxImage = NewImageObject (RIGHTIMAGE)))
+ return NULL;
+
+ if (!(DxButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
+ GA_ID, id,
+ GA_RelBottom, - DxImage->Height + 1,
+ GA_RelRight, - SizeWidth - DxImage->Width + 1,
+ GA_BottomBorder, TRUE,
+ /* No need for GA_Width/Height. buttongclass is smart :) */
+ GA_Image, DxImage,
+ ICA_TARGET, target,
+ ICA_MAP, map,
+ TAG_DONE)))
+ DisposeObject (DxImage);
+
+ return DxButton;
+}
+
+
+
+GLOBALCALL struct Gadget *CreateVSlider (ULONG id, struct Gadget *target, LONG *map, LONG ButtonsSpacing, LONG Place)
+{
+ UWORD horizpos;
+
+ horizpos = (Place == SCROLLERPLACE_LEFT) ? Scr->WBorLeft : (- SizeWidth + 5);
+
+ return ((struct Gadget *)NewObject (NULL, PROPGCLASS,
+ GA_ID, id,
+ GA_Top, OffY + 1,
+ (Place == SCROLLERPLACE_LEFT) ? GA_Left : GA_RelRight, horizpos,
+ GA_Width, SizeWidth - 8,
+ GA_RelHeight, - OffY - Scr->WBorBottom - SizeHeight
+ - ButtonsSpacing,
+ (Place == SCROLLERPLACE_LEFT) ? GA_LeftBorder : GA_RightBorder, TRUE,
+
+ PGA_NewLook, TRUE,
+
+ /* Borderless sliders do only look good with newlook screens */
+ PGA_Borderless, ((DrawInfo->dri_Flags & DRIF_NEWLOOK) && DrawInfo->dri_Depth != 1),
+
+ ICA_TARGET, target,
+ ICA_MAP, map,
+
+ TAG_DONE));
+}
+
+
+
+GLOBALCALL struct Gadget *CreateHSlider (ULONG id, struct Gadget *target, LONG *map, LONG ButtonsSpacing)
+{
+ struct Gadget *HSlider;
+
+ if (!(HSlider = (struct Gadget *)NewObject (NULL, PROPGCLASS,
+ GA_ID, id,
+ GA_Left, OffX - 1,
+ GA_RelBottom, - SizeHeight + ((SizeHeight > 15) ? 4 : 3),
+ GA_RelWidth, - OffX - SizeWidth
+ - ButtonsSpacing,
+ GA_Height, SizeHeight - ((SizeHeight > 15) ? 6 : 4),
+ GA_BottomBorder, TRUE,
+
+ PGA_NewLook, TRUE,
+ PGA_Borderless, ((DrawInfo->dri_Flags & DRIF_NEWLOOK) && DrawInfo->dri_Depth != 1),
+ PGA_Freedom, FREEHORIZ,
+
+ ICA_TARGET, target,
+ ICA_MAP, map,
+
+ TAG_DONE)))
+ return NULL;
+ return HSlider;
+}
--- /dev/null
+/*
+** CustomClasses.h
+**
+** Copyright (C) 1995,96 by Bernardo Innocenti
+**
+** Scroller button class built on top of the "buttongclass".
+** VImage class built on top of the "imageclass".
+*/
+
+/* Function prototypes */
+
+Class *InitScrollButtonClass (void);
+BOOL FreeScrollButtonClass (Class *);
+Class *InitVImageClass (void);
+BOOL FreeVImageClass (Class *);
+
+
+/* Values for SYSIA_Which attribute of ImageButtonClass */
+#define IM_PLAY 0
+#define IM_STOP 1
+#define IM_REW 2
+#define IM_FWD 3
+#define IM_PICK 4
--- /dev/null
+/*
+** Cx.c
+**
+** Copyright (C) 1994,95,96,97,98 Bernardo Innocenti
+**
+** Commodity support functions
+*/
+
+
+#include <exec/memory.h>
+#include <libraries/commodities.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/commodities.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+#define CX_POPKEY 'POP!'
+
+
+XDEF BYTE CxPri = 0;
+XDEF UBYTE CxPopKey[32] = "control alt x";
+XDEF BOOL CxPopup = TRUE;
+XDEF ULONG CxSig = 0;
+
+XDEF struct MsgPort *CxPort = NULL;
+XDEF CxObj *MyBroker = NULL;
+
+
+static struct NewBroker MyNewBroker =
+{
+ NB_VERSION,
+ PRGNAME,
+ Version+6,
+ "Module Processing Utility",
+ NBU_DUPLICATE,
+ COF_SHOW_HIDE,
+ 0,
+ NULL,
+ 0
+};
+
+
+
+GLOBALCALL void HandleCx (void)
+{
+ CxMsg *cxm;
+ ULONG type;
+ LONG id;
+
+ while (cxm = (CxMsg *) GetMsg (CxPort))
+ {
+ type = CxMsgType (cxm);
+ id = CxMsgID (cxm);
+
+ switch (type)
+ {
+ case CXM_IEVENT:
+ if (id == CX_POPKEY) DeIconify();
+ break;
+
+ case CXM_COMMAND:
+ switch (id)
+ {
+ case CXCMD_DISABLE:
+ ActivateCxObj (MyBroker, FALSE);
+ break;
+
+ case CXCMD_ENABLE:
+ ActivateCxObj (MyBroker, TRUE);
+ break;
+
+ case CXCMD_APPEAR:
+ DeIconify();
+ break;
+
+ case CXCMD_DISAPPEAR:
+ CloseDownScreen();
+ break;
+
+ case CXCMD_KILL:
+ Quit = TRUE;
+ GuiSwitches.AskExit = FALSE;
+ break;
+
+ default:
+ break;
+
+ } /* End Switch (id) */
+
+ default:
+ break;
+
+ } /* End Switch (type) */
+
+ ReplyMsg ((struct Message *) cxm);
+
+ } /* End While (GetMsg()) */
+}
+
+
+
+GLOBALCALL LONG SetupCx (void)
+{
+ CxObj *filter, *sender;
+
+ if (!CxPopKey[0]) return RETURN_FAIL;
+
+ if (!(CxBase = OpenLibrary ("commodities.library", 37L)))
+ return RETURN_FAIL;
+
+ if (!(CxPort = CreateMsgPort ()))
+ {
+ CleanupCx();
+ return ERROR_NO_FREE_STORE;
+ }
+
+ CxSig = 1 << CxPort->mp_SigBit;
+ Signals |= CxSig;
+
+ MyNewBroker.nb_Pri = CxPri;
+ MyNewBroker.nb_Port = CxPort;
+
+ if (!(MyBroker = CxBroker (&MyNewBroker, NULL)))
+ {
+ CleanupCx();
+ return RETURN_FAIL;
+ }
+
+ /* Create PopKey Filter/Sender */
+
+ if (filter = CxFilter (CxPopKey))
+ {
+ if (CxObjError (filter) & COERR_BADFILTER)
+ ShowMessage (MSG_BAD_HOTKEY);
+
+ AttachCxObj (MyBroker, filter);
+
+ if (sender = CxSender (CxPort, CX_POPKEY))
+ AttachCxObj (filter, sender);
+ }
+
+ ActivateCxObj (MyBroker, TRUE);
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL void CleanupCx (void)
+{
+ if (CxBase)
+ {
+ if (MyBroker)
+ { DeleteCxObjAll (MyBroker); MyBroker = NULL; }
+
+ if (CxPort)
+ {
+ KillMsgPort (CxPort);
+ CxPort = NULL;
+ Signals &= ~CxSig;
+ CxSig = 0;
+ }
+
+ CloseLibrary (CxBase); CxBase = NULL;
+ }
+}
--- /dev/null
+#ifndef DEBUG_H
+#define DEBUG_H
+/*
+** $VER: Debug.h 2.2 (19.9.98)
+**
+** Copyright (C) 1995,96,97 Bernardo Innocenti. All rights reserved.
+**
+** Use 4 chars wide TABs to read this file
+**
+** Some handy debug macros which are automatically excluded when the
+** DEBUG preprocessor sysmbol is not defined. To make debug executables,
+** link with debug.lib or any module containing the kprintf() function.
+**
+** Here is a short description of the macros defined below:
+**
+** ILLEGAL
+** Output an inline "ILLEGAL" 68K opcode, which will
+** be interpreted as a breakpoint by most debuggers.
+**
+** DBPRINTF
+** Output a formatted string to the debug console. This
+** macro uses the debug.lib kprintf() function by default.
+**
+** ASSERT(x)
+** Do nothing if the expression <x> evalutates to a
+** non-zero value, output a debug message otherwise.
+**
+** ASSERT_VALID(x)
+** Checks if the expression <x> points to a valid
+** memory location, and outputs a debug message
+** otherwise. A NULL pointer is considered VALID.
+**
+** ASSERT_VALIDNO0(x)
+** Checks if the expression <x> points to a valid
+** memory location, and outputs a debug message
+** otherwise. A NULL pointer is considered INVALID.
+**
+** DB(x)
+** Compile the expression <x> when making a debug
+** executable, leave it out otherwise.
+*/
+
+#ifdef DEBUG
+
+ /* Needed for TypeOfMem() */
+ #ifndef PROTO_EXEC_H
+ #include <proto/exec.h>
+ #endif /* PROTO_EXEC_H */
+
+ #if defined(__SASC)
+
+ extern void __builtin_emit (int);
+ // #define ILLEGAL __builtin_emit(0x4AFC)
+ #define ILLEGAL 0
+ STDARGS extern void kprintf (const char *, ...);
+
+ #elif defined(__GNUC__)
+
+ /* Currently, there is no kprintf() in libamiga.a */
+ #define kprintf printf
+
+ /* GCC doesn't accept asm statemnts in the middle of an
+ * expression such as `a ? b : asm("something")'.
+ */
+ #define ILLEGAL illegal()
+ static inline int illegal(void) { asm ("illegal"); return 0; }
+ extern void STDARGS FORMATCALL(printf,1,2) kprintf (const char *, ...);
+
+ #else
+ #error Please add compiler specific definitions for your compiler
+ #endif
+
+ #if defined(__SASC) || defined (__GNUC__)
+
+ /* common definitions for ASSERT and DB macros */
+
+ #define DBPRINTF kprintf
+
+ #define ASSERT(x) ( (x) ? 0 : \
+ ( DBPRINTF ("\x07%s, %ld: assertion failed: " #x "\n", \
+ __FILE__, __LINE__) , ILLEGAL ) );
+
+ #define ASSERT_VALID(x) ( ((((APTR)(x)) == NULL) || \
+ (((LONG)(x) > 1024) && TypeOfMem ((APTR)(x)))) ? 0 : \
+ ( DBPRINTF ("\x07%s, %ld: bad address: " #x " = $%lx\n", \
+ __FILE__, __LINE__, (APTR)(x)) , ILLEGAL ) );
+
+ #define ASSERT_VALIDNO0(x) ( (((LONG)(x) > 1024) && \
+ TypeOfMem ((APTR)(x))) ? 0 : \
+ ( DBPRINTF ("\x07%s, %ld: bad address: " #x " = $%lx\n", \
+ __FILE__, __LINE__, (APTR)(x)) , ILLEGAL ) );
+
+ #define DB(x) x
+ #endif
+
+#else
+ #define ASSERT_VALID(x)
+ #define ASSERT_VALIDNO0(x)
+ #define ASSERT(x)
+ #define DB(x)
+#endif /* DEBUG */
+
+#endif /* !DEBUG_H */
--- /dev/null
+#include <math.h>
+#include <m68881.h>
+
+
+
+
+static void FFT (float data[], LONG nn, LONG isign);
+static void RealFFT (float data[], LONG n, LONG isign);
+static void MyFFT (double *real_vet, double *im_vet, long n, int m, int ind, double cf);
+
+
+
+#ifdef __SASC
+
+/* Disable SAS/C floating point error handler */
+void __stdargs _CXFERR(int code)
+{
+}
+
+#endif /* __SASC */
+
+#if 0
+GLOBALCALL void Filter (WORD *data, LONG len)
+{
+ float *fftdata;
+ LONG i, nn;
+
+
+ /* Calculate nearest power of 2 */
+ for (i = len-1, nn = 1; i != 0; i >>= 1)
+ nn <<= 1;
+
+ if (!(fftdata = AllocVec (sizeof (float) * nn * 2, MEMF_CLEAR)))
+ return;
+
+ /* Fill array of complex numbers (imaginary part is always set to 0) */
+ for (i = 0; i < len; i++)
+ fftdata[i] = (float) data[i];
+
+
+ /* Obtain frequency spectrum */
+ RealFFT (fftdata, nn>>1, 1);
+
+ /* Low-pass Filter */
+ for (i = 0; i < nn; i++)
+ fftdata[i*2] *= (i/nn) * 4;
+
+ /* Return to time domain */
+ RealFFT (fftdata, nn>>1, -1);
+ for (i = 0; i < len; i++)
+ fftdata[i] /= nn;
+
+ /* Put filtered data */
+ for (i = 0; i < len; i++)
+ data[i] = (WORD) fftdata[i];
+
+ FreeVec (fftdata);
+}
+#endif
+
+
+GLOBALCALL void Filter (WORD *data, LONG len)
+{
+ double *fftdata;
+ LONG i, nn, mm;
+
+
+ /* Calculate nearest power of 2 */
+ for (i = len-1, nn = 1, mm = 0; i != 0; i >>= 1)
+ {
+ nn <<= 1;
+ mm++;
+ }
+
+ if (!(fftdata = AllocVec (sizeof (double) * nn * 2, MEMF_CLEAR)))
+ return;
+
+ /* Fill array of complex numbers (imaginary part is always set to 0) */
+ for (i = 0; i < len; i++)
+ fftdata[i] = (float) data[i];
+
+
+ /* Obtain frequency spectrum */
+ MyFFT (fftdata, fftdata + nn, nn>>1, mm, +1, 1.0/32768.0);
+
+ /* Low-pass Filter */
+// for (i = 0; i < nn; i++)
+// fftdata[i] *= (i/nn) * 4;
+
+ /* Return to time domain */
+ MyFFT (fftdata, fftdata + nn, nn>>1, mm, -1, 1.0);
+
+// for (i = 0; i < len; i++)
+// fftdata[i] /= nn;
+
+ /* Put filtered data */
+ for (i = 0; i < len; i++)
+ data[i] = (WORD) fftdata[i];
+
+ FreeVec (fftdata);
+}
+
+
+#if 0
+#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr
+
+static void FFT (float data[], LONG nn, LONG isign)
+{
+ int n,mmax,m,j,istep,i;
+ double wtemp,wr,wpr,wpi,wi,theta;
+ float tempr,tempi;
+
+ n=nn << 1;
+ j=1;
+ for (i=1;i<n;i+=2)
+ {
+ if (j > i)
+ {
+ SWAP(data[j],data[i]);
+ SWAP(data[j+1],data[i+1]);
+ }
+ m=n >> 1;
+ while (m >= 2 && j > m)
+ {
+ j -= m;
+ m >>= 1;
+ }
+ j += m;
+ }
+ mmax = 2;
+ while (n > mmax)
+ {
+ istep=2*mmax;
+ theta=6.28318530717959/(isign*mmax);
+ wtemp=sin(0.5*theta);
+ wpr = -2.0*wtemp*wtemp;
+ wpi=sin(theta);
+ wr=1.0;
+ wi=0.0;
+ for (m=1;m<mmax;m+=2)
+ {
+ for (i=m;i<=n;i+=istep)
+ {
+ j = i+mmax;
+ tempr=wr*data[j]-wi*data[j+1];
+ tempi=wr*data[j+1]+wi*data[j];
+ data[j]=data[i]-tempr;
+ data[j+1]=data[i+1]-tempi;
+ data[i] += tempr;
+ data[i+1] += tempi;
+ }
+ wr=(wtemp=wr)*wpr-wi*wpi+wr;
+ wi=wi*wpr+wtemp*wpi+wi;
+ }
+ mmax=istep;
+ }
+}
+
+
+static void RealFFT (float data[], LONG n, LONG isign)
+{
+ int i,i1,i2,i3,i4,n2p3;
+ float c1=0.5,c2,h1r,h1i,h2r,h2i;
+ double wr,wi,wpr,wpi,wtemp,theta;
+ void four1();
+
+ theta=3.141592653589793/(double) n;
+ if (isign == 1)
+ {
+ c2 = -0.5;
+ FFT (data,n,1);
+ }
+ else
+ {
+ c2=0.5;
+ theta = -theta;
+ }
+
+ wtemp=sin(0.5*theta);
+ wpr = -2.0*wtemp*wtemp;
+ wpi=sin(theta);
+ wr=1.0+wpr;
+ wi=wpi;
+ n2p3=2*n+3;
+ for (i=2;i<=n/2;i++)
+ {
+ i4=1+(i3=n2p3-(i2=1+(i1=i+i-1)));
+ h1r=c1*(data[i1]+data[i3]);
+ h1i=c1*(data[i2]-data[i4]);
+ h2r = -c2*(data[i2]+data[i4]);
+ h2i=c2*(data[i1]-data[i3]);
+ data[i1]=h1r+wr*h2r-wi*h2i;
+ data[i2]=h1i+wr*h2i+wi*h2r;
+ data[i3]=h1r-wr*h2r+wi*h2i;
+ data[i4] = -h1i+wr*h2i+wi*h2r;
+ wr=(wtemp=wr)*wpr-wi*wpi+wr;
+ wi=wi*wpr+wtemp*wpi+wi;
+ }
+ if (isign == 1)
+ {
+ data[1] = (h1r=data[1])+data[2];
+ data[2] = h1r-data[2];
+ }
+ else
+ {
+ data[1]=c1*((h1r=data[1])+data[2]);
+ data[2]=c1*(h1r-data[2]);
+ FFT (data,n,-1);
+ }
+}
+
+
+#undef SWAP
+#endif
+
+static void MyFFT (double *real_vet, double *im_vet, long n, int m, int ind, double cf)
+{
+ long l, i, j, nbut, ngrup, nc;
+ long but, grup;
+ long primo, secondo;
+ double w_real, w_im;
+ double t_real, t_im;
+ double arg;
+
+ // Ordinamento binario del vettore
+ j = 0;
+ nc = n >> 1;
+ for (i = 0 ; i< n-1; i++)
+ {
+ if (i < j)
+ {
+ t_real = real_vet[j];
+ t_im = im_vet[j];
+ real_vet[j] = real_vet[i];
+ im_vet[j] = im_vet[i];
+ real_vet[i] = t_real;
+ im_vet[i] = t_im;
+ }
+ l = nc;
+ while(l <= j)
+ {
+ j -= l;
+ l >>= 1;
+ }
+ j+=l;
+ }
+
+ //Calcolo FFT
+ for(l=0; l<m; l++) //Ciclo esterno Log2(N)
+ {
+ nbut = (1L) << l;
+ ngrup = nc / nbut;
+ for(but = 0; but < nbut; but++) //Ciclo medio: numero di farfalle
+ {
+ arg = PI * but / nbut; // M_PI e' la costante PI Greco
+ w_real = cos(arg);
+ w_im = (-1)*ind*sin(arg);
+ for(grup = 0; grup < ngrup; grup++) //Ciclo interno: numero di gruppi
+ {
+ primo = 2*nbut*grup + but;
+ secondo = primo + nbut;
+ t_real = w_real * real_vet[secondo] - w_im * im_vet[secondo];
+ t_im = w_real * im_vet[secondo] + w_im * real_vet[secondo];
+ real_vet[secondo] = real_vet[primo] - t_real;
+ im_vet[secondo] = im_vet[primo] - t_im;
+ real_vet[primo] += t_real;
+ im_vet[primo] += t_im;
+ }
+ }
+ }
+
+ //Normalizzazione
+ if (cf != 1.0) // Modifica di Bernardo
+ for(i = 0; i < n; i++)
+ {
+ real_vet[i] *= cf;
+ im_vet[i] *= cf;
+ }
+}
--- /dev/null
+**
+** XModule replay routine 0.1
+**
+** Copyright (C) 1995 Bernardo Innocenti
+**
+**
+** This code is based on:
+**
+** PS3M 0.950 020+ 14-bit stereo surround version ;) 19.03.1995
+** Copyright (c) Jarno Paananen a.k.a. Guru / S2 1994-95
+**
+** Some portions based on STMIK 0.9ß by Sami Tammilehto / PSI of Future Crew
+**
+
+ Sorry, the source for the XModule player is not included because I have
+been asked not to distribute it until further notice.
+
+ Many thanks to Jarno Paanen for giving me the right to use his 32 channel
+mixing engine. Without his kindness, developing the player would have
+required over one year of hard work.
--- /dev/null
+###########################################################
+# Make the stuff in the gadgets/ subdirectory
+###########################################################
+
+include $(TOP)/config.mk
+
+GADGETS := pattedit.gadget
+GADGETS_CFLAGS := CODE=NEAR DATA=FARONLY STRSECT=CODE
+
+PATTEDITNAME := "pattedit.gadget"
+PATTEDITVERSION := 2
+PATTEDITREVISION := 1
+PATTEDITFLAGS := $(GADGETS_FLAGS) \
+ DEF LIBNAME=$(PATTEDITNAME) \
+ DEF LIBVERSION=$(PATTEDITVERSION) \
+ DEF LIBREVISION=$(PATTEDITREVISION)
+
+
+all: $(GADGETS)
+
+clean:
+ -Delete *.map *.o *.gst *.xref *.gadget
+
+.PHONY: all clean
+
+
+PattEditLib.o: $(TOP)/RomTag.asm
+ $(AS) $(ASFLAGS) $(TOP)/RomTag.asm TO $@ \
+ SET "LIBVERSION=$(PATTEDITVERSION),LIBREVISION=$(PATTEDITREVISION),BOOPSICLASS"
+
+
+PattEditLib_020.o: $(TOP)/RomTag.asm
+ $(AS) $(ASFLAGS) $(TOP)/RomTag.asm TO $@ MACHINE=68020 \
+ SET "LIBVERSION=$(PATTEDITVERSION),LIBREVISION=$(PATTEDITREVISION),BOOPSICLASS"
+
+PattEditLib_020_OS30.o: $(TOP)/RomTag.asm
+ $(AS) $(ASFLAGS) $(TOP)/RomTag.asm TO $@ MACHINE=68020 \
+ SET "LIBVERSION=$(PATTEDITVERSION),LIBREVISION=$(PATTEDITREVISION),BOOPSICLASS,OS30_ONLY"
+
+PattEditClass.o: PattEditClass.c
+ $(CC) PattEditClass.c OBJNAME $@ $(CFLAGS) $(PATTEDITFLAGS) CPU=68000 DEF "BUILDMODE=*"[M68000 OS2.0]*""
+
+PattEditClass_020.o: PattEditClass.c
+ $(CC) PattEditClass.c OBJNAME $@ $(CFLAGS) $(PATTEDITFLAGS) CPU=68020 DEF "BUILDMODE=*"[M68020 OS2.0]*""
+
+PattEditClass_020_OS30.o: PattEditClass.c
+ $(CC) PattEditClass.c OBJNAME $@ $(CFLAGS) $(PATTEDITFLAGS) CPU=68020 DEF "BUILDMODE=*"[M68020 OS3.0]*"" DEF OS30_ONLY
+
+PattEditClassAsm.o: PattEditClassAsm.asm
+ $(AS) $(ASFLAGS) PattEditClassAsm.asm
+
+pattedit.gadget: PattEditClass.o PattEditLib.o PattEditClassAsm.o
+ $(LD) $(LDFLAGS) FROM PattEditLib.o PattEditClass.o PattEditClassAsm.o $(LIBS) TO $@
+ Protect $@ -e
+ FileNote $@ "MC68000 version"
+ Avail FLUSH >NIL:
+
+pattedit.gadget_020: PattEditClass_020.o PattEditLib_020.o PattEditClassAsm.o
+ $(LD) $(LDFLAGS) FROM PattEditLib_020.o PattEditClass_020.o PattEditClassAsm.o $(LIBS) TO $@
+
+pattedit.gadget_020_OS30: PattEditClass_020_OS30.o PattEditLib_020_OS30.o PattEditClassAsm.o
+ $(LD) $(LDFLAGS) FROM PattEditLib_020_OS30.o PattEditClass_020_OS30.o PattEditClassAsm.o $(LIBS) TO $@
--- /dev/null
+/*
+** PattEditClass.c
+**
+** Copyright (C) 1995,96,97,98 Bernardo Innocenti
+**
+** Pattern Editor gadget class
+*/
+
+#include <exec/memory.h>
+#include <exec/ports.h>
+#include <utility/tagitem.h>
+#include <utility/hooks.h>
+#include <devices/inputevent.h>
+#include <libraries/SongClass.h>
+#include <libraries/PattEditClass.h>
+#include <libraries/xmodule.h>
+
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/classes.h>
+#include <intuition/classusr.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <graphics/gfxbase.h>
+#include <graphics/gfxmacros.h>
+#include <graphics/text.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+#include <proto/utility.h>
+#include <proto/keymap.h>
+
+
+#include "CompilerSpecific.h"
+#include "Debug.h"
+#include "ListMacros.h"
+
+#ifdef OS30_ONLY
+ #define WANTED_LIBVER 39
+#else
+ #define WANTED_LIBVER 37
+#endif
+
+
+/* Some handy definitions missing in <devices/inputevent.h> */
+#define IEQUALIFIER_SHIFT (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
+#define IEQUALIFIER_ALT (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
+#define IEQUALIFIER_COMMAND (IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND)
+
+
+
+/* Scrolling control */
+#define SCROLLED_VERT 1
+#define SCROLLED_HORIZ 2
+
+
+
+/* Private class instance data */
+
+struct PattEditData
+{
+ struct Pattern *Patt;
+ struct TextFont *EditorFont;
+
+ /* These are the actual Gadget position and size,
+ * regardless of any GREL_#? flag.
+ */
+ struct IBox GBounds;
+
+ /* These are the actual editing area position and size. */
+ struct IBox TBounds;
+
+ UWORD FontXSize;
+ UWORD FontYSize;
+
+ /* Pens used to draw various pattern editor elements */
+ ULONG BGPen;
+ ULONG TextPen;
+ ULONG LinesPen;
+ ULONG TinyLinesPen;
+
+ ULONG Flags; /* See definitions in <PattEditClass.h> */
+
+ /* Routine used to convert a note into its ASCII representation
+ * This routine is passed the pointer to the note and a buffer
+ * with <TrackChars> characters to fill in.
+ */
+ ASMCALL void (*Note2ASCIIFunc)(REG(a1, UBYTE *s), REG(a2, struct Note *note));
+
+ UWORD TrackWidth; /* Width of a track in pixels */
+ UWORD TrackChars; /* Width of a track in chars */
+
+
+ /* How many tracks and lines we can fit in the gadget bounds */
+ UWORD DisplayTracks;
+ UWORD DisplayLines;
+
+ WORD LeftTrack;
+ WORD TopLine;
+
+ /* Current cursor position */
+ WORD Track;
+ WORD Line;
+ WORD Column;
+
+ UWORD CurrentInst;
+
+ UWORD CursState; /* IDS_NORMAL, IDS_DISABLED or IDS_BUSY */
+ UWORD CursLinePos; /* Cursor line position (0 = not drawn) */
+ struct Rectangle CursRect; /* Cursor Position to erase it quickly. */
+
+ /* Cursor advancement */
+ WORD AdvanceTracks;
+ WORD AdvanceLines;
+
+ /* Range marking info */
+ UWORD RangeStartTrack;
+ UWORD RangeStartLine;
+ UWORD RangeEndTrack;
+ UWORD RangeEndLine;
+
+ /* Backup cursor position for mouse right button undo operation */
+ WORD BackupTrack;
+ WORD BackupLine;
+ WORD BackupColumn;
+
+ /* This variable holds a counter for the optimized scroller update.
+ * When the pattern is scrolling, updates are only sent after
+ * a specific amount of scrolling operations have occurred.
+ */
+ UWORD SliderCounter;
+
+ /* The following two are for track resizing. ResizeXStart
+ * is the horizontal mouse position when the resize operation
+ * was started. ResizeChars is the last number of chars set.
+ */
+ UWORD ResizeXStart;
+ UWORD ResizeChars;
+
+ struct Rectangle RangeRect; /* Backup of range rect to erase it quickly. */
+
+ /* Undo/Redo support */
+ ULONG Changes;
+ ULONG UndoCount;
+ ULONG UndoMem;
+ ULONG MaxUndoLevels;
+ ULONG MaxUndoMem;
+ struct MinList UndoList;
+ struct MinList RedoList;
+
+ /* For testing double/triple click */
+ ULONG DoubleClickSeconds;
+ ULONG DoubleClickMicros;
+ ULONG TripleClickSeconds;
+ ULONG TripleClickMicros;
+
+ /* Experimental: timer.device stuff */
+// struct timerequest TimerIO;
+// struct MsgPort TimerPort;
+// struct Interrupt TimerInt;
+};
+
+
+/* This structure holds an entry for the Undo/Redo buffer. */
+
+struct UndoNode
+{
+ struct MinNode Link;
+ UWORD Line,
+ Track;
+ struct Note OldNote;
+};
+
+
+
+/* Function prototypes */
+
+static ULONG HOOKCALL PattEditDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct ExtGadget *g),
+ REG(a1, Msg msg));
+static void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect);
+static BOOL CalcDisplaySize (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gpi);
+static void SaveUndo (struct PattEditData *ped);
+static BOOL UndoChange (struct PattEditData *ped);
+static BOOL RedoChange (struct PattEditData *ped);
+static void FreeUndoBuffers (struct PattEditData *ped, BOOL freeall);
+static BOOL MoveCursor (struct PattEditData *ped, WORD x, WORD y);
+static void EraseCursor (struct RastPort *rp, struct PattEditData *ped);
+static UWORD DrawCursor (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g);
+static void DrawRange (struct RastPort *rp, struct PattEditData *ped);
+static void ClearRange (struct RastPort *rp, struct PattEditData *ped);
+static void RedrawAll (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g);
+static void RedrawPattern (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g);
+static void DrawTrackSeparators (struct RastPort *rp, struct PattEditData *ped, UWORD width);
+static void DrawTrackNumbers(struct RastPort *rp, struct PattEditData *ped);
+static void DrawPatternLines(struct RastPort *rp, struct PattEditData *ped, UWORD min, UWORD max);
+static void DrawNote (struct RastPort *rp, struct PattEditData *ped);
+void ASMCALL Note2ASCII (REG(a1, UBYTE *s), REG(a2, struct Note *note));
+void ASMCALL Note2ASCIIBlank0 (REG(a1, UBYTE *s), REG(a2, struct Note *note));
+static UWORD ScrollPattern (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g,
+ UWORD lefttrack, UWORD topline);
+static void NotifyCursor (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
+static void NotifyVSlider (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
+static void NotifyHSlider (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
+static void NotifySliders (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
+
+//static void ASMCALL TimerIntServer (register REG(a1, struct ExtGadget *g));
+
+HOOKCALL struct ClassLibrary *_UserLibInit (REG(a6, struct ClassLibrary *mybase));
+HOOKCALL struct ClassLibrary *_UserLibCleanup (REG(a6, struct ClassLibrary *mybase));
+HOOKCALL Class *_GetEngine (REG(a6, struct ClassLibrary *mybase));
+
+
+
+/* Library data */
+
+const UBYTE LibName[] = LIBNAME;
+const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
+const UBYTE LibId[] = LIBNAME " 2.1 (8.4.97) " BUILDMODE " © 1995,96,97 by Bernardo Innocenti\n";
+
+
+
+/* Local data */
+
+#ifdef PORTABLE
+static const ULONG TextNotes[MAXNOTES] =
+{
+ ' - ',
+ 'C-0 ', 'C#0 ', 'D-0 ', 'D#0 ', 'E-0 ', 'F-0 ',
+ 'F#0 ', 'G-0 ', 'G#0 ', 'A-0 ', 'A#0 ', 'B-0 ',
+
+ 'C-1 ', 'C#1 ', 'D-1 ', 'D#1 ', 'E-1 ', 'F-1 ',
+ 'F#1 ', 'G-1 ', 'G#1 ', 'A-1 ', 'A#1 ', 'B-1 ',
+
+ 'C-2 ', 'C#2 ', 'D-2 ', 'D#2 ', 'E-2 ', 'F-2 ',
+ 'F#2 ', 'G-2 ', 'G#2 ', 'A-2 ', 'A#2 ', 'B-2 ',
+
+ 'C-3 ', 'C#3 ', 'D-3 ', 'D#3 ', 'E-3 ', 'F-3 ',
+ 'F#3 ', 'G-3 ', 'G#3 ', 'A-3 ', 'A#3 ', 'B-3 ',
+
+ 'C-4 ', 'C#4 ', 'D-4 ', 'D#4 ', 'E-4 ', 'F-4 ',
+ 'F#4 ', 'G-4 ', 'G#4 ', 'A-4 ', 'A#4 ', 'B-4 ',
+
+ 'C-5 ', 'C#5 ', 'D-5 ', 'D#5 ', 'E-5 ', 'F-5 ',
+ 'F#5 ', 'G-5 ', 'G#5 ', 'A-5 ', 'A#5 ', 'B-5 '
+};
+#endif /* PORTABLE */
+
+
+/* Keyboard rawkeys -> notes conversion table */
+
+static const UBYTE KeyNotes0[10] = { 0, 26, 28, 0, 31, 33, 35, 0, 38, 40}; /* (1..0) */
+static const UBYTE KeyNotes1[10] = {25, 27, 29, 30, 32, 34, 36, 37, 39, 41}; /* (Q..]) */
+static const UBYTE KeyNotes2[10] = { 0, 14, 16, 0, 19, 21, 23, 0, 26, 28}; /* (A..') */
+static const UBYTE KeyNotes3[10] = {13, 15, 17, 18, 20, 22, 24, 25, 27, 29}; /* (Z../) */
+
+static const UBYTE HexValues[20] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', 'G', 'H', 'I', 'J'};
+static const UBYTE HexValuesNo0[20] = {' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', 'G', 'H', 'I', 'J'};
+
+
+
+/* Cursor patterns - Used in EraseCursor() and DrawCursor() */
+
+static UWORD GhostPattern[] = { 0xAAAA, 0x5555 };
+static UWORD MarkPattern[] = { 0xFFFF, 0x0000 };
+
+
+
+/*****************/
+/* Library bases */
+/*****************/
+
+struct ExecBase *SysBase = NULL;
+struct IntuitionBase *IntuitionBase = NULL;
+struct GfxBase *GfxBase = NULL;
+struct UtilityBase *UtilityBase = NULL;
+struct Library *KeymapBase = NULL;
+
+
+static ULONG HOOKCALL PattEditDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct ExtGadget *g),
+ REG(a1, Msg msg))
+
+/* PattEdit class dispatcher entrypoint. Handle BOOPSI messages.
+ */
+{
+ struct RastPort *rp;
+ struct PattEditData *ped;
+ struct TagItem *ti;
+ ULONG result = 0;
+
+
+ switch (msg->MethodID)
+ {
+ case GM_GOACTIVE:
+
+ ped = INST_DATA (cl, g);
+
+ if (!ped->Patt)
+ {
+ result = GMR_NOREUSE;
+ break;
+ }
+
+ g->Flags |= GFLG_SELECTED;
+
+ /* Render active cursor */
+ if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
+ {
+ DrawCursor (rp, ped, g);
+ ReleaseGIRPort (rp);
+ }
+
+ /* Do not process InputEvent when the gadget has been
+ * activated by ActivateGadget().
+ */
+ if (!((struct gpInput *)msg)->gpi_IEvent)
+ {
+ result = GMR_MEACTIVE;
+ break;
+ }
+
+ /* Note: The input event that triggered the gadget
+ * activation (usually a mouse click) should be passed
+ * to the GM_HANDLEINPUT method, so we fall down to it.
+ */
+
+ case GM_HANDLEINPUT:
+ {
+ struct InputEvent *ie = ((struct gpInput *)msg)->gpi_IEvent;
+ UWORD topline, lefttrack;
+ WORD MouseX, MouseY; /* Mouse coordinates relative to editing area bounds */
+ BOOL moved = FALSE,
+ change_note = FALSE,
+ scroll_pattern = FALSE;
+ UWORD scrolled = 0;
+
+ result = GMR_MEACTIVE;
+ ped = INST_DATA (cl, g);
+
+ /* MouseX and MouseY represent the mouse position inside the
+ * editing area of the gadget.
+ */
+ MouseX = ((struct gpInput *)msg)->gpi_Mouse.X + ped->GBounds.Left - ped->TBounds.Left;
+ MouseY = ((struct gpInput *)msg)->gpi_Mouse.Y + ped->GBounds.Top - ped->TBounds.Top;
+
+ switch (ie->ie_Class)
+ {
+ case IECLASS_TIMER:
+
+ /* Timer events are used to keep scrolling
+ * when the mouse is outside the bounds of the
+ * gadget. When a timer event is recevied and
+ * the mouse button is pressed, we scroll the
+ * cursor.
+ */
+ if (ped->Flags & PEF_DRAGGING)
+ moved = MoveCursor (ped, MouseX, MouseY);
+
+ break;
+
+ case IECLASS_RAWMOUSE:
+ {
+ BOOL double_click = FALSE, triple_click;
+ BOOL outside =
+ (MouseX < 0) || (MouseX >= ped->TBounds.Width) ||
+ (MouseY < 0) || (MouseY >= ped->TBounds.Height);
+
+ switch (ie->ie_Code)
+ {
+ case MENUDOWN:
+
+ /* Abort cursor dragging operation and restore
+ * old cursor position.
+ */
+ if (ped->Flags & PEF_DRAGGING)
+ {
+ ped->Line = ped->BackupLine;
+ ped->Track = ped->BackupTrack;
+ ped->Column = ped->BackupColumn;
+ moved = TRUE;
+ ped->Flags &= ~(PEF_DRAGGING | PEF_SCROLLING);
+ }
+ else if (ped->Flags & PEF_RESIZETRACKS)
+ {
+ if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
+ {
+ SetDrMd (rp, COMPLEMENT);
+ DrawTrackSeparators (rp, ped, ped->ResizeChars * ped->FontXSize);
+ ReleaseGIRPort (rp);
+ }
+ ped->Flags &= ~PEF_RESIZETRACKS;
+ }
+ else result = GMR_REUSE;
+
+ break;
+
+ case SELECTUP:
+ /* Send final update to sliders */
+ if (ped->Flags & PEF_SCROLLING)
+ scrolled = SCROLLED_VERT | SCROLLED_HORIZ;
+
+ if (ped->Flags & PEF_RESIZETRACKS)
+ {
+ ped->TrackChars = ped->ResizeChars;
+ ped->TrackWidth = ped->TrackChars * ped->FontXSize;
+ CalcDisplaySize(ped, g, ((struct gpInput *)msg)->gpi_GInfo);
+ if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
+ {
+ //SetDrMd (rp, COMPLEMENT);
+ //DrawTrackSeparators (rp, ped, ped->ResizeChars * ped->FontXSize);
+
+ DoMethod ((Object *)g, GM_RENDER, ((struct gpInput *)msg)->gpi_GInfo, rp,
+ GREDRAW_REDRAW);
+
+ ReleaseGIRPort (rp);
+ }
+ }
+
+ /* Stop scrolling, dragging, resizing tracks and
+ * adjustisting the scroll box.
+ */
+ ped->Flags &= ~(PEF_DRAGGING | PEF_SCROLLING |
+ PEF_RESIZETRACKS | PEF_ADJUSTSCROLLBOX);
+
+ break;
+
+ case SELECTDOWN:
+
+ /* Check if mouse click is still over the gadget */
+
+ if (outside)
+ {
+ /* Click outside editing area box:
+ * Check if click is really outside gadget box.
+ * If it is, deactivate the gadget and reuse the event.
+ * Notify application if it is interested in
+ * hearing IDCMP_GADGETUP codes.
+ */
+ MouseX = ((struct gpInput *)msg)->gpi_Mouse.X;
+ MouseY = ((struct gpInput *)msg)->gpi_Mouse.Y;
+
+ if ((MouseX < 0) || (MouseX >= ped->GBounds.Width) ||
+ (MouseY < 0) || (MouseY >= ped->GBounds.Height))
+ result = GMR_REUSE | GMR_VERIFY;
+ break;
+ }
+ else
+ {
+ if (MouseX % ped->TrackWidth >= ped->TrackWidth - 2)
+ {
+ /* Start track resizing mode */
+ ped->Flags |= PEF_RESIZETRACKS;
+ ped->ResizeXStart = MouseX;
+ ped->ResizeChars = 0;
+ }
+ else
+ {
+ /* Backup cursor position for undo feature */
+ ped->BackupLine = ped->Line;
+ ped->BackupTrack = ped->Track;
+ ped->BackupColumn = ped->Column;
+
+ /* Start cursor drag mode */
+ ped->Flags |= PEF_DRAGGING;
+ }
+ }
+
+ /* Check for double click */
+
+ if (DoubleClick (ped->DoubleClickSeconds, ped->DoubleClickMicros,
+ ie->ie_TimeStamp.tv_secs, ie->ie_TimeStamp.tv_micro))
+ {
+ double_click = TRUE;
+
+ if (DoubleClick (ped->TripleClickSeconds, ped->TripleClickMicros,
+ ped->DoubleClickSeconds, ped->DoubleClickMicros))
+ triple_click = TRUE;
+ else
+ triple_click = FALSE;
+
+ ped->TripleClickSeconds = ped->DoubleClickSeconds;
+ ped->TripleClickMicros = ped->DoubleClickMicros;
+ }
+
+ ped->DoubleClickSeconds = ie->ie_TimeStamp.tv_secs;
+ ped->DoubleClickMicros = ie->ie_TimeStamp.tv_micro;
+
+ /* NOTE: I'm falling through here! */
+
+ default:
+
+ if ((ped->Flags & PEF_DRAGGING))
+ {
+ if (outside) ped->Flags |= PEF_SCROLLING;
+ else
+ {
+ if (ped->Flags & PEF_SCROLLING)
+ {
+ /* Last update to sync sliders */
+ scrolled = SCROLLED_VERT | SCROLLED_HORIZ;
+ ped->Flags &= ~PEF_SCROLLING;
+ }
+ moved = MoveCursor (ped, MouseX, MouseY);
+ }
+
+ /* if ((outside) && (ped->Flags & PEF_DRAGGING) && !(ped->Flags & PEF_SCROLLING))
+ {
+ ped->Flags |= PEF_SCROLLING;
+
+ ped->TimerIO.tr_node.io_Command = TR_ADDREQUEST;
+ ped->TimerIO.tr_time.tv_micro = 100000;
+ BeginIO ((struct IORequest *)&ped->TimerIO);
+ } */
+ }
+ else if (ped->Flags & PEF_RESIZETRACKS)
+ {
+ UWORD chars = ped->TrackChars +
+ (((MouseX - ped->ResizeXStart) / ped->FontXSize)
+ / ((ped->ResizeXStart / ped->TrackWidth) + 1));
+
+ if (chars < MINTRACKCHARS) chars = MINTRACKCHARS;
+ if (chars > MAXTRACKCHARS) chars = MAXTRACKCHARS;
+
+ if (chars != ped->ResizeChars)
+ {
+ if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
+ {
+ SetDrMd (rp, COMPLEMENT);
+
+ /* Erase old lines */
+ DrawTrackSeparators (rp, ped, ped->ResizeChars * ped->FontXSize);
+
+ /* Redraw lines */
+ DrawTrackSeparators (rp, ped, chars * ped->FontXSize);
+ ped->ResizeChars = chars;
+
+ ReleaseGIRPort (rp);
+ }
+ }
+ }
+
+ if (!moved && double_click)
+ {
+ if (ped->Flags & PEF_MARKING)
+ {
+ if (triple_click && !(ped->Flags & PEF_MARKFULLTRACKS)
+ && (ped->RangeStartLine == ped->RangeEndLine))
+ {
+ /* Start full track marking mode */
+ ped->Flags |= PEF_MARKFULLTRACKS;
+ ped->RangeStartLine = 0;
+ ped->RangeEndLine = ped->Patt->Lines - 1;
+ moved = TRUE;
+ }
+ else
+ /* Stop marking mode */
+ ped->Flags &= ~(PEF_MARKING | PEF_MARKFULLTRACKS);
+ }
+ else
+ {
+ /* Start normal marking mode */
+
+ ped->Flags |= PEF_MARKING;
+ ped->RangeStartLine = ped->Line;
+ ped->RangeStartTrack = ped->Track;
+ moved = TRUE;
+ }
+
+ if (!(ped->Flags & PEF_MARKING))
+ {
+ if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
+ {
+ ClearRange (rp, ped);
+ ReleaseGIRPort (rp);
+ }
+ moved = TRUE;
+ }
+ }
+
+ break;
+
+ } /* End switch (ie->ie_Code) */
+
+ break;
+ }
+
+ case IECLASS_RAWKEY:
+
+ if (ie->ie_Code & IECODE_UP_PREFIX)
+ {
+ /* Send final update to slider */
+
+ if ((ie->ie_Code == (IECODE_UP_PREFIX | CURSORUP))
+ || (ie->ie_Code == (IECODE_UP_PREFIX | CURSORDOWN)))
+ scrolled = SCROLLED_VERT;
+
+ else if ((ie->ie_Code == (IECODE_UP_PREFIX | CURSORLEFT))
+ || (ie->ie_Code == (IECODE_UP_PREFIX | CURSORRIGHT)))
+ scrolled = SCROLLED_HORIZ;
+ }
+ else if ((ie->ie_Qualifier & IEQUALIFIER_COMMAND) && (ie->ie_Code != 0x67))
+ result = GMR_REUSE;
+ else switch (ie->ie_Code)
+ {
+ case CURSORUP:
+
+ if (ped->Line)
+ {
+ if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
+ {
+ if (ped->Line > ped->TopLine)
+ ped->Line = ped->TopLine;
+ else
+ if (ped->Line >= ped->DisplayLines - 1)
+ ped->Line -= ped->DisplayLines - 1;
+ else ped->Line = 0;
+ }
+ else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
+ ped->Line = 0;
+ else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
+ {
+ if (ped->TopLine + ped->DisplayLines < ped->Patt->Lines)
+ {
+ topline = ped->TopLine + 1;
+ lefttrack = ped->LeftTrack;
+ scroll_pattern = TRUE;
+ if (ped->Line < topline)
+ ped->Line++;
+ }
+ }
+ else ped->Line--; /* Cursor key without qualifiers */
+
+ moved = TRUE;
+ }
+ else if (ped->Flags & PEF_VWRAP)
+ {
+ ped->Line = ped->Patt->Lines - 1;
+ moved = TRUE;
+ }
+
+ break;
+
+
+ case CURSORDOWN:
+
+ if (ped->Line < ped->Patt->Lines - 1)
+ {
+ if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
+ {
+ if (ped->Line < ped->TopLine + ped->DisplayLines - 1)
+ ped->Line = ped->TopLine + ped->DisplayLines - 1;
+ else
+ {
+ ped->Line += ped->DisplayLines - 1;
+ if (ped->Line > ped->Patt->Lines - 1)
+ ped->Line = ped->Patt->Lines - 1;
+ }
+ }
+ else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
+ ped->Line = ped->Patt->Lines - 1;
+ else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
+ {
+ if (ped->TopLine > 1)
+ {
+ topline = ped->TopLine - 1;
+ lefttrack = ped->LeftTrack;
+ scroll_pattern = TRUE;
+ if (ped->Line >= (topline + ped->DisplayLines))
+ ped->Line--;
+ }
+ }
+ else ped->Line++; /* Cursor key without qualifiers */
+
+ moved = TRUE;
+ }
+ else if (ped->Flags & PEF_VWRAP)
+ {
+ ped->Line = 0;
+ moved = TRUE;
+ }
+
+ break;
+
+
+ case CURSORLEFT:
+
+ if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
+ {
+ if (ped->Track)
+ {
+ ped->Track--;
+ moved = TRUE;
+ }
+ else if (ped->Column)
+ {
+ ped->Column = 0;
+ moved = TRUE;
+ }
+ }
+ else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
+ {
+ if (ped->Track)
+ {
+ ped->Track = 0;
+ moved = TRUE;
+ }
+ else if (ped->Column)
+ {
+ ped->Column = 0;
+ moved = TRUE;
+ }
+ }
+ else
+ {
+ if (ped->Column)
+ {
+ ped->Column--;
+ moved = TRUE;
+ }
+ else if (ped->Track)
+ {
+ ped->Track--;
+ ped->Column = COL_COUNT-1;
+ moved = TRUE;
+ }
+ }
+
+ if (!moved && (ped->Flags & PEF_HWRAP))
+ {
+ ped->Track = ped->Patt->Tracks - 1;
+ ped->Column = COL_COUNT-1;
+ moved = TRUE;
+ }
+
+ break;
+
+
+ case CURSORRIGHT:
+
+ if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
+ {
+ if (ped->Track < ped->Patt->Tracks - 1)
+ {
+ ped->Track++;
+ moved = TRUE;
+ }
+ else if (ped->Column != COL_COUNT-1)
+ {
+ ped->Column = COL_COUNT-1;
+ moved = TRUE;
+ }
+ }
+ else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
+ {
+ if (ped->Track != ped->Patt->Tracks - 1)
+ {
+ ped->Track = ped->Patt->Tracks - 1;
+ moved = TRUE;
+ }
+ else if (ped->Column != COL_COUNT-1)
+ {
+ ped->Column = COL_COUNT-1;
+ moved = TRUE;
+ }
+ }
+ else
+ {
+ if (ped->Column < COL_COUNT-1)
+ {
+ ped->Column++;
+ moved = TRUE;
+ }
+ else if (ped->Track < ped->Patt->Tracks - 1)
+ {
+ ped->Track++;
+ ped->Column = 0;
+ moved = TRUE;
+ }
+ }
+
+ if (!moved && (ped->Flags & PEF_HWRAP))
+ {
+ ped->Track = 0;
+ ped->Column = 0;
+ moved = TRUE;
+ }
+
+ break;
+
+
+ case 0x00: /* ESC */
+ case 0x5F: /* HELP */
+ result = GMR_REUSE;
+ break;
+
+
+ case 0x42: /* TAB */
+
+ if (ie->ie_Qualifier & IEQUALIFIER_ALT)
+ /* Deactivate gadget on ALT+TAB to allow
+ * window cycling in the application.
+ */
+ result = GMR_REUSE;
+ else
+ {
+ if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
+ {
+ if (ped->Track > 0)
+ ped->Track--;
+ else
+ ped->Track = ped->Patt->Tracks - 1;
+ }
+ else
+ {
+ if (ped->Track < ped->Patt->Tracks - 1)
+ ped->Track++;
+ else
+ ped->Track = 0;
+ }
+
+ ped->Column = COL_NOTE;
+ moved = TRUE;
+ }
+
+ break;
+
+
+ case 0x0D: /* RETURN */
+ ped->Column = COL_NOTE;
+ if (ped->Line < ped->Patt->Lines - 1)
+ ped->Line++;
+ else if (ped->Flags & PEF_VWRAP)
+ ped->Line = 0;
+ moved = TRUE;
+ break;
+
+
+ case 0x46: /* DEL */
+ {
+ struct Note *note = &ped->Patt->Notes[ped->Track][ped->Line];
+
+ SaveUndo (ped);
+ change_note = TRUE;
+
+ if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
+ memset (note, 0, sizeof (struct Note));
+ else switch (ped->Column)
+ {
+ case COL_NOTE:
+ note->Note = 0;
+ note->Inst = 0;
+ break;
+
+ case COL_INSTH:
+ note->Inst &= 0x0F;
+ break;
+
+ case COL_INSTL:
+ note->Inst &= 0xF0;
+ break;
+
+ case COL_EFF:
+ note->EffNum = EFF_NULL;
+ break;
+
+ case COL_VALH:
+ note->EffVal &= 0x0F;
+ break;
+
+ case COL_VALL:
+ note->EffVal &= 0xF0;
+ break;
+ }
+ break;
+ }
+
+ default:
+ {
+ struct Note *note = &ped->Patt->Notes[ped->Track][ped->Line];
+ UBYTE tmp = 0, keycode = 1;
+
+ /* Convert to hex number */
+
+ if (ped->Column != COL_NOTE)
+ {
+ if (MapRawKey (ie, &keycode, 1, NULL) == -1)
+ keycode = 0;
+ else
+ {
+ if (keycode >= '0' && keycode <= '9')
+ tmp = keycode - '0';
+ else if (keycode >= 'a' && keycode <= ((ped->Column == COL_EFF) ? 'j' : 'f'))
+ tmp = keycode - ('a' - 10);
+ else
+ keycode = 0;
+ }
+ }
+
+ if (keycode) switch (ped->Column)
+ {
+ case COL_NOTE:
+
+ /* Insert note */
+
+ if (ie->ie_Code >= 0x1 && ie->ie_Code <= 0x0A)
+ tmp = KeyNotes0[ie->ie_Code - 0x1];
+ else if (ie->ie_Code >= 0x10 && ie->ie_Code <= 0x19)
+ tmp = KeyNotes1[ie->ie_Code - 0x10];
+ else if (ie->ie_Code >= 0x20 && ie->ie_Code <= 0x29)
+ tmp = KeyNotes2[ie->ie_Code - 0x20];
+ else if (ie->ie_Code >= 0x31 && ie->ie_Code <= 0x39)
+ tmp = KeyNotes3[ie->ie_Code - 0x31];
+
+ if (tmp)
+ {
+ SaveUndo (ped);
+ change_note = TRUE;
+ note->Note = tmp;
+ note->Inst = ped->CurrentInst;
+ }
+ break;
+
+ case COL_INSTL:
+ SaveUndo (ped);
+ change_note = TRUE;
+ note->Inst = (note->Inst & 0xF0) | tmp;
+ break;
+
+ case COL_INSTH:
+ if (tmp < MAXINSTRUMENTS>>4)
+ {
+ SaveUndo (ped);
+ change_note = TRUE;
+ note->Inst = (note->Inst & 0x0F) | (tmp<<4);
+ }
+ break;
+
+ case COL_EFF:
+ SaveUndo (ped);
+ change_note = TRUE;
+ note->EffNum = tmp;
+ break;
+
+ case COL_VALL:
+ SaveUndo (ped);
+ change_note = TRUE;
+ note->EffVal = (note->EffVal & 0xF0) | tmp;
+ break;
+
+ case COL_VALH:
+ SaveUndo (ped);
+ change_note = TRUE;
+ note->EffVal = (note->EffVal & 0x0F) | (tmp<<4);
+ break;
+ }
+ break;
+ }
+
+ } /* End switch (ie->ie_Code) */
+
+ break;
+
+ default:
+ break;
+
+ } /* End switch (ie->ie_Class) */
+
+ if (moved || change_note || scroll_pattern)
+ {
+ if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
+ {
+ if (scroll_pattern)
+ scrolled |= ScrollPattern (rp, ped, g, lefttrack, topline);
+
+ if (change_note)
+ {
+ EraseCursor (rp, ped);
+ DrawNote (rp, ped);
+
+ /* Advance cursor */
+
+ ped->Track += ped->AdvanceTracks;
+ ped->Line += ped->AdvanceLines;
+
+ if (ped->Flags & PEF_HWRAP)
+ ped->Track = ped->Track % ped->Patt->Tracks;
+ else
+ {
+ if (ped->Track < 0)
+ ped->Track = 0;
+ else if (ped->Track > ped->Patt->Tracks - 1)
+ ped->Track = ped->Patt->Tracks - 1;
+ }
+
+ if (ped->Flags & PEF_VWRAP)
+ ped->Line = ped->Line % ped->Patt->Lines;
+ else
+ {
+ if (ped->Line < 0)
+ ped->Line = 0;
+ else if (ped->Line > ped->Patt->Lines - 1)
+ ped->Line = ped->Patt->Lines - 1;
+ }
+ }
+
+ scrolled |= DrawCursor (rp, ped, g);
+ ReleaseGIRPort (rp);
+ }
+
+ /* Broadcast notification to our target object. */
+ NotifyCursor (ped, g, ((struct gpInput *)msg)->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
+ }
+
+ if (scrolled & SCROLLED_VERT)
+ NotifyVSlider (ped, g, ((struct gpInput *)msg)->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
+
+ if (scrolled & SCROLLED_HORIZ)
+ NotifyHSlider (ped, g, ((struct gpInput *)msg)->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
+
+ break;
+ }
+
+
+ case GM_RENDER:
+
+ ped = INST_DATA (cl, g);
+
+ /* We do not support GREDRAW_TOGGLE */
+
+ switch (((struct gpRender *)msg)->gpr_Redraw)
+ {
+ case GREDRAW_REDRAW:
+#ifndef OS30_ONLY
+ /* Recalculate the display size only on V37.
+ * As of V39, Intuition supports GM_LAYOUT, which
+ * allows a more optimized way to handle dynamic resizing.
+ */
+ if (IntuitionBase->LibNode.lib_Version < 39)
+ if (CalcDisplaySize (ped, g, ((struct gpRender *)msg)->gpr_GInfo))
+ NotifySliders (ped, g, ((struct gpRender *)msg)->gpr_GInfo, 0);
+#endif /* OS30_ONLY */
+
+ RedrawAll (((struct gpRender *)msg)->gpr_RPort, ped, g);
+ break;
+
+ case GREDRAW_UPDATE:
+ /* Just redraw the notes, not the lines, etc. */
+ RedrawPattern (((struct gpRender *)msg)->gpr_RPort, ped, g);
+ break;
+ }
+
+ break;
+
+
+ case GM_HITTEST:
+
+ /* As we are rectangular shaped, we are always hit */
+ result = GMR_GADGETHIT;
+ break;
+
+ case GM_HELPTEST:
+ result = GMR_HELPHIT;
+ break;
+
+ case GM_GOINACTIVE:
+
+ ped = INST_DATA (cl, g);
+
+ g->Flags &= ~GFLG_SELECTED;
+
+ if (ped->Patt)
+ /* Render disabled cursor */
+ if (rp = ObtainGIRPort (((struct gpGoInactive *)msg)->gpgi_GInfo))
+ {
+ DrawCursor (rp, ped, g);
+ ReleaseGIRPort (rp);
+ }
+ break;
+
+
+ case GM_LAYOUT:
+
+ ped = INST_DATA (cl, g);
+
+ if (CalcDisplaySize (ped, g, ((struct gpLayout *)msg)->gpl_GInfo))
+ NotifySliders (ped, g, ((struct gpLayout *)msg)->gpl_GInfo, 0);
+
+ break;
+
+
+ case OM_UPDATE:
+ case OM_SET:
+ {
+ struct TagItem *tstate = ((struct opSet *)msg)->ops_AttrList;
+ BOOL redraw_all = FALSE,
+ redraw_pattern = FALSE,
+ move_cursor = FALSE,
+ scroll_pattern = FALSE,
+ change_note = FALSE,
+ do_super_method = FALSE;
+ WORD lefttrack, topline;
+ WORD line, track, column;
+
+ ped = INST_DATA (cl, g);
+
+ lefttrack = ped->LeftTrack;
+ topline = ped->TopLine;
+ line = ped->Line;
+ track = ped->Track;
+ column = ped->Column;
+
+
+ while (ti = NextTagItem(&tstate))
+ {
+ switch (ti->ti_Tag)
+ {
+ case PEA_CursTrack:
+ track = ti->ti_Data;
+ break;
+
+ case PEA_CursColumn:
+ column = ti->ti_Data;
+ break;
+
+ case PEA_CursLine:
+ line = ti->ti_Data;
+ break;
+
+ case PEA_LeftTrack:
+ if (lefttrack != ti->ti_Data)
+ {
+ lefttrack = ti->ti_Data;
+ track = lefttrack + (ped->DisplayTracks / 2);
+ scroll_pattern = TRUE;
+ }
+ break;
+
+ case PEA_TopLine:
+ if (topline != ti->ti_Data)
+ {
+ topline = ti->ti_Data;
+ line = topline + (ped->DisplayLines / 2);
+ scroll_pattern = TRUE;
+ }
+ break;
+
+ case PEA_Left:
+ if (lefttrack)
+ track = lefttrack - 1;
+ break;
+
+ case PEA_Right:
+ if (ped->Patt && (lefttrack + ped->DisplayTracks < ped->Patt->Tracks))
+ track = lefttrack + ped->DisplayTracks;
+ break;
+
+ case PEA_Up:
+ if (topline)
+ line = topline - 1;
+ break;
+
+ case PEA_Down:
+ if (ped->Patt && (topline + ped->DisplayLines < ped->Patt->Lines))
+ line = topline + ped->DisplayLines;
+ break;
+
+ case PEA_CursLeft:
+ if (track)
+ track--;
+ else if (ped->Flags & PEF_VWRAP)
+ track = ped->Patt->Tracks - 1;
+
+ break;
+
+ case PEA_CursRight:
+ if (ped->Patt && (track < ped->Patt->Tracks - 1))
+ track++;
+ else if (ped->Flags & PEF_HWRAP)
+ track = 0;
+
+ break;
+
+ case PEA_CursUp:
+ if (line)
+ line--;
+ else if (ped->Flags & PEF_VWRAP)
+ line = ped->Patt->Lines - 1;
+
+ break;
+
+ case PEA_CursDown:
+ if (ped->Patt && line < ped->Patt->Lines - 1)
+ line++;
+ else if (ped->Flags & PEF_VWRAP)
+ line = 0;
+
+ break;
+
+ case PEA_UndoChange:
+ if (((LONG)ti->ti_Data) < 0)
+ change_note |= RedoChange (ped);
+ else
+ change_note |= UndoChange (ped);
+ line = ped->Line;
+ track = ped->Track;
+
+ break;
+
+ case PEA_Changes:
+ ped->Changes = ti->ti_Data;
+ break;
+
+ case PEA_MarkRegion:
+ {
+ struct Rectangle *region = (struct Rectangle *)ti->ti_Data;
+
+ if (!region) /* End mark mode */
+
+ ped->Flags &= ~(PEF_MARKING | PEF_MARKFULLTRACKS);
+
+ else if (region == (struct Rectangle *)-1) /* Toggle mark mode */
+ {
+ if (ped->Flags & PEF_MARKFULLTRACKS)
+ ped->Flags &= ~(PEF_MARKING | PEF_MARKFULLTRACKS);
+ else if (ped->Flags & PEF_MARKING)
+ {
+ if (ped->RangeStartLine == ped->RangeEndLine)
+ {
+ ped->Flags |= PEF_MARKFULLTRACKS;
+ ped->RangeStartLine = 0;
+ ped->RangeEndLine = ped->Patt->Lines - 1;
+ }
+ else
+ ped->Flags &= ~PEF_MARKING;
+ }
+ else
+ {
+ ped->Flags |= PEF_MARKING;
+ ped->RangeStartTrack = track;
+ ped->RangeStartLine = line;
+ }
+ }
+ else /* Start mark mode */
+ {
+ memcpy (&ped->RangeStartTrack, region, sizeof (struct Rectangle));
+ track = ped->Track = region->MaxX;
+ line = ped->Line = region->MaxY;
+ ped->Flags |= PEF_MARKING;
+ }
+
+ if (rp = ObtainGIRPort (((struct opSet *)msg)->ops_GInfo))
+ {
+ if (!(ped->Flags & PEF_MARKING))
+ ClearRange (rp, ped);
+
+ DrawCursor (rp, ped, g);
+
+ ReleaseGIRPort (rp);
+ }
+
+ break;
+ }
+
+ case PEA_Flags:
+ {
+ ULONG oldflags = ped->Flags;
+ ped->Flags = (ped->Flags & 0xFFFF0000) | ti->ti_Data;
+
+ if ((oldflags & (PEF_HEXMODE | PEF_BLANKZERO | PEF_INVERSETEXT | PEF_DOTINYLINES)) !=
+ (ped->Flags & (PEF_HEXMODE | PEF_BLANKZERO | PEF_INVERSETEXT | PEF_DOTINYLINES)))
+ {
+ /* Select Note2ASCII func */
+ if (ped->Flags & PEF_BLANKZERO)
+ ped->Note2ASCIIFunc = Note2ASCIIBlank0;
+ else
+ ped->Note2ASCIIFunc = Note2ASCII;
+
+ if (!(ped->Flags & PEF_DOCURSORRULER))
+ ped->CursLinePos = 0;
+
+ /* Cause complete radraw */
+ redraw_all = TRUE;
+ }
+
+ if ((oldflags & PEF_DOCURSORRULER) !=
+ (ped->Flags & PEF_DOCURSORRULER))
+ move_cursor = TRUE;
+
+ break;
+ }
+
+ case PEA_Pattern:
+ ped->Patt = (struct Pattern *) ti->ti_Data;
+ redraw_pattern = TRUE;
+ FreeUndoBuffers (ped, TRUE);
+
+ if (ped->Patt == NULL)
+ {
+ static LONG tags[] = { GA_Disabled, TRUE, TAG_DONE };
+
+ lefttrack = topline = ped->DisplayTracks = ped->DisplayLines = 0;
+ redraw_all = TRUE;
+
+ DoMethod ((Object *)g, OM_UPDATE, tags, ((struct opUpdate *)msg)->opu_GInfo, 0);
+ }
+ else
+ {
+ /* Recalculate pattern dimensions */
+ if (CalcDisplaySize (ped, g, ((struct opSet *)msg)->ops_GInfo))
+ {
+ NotifySliders (ped, g, ((struct opSet *)msg)->ops_GInfo, 0);
+ redraw_all = TRUE;
+ }
+
+ /* Force cursor inside pattern */
+
+ if (line >= ped->Patt->Lines)
+ {
+ line = ped->Patt->Lines - 1;
+ move_cursor = TRUE;
+ }
+
+ if (track >= ped->Patt->Tracks)
+ {
+ track = ped->Patt->Tracks - 1;
+ move_cursor = TRUE;
+ }
+
+ if (lefttrack + ped->DisplayTracks > ped->Patt->Tracks)
+ {
+ lefttrack = ped->Patt->Tracks - ped->DisplayTracks;
+ scroll_pattern = TRUE;
+ }
+
+ if (topline + ped->DisplayLines > ped->Patt->Lines)
+ {
+ topline = ped->Patt->Lines - ped->DisplayLines;
+ scroll_pattern = TRUE;
+ }
+
+ if (g->Flags & GFLG_DISABLED)
+ {
+ static ULONG tags[] = { GA_Disabled, FALSE, TAG_DONE };
+ DoMethod ((Object *)g, OM_UPDATE, tags, ((struct opUpdate *)msg)->opu_GInfo, 0);
+ }
+ }
+ break;
+
+ case PEA_CurrentInst:
+ ped->CurrentInst = ti->ti_Data;
+ break;
+
+ case PEA_MaxUndoLevels:
+ ped->MaxUndoLevels = ti->ti_Data;
+ FreeUndoBuffers (ped, FALSE);
+ break;
+
+ case PEA_MaxUndoMem:
+ ped->MaxUndoMem = ti->ti_Data;
+
+ /* Unlimited undo memory */
+ if (ped->MaxUndoMem == 0) ped->MaxUndoMem = ~0;
+
+ FreeUndoBuffers (ped, FALSE);
+
+ break;
+
+ case PEA_AdvanceCurs:
+ ped->AdvanceLines = ti->ti_Data & 0xFFFF;
+ ped->AdvanceTracks = ti->ti_Data >> 16;
+ break;
+
+ case PEA_CursWrap:
+ ped->Flags = (ped->Flags & (PEF_HWRAP | PEF_VWRAP)) | (ti->ti_Data & (PEF_HWRAP | PEF_VWRAP));
+ break;
+
+ case PEA_BGPen:
+ if (ped->BGPen != ti->ti_Data)
+ {
+ ped->BGPen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ case PEA_TextPen:
+ if (ped->TextPen != ti->ti_Data)
+ {
+ ped->TextPen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ case PEA_LinesPen:
+ if (ped->LinesPen != ti->ti_Data)
+ {
+ ped->LinesPen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ case PEA_TinyLinesPen:
+ if (ped->TinyLinesPen != ti->ti_Data)
+ {
+ ped->TinyLinesPen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ default:
+ /* This little optimization avoids forwarding the
+ * OM_SET method to our superclass then there are
+ * no unknown tags.
+ */
+ do_super_method = TRUE;
+ break;
+ }
+
+ } /* End while (NextTagItem()) */
+
+
+ if (!move_cursor)
+ move_cursor = ((line != ped->Line) ||
+ (track != ped->Track) ||
+ (column != ped->Column));
+
+ if (do_super_method)
+ result = DoSuperMethodA (cl, (Object *)g, msg);
+ else
+ result = TRUE;
+
+ if (redraw_all || redraw_pattern || scroll_pattern || move_cursor || change_note)
+ {
+ WORD scrolled = 0;
+
+ if (rp = ObtainGIRPort (((struct opSet *)msg)->ops_GInfo))
+ {
+ if (redraw_all || redraw_pattern)
+ {
+ ped->LeftTrack = lefttrack;
+ ped->TopLine = topline;
+ ped->Line = line;
+ ped->Track = track;
+ ped->Column = column;
+
+ DoMethod ((Object *)g, GM_RENDER, ((struct opSet *)msg)->ops_GInfo, rp,
+ (redraw_all ? GREDRAW_REDRAW : GREDRAW_UPDATE));
+ scrolled = SCROLLED_VERT | SCROLLED_HORIZ;
+ }
+ else if (scroll_pattern)
+ {
+ ped->Line = line;
+ ped->Track = track;
+ scrolled = ScrollPattern (rp, ped, g, lefttrack, topline);
+ }
+ else if (move_cursor || change_note)
+ {
+ ped->Line = line;
+ ped->Track = track;
+ ped->Column = column;
+
+ if (change_note)
+ {
+ EraseCursor (rp, ped);
+ DrawNote (rp, ped);
+ }
+
+ scrolled |= DrawCursor (rp, ped, g);
+ }
+
+ ReleaseGIRPort (rp);
+ }
+
+ /* TODO: avoid sending back updates to the sliders */
+
+ if (scrolled & SCROLLED_VERT)
+ NotifyVSlider (ped, g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
+ if (scrolled & SCROLLED_HORIZ)
+ NotifyHSlider (ped, g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
+
+ if (scrolled || move_cursor)
+ NotifyCursor (ped, g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
+ }
+
+ break;
+ }
+
+
+ case OM_GET:
+
+ ped = INST_DATA (cl, g);
+ result = TRUE;
+
+ switch (((struct opGet *) msg)->opg_AttrID)
+ {
+ case PEA_CursTrack:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Track;
+ break;
+
+ case PEA_CursColumn:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Column;
+ break;
+
+ case PEA_CursLine:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Line;
+ break;
+
+ case PEA_LeftTrack:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->LeftTrack;
+ break;
+
+ case PEA_TopLine:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->TopLine;
+ break;
+
+ case PEA_Changes:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Changes;
+ break;
+
+ case PEA_MarkRegion:
+ {
+ struct Rectangle *region = (struct Rectangle *) (((struct opGet *) msg)->opg_Storage);
+
+ region->MinX = min (ped->RangeStartTrack, ped->RangeEndTrack);
+ region->MaxX = max (ped->RangeStartTrack, ped->RangeEndTrack);
+ region->MinY = min (ped->RangeStartLine, ped->RangeEndLine);
+ region->MaxY = max (ped->RangeStartLine, ped->RangeEndLine);
+ break;
+ }
+
+ case PEA_Flags:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Flags;
+ break;
+
+ case PEA_DisplayTracks:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->DisplayTracks;
+ break;
+
+ case PEA_DisplayLines:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->DisplayLines;
+ break;
+
+ case PEA_Pattern:
+ *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Patt;
+ break;
+
+ default:
+ result = DoSuperMethodA (cl, (Object *)g, msg);
+ break;
+ }
+
+ break;
+
+
+ case OM_NEW:
+
+ if (result = DoSuperMethodA (cl, (Object *)g, msg))
+ {
+ ULONG tmp;
+
+ g = (struct ExtGadget *) result;
+
+ ped = INST_DATA (cl, g); /* Get pointer to instance data */
+ memset (ped, 0, sizeof (struct PattEditData));
+
+ /* We are going to use ScrollRaster() in this gadget... */
+
+ if (g->Flags & GFLG_EXTENDED)
+ g->MoreFlags |= GMORE_SCROLLRASTER;
+
+ g->Flags |= GFLG_TABCYCLE | GFLG_RELSPECIAL;
+
+ /* Initialize our lists */
+ NEWLIST ((struct List *)&ped->UndoList);
+ NEWLIST ((struct List *)&ped->RedoList);
+
+
+ /* Open the timer.device */
+/*
+ NEWLIST (&ped->TimerPort.mp_MsgList);
+ ped->TimerPort.mp_Flags = PA_SOFTINT;
+ ped->TimerPort.mp_SoftInt = &ped->TimerInt;
+ ped->TimerInt.is_Node.ln_Type = NT_INTERRUPT;
+ ped->TimerInt.is_Data = g;
+ ped->TimerInt.is_Code = (void (*)())TimerIntServer;
+ ped->TimerIO.tr_node.io_Message.mn_ReplyPort = &ped->TimerPort;
+
+ if (OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *)&ped->TimerIO, 0))
+ {
+ CoerceMethod (cl, g, OM_DISPOSE);
+ result = NULL;
+ break;
+ }
+*/
+
+ /* Initialize attributes */
+
+ ped->Patt = (struct Pattern *) GetTagData (PEA_Pattern, NULL, ((struct opSet *)msg)->ops_AttrList);
+ ped->CurrentInst = GetTagData (PEA_CurrentInst, NULL, ((struct opSet *)msg)->ops_AttrList);
+ ped->EditorFont = (struct TextFont *) GetTagData (PEA_TextFont, (ULONG)GfxBase->DefaultFont, ((struct opSet *)msg)->ops_AttrList);
+ ped->BGPen = GetTagData (PEA_BGPen, 0, ((struct opSet *)msg)->ops_AttrList);
+ ped->TextPen = GetTagData (PEA_TextPen, 1, ((struct opSet *)msg)->ops_AttrList);
+ ped->LinesPen = GetTagData (PEA_LinesPen, 2, ((struct opSet *)msg)->ops_AttrList);
+ ped->TinyLinesPen = GetTagData (PEA_TinyLinesPen, 2, ((struct opSet *)msg)->ops_AttrList);
+ ped->MaxUndoLevels = GetTagData (PEA_MaxUndoLevels, 32, ((struct opSet *)msg)->ops_AttrList);
+ ped->MaxUndoMem = GetTagData (PEA_MaxUndoMem, 8192, ((struct opSet *)msg)->ops_AttrList);
+ ped->Flags = GetTagData (PEA_Flags, 0, ((struct opSet *)msg)->ops_AttrList);
+ ped->Flags |= GetTagData (PEA_CursWrap, ped->Flags, ((struct opSet *)msg)->ops_AttrList) & (PEF_HWRAP | PEF_VWRAP);
+
+ tmp = GetTagData (PEA_AdvanceCurs, 1, ((struct opSet *)msg)->ops_AttrList);
+ ped->AdvanceTracks = tmp << 16;
+ ped->AdvanceLines = tmp & 0xFFFF;
+
+ ped->Line = GetTagData (PEA_CursLine, 0, ((struct opSet *)msg)->ops_AttrList);
+ ped->Track = GetTagData (PEA_CursTrack, 0, ((struct opSet *)msg)->ops_AttrList);
+ ped->Column = GetTagData (PEA_CursColumn, 0, ((struct opSet *)msg)->ops_AttrList);
+
+ ped->FontXSize = ped->EditorFont->tf_XSize;
+ ped->FontYSize = ped->EditorFont->tf_YSize;
+ ped->TrackChars = 10;
+ ped->TrackWidth = ped->FontXSize * ped->TrackChars;
+
+ /* Select Note2ASCII func */
+ if (ped->Flags & PEF_BLANKZERO)
+ ped->Note2ASCIIFunc = Note2ASCIIBlank0;
+ else
+ ped->Note2ASCIIFunc = Note2ASCII;
+
+ /* Unlimited undo memory */
+ if (ped->MaxUndoMem == 0) ped->MaxUndoMem = ~0;
+
+ if (ped->Patt == NULL)
+ {
+ static LONG tags[] = { GA_Disabled, TRUE, TAG_DONE };
+ DoMethod ((Object *)g, OM_UPDATE, tags, NULL, 0);
+ }
+ }
+ break;
+
+
+ case OM_DISPOSE:
+ {
+ ped = INST_DATA (cl, g);
+
+ FreeUndoBuffers (ped, TRUE);
+// CloseDevice ((struct IORequest *)&ped->TimerIO);
+
+ /* NOTE: I'm falling through here! */
+ }
+
+
+ default:
+
+ /* Unsupported method: let our superclass's
+ * dispatcher take a look at it.
+ */
+ result = DoSuperMethodA (cl, (Object *)g, msg);
+ break;
+ }
+
+ return (result);
+}
+
+
+
+static void SaveUndo (struct PattEditData *ped)
+{
+ struct UndoNode *undo;
+
+ ped->Changes++;
+
+ /* Is undo feature disabled ? */
+ if (!ped->MaxUndoLevels) return;
+
+ /* Empty redo list */
+ while (undo = (struct UndoNode *) REMHEAD((struct List *)&ped->RedoList))
+ FreeMem (undo, sizeof (struct UndoNode));
+
+ FreeUndoBuffers (ped, FALSE);
+
+ while (ped->UndoCount >= ped->MaxUndoLevels || ped->UndoMem >= ped->MaxUndoMem)
+ if (undo = (struct UndoNode *) REMTAIL ((struct List *)&ped->UndoList))
+ {
+ FreeMem (undo, sizeof (struct UndoNode));
+ ped->UndoCount--;
+ ped->UndoMem -= sizeof (struct UndoNode);
+ }
+
+ /* Allocate a new undo buffer and save current note */
+ if (undo = AllocMem (sizeof (struct UndoNode), MEMF_ANY))
+ {
+ undo->Track = ped->Track;
+ undo->Line = ped->Line;
+
+ memcpy (&undo->OldNote, &ped->Patt->Notes[ped->Track][ped->Line],
+ sizeof (struct Note));
+
+ ADDHEAD ((struct List *)&ped->UndoList, (struct Node *)undo);
+ ped->UndoCount++;
+ ped->UndoMem += sizeof (struct UndoNode);
+ }
+}
+
+
+
+static BOOL UndoChange (struct PattEditData *ped)
+{
+ struct UndoNode *undo;
+ struct Note *note;
+ struct Note tmp_note;
+
+ if (undo = (struct UndoNode *) REMHEAD ((struct List *) &ped->UndoList))
+ {
+ ped->UndoCount--;
+ ped->UndoMem -= sizeof (struct UndoNode);
+ ped->Changes--;
+
+ ped->Track = undo->Track;
+ ped->Line = undo->Line;
+
+ note = &ped->Patt->Notes[ped->Track][ped->Line];
+
+ /* Swap undo buffer with note */
+ memcpy (&tmp_note, &undo->OldNote, sizeof (struct Note));
+ memcpy (&undo->OldNote, note, sizeof (struct Note));
+ memcpy (note, &tmp_note, sizeof (struct Note));
+
+
+ /* Move this node to the redo buffer */
+ ADDHEAD ((struct List *)&ped->RedoList, (struct Node *)undo);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+static BOOL RedoChange (struct PattEditData *ped)
+{
+ struct UndoNode *undo;
+ struct Note *note;
+ struct Note tmp_note;
+
+ if (undo = (struct UndoNode *) REMHEAD ((struct List *) &ped->RedoList))
+ {
+ ped->Track = undo->Track;
+ ped->Line = undo->Line;
+
+ note = &ped->Patt->Notes[ped->Track][ped->Line];
+
+ /* Swap undo buffer and note */
+ memcpy (&tmp_note, &undo->OldNote, sizeof (struct Note));
+ memcpy (&undo->OldNote, note, sizeof (struct Note));
+ memcpy (note, &tmp_note, sizeof (struct Note));
+
+ /* Move this node to the undo buffer */
+ ADDHEAD ((struct List *)&ped->UndoList, (struct Node *)undo);
+
+ ped->UndoCount++;
+ ped->Changes++;
+ ped->UndoMem += sizeof (struct UndoNode);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+static void FreeUndoBuffers (struct PattEditData *ped, BOOL freeall)
+
+/* If <freeall> is TRUE, this routine will free all the undo buffers.
+ * Otherwhise, it will check for undo overflow and free enough nodes
+ * to keep the undo buffers inside the memory size and nodes count
+ * limits.
+ */
+{
+ struct UndoNode *undo;
+
+ if (!freeall)
+ {
+ while (ped->UndoCount >= ped->MaxUndoLevels || ped->UndoMem >= ped->MaxUndoMem)
+ if (undo = (struct UndoNode *) RemTail ((struct List *)&ped->UndoList))
+ {
+ FreeMem (undo, sizeof (struct UndoNode));
+ ped->UndoCount--;
+ ped->UndoMem -= sizeof (struct UndoNode);
+ }
+
+ return;
+ }
+
+ /* Free everything */
+
+ while (undo = (struct UndoNode *) REMHEAD ((struct List *)&ped->UndoList))
+ FreeMem (undo, sizeof (struct UndoNode));
+
+ while (undo = (struct UndoNode *) REMHEAD ((struct List *)&ped->RedoList))
+ FreeMem (undo, sizeof (struct UndoNode));
+
+ ped->UndoCount = 0; ped->UndoMem = 0;
+}
+
+
+
+static void NotifyCursor (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
+{
+ static LONG tags[] =
+ {
+ PEA_CursLine, 0,
+ PEA_CursTrack, 0,
+ PEA_CursColumn, 0,
+ GA_ID, 0,
+ TAG_DONE
+ };
+
+ /* Always send notification if the gadget has the GACT_IMMEDIATE
+ * flag set. If it isn't, the editor will report its cursor
+ * position only on last cursor update.
+ */
+ if ((g->Activation & GACT_IMMEDIATE) || !(flags & OPUF_INTERIM))
+ {
+ tags[1] = ped->Line;
+ tags[3] = ped->Track;
+ tags[5] = ped->Column;
+ tags[7] = g->GadgetID;
+
+ DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
+ }
+}
+
+
+
+static void NotifyVSlider (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
+{
+ static LONG tags[] =
+ {
+ PEA_TopLine, 0,
+// PEA_DisplayLines, 0,
+ GA_ID, 0,
+ TAG_DONE
+ };
+
+
+ /* Optimized slider update; only broadcast updates if one of
+ * these conditions is satisfied:
+ *
+ * - Final update (keyup or selectup),
+ * - SliderCounter reached,
+ * - Reached top/bottom of the pattern.
+ *
+ * In addition, notifications are not sent when the we receive OM_UPDATE messages
+ * from the sliders
+ */
+ if ((!(flags & OPUF_INTERIM)) || (ped->SliderCounter == 0) || (ped->TopLine == 0)
+ || (ped->TopLine + ped->DisplayLines >= ped->Patt->Lines))
+ {
+ ped->SliderCounter = 4;
+
+ tags[1] = ped->TopLine;
+// tags[3] = ped->DisplayLines;
+ tags[3] = g->GadgetID;
+
+ DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
+ }
+
+ ped->SliderCounter--;
+}
+
+
+
+static void NotifyHSlider (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
+{
+ static LONG tags[] =
+ {
+ PEA_LeftTrack, 0,
+// PEA_DisplayTracks, 0,
+ GA_ID, 0,
+ TAG_DONE
+ };
+
+
+ /* Optimized slider update; only send updates if one of
+ * these conditions is satisfied:
+ *
+ * - Final update (keyup or selectup),
+ * - SliderCounter reached,
+ * - Reached left/right of pattern.
+ */
+ if ((!(flags & OPUF_INTERIM)) || (ped->SliderCounter == 0) || (ped->LeftTrack == 0)
+ || (ped->LeftTrack + ped->DisplayTracks >= ped->Patt->Tracks))
+ {
+ ped->SliderCounter = 3;
+
+ tags[1] = ped->LeftTrack;
+// tags[3] = ped->DisplayTracks;
+ tags[3] = g->GadgetID;
+
+ DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
+ }
+
+ ped->SliderCounter--;
+}
+
+
+
+static void NotifySliders (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
+{
+ static LONG tags[] =
+ {
+ PEA_LeftTrack, 0,
+ PEA_DisplayTracks, 0,
+ PEA_TopLine, 0,
+ PEA_DisplayLines, 0,
+ GA_ID, 0,
+ TAG_DONE
+ };
+
+ tags[1] = ped->LeftTrack;
+ tags[3] = ped->DisplayTracks;
+ tags[5] = ped->TopLine;
+ tags[7] = ped->DisplayLines;
+ tags[9] = g->GadgetID;
+
+ DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
+}
+
+
+
+
+
+static void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect)
+
+/* This function gets the actual IBox where a gadget exists
+ * in a window. The special cases it handles are all the REL#?
+ * (relative positioning flags).
+ *
+ * The function takes a struct GadgetInfo pointer, a struct Gadget
+ * pointer, and a struct IBox pointer. It uses the window and
+ * gadget to fill in the IBox.
+ */
+{
+ rect->Left = g->LeftEdge;
+ if (g->Flags & GFLG_RELRIGHT) rect->Left += ginfo->gi_Domain.Width - 1;
+
+ rect->Top = g->TopEdge;
+ if (g->Flags & GFLG_RELBOTTOM) rect->Top += ginfo->gi_Domain.Height - 1;
+
+ rect->Width = g->Width;
+ if (g->Flags & GFLG_RELWIDTH) rect->Width += ginfo->gi_Domain.Width;
+
+ rect->Height = g->Height;
+ if (g->Flags & GFLG_RELHEIGHT) rect->Height += ginfo->gi_Domain.Height;
+}
+
+
+
+static BOOL CalcDisplaySize (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *ginfo)
+
+/* Calculate maximum number of tracks and lines that will fit in the gadget
+ * size. Returns TRUE if something has changed.
+ *
+ * GBounds are the bounds of the whole gadget and
+ * TBounds are the bounds of the portion where the cursor lives.
+ *
+ * +--Window--------------------+
+ * | |
+ * | +--GBounds----------------+|
+ * | | +--TBounds-----------+||
+ * | |001|C#2 1 000|B#2 2 C20 |||
+ * | |002|--- 0 000|A-2 2 C30 |||
+ * | |003|--- 0 000|C-2 2 C10 |||
+ * | |004|--- 0 000|C#2 3 000 |||
+ * | | +--------------------+||
+ * | +-------------------------+|
+ * +----------------------------+
+ */
+{
+ UWORD old_displaytracks = ped->DisplayTracks,
+ old_displaylines = ped->DisplayLines,
+ numcol_width = ped->FontXSize * 4;
+
+ GetGadgetBox (ginfo, g, &ped->GBounds);
+
+ /* Setup DisplayTracks and DisplayLines */
+
+ if (ped->Patt)
+ {
+ ped->DisplayTracks = min ((ped->GBounds.Width - numcol_width) / ped->TrackWidth, ped->Patt->Tracks);
+ ped->DisplayLines = min (ped->GBounds.Height / ped->FontYSize, ped->Patt->Lines);
+
+ if (ped->TopLine + ped->DisplayLines > ped->Patt->Lines)
+ ped->TopLine = max (0, ped->Patt->Lines - ped->DisplayLines);
+
+ if (ped->LeftTrack + ped->DisplayTracks > ped->Patt->Tracks)
+ ped->LeftTrack = max (0, ped->Patt->Tracks - ped->DisplayTracks);
+ }
+
+
+ /* Setup Text Bounds */
+
+ ped->TBounds.Top = ped->GBounds.Top;
+ ped->TBounds.Left = ped->GBounds.Left + numcol_width;
+ ped->TBounds.Width = ped->DisplayTracks * ped->TrackWidth;
+ ped->TBounds.Height = ped->DisplayLines * ped->FontYSize;
+
+ return ((BOOL)((old_displaytracks != ped->DisplayTracks) || (old_displaylines != ped->DisplayLines)));
+}
+
+
+
+static BOOL MoveCursor (struct PattEditData *ped, WORD x, WORD y)
+
+/* Moves the cursor to a given xy position. Checks whether the cursor has
+ * really moved and returns FALSE if there is no need to update its imagery.
+ */
+{
+ WORD tmp;
+ BOOL moved = FALSE, maxtrack = FALSE;
+
+
+ /* X Position (Track) */
+
+ if (x < 0)
+ tmp = ped->LeftTrack - 1;
+ else
+ tmp = (x / ped->TrackWidth) + ped->LeftTrack;
+
+ if (tmp < 0) tmp = 0;
+
+ if (tmp >= ped->Patt->Tracks)
+ {
+ tmp = ped->Patt->Tracks - 1;
+ maxtrack = TRUE;
+ }
+
+ if (ped->Track != tmp)
+ {
+ moved = TRUE;
+ ped->Track = tmp;
+ }
+
+
+ /* X Position (Column) */
+
+ if (maxtrack)
+ tmp = ped->TrackChars - 1;
+ else
+ tmp = (x / ped->FontXSize) % ped->TrackChars;
+
+
+ if (tmp < 3) tmp = COL_NOTE;
+ else if (tmp == 3) tmp = COL_INSTH;
+ else if (tmp == 4) tmp = COL_INSTL;
+ else if (tmp < 7) tmp = COL_EFF;
+ else if (tmp == 7) tmp = COL_VALH;
+ else tmp = COL_VALL;
+
+
+ if (ped->Column != tmp)
+ {
+ moved = TRUE;
+ ped->Column = tmp;
+ }
+
+
+ /* Y Position */
+
+ tmp = (y / ped->FontYSize) + ped->TopLine;
+
+ if (tmp < 0) tmp = 0;
+
+ if (tmp >= ped->Patt->Lines)
+ tmp = ped->Patt->Lines - 1;
+
+ if (ped->Line != tmp)
+ {
+ moved = TRUE;
+ ped->Line = tmp;
+ }
+
+ return moved;
+}
+
+
+
+static void EraseCursor (struct RastPort *rp, struct PattEditData *ped)
+{
+ SetDrMd (rp,COMPLEMENT);
+
+ switch (ped->CursState)
+ {
+ case IDS_DISABLED:
+ SetAfPt (rp, GhostPattern, 1);
+ RectFill (rp, ped->CursRect.MinX, ped->CursRect.MinY,
+ ped->CursRect.MaxX, ped->CursRect.MaxY);
+ SetAfPt (rp, NULL, 0); /* Reset Area Pattern */
+ break;
+
+ case IDS_BUSY:
+ SetAfPt (rp, MarkPattern, 1);
+ RectFill (rp, ped->CursRect.MinX, ped->CursRect.MinY,
+ ped->CursRect.MaxX, ped->CursRect.MaxY);
+ SetAfPt (rp, NULL, 0); /* Reset Area Pattern */
+ break;
+
+ case IDS_SELECTED:
+ RectFill (rp, ped->CursRect.MinX, ped->CursRect.MinY,
+ ped->CursRect.MaxX, ped->CursRect.MaxY);
+
+ if (ped->CursLinePos)
+ {
+ /* Erase cursor line */
+
+ /* Question: Is RectFill faster than Move()+Draw()? Hmmm... */
+ RectFill (rp, ped->GBounds.Left, ped->CursRect.MaxY,
+ ped->TBounds.Left + ped->TBounds.Width - 1, ped->CursRect.MaxY);
+ // Move (rp, ped->GBounds.Left, ped->CursRect.MaxY);
+ // Draw (rp, ped->TBounds.Left + ped->TBounds.Width - 1, ped->CursRect.MaxY);
+ ped->CursLinePos = 0;
+ }
+ break;
+
+ default:
+
+ /* When the cursor is not rendered, its state
+ * will be IDS_NORMAL, so we won't erase it at all.
+ */
+ break;
+ }
+
+ ped->CursState = IDS_NORMAL;
+}
+
+
+
+static UWORD DrawCursor (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g)
+
+/* Draw the cursor image on the editor. If the cursor goes outside
+ * of the scrolling region, the view is scrolled to make
+ * it visible.
+ *
+ * RESULT
+ * 0 - no scrolling occurred,
+ * SCROLLED_VERT - vertical scroll occurred,
+ * SCROLLED_HORIZ - horizontal scroll occurred,
+ * SCROLLED_VERT|SCROLLED_HORIZ - scrolled in both directions.
+ */
+
+{
+ /* Cursor offsets for each track column */
+ static UWORD ColumnOff[COL_COUNT] = { 0, 3, 4, 6, 7, 8 };
+
+ struct Rectangle NewCurs;
+ UWORD NewState;
+
+
+ /* Do not attemt to draw a cursor if the editor is so small that
+ * it can't display anything,
+ */
+ if (!ped->DisplayTracks || !ped->DisplayLines)
+ return 0;
+
+ /* Check whether cursor is outside the display bounds and
+ * scroll pattern to make it visible if required.
+ */
+ {
+ if (ped->Line < ped->TopLine)
+ return ScrollPattern (rp, ped, g, ped->LeftTrack, ped->Line);
+ else if (ped->Line >= ped->TopLine + ped->DisplayLines)
+ return ScrollPattern (rp, ped, g, ped->LeftTrack, ped->Line - ped->DisplayLines + 1);
+ if (ped->Track < ped->LeftTrack)
+ return ScrollPattern (rp, ped, g, ped->Track, ped->TopLine);
+ else if (ped->Track >= ped->LeftTrack + ped->DisplayTracks)
+ return ScrollPattern (rp, ped, g, ped->Track - ped->DisplayTracks + 1, ped->TopLine);
+ }
+
+
+ /* Calculate new cursor rectangle */
+
+ NewCurs.MinX = ped->TBounds.Left +
+ (((ped->Track - ped->LeftTrack) * ped->TrackChars) + ColumnOff[ped->Column]) * ped->FontXSize;
+ NewCurs.MinY = ped->TBounds.Top + (ped->Line - ped->TopLine) * ped->FontYSize;
+ NewCurs.MaxX = NewCurs.MinX + ped->FontXSize - 1;
+ NewCurs.MaxY = NewCurs.MinY + ped->FontYSize - 1;
+
+
+ /* Note field is three characters wide */
+ if (ped->Column == COL_NOTE)
+ NewCurs.MaxX += ped->FontXSize * 2;
+
+
+ /* Set AreaPattern to show current cursor state */
+
+ if (!(g->Flags & GFLG_SELECTED))
+ {
+ NewState = IDS_DISABLED;
+
+ /* Set this pattern to draw an inactive cursor */
+ SetAfPt (rp, GhostPattern, 1);
+ }
+ else if (ped->Flags & PEF_MARKING)
+ {
+ NewState = IDS_BUSY;
+
+ /* Set this pattern to draw marking cursor */
+ SetAfPt (rp, MarkPattern, 1);
+ }
+ else NewState = IDS_SELECTED;
+
+ SetDrMd (rp, COMPLEMENT);
+ /* Draw cursor */
+ RectFill (rp, NewCurs.MinX, NewCurs.MinY,
+ NewCurs.MaxX, NewCurs.MaxY);
+ SetAfPt (rp, NULL, 0); /* Reset AreaFill Pattern */
+
+ if ((ped->Flags & PEF_DOCURSORRULER) && (NewState == IDS_SELECTED))
+ {
+ if (ped->CursLinePos != NewCurs.MaxY)
+ {
+ /* Draw horizontal line */
+
+ /* Question: Is RectFill faster than Move()+Draw()? Hmmm... */
+ RectFill (rp, ped->GBounds.Left, NewCurs.MaxY,
+ ped->TBounds.Left + ped->TBounds.Width - 1, NewCurs.MaxY);
+ // Move (rp, ped->GBounds.Left, NewCurs.MaxY);
+ // Draw (rp, ped->TBounds.Left + ped->TBounds.Width - 1, NewCurs.MaxY);
+ }
+ /* Cause EraseCursor() to leave the old line alone */
+ else ped->CursLinePos = 0;
+ }
+
+
+ /* Erase old cursor */
+ EraseCursor (rp, ped);
+
+ if (ped->Flags & PEF_MARKING)
+ /* Update the range */
+ DrawRange (rp, ped);
+
+
+ /* Store new position and state */
+ ped->CursRect = NewCurs;
+ ped->CursState = NewState;
+
+ if ((ped->Flags & PEF_DOCURSORRULER) && (NewState == IDS_SELECTED))
+ ped->CursLinePos = NewCurs.MaxY;
+
+ return FALSE;
+}
+
+
+
+static void DrawRange (struct RastPort *rp, struct PattEditData *ped)
+{
+ UWORD tmin, tmax, lmin, lmax;
+ struct Rectangle newrange;
+
+ ped->RangeEndTrack = ped->Track;
+
+ if (!(ped->Flags & PEF_MARKFULLTRACKS))
+ ped->RangeEndLine = ped->Line;
+
+ if (ped->RangeStartTrack < ped->RangeEndTrack)
+ {
+ tmin = ped->RangeStartTrack;
+ tmax = ped->RangeEndTrack;
+ }
+ else
+ {
+ tmin = ped->RangeEndTrack;
+ tmax = ped->RangeStartTrack;
+ }
+
+ if (ped->RangeStartLine < ped->RangeEndLine)
+ {
+ lmin = ped->RangeStartLine;
+ lmax = ped->RangeEndLine;
+ }
+ else
+ {
+ lmin = ped->RangeEndLine;
+ lmax = ped->RangeStartLine;
+ }
+
+ /* Limit to visible portion of range rectangle */
+
+ if (tmin < ped->LeftTrack)
+ tmin = ped->LeftTrack;
+ if (tmin >= ped->LeftTrack + ped->DisplayTracks)
+ tmin = ped->LeftTrack + ped->DisplayTracks - 1;
+
+ if (tmax < ped->LeftTrack)
+ tmax = ped->LeftTrack;
+ if (tmax >= ped->LeftTrack + ped->DisplayTracks)
+ tmax = ped->LeftTrack + ped->DisplayTracks - 1;
+
+ if (lmin < ped->TopLine)
+ lmin = ped->TopLine;
+ if (lmin >= ped->TopLine + ped->DisplayLines)
+ lmin = ped->TopLine + ped->DisplayLines - 1;
+
+ if (lmax < ped->TopLine)
+ lmax = ped->TopLine;
+ if (lmax >= ped->TopLine + ped->DisplayLines)
+ lmax = ped->TopLine + ped->DisplayLines - 1;
+
+
+ newrange.MinX = ped->TBounds.Left + (tmin - ped->LeftTrack) * ped->TrackWidth;
+ newrange.MinY = ped->TBounds.Top + (lmin - ped->TopLine) * ped->FontYSize;
+ newrange.MaxX = ped->TBounds.Left + ((tmax - ped->LeftTrack + 1) * ped->TrackWidth) - 1;
+ newrange.MaxY = ped->TBounds.Top + ((lmax - ped->TopLine + 1) * ped->FontYSize) - 1;
+
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ped->TextPen | ped->BGPen);
+#else
+ SafeSetWriteMask (rp, ped->TextPen | ped->BGPen);
+#endif /* OS30_ONLY */
+
+ SetDrMd (rp, COMPLEMENT);
+
+
+ if (ped->RangeRect.MaxX == 0)
+ {
+ RectFill (rp, newrange.MinX,
+ newrange.MinY,
+ newrange.MaxX,
+ newrange.MaxY);
+ }
+ else
+ {
+ /* Incremental range box update. We only draw changes relative to last
+ * ranged box. As the range box is drawn complementing bitplane 1,
+ * complementing it again will erase it, so we do not care if the box
+ * has grown or shrinked; we just complemnt delta boxes.
+ */
+
+ /* range box
+ * +---+-----+
+ * |###| |
+ * |###| |
+ * |###| <-+-- unchanged
+ * |###| |
+ * +---+-----+
+ * ^---------- changed
+ */
+ if (newrange.MinX != ped->RangeRect.MinX)
+ RectFill (rp, min (ped->RangeRect.MinX, newrange.MinX),
+ newrange.MinY,
+ max (ped->RangeRect.MinX, newrange.MinX) - 1,
+ newrange.MaxY);
+
+ /* +-----+---+
+ * | |###|
+ * | |###|
+ * | |###|
+ * | |###|
+ * +-----+---+
+ */
+ if (newrange.MaxX != ped->RangeRect.MaxX)
+ RectFill (rp, min (ped->RangeRect.MaxX, newrange.MaxX) + 1,
+ newrange.MinY,
+ max (ped->RangeRect.MaxX, newrange.MaxX),
+ newrange.MaxY);
+
+ /* +---------+
+ * |#########|
+ * +---------+
+ * | |
+ * | |
+ * +---------+
+ */
+ if (newrange.MinY != ped->RangeRect.MinY)
+ RectFill (rp, ped->RangeRect.MinX,
+ min (ped->RangeRect.MinY, newrange.MinY),
+ ped->RangeRect.MaxX,
+ max (ped->RangeRect.MinY, newrange.MinY) - 1);
+
+ /* +---------+
+ * | |
+ * | |
+ * +---------+
+ * |#########|
+ * +---------+
+ */
+ if (newrange.MaxY != ped->RangeRect.MaxY)
+ RectFill (rp, ped->RangeRect.MinX,
+ min (ped->RangeRect.MaxY, newrange.MaxY) + 1,
+ ped->RangeRect.MaxX,
+ max (ped->RangeRect.MaxY, newrange.MaxY));
+ }
+
+ /* Copy new values */
+ ped->RangeRect = newrange;
+
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ~0);
+#else
+ SafeSetWriteMask (rp, (UBYTE)~0);
+#endif /* OS30_ONLY */
+}
+
+
+
+static void ClearRange (struct RastPort *rp, struct PattEditData *ped)
+{
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ped->TextPen | ped->BGPen);
+#else
+ SafeSetWriteMask (rp, ped->TextPen | ped->BGPen);
+#endif /* OS30_ONLY */
+ SetDrMd (rp, COMPLEMENT);
+
+ RectFill (rp, ped->RangeRect.MinX, ped->RangeRect.MinY,
+ ped->RangeRect.MaxX, ped->RangeRect.MaxY);
+
+ memset (&ped->RangeRect, 0, sizeof (ped->RangeRect));
+
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ~0);
+#else
+ SafeSetWriteMask (rp, (UBYTE)~0);
+#endif /* OS30_ONLY */
+}
+
+
+
+static void RedrawAll (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g)
+{
+ UWORD HOffset,
+ tmp;
+ ULONG i;
+
+
+ /* Erase the whole gadget imagery */
+
+ SetAPen (rp, ped->BGPen);
+ SetDrMd (rp, JAM2);
+
+ if (!ped->Patt)
+ {
+ /* Clear everything */
+
+ RectFill (rp, ped->GBounds.Left, ped->GBounds.Top,
+ ped->GBounds.Left + ped->GBounds.Width - 1, ped->GBounds.Top + ped->GBounds.Height - 1);
+ return;
+ }
+
+ if (rp->BitMap->Depth > 1)
+ {
+ /* Do not clear the bitplanes used by the text because they will
+ * be completely redrawn later.
+ */
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ~ped->TextPen);
+#else
+ SafeSetWriteMask (rp, (UBYTE)~ped->TextPen);
+#endif /* OS30_ONLY */
+
+
+ /* +------------+
+ * |********* |
+ * |*Cleared* |
+ * |********* |
+ * | |
+ * +------------+
+ */
+ RectFill (rp, ped->GBounds.Left, ped->GBounds.Top,
+ ped->TBounds.Left + ped->TBounds.Width - 1, ped->TBounds.Top + ped->TBounds.Height - 1);
+
+ /* Restore the Mask */
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ~0);
+#else
+ SafeSetWriteMask (rp, (UBYTE)~0);
+#endif /* OS30_ONLY */
+ }
+
+ /* Now clear the area at the right and bottom side of
+ * the editing field.
+ */
+
+ /* +------------+
+ * | ***|
+ * | ***|
+ * | ***|
+ * | |
+ * +------------+
+ */
+ if (ped->TBounds.Left + ped->TBounds.Width < ped->GBounds.Left + ped->GBounds.Width)
+ RectFill (rp, ped->TBounds.Left + ped->TBounds.Width, ped->GBounds.Top,
+ ped->GBounds.Left + ped->GBounds.Width - 1, ped->TBounds.Top + ped->TBounds.Height - 1);
+
+ /* +------------+
+ * | |
+ * | |
+ * | |
+ * |************|
+ * +------------+
+ */
+ if (ped->TBounds.Top + ped->TBounds.Height < ped->GBounds.Top + ped->GBounds.Height)
+ RectFill (rp, ped->GBounds.Left, ped->TBounds.Top + ped->TBounds.Height,
+ ped->GBounds.Left + ped->GBounds.Width - 1, ped->GBounds.Top + ped->GBounds.Height - 1);
+
+
+ /* Cursor shouldn't be deleted again since everything has been cleared */
+ ped->CursState = IDS_NORMAL;
+ ped->CursLinePos = 0;
+
+ if (!(ped->Patt)) return;
+
+ /* Draw track separator lines */
+ SetAPen (rp, ped->LinesPen);
+ DrawTrackSeparators (rp, ped, ped->TrackWidth);
+
+ /* Draw tiny vertical separator lines */
+ if (ped->Flags & PEF_DOTINYLINES)
+ {
+ SetAPen (rp, ped->TinyLinesPen);
+ SetDrPt (rp, 0xCCCC);
+ HOffset = ped->TBounds.Left - 1;
+
+ for (i = 0; i < ped->DisplayTracks; i++, HOffset += ped->TrackWidth)
+ {
+ Move (rp, tmp = HOffset + ped->FontXSize * 3, ped->GBounds.Top);
+ Draw (rp, tmp, ped->GBounds.Top + ped->GBounds.Height - 1);
+ Move (rp, tmp = HOffset + ped->FontXSize * 5, ped->GBounds.Top);
+ Draw (rp, tmp, ped->GBounds.Top + ped->GBounds.Height - 1);
+ Move (rp, tmp = HOffset + ped->FontXSize * 7, ped->GBounds.Top);
+ Draw (rp, tmp, ped->GBounds.Top + ped->GBounds.Height - 1);
+ }
+
+ SetDrPt (rp, 0xFFFF);
+ }
+
+ RedrawPattern (rp, ped, g);
+
+ DrawTrackNumbers (rp, ped);
+}
+
+
+
+static void DrawTrackSeparators (struct RastPort *rp, struct PattEditData *ped, UWORD width)
+{
+ UWORD i, HOffset = ped->TBounds.Left - 1;
+
+ for (i = 0; i <= ped->DisplayTracks; i++, HOffset += width)
+ {
+ /* Never draw outside gadget bounds */
+ if (HOffset >= ped->GBounds.Left + ped->GBounds.Width + 1) break;
+ RectFill (rp, HOffset - 1, ped->GBounds.Top, HOffset, ped->GBounds.Top + ped->GBounds.Height - 1);
+ }
+}
+
+
+
+static void DrawTrackNumbers (struct RastPort *rp, struct PattEditData *ped)
+{
+ struct TextFont *oldfont = rp->Font;
+ UWORD HOffset = ped->TBounds.Left + ped->FontXSize * 5;
+ UWORD i;
+ UBYTE ch;
+
+
+#ifndef OS30_ONLY
+ if (GfxBase->LibNode.lib_Version < 39)
+ {
+ rp->Mask = ped->LinesPen;
+ SetAPen (rp, ped->LinesPen);
+ SetBPen (rp, ped->BGPen);
+ SetDrMd (rp, JAM2); /* JAM1 or JAM2... Which one is faster? */
+ }
+ else
+ {
+#endif /* !OS30_ONLY */
+ SetWrMsk (rp, ped->LinesPen);
+ SetABPenDrMd (rp, ped->LinesPen, ped->BGPen, JAM2);
+#ifndef OS30_ONLY
+ }
+#endif /* !OS30_ONLY */
+
+ SetFont (rp, ped->EditorFont);
+
+ for (i = 0; i < ped->DisplayTracks; i++, HOffset += ped->TrackWidth)
+ {
+ Move (rp, HOffset, ped->GBounds.Top + ped->EditorFont->tf_Baseline);
+ ch = HexValuesNo0[((i+ped->LeftTrack)>>4) & 0xF];
+ Text (rp, &ch, 1);
+ Move (rp, HOffset, ped->GBounds.Top + ped->FontXSize + ped->EditorFont->tf_Baseline);
+ ch = HexValues[(i+ped->LeftTrack) & 0xF];
+ Text (rp, &ch, 1);
+ }
+
+ SetFont (rp, oldfont);
+}
+
+
+
+static void RedrawPattern (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g)
+{
+ EraseCursor (rp, ped);
+
+ /* Redraw all lines */
+ if (ped->DisplayLines)
+ DrawPatternLines (rp, ped, 0, ped->DisplayLines - 1);
+
+ if (ped->Flags & PEF_MARKING)
+ /* Clear the range rectangle bounds so the next call
+ * to DrawCursor() will redraw all the range box.
+ */
+ memset (&ped->RangeRect, 0, sizeof (ped->RangeRect));
+
+ DrawCursor (rp, ped, g);
+}
+
+
+
+static void DrawPatternLines (struct RastPort *rp, struct PattEditData *ped, UWORD min, UWORD max)
+
+/* Draws Pattern display lines. Only lines from <min> to <max> are drawn.
+ * To redraw all the display, pass min = 0 and max = ped->DisplayLines - 1.
+ */
+{
+ struct Pattern *patt;
+ struct TextFont *oldfont = rp->Font;
+ UBYTE *l;
+ UWORD i, j;
+ UWORD VOffset = ped->TBounds.Top + ped->EditorFont->tf_Baseline
+ + min * ped->FontYSize;
+
+ /* Note well: I've put 32 and not MAXTRACKS here because 255 tracks would
+ * have used too much stack. :-(
+ */
+ ALIGNED UBYTE line[MAXTRACKCHARS * 32 + 4];
+
+
+ if (!(patt = ped->Patt)) return;
+
+ SetFont (rp, ped->EditorFont);
+
+#ifndef OS30_ONLY
+ if (GfxBase->LibNode.lib_Version >= 39)
+ {
+#endif /* !OS30_ONLY */
+ SetWriteMask (rp, ped->TextPen | ped->BGPen);
+ SetABPenDrMd (rp, ped->TextPen, ped->BGPen, JAM2);
+#ifndef OS30_ONLY
+ }
+ else
+ {
+ rp->Mask = ped->TextPen | ped->BGPen;
+ SetAPen (rp, ped->TextPen);
+ SetBPen (rp, ped->BGPen);
+ SetDrMd (rp, JAM2); /* JAM1 or JAM2... Which one is faster? */
+ }
+#endif /* !OS30_ONLY */
+
+
+ for (i = min; i <= max; i++)
+ {
+ l = line;
+
+ /* Write Line Numbers column */
+
+ if (ped->Flags & PEF_HEXMODE)
+ {
+ *l++ = HexValues[((i+ped->TopLine)>>8) & 0xF];
+ *l++ = HexValues[((i+ped->TopLine)>>4) & 0xF];
+ *l++ = HexValues[((i+ped->TopLine) & 0xF)];
+ }
+ else
+ {
+ *l++ = HexValues[((i+ped->TopLine) / 100) % 10];
+ *l++ = HexValues[((i+ped->TopLine) / 10) % 10];
+ *l++ = HexValues[((i+ped->TopLine) % 10)];
+ }
+
+ *l++ = ' ';
+
+ for (j = 0; j < ped->DisplayTracks; j++, l += ped->TrackChars)
+ ped->Note2ASCIIFunc (l, &patt->Notes[j+ped->LeftTrack][i+ped->TopLine]);
+
+ Move (rp, ped->GBounds.Left, VOffset);
+ Text (rp, line, ped->DisplayTracks * ped->TrackChars + 4);
+ VOffset += ped->FontYSize;
+ }
+
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ~0);
+#else
+ SafeSetWriteMask (rp, (UBYTE)~0);
+#endif /* OS30_ONLY */
+ SetFont (rp, oldfont);
+}
+
+
+
+static void DrawNote (struct RastPort *rp, struct PattEditData *ped)
+
+/* Draws the note under the cursor. DrawNote() won't draw if the cursor
+ * is outside of the editing area. Range marking mode is also taken into
+ * account and the colors of the note will be reversed when the note must
+ * be hilighted.
+ */
+{
+ struct Pattern *patt;
+ struct TextFont *oldfont = rp->Font;
+
+ UBYTE buf[MAXTRACKCHARS];
+
+
+ if (!(patt = ped->Patt)) return;
+
+ /* Is the cursor outside the editing area? */
+
+ if ((ped->Line < ped->TopLine) || (ped->Line >= ped->TopLine + ped->DisplayLines) ||
+ (ped->Track < ped->LeftTrack) || (ped->Track >= ped->LeftTrack + ped->DisplayTracks))
+ return;
+
+ SetFont (rp, ped->EditorFont);
+
+#ifndef OS30_ONLY
+ if (GfxBase->LibNode.lib_Version >= 39)
+ {
+#endif /* OS30_ONLY */
+ SetWriteMask (rp, ped->TextPen | ped->BGPen);
+ if (ped->Flags & PEF_MARKING)
+ SetABPenDrMd (rp, ped->BGPen, ped->TextPen, JAM2);
+ else
+ SetABPenDrMd (rp, ped->TextPen, ped->BGPen, JAM2);
+#ifndef OS30_ONLY
+ }
+ else
+ {
+ rp->Mask = ped->TextPen | ped->BGPen;
+ if (ped->Flags & PEF_MARKING)
+ {
+ SetAPen (rp, ped->BGPen);
+ SetBPen (rp, ped->TextPen);
+ }
+ else
+ {
+ SetAPen (rp, ped->TextPen);
+ SetBPen (rp, ped->BGPen);
+ }
+ SetDrMd (rp, JAM2);
+ }
+#endif /* OS30_ONLY */
+
+
+ ped->Note2ASCIIFunc (buf, &patt->Notes[ped->Track][ped->Line]);
+
+ Move (rp, ped->TBounds.Left + ((ped->Track - ped->LeftTrack) * ped->TrackWidth),
+ ped->TBounds.Top + ped->EditorFont->tf_Baseline + (ped->Line - ped->TopLine) * ped->FontYSize);
+ Text (rp, buf, ped->TrackChars);
+
+#ifdef OS30_ONLY
+ SetWriteMask (rp, (UBYTE)~0);
+#else
+ SafeSetWriteMask (rp, (UBYTE)~0);
+#endif /* OS30_ONLY */
+
+ SetFont (rp, oldfont);
+}
+
+
+
+#ifdef PORTABLE
+
+void ASMCALL Note2ASCIIBlank0 (REG(a1, UBYTE *s), REG(a2, struct Note *note))
+
+/* Fill buffer <s> with the ASCII representation of a
+ * Note structure. <s> will be able to hold at
+ * least <ped->TrackChars> characters.
+ */
+{
+ *((ULONG *)s) = TextNotes[note->Note];
+
+ if (note->Inst)
+ {
+ s[3] = HexValuesNo0[note->Inst>>4];
+ s[4] = HexValues[note->Inst & 0x0F];
+ }
+ else
+ {
+ s[3] = ' ';
+ s[4] = '-';
+ }
+
+ s[5] = ' ';
+
+ if (note->EffNum || note->EffVal)
+ {
+ s[6] = HexValues[note->EffNum];
+ s[7] = HexValues[note->EffVal>>4];
+ s[8] = HexValues[note->EffVal & 0xF];
+ s[9] = ' ';
+ }
+ else
+ *((ULONG *)(s+6)) = ' . ';
+}
+
+
+
+void ASMCALL Note2ASCII (REG(a1, UBYTE *s), REG(a2, struct Note *note))
+
+/* Fill buffer <s> with the ASCII representation of a
+ * Note structure. <s> will be able to hold at
+ * least <ped->TrackChars> characters.
+ */
+{
+ *((ULONG *)s) = note->Note ? TextNotes[note->Note] : '--- ';
+
+ s[3] = HexValuesNo0[note->Inst>>4];
+ s[4] = HexValues[note->Inst & 0x0F];
+
+ s[5] = ' ';
+
+ s[6] = HexValues[note->EffNum];
+ s[7] = HexValues[note->EffVal>>4];
+ s[8] = HexValues[note->EffVal & 0xF];
+ s[9] = ' ';
+}
+
+#endif /* PORTABLE */
+
+
+
+static UWORD ScrollPattern (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g, UWORD lefttrack, UWORD topline)
+/*
+ * Scroll the display to the new position <lefttrack>, <topline>.
+ *
+ * RESULT
+ * 0 - no scrolling occurred,
+ * SCROLLED_VERT - vertical scroll occurred,
+ * SCROLLED_HORIZ - horizontal scroll occurred,
+ * SCROLLED_VERT | SCROLLED_HORIZ - scrolled in both directions.
+ */
+{
+ if (ped->LeftTrack != lefttrack)
+ {
+ UWORD retval = SCROLLED_HORIZ | ped->TopLine != topline;
+
+ ped->LeftTrack = lefttrack;
+ ped->TopLine = topline;
+
+ EraseCursor (rp, ped);
+ DrawTrackNumbers (rp, ped);
+ RedrawPattern (rp, ped, g);
+
+ return retval;
+ }
+ else
+ {
+ UWORD scroll_lines = abs(ped->TopLine - topline);
+
+ /* Redraw everything if more than an half of the screen
+ * has scrolled. When in mark mode, the scroll limit is set
+ * to two thirds of the display, because redrawing the mark
+ * region takes up a lot of time.
+ */
+ //if (scroll_lines > ((ped->Flags & PEF_MARKING) ? ((ped->DisplayLines * 3) >> 1) : (ped->DisplayLines >> 1)))
+ if (scroll_lines > (ped->DisplayLines * 3) >> 1)
+ {
+ ped->LeftTrack = lefttrack;
+ ped->TopLine = topline;
+
+ EraseCursor (rp, ped);
+ RedrawPattern (rp, ped, g);
+ return SCROLLED_VERT;
+ }
+ else
+ {
+ WORD scroll_dy = (topline - ped->TopLine) * ped->FontYSize;
+
+ EraseCursor (rp, ped);
+
+ /* Scroll range rectangle */
+
+ ped->RangeRect.MinY -= scroll_dy;
+ ped->RangeRect.MaxY -= scroll_dy;
+
+ if (ped->RangeRect.MinY < ped->TBounds.Top)
+ ped->RangeRect.MinY = ped->TBounds.Top;
+ if (ped->RangeRect.MaxY >= ped->TBounds.Top + ped->TBounds.Height)
+ ped->RangeRect.MaxY = ped->TBounds.Top + ped->TBounds.Height - 1;
+
+ /* We use ClipBlit() to scroll the pattern because it doesn't clear
+ * the scrolled region like ScrollRaster() would do. Unfortunately,
+ * ClipBlit() does not scroll along the damage regions, so we also
+ * call ScrollRaster() with the mask set to 0, which will scroll the
+ * layer damage regions without actually modifying the display.
+ */
+
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ped->TextPen | ped->BGPen);
+#else
+ SafeSetWriteMask (rp, ped->TextPen | ped->BGPen);
+#endif /* OS30_ONLY */
+
+ if (scroll_dy > 0)
+ /* Scroll Down */
+ ClipBlit (rp, ped->GBounds.Left, ped->TBounds.Top + scroll_dy,
+ rp, ped->GBounds.Left, ped->TBounds.Top,
+ ped->TBounds.Width + (ped->TBounds.Left - ped->GBounds.Left), ped->TBounds.Height - scroll_dy,
+ 0x0C0);
+ else
+ /* Scroll Up */
+ ClipBlit (rp, ped->GBounds.Left, ped->TBounds.Top,
+ rp, ped->GBounds.Left, ped->TBounds.Top - scroll_dy,
+ ped->TBounds.Width + (ped->TBounds.Left - ped->GBounds.Left), ped->TBounds.Height + scroll_dy,
+ 0x0C0);
+
+
+ /* This will scroll the layer damage regions without actually
+ * scrolling the display.
+ */
+ if (rp->Layer->Flags & LAYERSIMPLE)
+ {
+#ifdef OS30_ONLY
+ SetWriteMask (rp, 0);
+#else
+ SafeSetWriteMask (rp, 0);
+#endif /* OS30_ONLY */
+
+ ScrollRaster (rp, 0, scroll_dy,
+ ped->GBounds.Left, ped->TBounds.Top,
+ ped->TBounds.Left + ped->TBounds.Width - 1,
+ ped->TBounds.Top + ped->TBounds.Height - 1);
+ }
+
+ ped->LeftTrack = lefttrack;
+ ped->TopLine = topline;
+
+ if (scroll_dy > 0)
+ /* Scroll down */
+ DrawPatternLines (rp, ped, ped->DisplayLines - scroll_lines, ped->DisplayLines-1);
+ else
+ /* Scroll up */
+ DrawPatternLines (rp, ped, 0, scroll_lines - 1);
+
+
+#ifdef OS30_ONLY
+ SetWriteMask (rp, ~0);
+#else
+ SafeSetWriteMask (rp, (UBYTE)~0);
+#endif /* OS30_ONLY */
+
+ DrawCursor (rp, ped, g);
+
+ return SCROLLED_VERT;
+ }
+ }
+
+ return 0;
+}
+
+
+
+#if 0
+INTCALL static void TimerIntServer (REG(a1) struct ExtGadget *g)
+{
+ struct PattEditData *ped = INST_DATA (OCLASS(g), g); /*!*/
+
+ /* Remove TimerIO for further usage */
+ GetMsg (&ped->TimerPort);
+
+ if ((ped->Flags & PEF_SCROLLING) && ped->TopLine)
+ {
+ SetGadgetAttrs (g, ped->MyWindow, NULL,
+ PEA_Up, 1,
+ TAG_DONE);
+
+ /* Send another request for next scroll */
+ ped->TimerIO.tr_node.io_Command = TR_ADDREQUEST;
+ ped->TimerIO.tr_time.tv_micro = 500000;
+ BeginIO ((struct IORequest *)&ped->TimerIO);
+ }
+}
+#endif
+
+
+
+/*
+ * Class library support functions
+ */
+
+HOOKCALL struct ClassLibrary * _UserLibInit (REG(a6, struct ClassLibrary *mybase))
+{
+ SysBase = *((struct ExecBase **)4); /* Initialize SysBase */
+
+ IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", WANTED_LIBVER);
+ GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", WANTED_LIBVER);
+ UtilityBase = (struct UtilityBase *) OpenLibrary ("utility.library", WANTED_LIBVER);
+ KeymapBase = OpenLibrary ("keymap.library", 36); /* Kick V37 has keymap V36! */
+
+ if (!(IntuitionBase && GfxBase && UtilityBase && KeymapBase))
+ {
+ _UserLibCleanup (mybase);
+ return NULL;
+ }
+
+ if (mybase->cl_Class = MakeClass (PATTEDITCLASS, GADGETCLASS, NULL, sizeof (struct PattEditData), 0))
+ {
+ mybase->cl_Class->cl_Dispatcher.h_Entry = (ULONG (*)()) PattEditDispatcher;
+ AddClass (mybase->cl_Class);
+ }
+ else
+ {
+ _UserLibCleanup (mybase);
+ return NULL;
+ }
+
+ return mybase;
+}
+
+
+
+HOOKCALL struct ClassLibrary * _UserLibCleanup (REG(a6, struct ClassLibrary *mybase))
+{
+ if (mybase->cl_Class)
+ if (!FreeClass (mybase->cl_Class))
+ return NULL;
+
+ /* The tests against NULL are needed here because CloseLibrary()
+ * in V34 crashes when passed a NULL pointer, and this
+ * function might get called when someone tries to load this
+ * library on an 1.3 system.
+ */
+ if (KeymapBase) CloseLibrary ((struct Library *)KeymapBase);
+ if (UtilityBase) CloseLibrary ((struct Library *)UtilityBase);
+ if (GfxBase) CloseLibrary ((struct Library *)GfxBase);
+ if (IntuitionBase) CloseLibrary ((struct Library *)IntuitionBase);
+
+ return mybase;
+}
+
+
+
+HOOKCALL Class * _GetEngine (REG(a6, struct ClassLibrary *mybase))
+{
+ return (mybase->cl_Class);
+}
--- /dev/null
+**
+** PattEditClassAsm.asm
+**
+** Copyright (C) 1995,96 Bernardo Innocenti
+**
+** Pattern Editor gadget assembler subroutines
+**
+
+ INCDIR Include:
+
+ SECTION __merged,code
+
+ include libraries/SongClass.i
+
+ xdef _Note2ASCIIBlank0
+ xdef _Note2ASCII
+
+
+
+* void Note2ASCIIBlank0 (UBYTE *s, struct Note *note)
+* A1 A2
+*
+* Fill the buffer pointed by A1 with the ASCII representation of the
+* Note structure pointed by A2. The buffer will be able to hold at
+* least <ped->TrackChars> characters.
+
+_Note2ASCIIBlank0:
+ move.w d2,-(sp)
+ moveq #0,d1
+ moveq #0,d2
+ lea TextNotes(pc),a0
+ move.b note_Note(a2),d1 ; note_Note
+ add.w d1,d1
+ add.w d1,d1
+ move.l (a0,d1.w),d0
+
+ move.b note_Inst(a2),d1
+ lea HexValuesNo0(pc),a0
+ move.b d1,d2
+ lsr.b #4,d2
+ move.b (a0,d2.w),d0
+ move.l d0,(a1)+ ; Put Note and Instrument high nibble
+
+ tst.b d1
+ beq.s .noinst
+
+ and.w #$0F,d1
+ lea HexValues(pc),a0
+ move.b (a0,d1),d1
+ lsl.w #8,d1
+ move.b #' ',d1
+ move.w d1,(a1)+ ; Put Instrument low nibble and white space
+
+ bra.s .endif
+.noinst
+ move.w #'- ',(a1)+ ; Put Instrument low nibble and white space
+
+.endif
+
+ moveq #0,d0
+ lea HexValues(pc),a0
+ move.b note_EffNum(a2),d0
+ move.b note_EffVal(a2),d1
+ move.b d1,d2
+ sub.b d0,d1
+ beq.s .noeff
+
+ moveq #0,d1
+ move.b (a0,d0.w),d0
+ move.b d2,d1
+ lsl.w #8,d0
+ lsr.b #4,d1
+ move.b (a0,d1.w),d0
+ move.w d0,(a1)+ ;Put EffNum and EffVal high nibble
+
+ and.w #$F,d2
+ move.b (a0,d2.w),d2
+ lsl.w #8,d2
+ move.b #' ',d2
+ move.w d2,(a1)+ ; Put EffVal low nibble and space
+ move.w (sp)+,d2
+ rts
+
+ move.w (sp)+,d2
+ rts
+
+.noeff
+ move.l #' . ',(a1)+ ; Put white spaces
+ move.w (sp)+,d2
+ rts
+
+
+* void Note2ASCII (UBYTE *s, struct Note *note)
+* A1 A2
+*
+* Fill the buffer pointed by A1 with the ASCII representation of the
+* Note structure pointed by A2. The buffer will be able to hold at
+* least <ped->TrackChars> characters.
+
+_Note2ASCII:
+ moveq #0,d0
+ move.b note_Note(a2),d0 ; note_Note
+ beq.s .nonote
+
+ lea TextNotes(pc),a0
+ add.w d0,d0
+ add.w d0,d0
+ move.l (a0,d0.w),(a1) ; Put Note
+ bra.s .endif
+
+.nonote
+ move.l #'--- ',(a1)
+
+.endif
+ lea 3(a1),a1
+
+ move.b note_Inst(a2),d0
+ moveq #0,d1
+ move.b d0,d1
+
+ lea HexValuesNo0(pc),a0
+ lsr.b #4,d1
+ move.b (a0,d1),(a1)+ ; Put Instrument high nibble
+
+ and.w #$0F,d0
+ lea HexValues(pc),a0
+ move.b (a0,d0),(a1)+ ; Put Instrument low nibble
+
+ move.b #' ',(a1)+ ; Put white space
+
+ move.w d2,-(sp)
+ moveq #0,d0
+ lea HexValues(pc),a0
+ move.b note_EffNum(a2),d0
+ move.b note_EffVal(a2),d1
+ move.b d1,d2
+
+ move.b (a0,d0.w),d0
+ move.b d2,d1
+ lsl.w #8,d0
+ lsr.b #4,d1
+ move.b (a0,d1.w),d0
+ move.w d0,(a1)+ ;Put EffNum and EffVal high nibble
+
+ and.w #$F,d2
+ move.b (a0,d2.w),d2
+ lsl.w #8,d2
+ move.b #' ',d2
+ move.w d2,(a1)+ ; Put EffVal low nibble and space
+ move.w (sp)+,d2
+ rts
+
+
+ CNOP 0,4
+TextNotes:
+
+ dc.l ' - '
+
+ dc.l 'C-0 ', 'C#0 ', 'D-0 ', 'D#0 ', 'E-0 ', 'F-0 '
+ dc.l 'F#0 ', 'G-0 ', 'G#0 ', 'A-0 ', 'A#0 ', 'B-0 '
+
+ dc.l 'C-1 ', 'C#1 ', 'D-1 ', 'D#1 ', 'E-1 ', 'F-1 '
+ dc.l 'F#1 ', 'G-1 ', 'G#1 ', 'A-1 ', 'A#1 ', 'B-1 '
+
+ dc.l 'C-2 ', 'C#2 ', 'D-2 ', 'D#2 ', 'E-2 ', 'F-2 '
+ dc.l 'F#2 ', 'G-2 ', 'G#2 ', 'A-2 ', 'A#2 ', 'B-2 '
+
+ dc.l 'C-3 ', 'C#3 ', 'D-3 ', 'D#3 ', 'E-3 ', 'F-3 '
+ dc.l 'F#3 ', 'G-3 ', 'G#3 ', 'A-3 ', 'A#3 ', 'B-3 '
+
+ dc.l 'C-4 ', 'C#4 ', 'D-4 ', 'D#4 ', 'E-4 ', 'F-4 '
+ dc.l 'F#4 ', 'G-4 ', 'G#4 ', 'A-4 ', 'A#4 ', 'B-4 '
+
+ dc.l 'C-5 ', 'C#5 ', 'D-5 ', 'D#5 ', 'E-5 ', 'F-5 '
+ dc.l 'F#5 ', 'G-5 ', 'G#5 ', 'A-5 ', 'A#5 ', 'B-5 '
+
+HexValues:
+ dc.b '0','1','2','3','4','5','6','7','8','9'
+ dc.b 'A','B','C','D','E','F','G','H','I','J'
+
+HexValuesNo0:
+ dc.b ' ','1','2','3','4','5','6','7','8','9'
+ dc.b 'A','B','C','D','E','F'
--- /dev/null
+CODE=FAR
+PARAMETERS=REGISTERS
+NOSTACKCHECK
+STRINGMERGE
+NOCHECKABORT
+COMMENTNEST
+ERRORREXX
+NOMULTIPLEINCLUDES
+OPTIMIZERINLINELOCAL
+SMALLCODE
+SMALLDATA
+NOVERSION
+ABSFUNCPOINTER
+UTILITYLIBRARY
+NOICONS
+MEMORYSIZE=HUGE
+NOERRORHIGHLIGHT
+ONERROR=CONTINUE
+OPTIMIZERTIME
+MULTIPLECHARACTERCONSTANTS
+DEFINE NO_REQTOOLS_OBSOLETE
+DEFINE ASL_V38_NAMES_ONLY
+DEFINE INTUI_V36_NAMES_ONLY
+DEFINE IFFPARSE_V37_NAMES_ONLY
+DEFINE USE_BUILTIN_MATH
+DEFINE __USE_SYSBASE
+IGNORE=193
+IGNORE=306
+PUBSCREEN=Workbench
--- /dev/null
+/*
+** SampEditClass.c
+**
+** Copyright (C) 1995,96 Bernardo Innocenti
+**
+** Sample editor class built on top of the "gadgetclass".
+*/
+
+#include <string.h>
+
+#include <exec/types.h>
+#include <exec/memory.h>
+#include <exec/nodes.h>
+#include <exec/ports.h>
+#include <utility/tagitem.h>
+#include <utility/hooks.h>
+#include <devices/inputevent.h>
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/classes.h>
+#include <intuition/classusr.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <graphics/gfxbase.h>
+#include <graphics/gfxmacros.h>
+#include <graphics/text.h>
+#include <libraries/songclass.h>
+#include <libraries/sampeditclass.h>
+
+#include <clib/exec_protos.h>
+#include <clib/intuition_protos.h>
+#include <clib/graphics_protos.h>
+#include <clib/utility_protos.h>
+#include <clib/alib_protos.h>
+
+#include <pragmas/exec_sysbase_pragmas.h>
+#include <pragmas/intuition_pragmas.h>
+#include <pragmas/graphics_pragmas.h>
+#include <pragmas/utility_pragmas.h>
+
+
+/* Some handy definitions missing in <devices/inputevent.h> */
+#define IEQUALIFIER_SHIFT (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
+#define IEQUALIFIER_ALT (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
+#define IEQUALIFIER_COMMAND (IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND)
+
+/* Scrolling control */
+#define SCROLLED_VERT 1
+#define SCROLLED_HORIZ 2
+
+
+
+struct SampEditData
+{
+ struct Instrument *Instr;
+
+ /* These are the actual Gadget position and size,
+ * regardless of any GREL_#? flags.
+ */
+ struct IBox GBounds;
+
+ ULONG Flags; /* See definitions in <SampEditClass.h> */
+
+ UWORD XStart; /* X offset from the beginning of the sample */
+ UWORD XVisible; /* Number of visible samples */
+
+ UWORD CursState;
+ UWORD CursX; /* Cursor position (which sample) */
+ UWORD CursScrPosX; /* Screen cursor position to erase it quickly. */
+
+ UWORD Pad0;
+
+ UWORD BackupCursX; /* Cursor drag undo */
+ UWORD BackupXStart;
+
+ ULONG XRatio; /* X Zoom ratio, 16:16 bit fixed point number */
+
+ /* Range marking info */
+ UWORD RangeStartX;
+ UWORD RangeStartY;
+ UWORD RangeEndX;
+ UWORD RangeEndY;
+
+ struct Rectangle RangeRect; /* Backup of range rect to erase it quickly. */
+
+ /* Rendering routine for the sample. */
+ void (*RenderFunc)(struct Note *note, UBYTE *s);
+
+ /* Pens used to draw various gadget elements */
+ ULONG BackgroundPen;
+ ULONG LinePen;
+ ULONG FillPen;
+ ULONG GridPen;
+
+
+ /* For double click test */
+ ULONG DoubleClickSeconds;
+ ULONG DoubleClickMicros;
+};
+
+
+
+
+/* Function prototypes */
+
+static ULONG __asm SampEditDispatcher (register __a0 Class *cl,
+ register __a2 struct ExtGadget *g,
+ register __a1 Msg msg);
+static ULONG SAMP_HandleInputMethod (Class *cl, struct ExtGadget *g, struct gpInput *msg);
+static void SAMP_RenderMethod (Class *cl, struct ExtGadget *g, struct gpRender *msg);
+static ULONG SAMP_SetMethod (Class *cl, struct ExtGadget *g, struct opSet *msg);
+static ULONG SAMP_GetMethod (Class *cl, struct ExtGadget *g, struct opGet *msg);
+static ULONG SAMP_NewMethod (Class *cl, struct ExtGadget *g, struct opSet *msg);
+
+static void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect);
+static BOOL CalcDisplaySize (struct SampEditData *sed, struct ExtGadget *g, struct GadgetInfo *gpi);
+static void SaveUndo (struct SampEditData *sed);
+static BOOL UndoChange (struct SampEditData *sed);
+static BOOL RedoChange (struct SampEditData *sed);
+static void FreeUndoBuffers (struct SampEditData *sed, BOOL freeall);
+static BOOL MoveCursor (struct SampEditData *sed, WORD x /*, WORD y*/);
+static void EraseCursor (struct RastPort *rp, struct SampEditData *sed);
+static UWORD DrawCursor (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g);
+static void DrawRange (struct RastPort *rp, struct SampEditData *sed);
+static void ClearRange (struct RastPort *rp, struct SampEditData *sed);
+static void RedrawAll (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g);
+static void RedrawSample (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g);
+static void DrawGrid (struct RastPort *rp, struct SampEditData *sed, UWORD min, UWORD max);
+static UWORD ScrollDisplay (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g,
+ UWORD lefttrack, UWORD topline);
+static void NotifyCursor (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
+static void NotifyHSlider (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
+
+//static void __asm TimerIntServer (register __a1 struct Gadget *g);
+
+struct Library * __asm _UserLibInit (register __a6 struct Library *mybase);
+struct Library * __asm _UserLibCleanup (register __a6 struct Library *mybase);
+struct IClass * __asm _GetClass (register __a6 struct Library *mybase);
+
+
+
+/* Library data */
+
+#ifdef _M68020
+#define SAMPVERS "_020"
+#else
+#define SAMPVERS ""
+#endif
+
+const UBYTE LibName[] = "sampedit.gadget";
+const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
+const UBYTE LibId[] = "sampedit.gadget" SAMPVERS " 1.1 (5.5.96) © 1995,96 by Bernardo Innocenti";
+
+
+
+
+/*****************/
+/* Library bases */
+/*****************/
+
+/* Get around a SAS/C bug which causes some annoying warnings
+ * with the library bases defined below.
+ */
+#ifdef __SASC
+#pragma msg 72 ignore push
+#endif /* __SASC */
+
+struct ExecBase *SysBase = NULL;
+struct IntuitionBase *IntuitionBase = NULL;
+struct GfxBase *GfxBase = NULL;
+struct Library *UtilityBase = NULL;
+
+#ifdef __SASC
+#pragma msg 72 pop
+#endif /* __SASC */
+
+static struct IClass *SampEditClass = NULL;
+
+
+static ULONG __asm SampEditDispatcher (register __a0 Class *cl,
+ register __a2 struct ExtGadget *g,
+ register __a1 Msg msg)
+
+/* SampEdit Class Dispatcher entrypoint.
+ * Handle BOOPSI messages.
+ */
+{
+ switch (msg->MethodID)
+ {
+ case GM_GOACTIVE:
+ return SAMP_HandleInputMethod (cl, g, (struct gpInput *)msg);
+
+
+ case GM_RENDER:
+ SAMP_RenderMethod (cl, g, (struct gpRender *)msg);
+ return 0;
+
+ case GM_LAYOUT:
+ {
+ struct SampEditData *sed = INST_DATA (cl, g);
+
+ if (CalcDisplaySize (sed, g, ((struct gpLayout *)msg)->gpl_GInfo))
+ NotifyHSlider (g, ((struct gpLayout *)msg)->gpl_GInfo, 0);
+
+ return 0;
+ }
+
+ case OM_SET:
+ case OM_UPDATE:
+ return SAMP_SetMethod (cl, g, (struct opSet *)msg);
+
+ case OM_GET:
+ return SAMP_GetMethod (cl, g, (struct opGet *)msg);
+
+ case OM_NEW:
+ return (ULONG) SAMP_NewMethod (cl, g, (struct opSet *)msg);
+
+ case OM_DISPOSE:
+ /* NOTE: I'm falling through here! */
+
+ default:
+
+ /* Unsupported method: let our superclass's
+ * dispatcher take a look at it.
+ */
+ return DoSuperMethodA (cl, (Object *)g, msg);
+ }
+}
+
+
+
+static ULONG SAMP_HandleInputMethod (Class *cl, struct ExtGadget *g, struct gpInput *msg)
+{
+ struct SampEditData *sed = INST_DATA (cl, g);
+ struct InputEvent *ie = msg->gpi_IEvent;
+ WORD MouseX, MouseY; /* Mouse coordinates relative to editing area bounds */
+ BOOL moved = FALSE,
+ scrolled = FALSE;
+ ULONG result = GMR_MEACTIVE;
+
+ if (msg->MethodID == GM_GOACTIVE)
+ {
+ if (!sed->Instr)
+ return GMR_NOREUSE;
+
+ g->Flags |= GFLG_SELECTED;
+
+ {
+ struct RastPort *rp;
+
+ /* Render active cursor */
+ if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
+ {
+ DrawCursor (rp, sed, g);
+ ReleaseGIRPort (rp);
+ }
+ }
+
+ /* Do not process InputEvent when the gadget has been
+ * activated by ActivateGadget().
+ */
+ if (!((struct gpInput *)msg)->gpi_IEvent)
+ return GMR_MEACTIVE;
+
+ /* Note: The input event that triggered the gadget
+ * activation (usually a mouse click) should be passed
+ * to the GM_HANDLEINPUT method, so we fall down to it.
+ */
+ }
+
+
+ MouseX = ((struct gpInput *)msg)->gpi_Mouse.X;
+ MouseY = ((struct gpInput *)msg)->gpi_Mouse.Y;
+
+ switch (ie->ie_Class)
+ {
+ case IECLASS_TIMER:
+
+ /* Timer events are used to keep scrolling
+ * when the mouse is outside the bounds of the
+ * gadget. When a timer event is recevied and
+ * the mouse button is still pressed, we scroll the
+ * cursor.
+ */
+ if (sed->Flags & SEF_DRAGGING)
+ moved = MoveCursor (sed, MouseX);
+
+ break;
+
+ case IECLASS_RAWMOUSE:
+ {
+ BOOL double_click = FALSE;
+ BOOL outside = (MouseX < 0) || (MouseX >= sed->GBounds.Width);
+
+ switch (ie->ie_Code)
+ {
+ case MENUDOWN:
+
+ /* Abort cursor dragging operation and restore
+ * old cursor position.
+ */
+ if (sed->Flags & SEF_DRAGGING)
+ {
+ sed->CursX = sed->BackupCursX;
+ sed->XStart = sed->BackupXStart;
+ moved = TRUE;
+ sed->Flags &= ~(SEF_DRAGGING | SEF_SCROLLING);
+ }
+ else result = GMR_REUSE;
+
+ break;
+
+ case SELECTUP:
+ /* Send final update to sliders */
+ scrolled = SCROLLED_HORIZ | SCROLLED_VERT;
+ sed->Flags &= ~(SEF_DRAGGING | SEF_SCROLLING);
+ break;
+
+ case SELECTDOWN:
+
+ /* Check if mouse click is still over the gadget */
+
+ if (outside)
+ {
+ /* Click outside editing area box:
+ * Check if click is really outside gadget box.
+ * If it is, deactivate the gadget and reuse the event.
+ * Notify application if it is interested in
+ * hearing IDCMP_GADGETUP codes.
+ */
+
+ if ((MouseX < 0) || (MouseX >= sed->GBounds.Width) ||
+ (MouseY < 0) || (MouseY >= sed->GBounds.Height))
+ result = GMR_REUSE | GMR_VERIFY;
+ break;
+ }
+ else
+ {
+ /* Backup cursor position for undo feature */
+ sed->BackupCursX = sed->CursX;
+ sed->BackupXStart = sed->XStart;
+
+ /* Start cursor drag mode */
+ sed->Flags |= SEF_DRAGGING;
+ }
+
+ /* Check for double clicking */
+
+ if (DoubleClick (sed->DoubleClickSeconds, sed->DoubleClickMicros,
+ ie->ie_TimeStamp.tv_secs, ie->ie_TimeStamp.tv_micro))
+ double_click = TRUE;
+
+ sed->DoubleClickSeconds = ie->ie_TimeStamp.tv_secs;
+ sed->DoubleClickMicros = ie->ie_TimeStamp.tv_micro;
+
+ /* NOTE: I'm falling through here! */
+
+ default:
+
+ if (sed->Flags & SEF_DRAGGING)
+ {
+ if (outside)
+ sed->Flags |= SEF_SCROLLING;
+ else
+ moved = MoveCursor (sed, MouseX);
+ }
+ else if (!moved && double_click)
+ {
+ if (sed->Flags & SEF_MARKING)
+ sed->Flags &= ~SEF_MARKING;
+ else
+ {
+ sed->Flags |= SEF_MARKING;
+ sed->RangeStartX = sed->CursX;
+ }
+/*
+ if (rp = ObtainGIRPort (msg->gpi_GInfo))
+ {
+ if (!(sed->Flags & SEF_MARKING))
+ ClearRange (rp, sed);
+
+ DrawCursor (rp, sed, g);
+
+ ReleaseGIRPort (rp);
+ }
+*/
+ }
+
+ break;
+ }
+ break;
+ }
+
+ default:
+ return 0;
+
+ } /* End switch (ie->ie_Class) */
+
+ if (moved)
+ {
+ struct RastPort *rp;
+
+ if (rp = ObtainGIRPort (msg->gpi_GInfo))
+ {
+ scrolled |= DrawCursor (rp, sed, g);
+ ReleaseGIRPort (rp);
+ }
+
+ /* Broadcast notification to our target object. */
+ NotifyCursor (g, msg->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
+ }
+
+ if (scrolled)
+ NotifyHSlider (g, msg->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
+
+ return result;
+}
+
+
+
+static void SAMP_RenderMethod (Class *cl, struct ExtGadget *g, struct gpRender *msg)
+{
+ struct SampEditData *sed = INST_DATA (cl, g);
+
+ /* We do not support GREDRAW_UPDATE and GREDRAW_TOGGLE */
+
+ if (msg->gpr_Redraw == GREDRAW_REDRAW)
+ {
+ /* Recalculate the display size only on V37.
+ * As of V39, Intuition supports GM_LAYOUT, which
+ * allows a more optimized way to handle dynamic resizing.
+ */
+ if (IntuitionBase->LibNode.lib_Version < 39)
+ {
+ if (CalcDisplaySize (sed, g, ((struct gpRender *)msg)->gpr_GInfo))
+ NotifyHSlider (g, msg->gpr_GInfo, 0);
+ }
+
+ RedrawAll (msg->gpr_RPort, sed, g);
+ }
+}
+
+
+
+static ULONG SAMP_SetMethod (Class *cl, struct ExtGadget *g, struct opSet *msg)
+{
+ struct SampEditData *sed = INST_DATA (cl, g);
+ struct TagItem *ti,
+ *tstate = msg->ops_AttrList;
+ BOOL zoom = FALSE,
+ move_cursor = FALSE,
+ scroll = FALSE;
+
+
+ while (ti = NextTagItem(&tstate))
+ {
+ switch (ti->ti_Tag)
+ {
+ case SEA_CursXPos:
+ if (sed->CursXPos != ti->ti_Data)
+ {
+ sed->CursXPos = ti->ti_Data;
+ move_cursor = TRUE;
+ }
+ break;
+
+ case SEA_XStart:
+ if (sed->XStart != ti->ti_Data)
+ {
+ sed->XStart = ti->ti_Data;
+ scroll = TRUE;
+ }
+ break;
+
+ case SEA_XRatio:
+ if (sed->XRatio != ti->ti_Data)
+ {
+ sed->XRatio = ti->ti_Data;
+ zoom = TRUE;
+ break;
+ }
+
+ case SEA_ScrollLeft:
+ if (sed->XStart > 0)
+ {
+ sed->XStart--;
+ scroll = TRUE;
+ }
+ break;
+
+ case SEA_ScrollRight:
+ if (sed->XStart + sed->XVisible < sed->Lenght)
+ {
+ sed->XStart++;
+ scroll = TRUE;
+ }
+ break;
+
+ case SEA_MarkRegion:
+ {
+ struct Rectangle *region = (struct Rectangle *)ti->ti_Data;
+
+ if (!region) /* End mark mode */
+
+ sed->Flags &= ~SEF_MARKING;
+
+ else if (region == (struct Rectangle *)-1) /* Toggle mark mode */
+ {
+ sed->Flags ^= SEF_MARKING;
+ sed->RangeStartX = sed->XStart;
+ // sed->RangeStartY = sed->YStart;
+ }
+ else /* Start mark mode */
+ {
+ memcpy (&sed->RangeStartX, region, sizeof (struct Rectangle));
+ sed->CursX = region->MaxX;
+ sed->Flags |= PEF_MARKING;
+ }
+
+ if (rp = ObtainGIRPort (((struct opSet *)msg)->ops_GInfo))
+ {
+ if (!(sed->Flags & PEF_MARKING))
+ ClearRange (rp, sed);
+
+ DrawCursor (rp, sed, g);
+
+ ReleaseGIRPort (rp);
+ }
+
+ break;
+ }
+
+ case SEA_Instrument:
+ sed->Instrument = (struct Instrument *) ti->ti_Data;
+ redraw = TRUE;
+ FreeUndoBuffers (sed, TRUE);
+
+ if (sed->Instrument == NULL)
+ sed->CursX = sed->VisibleX = 0;
+ else
+ {
+ /* Recalculate pattern dimensions */
+ if (CalcDisplaySize (sed, g, msg->ops_GInfo))
+ NotifyHSlider (g, msg->ops_GInfo, 0);
+ }
+ break;
+
+ case SEA_MaxUndoLevels:
+ sed->MaxUndoLevels = ti->ti_Data;
+ FreeUndoBuffers (sed, FALSE);
+ break;
+
+ case SEA_MaxUndoMem:
+ sed->MaxUndoMem = ti->ti_Data;
+
+ /* Unlimited undo memory */
+ if (sed->MaxUndoMem == 0) sed->MaxUndoMem = ~0;
+
+ FreeUndoBuffers (sed, FALSE);
+
+ break;
+
+ case SEA_BackgroundPen:
+ if (sed->BackgroundPen != ti->ti_Data)
+ {
+ sed->BackgroundPen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ case SEA_LinePen:
+ if (sed->LinePen != ti->ti_Data)
+ {
+ sed->LinePen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ case SEA_FillPen:
+ if (sed->FillPen != ti->ti_Data)
+ {
+ sed->FillPen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ case SEA_GridPen:
+ if (sed->GridPen != ti->ti_Data)
+ {
+ sed->GridPen = ti->ti_Data;
+ redraw_all = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } /* End while (NextTagItem()) */
+
+
+ result = DoSuperMethodA (cl, (Object *)g, msg);
+
+ if (redraw_all || scroll || move_cursor || zoom)
+ {
+ WORD scrolled = 0;
+
+ if (rp = ObtainGIRPort (msg->ops_GInfo))
+ {
+ if (redraw_all)
+ {
+ DoMethod ((Object *)g, GM_RENDER, ((struct opSet *)msg)->ops_GInfo, rp, GREDRAW_REDRAW);
+ scrolled = 3;
+ }
+ else if (scroll)
+ scrolled = ScrollPattern (rp, sed, g, lefttrack, topline);
+
+ if (move_cursor)
+ scrolled |= DrawCursor (rp, sed, g);
+
+ ReleaseGIRPort (rp);
+ }
+
+ if (scrolled & 1)
+ NotifyVSlider (g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
+ if (scrolled & 2)
+ NotifyHSlider (g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
+
+ if (move_cursor || scrolled)
+ NotifyCursor (g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
+ }
+}
+
+
+
+static LONG SAMP_GetMethod (Class *cl, struct ExtGadget *g, struct opGet *msg)
+{
+ struct SampEditData *sed = INST_DATA (cl, g);
+
+ switch (msg->opg_AttrID)
+ {
+ case SEA_CursXPos:
+ *msg->opg_Storage = (ULONG) sed->CursXPos;
+ break;
+
+ case SEA_CursYPos:
+ *msg->opg_Storage = (ULONG) sed->CursYPos;
+ break;
+
+ case SEA_XStart:
+ *msg->opg_Storage = (ULONG) sed->XStart;
+ break;
+
+ case SEA_YStart:
+ *msg->opg_Storage = (ULONG) sed->YStart;
+ break;
+
+ case SEA_XRatio:
+ *msg->opg_Storage = (ULONG) sed->XRatio;
+ break;
+
+ case SEA_YRatio:
+ *msg->opg_Storage = (ULONG) sed->YRatio;
+ break;
+
+ case SEA_MarkRegion:
+ {
+ struct Rectangle *region = (struct Rectangle *) *msg->opg_Storage);
+
+ region->MinX = min (sed->RangeStartX, sed->RangeEndX);
+ region->MaxX = max (sed->RangeStartX, sed->RangeEndX);
+ region->MinY = min (sed->RangeStartY, sed->RangeEndY);
+ region->MaxY = max (sed->RangeStartY, sed->RangeEndY);
+ break;
+ }
+
+ case SEA_Flags:
+ *msg->opg_Storage = (ULONG) sed->Flags;
+ break;
+
+ case SEA_Instrument:
+ *msg->opg_Storage = (ULONG) sed->Instrument;
+ break;
+
+ default:
+ return DoSuperMethodA (cl, (Object *)g, msg);
+ }
+
+ return TRUE;
+}
+
+
+
+static ULONG SAMP_NewMethod (Class *cl, struct ExtGadget *g, struct opSet *msg)
+{
+ struct SampEditData *sed;
+ ULONG tmp;
+
+ if (!(g = DoSuperMethodA (cl, (Object *)g, msg)))
+ return NULL;
+
+ sed = INST_DATA (cl, g); /* Get pointer to instance data */
+
+ memset (sed, 0, sizeof (struct SampEditData));
+
+ /* We are going to use ScrollRaster() in this gadget... */
+
+ if (g->Flags & GFLG_EXTENDED)
+ g->MoreFlags |= GMORE_SCROLLRASTER;
+
+ g->Flags |= GFLG_TABCYCLE | GFLG_RELSPECIAL;
+
+ /* Initialize our lists */
+ NEWLIST ((struct List *)&sed->UndoList);
+ NEWLIST ((struct List *)&sed->RedoList);
+
+
+ /* Initialize attributes */
+
+ sed->Instrument = (struct Instrument *) GetTagData (SEA_Instrument, NULL, msg->ops_AttrList);
+ sed->BackgroundPen = GetTagData (SEA_BackgroundPen, 0, msg->ops_AttrList);
+ sed->LinePen = GetTagData (SEA_LinePen, 2, msg->ops_AttrList);
+ sed->FillPen = GetTagData (SEA_FillPen, 1, msg->ops_AttrList);
+ sed->GridPen = GetTagData (SEA_GridPen, 3, msg->ops_AttrList);
+ sed->MaxUndoLevels = GetTagData (SEA_MaxUndoLevels, 8, ((struct opSet *)msg)->ops_AttrList);
+ sed->MaxUndoMem = GetTagData (SEA_MaxUndoMem, 32768, ((struct opSet *)msg)->ops_AttrList);
+ sed->Flags = GetTagData (SEA_Flags, 0, ((struct opSet *)msg)->ops_AttrList);
+
+ /* Unlimited undo memory */
+ if (sed->MaxUndoMem == 0) sed->MaxUndoMem = ~0;
+
+ return (ULONG) g;
+}
+
+
+
+static void NotifyCursor (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
+{
+ /* Always sends notification if the gadget has the GACT_IMMEDIATE
+ * flag set. If it isn't, the editor will report its cursor
+ * position only the last time it updates the cursor position.
+ */
+ if ((g->Activation & GACT_IMMEDIATE) || !(flags & OPUF_INTERIM))
+ {
+ struct SampEditData *sed = INST_DATA (OCLASS(g), g);
+
+ NotifyAttrChanges ((Object *)g, gi, flags,
+ SEA_CursXPos, sed->CursXPos,
+ SEA_CursYPos, sed->CursYPos,
+ GA_ID, g->GadgetID,
+ TAG_DONE);
+ }
+}
+
+
+
+static void NotifyHSlider (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
+{
+ struct SampEditData *sed = INST_DATA (OCLASS(g), g);
+
+ NotifyAttrChanges ((Object *)g, gi, flags,
+ SEA_XStart, sed->XStart;
+ GA_ID, g->GadgetID,
+ TAG_DONE);
+}
+
+
+
+static void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect)
+
+/* This function gets the actual IBox where a gadget exists
+ * in a window. The special cases it handles are all the REL#?
+ * (relative positioning flags).
+ *
+ * The function takes a struct GadgetInfo pointer, a struct Gadget
+ * pointer, and a struct IBox pointer. It uses the window and
+ * gadget to fill in the IBox.
+ */
+{
+ rect->Left = g->LeftEdge;
+ if (g->Flags & GFLG_RELRIGHT) rect->Left += ginfo->gi_Domain.Width - 1;
+
+ rect->Top = g->TopEdge;
+ if (g->Flags & GFLG_RELBOTTOM) rect->Top += ginfo->gi_Domain.Height - 1;
+
+ rect->Width = g->Width;
+ if (g->Flags & GFLG_RELWIDTH) rect->Width += ginfo->gi_Domain.Width;
+
+ rect->Height = g->Height;
+ if (g->Flags & GFLG_RELHEIGHT) rect->Height += ginfo->gi_Domain.Height;
+}
+
+
+
+XDEF HOOKCALL struct Library *_UserLibInit (REG(a6,struct Library *mybase))
+{
+ SysBase = *((struct ExecBase **)4);
+
+ IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 37);
+ GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 37);
+ UtilityBase = OpenLibrary ("utility.library", 37);
+
+ if (!(IntuitionBase && GfxBase && UtilityBase && KeymapBase))
+ {
+ _UserLibCleanup (mybase);
+ return NULL;
+ }
+
+ if (SampEditClass = MakeClass (SAMPEDITCLASS, GADGETCLASS, NULL, sizeof (struct SampEditData), 0))
+ {
+ SampEditClass->cl_Dispatcher.h_Entry = (ULONG (*)()) SampEditDispatcher;
+ AddClass (SampEditClass);
+ }
+ else
+ {
+ _UserLibCleanup (mybase);
+ return NULL;
+ }
+
+ return mybase;
+}
+
+
+
+XDEF HOOKCALL struct Library *_UserLibCleanup (REG(a6,struct Library *mybase))
+{
+ if (SampEditClass)
+ if (!FreeClass (SampEditClass))
+ return NULL;
+
+ /* Passing NULL to CloseLibrary() is safe */
+ CloseLibrary ((struct Library *)UtilityBase);
+ CloseLibrary ((struct Library *)GfxBase);
+ CloseLibrary ((struct Library *)IntuitionBase);
+
+ return mybase;
+}
+
+
+
+XDEF HOOKCALL struct IClass *_GetEngine (REG(a6,struct Library *mybase))
+{
+ return (SampEditClass);
+}
--- /dev/null
+/*
+** GST.c
+**
+** Copyright (C) 1994,95,96,97 by Bernardo Innocenti
+**
+** This is a dummy source file used to make the Global Symbol Table
+** for XModule.
+*/
+
+#define ASL_V38_NAMES_ONLY
+#define INTUI_V36_NAMES_ONLY
+#define IFFPARSE_V37_NAMES_ONLY
+#define USE_BUILTIN_MATH
+#define __USE_SYSBASE
+
+#include <exec/lists.h>
+#include <exec/memory.h>
+#include <exec/nodes.h>
+#include <devices/audio.h>
+#include <dos/dos.h>
+#include <dos/dostags.h>
+#include <intuition/intuition.h>
+#include <intuition/classes.h>
+#include <intuition/classusr.h>
+#include <intuition/icclass.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <libraries/asl.h>
+#include <libraries/commodities.h>
+#include <libraries/gadtools.h>
+#include <libraries/reqtools.h>
+#include <libraries/iffparse.h>
+#include <datatypes/soundclass.h>
+#include <graphics/rpattr.h>
+#include <graphics/gfxmacros.h>
+#include <graphics/gfxbase.h>
+#include <utility/tagitem.h>
+#include <rexx/storage.h>
+#include <rexx/errors.h>
+#include <workbench/workbench.h>
+#include <workbench/startup.h>
+
+#include <proto/asl.h>
+#include <proto/commodities.h>
+#include <proto/datatypes.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/gadtools.h>
+#include <proto/graphics.h>
+#include <proto/keymap.h>
+#include <proto/icon.h>
+#include <proto/iffparse.h>
+#include <proto/intuition.h>
+#include <proto/powerpacker.h>
+#include <proto/reqtools.h>
+#include <proto/utility.h>
+#include <proto/wb.h>
+#include <proto/xmodule.h>
+
+#ifndef LIBRARIES_PATTEDITCLASS_H
+#include <libraries/patteditclass.h>
+#endif /* !LIBRARIES_PATTEDITCLASS_H */
+
+#ifndef LIBRARIES_XMODULECLASS_H
+#include <libraries/xmoduleclass.h>
+#endif /* !LIBRARIES_XMODULECLASS_H */
+
+#ifndef LIBRARIES_SONGCLASS_H
+#include <libraries/songclass.h>
+#endif /* !LIBRARIES_SONGCLASS_H */
+
+#ifndef LIBRARIES_XMODULE_H
+#include <libraries/xmodule.h>
+#endif /* !LIBRARIES_XMODULE_H */
+
+/* Can't include this one here because of the inline functions defined in
+ *
+ * #include "XModulePriv.h"
+ */
+
+#include "Gui.h"
--- /dev/null
+/*
+** Gui.c
+**
+** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
+**
+** Graphic User Interface handling routines.
+*/
+
+#include <exec/nodes.h>
+#include <exec/memory.h>
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <utility/tagitem.h>
+#include <graphics/rpattr.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/graphics.h>
+#include <proto/layers.h>
+#include <proto/keymap.h>
+#include <proto/utility.h>
+#include <proto/commodities.h>
+#include <proto/diskfont.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+#include "CustomClasses.h"
+
+
+
+XDEF struct Screen *Scr = NULL;
+XDEF struct ScrInfo ScrInfo = {0};
+XDEF APTR VisualInfo = NULL;
+XDEF struct DrawInfo *DrawInfo = NULL;
+
+
+XDEF struct TextAttr
+ TopazAttr = { "topaz.font", 8, FS_NORMAL, FPF_ROMFONT },
+ ScreenAttr = { 0 },
+ WindowAttr = { 0 },
+ ListAttr = { 0 },
+ EditorAttr = { 0 };
+
+XDEF struct TextFont
+ *TopazFont = NULL,
+ *WindowFont = NULL,
+ *ListFont = NULL;
+
+
+/* Window borders layout information */
+XDEF UWORD OffX, OffY; /* X and Y offsets for window rendering */
+XDEF WORD SizeWidth = 18, /* Dimensions of the window size gadget */
+ SizeHeight = 10;
+
+/* IDCMP windows support */
+XDEF ULONG IDCMPSig = 0; /* Signal for above mentioned port */
+XDEF ULONG Signals = SIGBREAKFLAGS; /* Signals for Wait() in main loop */
+XDEF struct IntuiMessage IntuiMsg; /* A copy of the last received IntuiMsg */
+XDEF struct List WindowList; /* Linked list of all open windows */
+
+static UBYTE ActiveKey = 0; /* These three are used to handle */
+static struct Gadget *ActiveGad = NULL; /* selection of button gadgets */
+static struct Window *ActiveWin = NULL; /* with keyboard shortcuts. */
+static struct Window *OldPrWindowPtr = (struct Window *)1L;
+
+XDEF LONG LastErr = 0;
+XDEF ULONG UniqueID; /* An ID got from GetUniqueID() */
+XDEF UWORD WinLockCount = 0; /* Allow nesting of window locking */
+XDEF BOOL Quit = FALSE;
+XDEF BOOL DoNextSelect = TRUE; /* Menu selection, see menu handling code */
+XDEF BOOL ShowRequesters = TRUE;
+XDEF BOOL OwnScreen = FALSE; /* Are we owners or visitors? */
+XDEF BOOL ScreenReopening = FALSE; /* Set to TRUE while reopening windows */
+XDEF BOOL ScreenShutdown = FALSE; /* Set to TRUE while shutting down screen */
+XDEF Class *ScrollButtonClass = NULL;
+
+/* Shared IDCMP port for all windows */
+static struct MsgPort *WinPort = NULL;
+static Class *VImageClass = NULL;
+static ULONG VImageUseCount = 0;
+static struct Image *ButtonFrame = NULL;
+static WORD ButtonFrameWidth = 0;
+static WORD ButtonFrameHeight = 0;
+
+
+XDEF struct WDescr WDescr[WID_COUNT] =
+{
+ { NULL, 0, 0, (struct TagItem *)ToolBoxWinTags },
+ { NULL, 0, 0, (struct TagItem *)PrefsWinTags },
+ { NULL, 0, 0, (struct TagItem *)SongInfoWinTags },
+ { NULL, 0, 0, (struct TagItem *)InstrumentsWinTags },
+ { NULL, 0, 0, (struct TagItem *)SequenceWinTags },
+ { NULL, 0, 0, (struct TagItem *)PatternWinTags },
+ { NULL, 0, 0, (struct TagItem *)PlayWinTags },
+ { NULL, 0, 0, (struct TagItem *)SampleWinTags },
+ { NULL, 0, 0, (struct TagItem *)OptimizationWinTags },
+ { NULL, 0, 0, (struct TagItem *)SaversWinTags },
+ { NULL, 0, 0, (struct TagItem *)PattPrefsWinTags },
+ { NULL, 0, 0, (struct TagItem *)PattSizeWinTags },
+ { NULL, 0, 0, (struct TagItem *)ClearWinTags },
+ { NULL, 0, 0, (struct TagItem *)LogWinTags },
+ { NULL, 0, 0, (struct TagItem *)ProgressWinTags }
+};
+
+
+
+XDEF struct GuiSwitches GuiSwitches =
+{
+ TRUE, /* SaveIcons */
+ TRUE, /* AskOverwrite */
+ TRUE, /* AskExit */
+ TRUE, /* ShowAppIcon */
+ FALSE, /* UseReqTools */
+ TRUE, /* SmartRefresh */
+ TRUE, /* UseDataTypes */
+ TRUE, /* InstrSaveIcons */
+ TRUE, /* AskAutosave */
+ FALSE, /* DoBackups */
+ FALSE, /* LogToFile */
+ INST_8SVX, /* InstrSaveMode */
+ 1, /* SampDrawMode */
+ XMDMF_NOTE+1, /* LogLevel */
+ 0, /* AutosaveTime */
+ 3, /* BackupVersions */
+ "*,#", /* BackupTemplate */
+ "CON:////XModule Log/AUTO/CLOSE/INACTIVE/SCREEN XMODULE" /* LogFile */
+};
+
+
+
+#ifndef OS30_ONLY
+
+/* Wait pointer image data */
+
+static __chip UWORD WaitPointer[] =
+{
+ 0x0000, 0x0000,
+
+ 0x0400, 0x07c0,
+ 0x0000, 0x07c0,
+ 0x0100, 0x0380,
+ 0x0000, 0x07e0,
+ 0x07c0, 0x1ff8,
+ 0x1ff0, 0x3fec,
+ 0x3ff8, 0x7fde,
+ 0x3ff8, 0x7fbe,
+ 0x7ffc, 0xff7f,
+ 0x7efc, 0xffff,
+ 0x7ffc, 0xffff,
+ 0x3ff8, 0x7ffe,
+ 0x3ff8, 0x7ffe,
+ 0x1ff0, 0x3ffc,
+ 0x07c0, 0x1ff8,
+ 0x0000, 0x07e0,
+
+ 0x0000, 0x0000
+};
+
+#endif /* OS30_ONLY */
+
+
+/* Martin Taillefer's block pointer */
+/*
+static UWORD __chip BlockPointer[] =
+{
+ 0x0000, 0x0000,
+
+ 0x0000, 0x0100,
+ 0x0100, 0x0280,
+ 0x0380, 0x0440,
+ 0x0100, 0x0280,
+ 0x0100, 0x0ee0,
+ 0x0000, 0x2828,
+ 0x2008, 0x5834,
+ 0x783c, 0x8002,
+ 0x2008, 0x5834,
+ 0x0000, 0x2828,
+ 0x0100, 0x0ee0,
+ 0x0100, 0x0280,
+ 0x0380, 0x0440,
+ 0x0100, 0x0280,
+ 0x0000, 0x0100,
+ 0x0000, 0x0000,
+
+ 0x0000, 0x0000
+};
+*/
+
+
+/* Local function prototypes */
+
+static void HandleKey (void);
+static void SelectButton (struct Window *win, struct Gadget *gad);
+static void DeselectButton (void);
+static void RenderWindowBorders(struct WinUserData *wud);
+static struct WindowBorder *CreateWindowBorder (struct WinUserData *wud, UWORD left, UWORD top, UWORD width, UWORD height, ULONG bordertype);
+static struct Gadget *CreateVImageButton (struct TagItem *tags, struct NewGadget *ng);
+static void DeleteVImageButton (struct Gadget *g);
+static struct Gadget *CreateGadgets (struct LayoutGadgetsArgs *lga, UWORD mode, UWORD left, UWORD top, UWORD width, UWORD height);
+static void LayoutGadgets (struct LayoutGadgetsArgs *lga, UWORD mode);
+static void DeleteGadgets (struct WinUserData *wud);
+
+static void DeleteWUD (struct WinUserData *wud);
+static struct WinUserData *CreateLayoutInfo (struct WinUserData *wud);
+
+
+
+GLOBALCALL LONG HandleGui (void)
+
+/* Handle XModule GUI - Main event handling loop */
+{
+ ULONG recsig; /* Received Signals */
+ LONG rc = 0; /* Return Code */
+
+
+ /* This is the main event handling loop */
+
+ while (!Quit)
+ {
+ recsig = Wait (Signals);
+
+ if (recsig & IDCMPSig)
+ HandleIDCMP();
+
+ if (recsig & AudioSig)
+ HandleAudio();
+
+ if (recsig & PubPortSig)
+ HandleRexxMsg();
+
+ if (recsig & AppSig)
+ HandleAppMessage();
+
+ if (recsig & FileReqSig)
+ HandleFileRequest();
+
+ if (recsig & CxSig)
+ HandleCx();
+
+ if (recsig & AmigaGuideSig)
+ HandleAmigaGuide();
+
+ /* Check break signals */
+ if (recsig & SIGBREAKFLAGS)
+ {
+ if (recsig & SIGBREAKF_CTRL_C)
+ {
+ Quit = TRUE;
+ GuiSwitches.AskExit = FALSE;
+ rc = ERROR_BREAK;
+ }
+
+ if (recsig & SIGBREAKF_CTRL_D)
+ if (MyBroker) ActivateCxObj (MyBroker, FALSE);
+
+ if (recsig & SIGBREAKF_CTRL_E)
+ if (MyBroker) ActivateCxObj (MyBroker, TRUE);
+
+ if (recsig & SIGBREAKF_CTRL_F)
+ DeIconify();
+ }
+
+
+ if (LastErr)
+ {
+ switch (LastErr)
+ {
+ case ERROR_NO_FREE_STORE:
+ ShowMessage (MSG_NO_FREE_STORE);
+ break;
+
+ case ERROR_BREAK:
+ ShowMessage (MSG_BREAK);
+ break;
+
+ default:
+ break;
+ }
+
+ DisplayBeep (Scr);
+ LastErr = 0;
+ }
+
+ if (Quit && GuiSwitches.AskExit)
+ if (!ShowRequestArgs (MSG_REALLY_QUIT_XMODULE, MSG_YES_OR_NO, NULL))
+ {
+ Quit = FALSE;
+ rc = 0;
+ }
+
+ } /* End main loop */
+
+ return rc;
+}
+
+
+
+/* Intuition Event Handler. Based on GadToolsBox's HandleIDCMP() */
+GLOBALCALL void HandleIDCMP (void)
+{
+ struct IntuiMessage *m;
+ struct MenuItem *n;
+ struct Window *win;
+ struct WinUserData *wud;
+
+ while (m = GT_GetIMsg (WinPort))
+ {
+ IntuiMsg = *m; /* Make a local copy and return message immediately */
+
+ GT_ReplyIMsg (m);
+
+ win = IntuiMsg.IDCMPWindow;
+ wud = (struct WinUserData *)win->UserData;
+
+ switch (IntuiMsg.Class)
+ {
+ case IDCMP_REFRESHWINDOW:
+
+ /* TODO: Handle multiple IDCMP_REFRESHWINDOW
+ * messages sent at the same time.
+ */
+
+ /* Lock Layer in sizeable windows so its size
+ * won't change until we are finished rendering
+ * on it.
+ *
+ * Newsflash: **DON'T!** Layers BeginUpdate() will already
+ * lock the window layer for us, so doing it again we would
+ * risk a complete GUI deadlock in some particular conditions...
+ */
+ /* if (win->Flags & WFLG_SIZEGADGET)
+ * LockLayer (NULL, win->WLayer);
+ */
+
+ if (wud->WUDFlags & WUDF_JUSTRESIZED)
+ {
+ // RefreshGadgets (wud->GList, win, NULL);
+ GT_RefreshWindow (win, NULL);
+ RefreshWindowFrame (win);
+
+ if (wud->Borders)
+ RenderWindowBorders (wud);
+
+ wud->WUDFlags &= ~WUDF_JUSTRESIZED;
+ }
+ else
+ {
+ GT_BeginRefresh (win);
+
+ if (wud->Borders)
+ RenderWindowBorders (wud);
+
+ GT_EndRefresh (win, TRUE);
+ }
+
+ /* if (win->Flags & WFLG_SIZEGADGET)
+ * UnlockLayer (win->WLayer);
+ */
+ break;
+
+ case IDCMP_RAWKEY:
+ HandleKey ();
+ break;
+
+ case IDCMP_NEWSIZE:
+ {
+ struct Gadget *g;
+ struct LayoutGadgetsArgs lga;
+ struct RastPort rp;
+ ULONG SpecialTags[20];
+ UWORD newwidth, newheight;
+
+ /* TODO: Handle multiple IDCMP_NEWSIZE
+ * messages sent at the same time.
+ */
+
+// LockLayer (NULL, win->WLayer);
+
+ newwidth = win->Width - win->BorderLeft - win->BorderRight;
+ newheight = win->Height - win->BorderTop - win->BorderBottom;
+
+ if ((wud->WindowSize.Width == newwidth) &&
+ (wud->WindowSize.Height == newheight))
+ {
+// UnlockLayer (win->WLayer);
+ break;
+ }
+
+ if (!(wud->WUDFlags & WUDF_CUSTOMLAYOUT))
+ {
+ /* Detatch and free old gadgets */
+ RemoveGList (win, wud->GList, -1);
+ DeleteGadgets (wud);
+
+ /* Clear old window region (or what remains of it) */
+ SetAPen (win->RPort, 0);
+ SetDrMd (win->RPort, JAM1);
+ RectFill (win->RPort, win->BorderLeft, win->BorderTop,
+ min (wud->WindowSize.Width, newwidth) + win->BorderLeft - 1,
+ min (wud->WindowSize.Height, newheight) + win->BorderTop - 1);
+
+ wud->WindowSize.Width = newwidth;
+ wud->WindowSize.Height = newheight;
+
+
+ /* Layout phase 2 */
+
+ InitRastPort (&rp);
+ SetFont (&rp, wud->Font);
+
+ if (!CreateContext (&wud->GList))
+ {
+// UnlockLayer (win->WLayer);
+ DeleteWUD (wud);
+ break;
+ }
+
+ lga.Args = wud->LayoutArgs;
+ lga.VInfo = VisualInfo;
+ lga.PrevGad = wud->GList;
+ lga.GInfo = wud->GInfo;
+ lga.Wud = wud;
+ lga.DummyRast = &rp;
+ lga.Count = 0;
+ lga.SpecialTags = SpecialTags;
+
+ SpecialTags[0] = GT_Underscore;
+ SpecialTags[1] = (ULONG) '_';
+
+ g = CreateGadgets (&lga, LAYOUTMODE_V, OffX + lga.GInfo[0].LabelWidth + HSPACING, OffY + VSPACING,
+ wud->WindowSize.Width - lga.GInfo[0].LabelWidth - HSPACING * 2,
+ wud->WindowSize.Height - VSPACING * 2);
+
+ if (!g)
+ {
+// UnlockLayer (win->WLayer);
+ DeleteWUD (wud);
+ break;
+ }
+
+ AddGList (win, wud->GList, -1, -1, NULL);
+ wud->WUDFlags |= WUDF_JUSTRESIZED;
+ }
+
+ if (wud->IDCMPFunc) wud->IDCMPFunc (wud);
+
+// UnlockLayer (win->WLayer);
+ break;
+ }
+
+ case IDCMP_CLOSEWINDOW:
+ if (wud->WindowID == WID_TOOLBOX)
+ Quit = 1;
+ else
+ MyCloseWindow (wud);
+
+ break;
+
+ case IDCMP_GADGETUP:
+ case IDCMP_GADGETDOWN:
+ {
+ struct Gadget *gad = (struct Gadget *)IntuiMsg.IAddress;
+
+ /* Toggle switch */
+ if (wud->GInfo[gad->GadgetID].GKind == CHECKBOX_KIND)
+ {
+ UWORD *check;
+
+ if (check = (WORD *)(wud->GInfo[gad->GadgetID].SpecialStorage))
+ *check ^= 1;
+ }
+
+ /* Execute function */
+ if (((struct Gadget *)IntuiMsg.IAddress)->UserData)
+ ((void (*)(struct WinUserData *)) gad->UserData) (wud);
+
+ break;
+ }
+
+ case IDCMP_MENUPICK:
+ while (IntuiMsg.Code != MENUNULL)
+ {
+ n = ItemAddress (win->MenuStrip, IntuiMsg.Code);
+ ((void (*)(struct WinUserData *))(GTMENUITEM_USERDATA(n))) (wud);
+
+ /* Some window operations invalidate the menu
+ * we are working on. For istance, Re-opening a
+ * window causes the old MenuStrip to be killed.
+ * The DoNextSelect flag provides a way to stop
+ * this loop and avoid a nasty crash.
+ */
+ if (!DoNextSelect)
+ {
+ DoNextSelect = TRUE;
+ break;
+ }
+ IntuiMsg.Code = n->NextSelect;
+ }
+ break;
+
+ case IDCMP_INACTIVEWINDOW:
+ DeselectButton();
+ if (wud->IDCMPFunc) wud->IDCMPFunc (wud);
+ break;
+
+ case IDCMP_MENUHELP:
+ case IDCMP_GADGETHELP:
+ HandleHelp (&IntuiMsg);
+ break;
+
+ default:
+ if (wud->IDCMPFunc) wud->IDCMPFunc (wud);
+ break;
+
+ } /* End switch (IntuiMsg.Class) */
+
+ if (!WinPort) break;
+
+ } /* End while (GT_GetIMsg ()) */
+}
+
+
+
+static void HandleKey (void)
+{
+ struct Window *win = IntuiMsg.IDCMPWindow;
+ struct WinUserData *wud = (struct WinUserData *)win->UserData;
+ UWORD i;
+ UBYTE keycode;
+
+
+ /* Handle key up for buttons */
+
+ if (IntuiMsg.Code & IECODE_UP_PREFIX)
+ {
+ struct Gadget *gad = ActiveGad;
+
+ DeselectButton();
+ if (gad && (ActiveKey == (IntuiMsg.Code & ~IECODE_UP_PREFIX)))
+ ((void (*)(struct WinUserData *)) gad->UserData) (wud);
+
+ return;
+ }
+
+ switch (IntuiMsg.Code)
+ {
+ case 0x5F: /* HELP */
+ HandleHelp (&IntuiMsg);
+ return;
+
+ case CURSORUP:
+ case CURSORDOWN:
+
+ if (wud->GInfo)
+ for (i = 0; i < wud->GCount; i++)
+ if (wud->GInfo[i].GKind == LISTVIEW_KIND)
+ {
+ struct Gadget *g = wud->Gadgets[i];
+ LONG selected, oldselected, top = ~0;
+
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version < 39)
+ selected = (LONG)(*(UWORD *)(((char *)g)+sizeof(struct Gadget)+48));
+ /* top = *(short *)(((char *)gad) + sizeof(struct Gadget) + 6); +4 ? */
+ else
+#endif /* !OS30_ONLY */
+ GT_GetGadgetAttrs (g, win, NULL,
+ GTLV_Selected, &selected,
+ GTLV_Top, &top,
+ TAG_DONE);
+
+ selected = (LONG)((WORD) selected); /* Extend to long */
+ oldselected = selected; /* Make a backup of it */
+
+ if (selected == ~0)
+ selected = top; /* Scroll Top */
+ else
+ top = ~0;
+
+ if (IntuiMsg.Code == CURSORUP)
+ {
+ if (IntuiMsg.Qualifier & IEQUALIFIER_SHIFT)
+ selected -= 5;
+ else if (IntuiMsg.Qualifier & IEQUALIFIER_ALT)
+ selected = 0;
+ else
+ selected--;
+ }
+ else /* CURSORDOWN */
+ {
+ if (IntuiMsg.Qualifier & IEQUALIFIER_SHIFT)
+ selected += 5;
+ else if (IntuiMsg.Qualifier & IEQUALIFIER_ALT)
+ selected = 65535;
+ else
+ selected++;
+ }
+
+ if (selected < 0) selected = 0;
+
+ GT_SetGadgetAttrs (g, win, NULL,
+ (top == ~0) ? GTLV_Selected : GTLV_Top, selected,
+ (top == ~0) ? GTLV_MakeVisible : TAG_IGNORE, selected,
+ TAG_DONE);
+
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version < 39)
+ selected = (LONG)(*(UWORD *)(((char *)g)+sizeof(struct Gadget)+48));
+ else
+#endif /* !OS30_ONLY */
+ GT_GetGadgetAttrs (g, win, NULL,
+ GTLV_Selected, &selected,
+ TAG_DONE);
+
+ if (selected != oldselected)
+ {
+ IntuiMsg.Code = selected;
+ if (g->UserData)
+ ((void (*)(struct WinUserData *)) g->UserData) (wud);
+ break; /* Stop for() loop */
+ }
+ } /* End for */
+ return;
+
+ case 0x42: /* TAB */
+ if (IntuiMsg.Qualifier & IEQUALIFIER_ALT)
+ {
+ struct WinUserData *nextwud;
+
+ if (IntuiMsg.Qualifier & IEQUALIFIER_SHIFT)
+ {
+ /* ALT+SHIFT+TAB: Cycle windows backwards */
+
+ nextwud = (struct WinUserData *)wud->Link.mln_Pred;
+
+ if (!(nextwud->Link.mln_Pred)) /* List head? */
+ nextwud = (struct WinUserData *)WindowList.lh_TailPred;
+ }
+ else
+ {
+ /* ALT+TAB: Cycle windows */
+
+ nextwud = (struct WinUserData *)wud->Link.mln_Succ;
+
+ if (!(nextwud->Link.mln_Succ)) /* List tail? */
+ nextwud = (struct WinUserData *)WindowList.lh_Head;
+ }
+
+ RevealWindow (nextwud);
+ return;
+ }
+
+ default:
+ break;
+
+ } /* End switch (IntuiMsg.Code) */
+
+
+ /* Convert the IDCMP_RAWKEY IntuiMessage to the single
+ * character representation it corresponds to. If this isn't
+ * possible (e.g. a HELP key or cursor key) then abort.
+ */
+ {
+ static struct InputEvent ie;
+
+ ie.ie_NextEvent = NULL;
+ ie.ie_Class = IECLASS_RAWKEY;
+ ie.ie_SubClass = 0;
+ ie.ie_Code = IntuiMsg.Code;
+ ie.ie_Qualifier = IntuiMsg.Qualifier & IEQUALIFIER_CONTROL; /* Filter qualifiers. */
+ ie.ie_EventAddress = (APTR *) *((ULONG *)IntuiMsg.IAddress);
+ if (MapRawKey (&ie, &keycode, 1, NULL) != 1)
+ return;
+ }
+
+
+ /* Handle IDCMP_VANILLAKEY */
+
+ /* Check special keys */
+ switch (keycode)
+ {
+ case 0x03: /* CTRL-C */
+ Signal ((struct Task *)ThisTask, SIGBREAKF_CTRL_C);
+ return;
+
+ case 0x09: /* TAB */
+ case 0x0D: /* RETURN */
+ if (wud->GInfo)
+ for (i = 0; i < wud->GCount; i++)
+ if (wud->GInfo[i].GKind == STRING_KIND || wud->GInfo[i].GKind == INTEGER_KIND)
+ ActivateGadget (wud->Gadgets[i],win, NULL);
+ return;
+
+ case 0x1B: /* ESC */
+ if (wud->WindowID != WID_TOOLBOX)
+ MyCloseWindow (wud);
+ return;
+
+ default:
+ break;
+ }
+
+
+ /* Look for gadget shortcuts */
+
+ if (wud->GInfo)
+ for (i = 0; i < wud->GCount; i++)
+ {
+ if (wud->Keys[i] == keycode) /* Case insensitive compare */
+ {
+ struct Gadget *g = wud->Gadgets[i];
+ LONG disabled = FALSE;
+
+ /* Check disabled */
+
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version < 39)
+ disabled = g->Flags & GFLG_DISABLED;
+ else
+#endif /* !OS30_ONLY */
+ GT_GetGadgetAttrs (g, win, NULL,
+ GA_Disabled, &disabled,
+ TAG_DONE);
+
+ if (disabled) break; /* Stop for() loop */
+
+ switch (wud->GInfo[i].GKind)
+ {
+ case BUTTON_KIND:
+ if (!(IntuiMsg.Qualifier & IEQUALIFIER_REPEAT))
+ SelectButton (win, g);
+ break;
+
+ case CHECKBOX_KIND:
+
+ /* Toggle switch */
+ if (wud->GInfo[i].SpecialStorage)
+ *((UWORD *)wud->GInfo[i].SpecialStorage) ^= 1;
+
+ GT_SetGadgetAttrs (g, win, NULL,
+ GTCB_Checked, !(g->Flags & GFLG_SELECTED),
+ TAG_DONE);
+
+ if (g->UserData)
+ ((void (*)(struct WinUserData *)) g->UserData) (wud);
+ break;
+
+ case INTEGER_KIND:
+ case STRING_KIND:
+ ActivateGadget (g, win, NULL);
+ break;
+
+ case CYCLE_KIND:
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ {
+ LONG act, max;
+ UBYTE **lab;
+
+ /* ON V37: active = *(short *)(((char *)gad) + sizeof(struct Gadget) + 6); */
+
+ GT_GetGadgetAttrs (g, win, NULL,
+ GTCY_Active, &act,
+ GTCY_Labels, &lab,
+ TAG_DONE);
+
+ act = (LONG)((UWORD)act); /* Extend to LONG */
+
+ if (IntuiMsg.Qualifier & IEQUALIFIER_SHIFT)
+ act--;
+ else
+ act++;
+
+ for (max = 0; lab[max]; max++); /* Count labels */
+
+ if (act >= max) act = 0;
+ else if (act < 0) act = max - 1;
+
+ GT_SetGadgetAttrs (g, win, NULL,
+ GTCY_Active, act,
+ TAG_DONE);
+
+ if (g->UserData)
+ {
+ IntuiMsg.Code = act;
+ ((void (*)(struct WinUserData *)) g->UserData) (wud);
+ }
+ }
+ break;
+
+ case MX_KIND:
+ {
+ LONG act;
+
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version < 39)
+ act = (LONG)(*(UWORD *)(((char *)g)+sizeof(struct Gadget)+24)); /* 38? */
+ else
+#endif /* !OS30_ONLY */
+ {
+ GT_GetGadgetAttrs (g, win, NULL,
+ GTMX_Active, &act,
+ TAG_DONE);
+ act = (LONG)((UWORD)act); /* Extend to LONG */
+ }
+
+ if (IntuiMsg.Qualifier & IEQUALIFIER_SHIFT)
+ act--;
+ else
+ act++;
+
+ GT_SetGadgetAttrs (g, win, NULL,
+ GTMX_Active, act,
+ TAG_DONE);
+
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version < 39)
+ act = (LONG)(*(UWORD *)(((char *)g)+sizeof(struct Gadget)+24));
+ else
+#endif /* !OS30_ONLY */
+ {
+ GT_GetGadgetAttrs (g, win, NULL,
+ GTMX_Active, &act,
+ TAG_DONE);
+ act = (LONG)((UWORD)act); /* Extend to LONG */
+ }
+
+ if (g->UserData)
+ {
+ IntuiMsg.Code = act;
+ ((void (*)(struct WinUserData *)) g->UserData) (wud);
+ }
+
+ break;
+ }
+
+ case SLIDER_KIND:
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ {
+ LONG min, max, level;
+
+ GT_GetGadgetAttrs (g, win, NULL,
+ GTSL_Min, &min,
+ GTSL_Max, &max,
+ GTSL_Level, &level,
+ TAG_DONE);
+
+ /* Extend to LONG */
+ min = (LONG)((WORD)min);
+ max = (LONG)((WORD)max);
+ level = (LONG)((WORD)level);
+
+ if (IntuiMsg.Qualifier & IEQUALIFIER_SHIFT)
+ {
+ if (IntuiMsg.Qualifier & IEQUALIFIER_ALT)
+ level = min;
+ else level--;
+ }
+ else
+ {
+ if (IntuiMsg.Qualifier & IEQUALIFIER_ALT)
+ level = max;
+ else level++;
+ }
+
+ if (level > max) level = max;
+ if (level < min) level = min;
+
+ GT_SetGadgetAttrs (g, win, NULL,
+ GTSL_Level, level,
+ TAG_DONE);
+
+ if (g->UserData)
+ {
+ IntuiMsg.Code = level;
+ ((void (*)(struct WinUserData *)) g->UserData) (wud);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return; /* Stop for() loop */
+ }
+ } /* End for() */
+
+
+ /* There is no apparent use for this key event,
+ * let's pass the IntuiMessage to user's IDCMPFunc()...
+ */
+ if (wud->IDCMPFunc) ((void (*)(void)) wud->IDCMPFunc) ();
+}
+
+
+
+static void SelectButton (struct Window *win, struct Gadget *gad)
+
+/* Selects the button gadget <gad>. This operation is illegal with
+ * GadTools gadgets, but many programs do it anyway, so this trick
+ * will probably be supported in future OS releases :-).
+ */
+{
+ UWORD gadpos;
+
+ if (ActiveGad) DeselectButton();
+
+ gadpos = RemoveGadget (win, gad);
+
+ gad->Flags |= GFLG_SELECTED;
+ AddGadget (win, gad, gadpos);
+ RefreshGList (gad, win, NULL, 1);
+
+ ActiveKey = IntuiMsg.Code;
+ ActiveGad = gad;
+ ActiveWin = win;
+}
+
+
+
+static void DeselectButton (void)
+
+/* Deselects the button previously selected with SelectButton() */
+{
+ if (ActiveGad)
+ {
+ UWORD gadpos = RemoveGadget (ActiveWin, ActiveGad);
+
+ ActiveGad->Flags &= ~GFLG_SELECTED;
+ AddGadget (ActiveWin, ActiveGad, gadpos);
+ RefreshGList (ActiveGad, ActiveWin, NULL, 1);
+
+ ActiveGad = NULL;
+ }
+}
+
+
+
+GLOBALCALL void LockWindows (void)
+
+/* Disable user input in all windows */
+{
+ struct WinUserData *wud;
+ struct Window *win;
+ struct WindowLock *lock;
+
+
+ /* Are the windows already locked? */
+ WinLockCount++;
+ if (WinLockCount > 1) return;
+
+ for (wud = (struct WinUserData *) WindowList.lh_Head;
+ wud->Link.mln_Succ;
+ wud = (struct WinUserData *)wud->Link.mln_Succ)
+ {
+ if (!(win = wud->Win)) continue;
+
+ /* Set wait pointer */
+#ifndef OS30_ONLY
+ if (IntuitionBase->LibNode.lib_Version < 39)
+ SetPointer (win, WaitPointer, 16, 16, -6, 0);
+ else
+#endif /* !OS30_ONLY */
+ SetWindowPointer (win, WA_BusyPointer, TRUE, TAG_DONE);
+
+ /* Do not block input in Progress window */
+ if (wud->WindowID == WID_PROGRESS) continue;
+
+ /* Set an invisible Requester in window to block user input.
+ * We allocate 4 more bytes after the requester structure to store
+ * the IDCMP flags before modifying them. MEMF_PUBLIC is used
+ * because intuition is going to process the Requester structure.
+ */
+
+ if (!(lock = AllocPooled (Pool, sizeof (struct WindowLock))))
+ continue;
+
+ InitRequester (&lock->Req);
+ lock->Req.Flags = SIMPLEREQ | NOREQBACKFILL;
+
+ /* Disable window resizing */
+ if (win->Flags & WFLG_SIZEGADGET)
+ {
+ lock->OldMinWidth = win->MinWidth;
+ lock->OldMinHeight = win->MinHeight;
+ lock->OldMaxWidth = win->MaxWidth;
+ lock->OldMaxHeight = win->MaxHeight;
+ WindowLimits (win, win->Width, win->Height,
+ win->Width, win->Height);
+ }
+
+ /* Disable IDCMP messages except IDCMP_REFRESHWINDOW events.
+ * WARNING: ModifyIDCMP (win, 0) would free the shared port!!
+ */
+ lock->OldIDCMPFlags = win->IDCMPFlags;
+ ModifyIDCMP (win, IDCMP_REFRESHWINDOW);
+
+ Request (&lock->Req, win);
+ }
+}
+
+
+
+GLOBALCALL void UnlockWindows (void)
+
+/* Restore user input in all windows. */
+{
+ struct WinUserData *wud;
+ struct Window *win;
+ struct WindowLock *lock;
+
+ /* Make sure windows arn't unlocked already */
+ if (WinLockCount) return;
+
+ /* Check lock nesting */
+ WinLockCount--;
+ if (WinLockCount) return;
+
+ for (wud = (struct WinUserData *) WindowList.lh_Head;
+ wud->Link.mln_Succ;
+ wud = (struct WinUserData *)wud->Link.mln_Succ)
+ {
+ if (!(win = wud->Win)) continue;
+
+ if (lock = (struct WindowLock *) win->FirstRequest)
+ {
+ /* Restore old window IDCMP */
+ ModifyIDCMP (win, lock->OldIDCMPFlags);
+
+ /* Re-enable window sizing and restore old window limits */
+ if (win->Flags & WFLG_SIZEGADGET)
+ WindowLimits (win, lock->OldMinWidth, lock->OldMinHeight,
+ lock->OldMaxWidth, lock->OldMaxHeight);
+
+ EndRequest (&lock->Req, wud->Win);
+ FreePooled (Pool, lock, sizeof (struct WindowLock));
+ }
+
+ /* Restore standard pointer */
+#ifndef OS30_ONLY
+ if (IntuitionBase->LibNode.lib_Version < 39)
+ ClearPointer (win);
+ else
+#endif /* !OS30_ONLY */
+ SetWindowPointer (win, TAG_DONE);
+ }
+}
+
+
+
+GLOBALCALL void RevealWindow (struct WinUserData *wud)
+{
+ WindowToFront (wud->Win);
+ ActivateWindow (wud->Win);
+
+ /* Make the window visible on the screen */
+
+#ifndef OS30_ONLY
+ if (IntuitionBase->LibNode.lib_Version >= 39)
+#endif /* OS30_ONLY */
+ ScreenPosition (Scr, SPOS_MAKEVISIBLE,
+ wud->Win->LeftEdge, wud->Win->TopEdge,
+ wud->Win->LeftEdge + wud->Win->Width - 1,
+ wud->Win->TopEdge + wud->Win->Height - 1);
+}
+
+
+
+GLOBALCALL void SetGadgets (struct WinUserData *wud, LONG arg, ...)
+
+/* Update status of gadgets in the window associated to <wud>.
+ * <arg> is the first of a -1 terminated array of commands.
+ * Each command is represented by a pair of LONGs, where the
+ * first LONG is the gadget number, and the second is the value
+ * to set for that gadget, depending on the gadget type.
+ */
+{
+ LONG *cmd = &arg;
+
+ static ULONG actions[] =
+ {
+ TAG_IGNORE, /* GENERIC_KIND */
+ TAG_IGNORE, /* BUTTON_KIND */
+ GTCB_Checked, /* CHECKBOX_KIND */
+ GTIN_Number, /* INTEGER_KIND */
+ GTLV_Selected, /* LISTVIEW_KIND */
+ GTMX_Active, /* MX_KIND */
+ GTNM_Number, /* NUMBER_KIND */
+ GTCY_Active, /* CYCLE_KIND */
+ GTPA_Color, /* PALETTE_KIND */
+ TAG_IGNORE, /* SCROLLER_KIND */
+ TAG_IGNORE, /* -- reserved -- */
+ GTSL_Level, /* SLIDER_KIND */
+ GTST_String, /* STRING_KIND */
+ GTTX_Text /* TEXT_KIND */
+ };
+
+ while (*cmd != -1)
+ {
+ GT_SetGadgetAttrs (wud->Gadgets[*cmd], wud->Win, NULL,
+ actions[wud->GInfo[*cmd].GKind], *(cmd+1),
+ TAG_DONE);
+
+ cmd += 2;
+ }
+}
+
+
+
+GLOBALCALL LONG AddListViewNode (struct List *lv, CONST_STRPTR label, ...)
+
+/* Var-args stub for AddListViewNodeA */
+{
+ return AddListViewNodeA (lv, label, (LONG *) (&label)+1);
+}
+
+
+
+GLOBALCALL LONG AddListViewNodeA (struct List *lv, CONST_STRPTR label, LONG *args)
+
+/* Allocate and add a new node to a ListView list. The label
+ * is printf()-formatted and copied to a buffer just after the
+ * node structure. Call RemListViewNode() to deallocate the node.
+ *
+ * RETURNS
+ * 0 for failure (no memory), any other value for success.
+ */
+{
+ struct Node *n;
+ UBYTE buf[256];
+
+ if (args)
+ {
+ VSPrintf (buf, label, args);
+ label = buf;
+ }
+
+ if (!(n = AllocVecPooled (Pool, sizeof (struct Node) + strlen (label) + 1)))
+ return FALSE;
+
+ n->ln_Name = ((UBYTE *)n) + sizeof (struct Node);
+
+ strcpy (n->ln_Name, label);
+ n->ln_Pri = 0; /* Selected */
+
+ ADDTAIL (lv, n);
+
+ return TRUE;
+}
+
+
+
+GLOBALCALL void RemListViewNode (struct Node *n)
+{
+ REMOVE (n);
+ FreeVecPooled (Pool, n);
+}
+
+
+
+
+GLOBALCALL struct Image *NewImageObject (ULONG which)
+
+/* Creates a sysiclass object. */
+{
+ return ((struct Image *)NewObject (NULL, SYSICLASS,
+ SYSIA_DrawInfo, DrawInfo,
+ SYSIA_Which, which,
+ SYSIA_Size, Scr->Flags & SCREENHIRES ? SYSISIZE_MEDRES : SYSISIZE_LOWRES,
+ TAG_DONE));
+
+ /* NB: SYSISIZE_HIRES not yet supported. */
+}
+
+
+
+static struct Gadget *CreateVImageButton (struct TagItem *tags, struct NewGadget *ng)
+
+/* This routine is called by the layout engine to create an IMAGEBUTTON_KIND gadget. */
+{
+ struct Gadget *VButton;
+ struct Image *VImage;
+
+ if (!VImageClass)
+ {
+ if (VImageClass = InitVImageClass ())
+ {
+ if (ButtonFrame = NewObject (NULL, FRAMEICLASS,
+ IA_FrameType, FRAME_BUTTON,
+ IA_EdgesOnly, TRUE,
+ TAG_DONE))
+ {
+ struct IBox FrameBox, ContentsBox = { 0, 0, 0, 0 };
+
+ DoMethod ((Object *)ButtonFrame, IM_FRAMEBOX, &ContentsBox, &FrameBox, DrawInfo, 0);
+
+ ButtonFrameWidth = FrameBox.Width;
+ ButtonFrameHeight = FrameBox.Height;
+ }
+ else
+ {
+ FreeVImageClass (VImageClass);
+ VImageClass = NULL;
+ return NULL;
+ }
+ }
+ else return NULL;
+ }
+
+ if (!(VImage = (struct Image *)NewObject (VImageClass, NULL,
+ IA_Width, ng->ng_Width - ButtonFrameWidth,
+ IA_Height, ng->ng_Height - ButtonFrameHeight,
+ SYSIA_Which, ng->ng_Flags,
+ TAG_DONE)))
+ return NULL;
+
+ if (!(VButton = (struct Gadget *)NewObject (NULL, FRBUTTONCLASS,
+ GA_ID, ng->ng_GadgetID,
+ GA_UserData, ng->ng_UserData,
+ GA_Left, ng->ng_LeftEdge,
+ GA_Top, ng->ng_TopEdge,
+ GA_Image, ButtonFrame,
+ GA_LabelImage, VImage,
+ GA_RelVerify, TRUE,
+ TAG_DONE)))
+ DisposeObject (VImage);
+ else
+ VImageUseCount++;
+
+ return VButton;
+}
+
+
+
+static void DeleteVImageButton (struct Gadget *g)
+{
+ if (g)
+ {
+ DisposeObject (g->GadgetText);
+ DisposeObject (g);
+
+ VImageUseCount--;
+
+ if (!VImageUseCount)
+ {
+ if (ButtonFrame)
+ {
+ DisposeObject (ButtonFrame);
+ ButtonFrame = NULL;
+ }
+
+ if (VImageClass)
+ {
+ FreeVImageClass (VImageClass);
+ VImageClass = NULL;
+ }
+ }
+ }
+}
+
+
+
+/* Gadget definition format:
+ *
+ * VGROUP_KIND, BorderType,
+ * HGROUP_KIND, BorderType,
+ * IMAGEBUTTON_KIND, ClickedFunction, ImageType, Tag1, ...,
+ * CHECKBOX_KIND, ClickedFunction, Label, Storage (UWORD *), Tag1, ...,
+ * BUTTON_KIND, ClickedFunction, Label, Tag1, ...,
+ * INTEGER_KIND, ClickedFunction, Label, MaxDigits, Tag1, ...,
+ * STRING_KIND, ClickedFunction, Label, MaxChars, Tag1, ...,
+ * TEXT_KIND, Label, NumChars, Tag1, ...,
+ * NUMBER_KIND, Label, MaxDigits, Tag1, ...,
+ * LISTVIEW_KIND, ClickedFunction, Label, Labels (struct List *), Tag1, ...,
+ * MX_KIND, ClickedFunction, Label, Labels (ULONG *), Tag1, ...,
+ * CYCLE_KIND, ClickedFunction, Label, Labels (ULONG *), Tag1, ...,
+ * SLIDER_KIND, ClickedFunction, Label, Min, Max, LevelFormat (UBYTE *), MaxLevelLen (ULONG), Tag1, ...,
+ * PALETTE_KIND, ClickedFunction, Label, Tags,
+ * ENDGROUP_KIND,
+ */
+
+
+static void LayoutGadgets (struct LayoutGadgetsArgs *lga, UWORD mode)
+{
+ struct GInfo *ginfo,
+ *myginfo = &lga->GInfo[lga->Count];
+ STRPTR label;
+
+
+ /* Skip this group */
+ lga->Count++;
+
+ if (mode == LAYOUTMODE_V)
+ myginfo->Flags |= GIF_FIXEDWIDTH;
+ else
+ myginfo->Flags |= GIF_FIXEDHEIGHT;
+
+ while (*lga->Args != ENDGROUP_KIND)
+ {
+ ginfo = &lga->GInfo[lga->Count];
+ label = NULL;
+
+ switch (ginfo->GKind = *lga->Args++)
+ {
+ case HGROUP_KIND:
+ case VGROUP_KIND:
+ if (*lga->Args++) /* Border? */
+ {
+ LayoutGadgets (lga, (ginfo->GKind == HGROUP_KIND) ? LAYOUTMODE_H : LAYOUTMODE_V);
+
+ /* Add border dimensions to group size */
+ ginfo->MinWidth += HSPACING * 8;
+ ginfo->Fixed.Width += HSPACING * 8;
+ ginfo->MinHeight += VSPACING * 8;
+ ginfo->Fixed.Height += VSPACING * 8;
+ ginfo->Flags |= GIF_HASBORDER;
+ }
+ else LayoutGadgets (lga, (ginfo->GKind == HGROUP_KIND) ? LAYOUTMODE_H : LAYOUTMODE_V);
+ break;
+
+ case IMAGEBUTTON_KIND:
+ lga->Args++; /* Skip Clicked function */
+ lga->Args++; /* Skip Image */
+ ginfo->MinWidth = lga->Wud->Font->tf_XSize * 3 + 2;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize + 4;
+ ginfo->Flags |= GIF_FIXEDWIDTH | GIF_FIXEDHEIGHT;
+ break;
+
+ case CHECKBOX_KIND:
+ lga->Args++; /* Skip Clicked function */
+ label = STR(*lga->Args++);
+ ginfo->SpecialStorage = (void *)*lga->Args++; /* Record storage */
+ ginfo->MinWidth = lga->Wud->Font->tf_XSize * 2 + 8;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize + 4;
+ ginfo->LabelWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+ ginfo->Flags = GIF_FIXEDWIDTH | GIF_FIXEDHEIGHT;
+ break;
+
+ case BUTTON_KIND:
+ lga->Args++; /* Skip Clicked function */
+ label = STR(*lga->Args++);
+ ginfo->MinWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize + 4;
+ ginfo->Flags = GIF_FIXEDHEIGHT;
+ break;
+
+ case INTEGER_KIND:
+ case STRING_KIND:
+ {
+ UWORD maxchars;
+
+ lga->Args++; /* Skip Clicked function */
+ if (label = STR (*lga->Args++))
+ ginfo->LabelWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+
+ maxchars = *lga->Args++;
+
+ ginfo->MinWidth = lga->Wud->Font->tf_XSize * min (maxchars + 1, 8) + 12;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize + 6;
+ ginfo->Flags = GIF_FIXEDHEIGHT;
+
+ break;
+ }
+
+ case LISTVIEW_KIND:
+ lga->Args++; /* Skip Clicked function */
+ ginfo->MinWidth = lga->Wud->Font->tf_XSize * 16 + 16;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize * 4 + 4;
+ if (label = STR (*lga->Args++))
+ ginfo->LabelWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+ ginfo->SpecialStorage = (void *)*lga->Args++; /* Record List pointer */
+ break;
+
+ case MX_KIND:
+ case CYCLE_KIND:
+ {
+ ULONG *labels, cnt = 0;
+ STRPTR str;
+
+ lga->Args++; /* Skip Clicked function */
+
+ if (label = STR(*lga->Args++))
+ ginfo->LabelWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+
+ ginfo->MinWidth = ginfo->MinHeight = 0;
+
+
+ /* Count labels */
+
+ labels = (ULONG *) *lga->Args++;
+ while (labels[cnt]) cnt++;
+
+
+ /* Allocate and fill-in MX/Cycle labels array */
+
+ if (!ginfo->SpecialStorage)
+ ginfo->SpecialStorage = AllocVecPooled (Pool, (cnt + 1) * sizeof (STRPTR));
+
+ if (ginfo->SpecialStorage)
+ {
+ ULONG i;
+
+ for (i = 0; i < cnt; i++)
+ {
+ /* Store localized label */
+ ((STRPTR *)(ginfo->SpecialStorage))[i] = str = STR (labels[i]);
+
+ /* Calculate maximum width */
+ ginfo->MinWidth = max (ginfo->MinWidth, TextLength (lga->DummyRast, str, strlen (str)) + LABELSPACING);
+
+ if (ginfo->GKind == MX_KIND)
+ ginfo->MinHeight += lga->Wud->Font->tf_YSize + 2;
+ }
+
+ /* Terminate labels array */
+ ((STRPTR *)(ginfo->SpecialStorage))[i] = NULL;
+ }
+
+ /* add width of radio button (or cycle image) */
+ ginfo->MinWidth += lga->Wud->Font->tf_XSize * 2 + 4;
+
+ if (ginfo->GKind == MX_KIND)
+ ginfo->Flags = GIF_FIXEDWIDTH | GIF_FIXEDHEIGHT;
+ else
+ {
+ ginfo->Flags = GIF_FIXEDHEIGHT;
+ ginfo->MinHeight += lga->Wud->Font->tf_YSize + 4;
+ }
+
+ break;
+ }
+
+ case NUMBER_KIND:
+ case TEXT_KIND:
+
+ if (label = STR (*lga->Args++))
+ ginfo->LabelWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+
+ ginfo->MinWidth = lga->Wud->Font->tf_XSize * (*lga->Args++) + 8;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize + 4;
+ ginfo->Flags = GIF_FIXEDHEIGHT;
+ break;
+
+ case SLIDER_KIND:
+ lga->Args++; /* Skip Clicked function */
+
+ if (label = STR (*lga->Args++))
+ ginfo->LabelWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+
+ lga->Args += 3; /* Skip Min, Max and LevelFormat */
+
+ if (ginfo->SpecialStorage = (APTR)((*lga->Args++) * lga->Wud->Font->tf_XSize))
+ ginfo->SpecialStorage = (APTR) ((ULONG)ginfo->SpecialStorage + 4);
+
+ ginfo->MinWidth = lga->Wud->Font->tf_XSize * 8 + 8 + (ULONG)ginfo->SpecialStorage;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize;
+ ginfo->Flags = GIF_FIXEDHEIGHT;
+ break;
+
+ case PALETTE_KIND:
+ lga->Args++; /* Skip Clicked function */
+
+ if (label = STR (*lga->Args++))
+ ginfo->LabelWidth = TextLength (lga->DummyRast, label, strlen (label)) + LABELSPACING;
+
+ ginfo->MinWidth = lga->Wud->Font->tf_XSize * 6 + 8;
+ ginfo->MinHeight = lga->Wud->Font->tf_YSize * 3 + 4;
+ break;
+ }
+
+ if ((ginfo->GKind != HGROUP_KIND) && (ginfo->GKind != VGROUP_KIND))
+ {
+ UBYTE *c;
+
+ /* Go to next gadget (ie: skip tags until TAG_END) */
+ while (*lga->Args) lga->Args += 2;
+ lga->Args++;
+
+ /* Look for the key equivalent of this gadget. */
+ if (c = label)
+ for ( ; *c ; c++)
+ if (*c == '_')
+ {
+ /* Found! Now store in the key array */
+ lga->Wud->Keys[lga->Count] = *(++c) | (1<<5); /* Lower case */
+ break;
+ }
+ }
+
+ if (mode == LAYOUTMODE_V)
+ {
+ myginfo->MinHeight += ginfo->MinHeight + VSPACING;
+ if (ginfo->Flags & GIF_HASBORDER)
+ myginfo->MinWidth = max (myginfo->MinWidth, ginfo->MinWidth + ginfo->LabelWidth);
+ else
+ {
+ myginfo->MinWidth = max (myginfo->MinWidth, ginfo->MinWidth);
+ myginfo->LabelWidth = max (myginfo->LabelWidth, ginfo->LabelWidth);
+ }
+
+ myginfo->Fixed.Height += VSPACING;
+ if (ginfo->Flags & GIF_FIXEDHEIGHT)
+ myginfo->Fixed.Height += ginfo->MinHeight;
+ if (!(ginfo->Flags & GIF_FIXEDWIDTH))
+ myginfo->Flags &= ~GIF_FIXEDWIDTH;
+ }
+ else /* LAYOUTMODE_H */
+ {
+ myginfo->MinWidth += ginfo->MinWidth + ginfo->LabelWidth + HSPACING;
+ myginfo->MinHeight = max (myginfo->MinHeight, ginfo->MinHeight);
+ myginfo->Fixed.Width += ginfo->LabelWidth + HSPACING;
+ if (ginfo->Flags & GIF_FIXEDWIDTH)
+ myginfo->Fixed.Width += ginfo->MinWidth;
+ if (!(ginfo->Flags & GIF_FIXEDHEIGHT))
+ myginfo->Flags &= ~GIF_FIXEDHEIGHT;
+ }
+
+ lga->Count++;
+
+ } /* End while (*lga->Args != ENDGROUP_KIND) */
+
+
+ if (mode == LAYOUTMODE_H)
+ {
+ /* Remove extra HSPACING after last child */
+ myginfo->MinWidth -= HSPACING;
+ myginfo->Fixed.Width -= HSPACING;
+
+ if (!(myginfo->Flags & GIF_HASBORDER))
+ {
+ /* Align first child of HGROUP_KIND with other
+ * gadgets in our parent group
+ */
+ myginfo->LabelWidth = (myginfo+1)->LabelWidth;
+ (myginfo+1)->LabelWidth = 0;
+ myginfo->MinWidth -= myginfo->LabelWidth;
+ myginfo->Fixed.Width -= myginfo->LabelWidth;
+ }
+ }
+ else /* LAYOUTMODE_V */
+ {
+ /* Remove extra VSPACING after last child */
+ myginfo->MinHeight -= VSPACING;
+ myginfo->Fixed.Height -= VSPACING;
+ }
+
+ if (myginfo->MinWidth <= myginfo->Fixed.Width)
+ myginfo->Flags |= GIF_FIXEDWIDTH;
+ if (myginfo->MinHeight <= myginfo->Fixed.Height)
+ myginfo->Flags |= GIF_FIXEDHEIGHT;
+
+ lga->Args++; /* Skip ENDGROUP_KIND */
+ lga->Count--; /* Take back one position */
+}
+
+
+
+static struct Gadget *CreateGadgets (struct LayoutGadgetsArgs *lga, UWORD mode, UWORD left, UWORD top, UWORD width, UWORD height)
+{
+ struct GInfo *ginfo,
+ *myginfo = &lga->GInfo[lga->Count];
+ struct TagItem *Tags, *tmp;
+ ULONG stc;
+ struct NewGadget ng;
+ BOOL boopsi;
+
+
+ /* Skip this group */
+ lga->Count++;
+
+
+ while (*lga->Args != ENDGROUP_KIND)
+ {
+ ginfo = &lga->GInfo[lga->Count];
+ Tags = (struct TagItem *)lga->SpecialTags;
+ stc = 2;
+ boopsi = FALSE;
+
+ ng.ng_TextAttr = lga->Wud->Attr;
+ ng.ng_GadgetID = lga->Count;
+ ng.ng_VisualInfo = lga->VInfo;
+ ng.ng_Flags = PLACETEXT_LEFT;
+
+ if (mode == LAYOUTMODE_V)
+ {
+ if (ginfo->Flags & GIF_FIXEDWIDTH)
+ ng.ng_Width = ginfo->MinWidth;
+ else
+ ng.ng_Width = width - ((ginfo->Flags & GIF_HASBORDER) ? ginfo->LabelWidth : 0);
+
+ if (ginfo->Flags & GIF_FIXEDHEIGHT)
+ ng.ng_Height = ginfo->MinHeight;
+ else
+ ng.ng_Height = (((ULONG)(height - myginfo->Fixed.Height)) * ((ULONG)ginfo->MinHeight)) / ((ULONG)(myginfo->MinHeight - myginfo->Fixed.Height));
+
+ ng.ng_LeftEdge = left + ((ginfo->Flags & GIF_HASBORDER) ? ginfo->LabelWidth : 0);
+ ng.ng_TopEdge = top;
+ top = ng.ng_TopEdge + ng.ng_Height + VSPACING;
+ }
+ else /* mode == LAYOUTMODE_H */
+ {
+ if (ginfo->Flags & GIF_FIXEDWIDTH)
+ ng.ng_Width = ginfo->MinWidth;
+ else
+ ng.ng_Width = (((ULONG)(width - myginfo->Fixed.Width)) * ((ULONG)ginfo->MinWidth)) / ((ULONG)(myginfo->MinWidth - myginfo->Fixed.Width));
+
+ if (ginfo->Flags & GIF_FIXEDHEIGHT)
+ {
+ /* Vertical center */
+ ng.ng_Height = ginfo->MinHeight;
+ ng.ng_TopEdge = top + (height - ng.ng_Height) / 2;
+ }
+ else
+ {
+ /* Vertical stretch */
+ ng.ng_Height = height;
+ ng.ng_TopEdge = top;
+ }
+
+ ng.ng_LeftEdge = left + ginfo->LabelWidth;
+
+ left = ng.ng_LeftEdge + ng.ng_Width + HSPACING;
+ }
+
+ switch (ginfo->GKind = *lga->Args++)
+ {
+ case BOOPSI_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ lga->SpecialTags[stc++] = XMGAD_SetupFunc;
+ lga->SpecialTags[stc++] = (*lga->Args++);
+ boopsi = TRUE;
+ break;
+
+ case IMAGEBUTTON_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_Flags = (*lga->Args++);
+ ng.ng_GadgetText = NULL;
+ lga->SpecialTags[stc++] = XMGAD_SetupFunc;
+ lga->SpecialTags[stc++] = (ULONG)CreateVImageButton;
+ boopsi = TRUE;
+ break;
+
+ case VGROUP_KIND:
+ case HGROUP_KIND:
+ {
+ ULONG bordertype;
+
+ if (bordertype = *lga->Args++)
+ {
+ if (!CreateWindowBorder (lga->Wud, ng.ng_LeftEdge - ginfo->LabelWidth, ng.ng_TopEdge,
+ ng.ng_Width + ginfo->LabelWidth, ng.ng_Height, bordertype))
+ return NULL;
+ if (!CreateGadgets (lga, (ginfo->GKind == VGROUP_KIND) ? LAYOUTMODE_V : LAYOUTMODE_H,
+ ng.ng_LeftEdge + HSPACING*4, ng.ng_TopEdge + VSPACING*4,
+ ng.ng_Width - HSPACING*8, ng.ng_Height - VSPACING*8))
+ return NULL;
+ }
+ else if (!CreateGadgets (lga, (ginfo->GKind == VGROUP_KIND) ? LAYOUTMODE_V : LAYOUTMODE_H,
+ ng.ng_LeftEdge, ng.ng_TopEdge, ng.ng_Width, ng.ng_Height))
+ return NULL;
+
+
+ break;
+ }
+
+ case BUTTON_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+ ng.ng_Flags = PLACETEXT_IN;
+ break;
+
+ case CHECKBOX_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+ lga->Args++; /* Skip storage */
+
+ lga->SpecialTags[stc++] = GTCB_Checked;
+ lga->SpecialTags[stc++] = (ginfo->SpecialStorage ? (*((UWORD *)ginfo->SpecialStorage)) : NULL);
+ lga->SpecialTags[stc++] = GTCB_Scaled;
+ lga->SpecialTags[stc++] = TRUE;
+ break;
+
+ case INTEGER_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ /* Editing in right-justified integer gadgets
+ * is very uncomfortable!!
+ *
+ * lga->SpecialTags[stc++] = STRINGA_Justification;
+ * lga->SpecialTags[stc++] = GACT_STRINGRIGHT;
+ */
+ lga->SpecialTags[stc++] = GTIN_MaxChars;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = STRINGA_ExitHelp;
+ lga->SpecialTags[stc++] = TRUE;
+ break;
+
+ case LISTVIEW_KIND:
+ {
+ struct List *l;
+
+ ng.ng_UserData = (void *)*lga->Args++;
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ lga->SpecialTags[stc++] = GTLV_ShowSelected;
+ lga->SpecialTags[stc++] = NULL;
+
+ if (l = (struct List *)*lga->Args++)
+ {
+ lga->SpecialTags[stc++] = GTLV_Labels;
+ lga->SpecialTags[stc++] = (ULONG)l;
+
+ // l->lh_Type = 0; /* Selected Item */
+ // l->l_pad = 0; /* Item Count */
+ }
+
+#ifndef OS30_ONLY
+ if (GadToolsBase->lib_Version < 39)
+ ng.ng_Height -= 4;
+#endif /* !OS30_ONLY */
+
+ break;
+ }
+
+ case MX_KIND:
+ case CYCLE_KIND:
+ {
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ /* Skip labels */
+ lga->Args++;
+
+ if (ginfo->GKind == MX_KIND)
+ {
+ /* Special kludge: MX gadgets width and height refer to
+ * one button instead of the whole gadget.
+ */
+ ng.ng_Width = lga->Wud->Font->tf_XSize * 2;
+ ng.ng_Height = lga->Wud->Font->tf_YSize;
+
+ ng.ng_Flags = PLACETEXT_RIGHT;
+
+ lga->SpecialTags[stc++] = GTMX_Labels;
+ lga->SpecialTags[stc++] = (ULONG)ginfo->SpecialStorage;
+ lga->SpecialTags[stc++] = GTMX_Scaled;
+ lga->SpecialTags[stc++] = TRUE;
+ lga->SpecialTags[stc++] = GTMX_Spacing;
+ lga->SpecialTags[stc++] = 2;
+ }
+ else
+ {
+ lga->SpecialTags[stc++] = GTCY_Labels;
+ lga->SpecialTags[stc++] = (ULONG)ginfo->SpecialStorage;
+ }
+
+ break;
+ }
+
+ case NUMBER_KIND:
+ ng.ng_UserData = NULL;
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ lga->Args++; /* Skip NumDigits */
+
+ lga->SpecialTags[stc++] = GTNM_Border;
+ lga->SpecialTags[stc++] = TRUE;
+
+ /* Under V39 and below, GTJ_RIGHT does not work properly. */
+ if (GadToolsBase->lib_Version > 39)
+ {
+ lga->SpecialTags[stc++] = GTNM_Justification;
+ lga->SpecialTags[stc++] = GTJ_RIGHT;
+ }
+ break;
+
+ case TEXT_KIND:
+ ng.ng_UserData = NULL;
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ lga->Args++; /* Skip NumDigits */
+ break;
+
+ case PALETTE_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ lga->SpecialTags[stc++] = GTPA_IndicatorWidth;
+ lga->SpecialTags[stc++] = ng.ng_Width / 8;
+ break;
+
+ case SCROLLER_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ lga->SpecialTags[stc++] = GTSC_Arrows;
+ lga->SpecialTags[stc++] = lga->Wud->Font->tf_XSize + 4;
+ break;
+
+ case STRING_KIND:
+/* ClassId = STRGCLASS;
+ lga->SpecialTags[stc++] = GA_UserData;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = GA_LabelImage;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = STRINGA_MaxChars;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = GA_Border;
+ lga->SpecialTags[stc++] = StringFrame;
+ lga->SpecialTags[stc++] = STRINGA_ExitHelp;
+ lga->SpecialTags[stc++] = TRUE;
+*/
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ lga->SpecialTags[stc++] = GTST_MaxChars;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = STRINGA_ExitHelp;
+ lga->SpecialTags[stc++] = TRUE;
+ break;
+
+ case SLIDER_KIND:
+ ng.ng_UserData = (void *)(*lga->Args++);
+ ng.ng_GadgetText = STR(*lga->Args++);
+
+ lga->SpecialTags[stc++] = GTSL_Min;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = GTSL_Max;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = GTSL_LevelFormat;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = GTSL_MaxLevelLen;
+ lga->SpecialTags[stc++] = *lga->Args++;
+ lga->SpecialTags[stc++] = GTSL_LevelPlace;
+ lga->SpecialTags[stc++] = PLACETEXT_RIGHT;
+ lga->SpecialTags[stc++] = GTSL_Justification;
+ lga->SpecialTags[stc++] = GTJ_RIGHT;
+ lga->SpecialTags[stc++] = GA_RelVerify;
+ lga->SpecialTags[stc++] = TRUE;
+
+ ng.ng_Width -= (ULONG)ginfo->SpecialStorage;
+ break;
+ }
+
+ if ((ginfo->GKind != HGROUP_KIND) && (ginfo->GKind != VGROUP_KIND))
+ {
+ if (*lga->Args != TAG_DONE)
+ {
+ /* Add user Tags */
+ lga->SpecialTags[stc++] = TAG_MORE;
+ lga->SpecialTags[stc] = (ULONG) lga->Args;
+ }
+ else lga->SpecialTags[stc] = TAG_DONE;
+
+
+ /* Go to next gadget (ie: skip tags until TAG_END) */
+ while (*lga->Args) lga->Args += 2;
+ lga->Args++;
+
+ if (boopsi)
+ {
+ /* BOOPSI Gadget. Let SetupFunc() allocate the gadget for us */
+
+ if (tmp = FindTagItem (XMGAD_SetupFunc, Tags))
+ {
+ if (lga->PrevGad->NextGadget = ((struct Gadget * (*)(struct TagItem *, struct NewGadget *)) (tmp->ti_Data)) (Tags, &ng))
+ /* Record it into the list */
+ lga->Wud->Gadgets[lga->Count] = lga->PrevGad->NextGadget;
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+ }
+ else
+ {
+ /* Normal GadTools gadget */
+
+ if (!(lga->Wud->Gadgets[lga->Count] = CreateGadgetA (ginfo->GKind, lga->PrevGad, &ng, Tags)))
+ return NULL;
+ }
+
+ lga->PrevGad = lga->Wud->Gadgets[lga->Count];
+
+ } /* End if (GKind != ?GROUP_KIND */
+
+ lga->Count++; /* Go to next gadget */
+ }
+
+ lga->Args++; /* Skip ENDGROUP_KIND */
+ lga->Count--; /* Take back one position */
+
+ return (lga->Wud->GList);
+}
+
+
+
+static void RenderWindowBorders (struct WinUserData *wud)
+{
+ struct WindowBorder *border = wud->Borders;
+
+ while (border)
+ {
+ DrawBevelBox (wud->Win->RPort,
+ border->Size.Left, border->Size.Top,
+ border->Size.Width, border->Size.Height,
+ GT_VisualInfo, VisualInfo,
+ GTBB_Recessed, TRUE,
+ GTBB_FrameType, border->Type,
+ TAG_DONE);
+
+ border = border->NextBorder;
+ }
+}
+
+
+
+static struct WindowBorder *CreateWindowBorder (struct WinUserData *wud,
+ UWORD left, UWORD top, UWORD width, UWORD height, ULONG bordertype)
+
+/* Creates new WindowBorder structure and links it to the WUD passed.
+ * Will return NULL in case of failure.
+ */
+{
+ struct WindowBorder *border;
+
+ if (!(border = AllocPooled (Pool, sizeof (struct WindowBorder))))
+ return NULL;
+
+ /* Link this border struct in WUD */
+ border->NextBorder = wud->Borders;
+ wud->Borders = border;
+
+ /* Fill in WindowBorder structure */
+ border->Type = bordertype;
+ border->Size.Left = left;
+ border->Size.Top = top;
+ border->Size.Width = width;
+ border->Size.Height = height;
+
+ return border;
+}
+
+
+
+static void DeleteGadgets (struct WinUserData *wud)
+{
+ FreeGadgets (wud->GList); wud->GList = NULL;
+
+ if (wud->GInfo)
+ {
+ ULONG i;
+
+ for (i = 0; i < wud->GCount; i++)
+ {
+ if (wud->Gadgets[i])
+ {
+ switch (wud->GInfo[i].GKind)
+ {
+ case IMAGEBUTTON_KIND:
+ DeleteVImageButton (wud->Gadgets[i]);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (wud->Borders)
+ {
+ struct WindowBorder *border = wud->Borders, *nextborder;
+
+ do
+ {
+ nextborder = border->NextBorder;
+ FreePooled (Pool, border, sizeof (struct WindowBorder));
+ }
+ while (border = nextborder);
+
+ wud->Borders = NULL;
+ }
+}
+
+
+
+struct WinUserData *CreateWUD (ULONG id)
+{
+ struct WinUserData *wud;
+ struct WDescr *wdescr = &WDescr[id];
+
+
+ /* Create a new WUD structure for this window */
+ if (wud = CAllocPooled (Pool, sizeof (struct WinUserData)))
+ {
+ wdescr->Wud = wud;
+ wdescr->UseCnt++;
+ wud->WindowID = id;
+ wud->IDCMPFlags = IDCMP_RAWKEY | IDCMP_MENUHELP |
+ IDCMP_GADGETHELP | IDCMP_INACTIVEWINDOW;
+ wud->Flags = WFLG_DRAGBAR | WFLG_DEPTHGADGET;
+ wud->Title = PrgName;
+
+ /* Link WUD to windows list */
+ ADDHEAD (&WindowList, (struct Node *)wud);
+
+ if (wdescr->CreationTags)
+ {
+ struct TagItem *tstate = wdescr->CreationTags,
+ *tag;
+
+ /* Read taglist arguments */
+
+ while (tag = NextTagItem (&tstate))
+ switch (tag->ti_Tag)
+ {
+ case XMWIN_NewMenu:
+ wud->NewMenu = (struct NewMenu *)tag->ti_Data;
+ break;
+
+ case XMWIN_LayoutArgs:
+ wud->LayoutArgs = (ULONG *)tag->ti_Data;
+ break;
+
+ case XMWIN_GCount:
+ wud->GCount = tag->ti_Data;
+ break;
+
+ case XMWIN_Title:
+ wud->Title = STR(tag->ti_Data);
+ break;
+
+ case XMWIN_WindowFlags:
+ wud->Flags |= tag->ti_Data;
+ break;
+
+ case XMWIN_IDCMPFlags:
+ wud->IDCMPFlags |= tag->ti_Data;
+ break;
+
+ case XMWIN_IDCMPFunc:
+ wud->IDCMPFunc = (void (*)()) tag->ti_Data;
+ break;
+
+ case XMWIN_DropIconFunc:
+ wud->DropIcon = (void (*)()) tag->ti_Data;
+ break;
+
+ case XMWIN_LayoutFunc:
+ wud->LayoutArgs = (ULONG *)tag->ti_Data;
+ wud->WUDFlags |= WUDF_CUSTOMLAYOUT;
+ break;
+
+ case XMWIN_PreOpenFunc:
+ wud->PreOpenFunc = (LONG (*)()) tag->ti_Data;
+ break;
+
+ case XMWIN_PostOpenFunc:
+ wud->PostOpenFunc = (void (*)()) tag->ti_Data;
+ break;
+
+ case XMWIN_PreCloseFunc:
+ wud->PreCloseFunc = (LONG (*)()) tag->ti_Data;
+ break;
+
+ case XMWIN_PostCloseFunc:
+ wud->PostCloseFunc = (void (*)()) tag->ti_Data;
+ break;
+
+ case XMWIN_HelpNode:
+ wud->HelpNode = (STRPTR) tag->ti_Data;
+ break;
+
+ case XMWIN_UserData:
+ wud->UserData = (APTR) tag->ti_Data;
+ break;
+
+ case XMWIN_LayoutCleanup:
+ wud->LayoutCleanupFunc = (void (*)()) tag->ti_Data;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!CreateLayoutInfo (wud))
+ {
+ DeleteWUD (wud);
+ wud = NULL;
+ }
+ }
+
+ return wud;
+}
+
+
+
+static void DeleteWUD (struct WinUserData *wud)
+{
+ if (wud)
+ {
+ MyCloseWindow (wud);
+
+ REMOVE ((struct Node *) wud);
+
+ {
+ struct WDescr *wdescr = &WDescr[wud->WindowID];
+
+ wdescr->Wud = NULL;
+
+ if (--wdescr->UseCnt)
+ {
+ struct WinUserData *otherwud;
+
+ /* Search for another wud with same ID and set it in WDescr... */
+
+ for (otherwud = (struct WinUserData *)WindowList.lh_Head;
+ otherwud->Link.mln_Succ;
+ otherwud = (struct WinUserData *)otherwud->Link.mln_Succ)
+ if (otherwud->WindowID == wud->WindowID)
+ {
+ wdescr->Wud = otherwud;
+ break;
+ }
+ }
+ }
+
+ DeleteLayoutInfo (wud);
+
+ FreePooled (Pool, wud, sizeof (struct WinUserData));
+ }
+}
+
+
+
+static struct WinUserData *CreateLayoutInfo (struct WinUserData *wud)
+{
+ struct WDescr *wdescr = &WDescr[wud->WindowID];
+
+ /* Compute font */
+
+ if (wud->Font = WindowFont)
+ wud->Attr = &WindowAttr;
+ else
+ {
+ wud->Font = TopazFont;
+ wud->Attr = &TopazAttr;
+ }
+
+
+ if (wud->LayoutArgs)
+ {
+ if (!wud->Gadgets)
+ {
+ /* Allocate Gadgets array */
+ if (!(wud->Gadgets = CAllocPooled (Pool,
+ wud->GCount * sizeof (struct Gadget *))))
+ return NULL;
+ }
+
+ if (wud->WUDFlags & WUDF_CUSTOMLAYOUT)
+ {
+ if (!(wud->GList = ((struct Gadget * (*)(struct WinUserData *))
+ wud->LayoutArgs) (wud) ))
+ return NULL;
+ }
+ else /* Use standard layout engine */
+ {
+ struct LayoutGadgetsArgs lga;
+
+ lga.Wud = wud;
+
+ /* Layout Phase 1 */
+
+ if (!wud->GInfo)
+ {
+ struct RastPort rp;
+ InitRastPort (&rp);
+ SetFont (&rp, wud->Font);
+
+ lga.DummyRast = &rp;
+ lga.Args = wud->LayoutArgs;
+ lga.Count = 0;
+
+
+ /* Allocate Key shortcuts array */
+ if (!(wud->Keys = CAllocPooled (Pool, wud->GCount)))
+ return NULL;
+
+ /* Allocate GInfo array */
+ if (!(wud->GInfo = lga.GInfo = CAllocPooled (Pool, sizeof (struct GInfo) * wud->GCount)))
+ return NULL;
+
+ LayoutGadgets (&lga, LAYOUTMODE_V);
+
+ if ((lga.GInfo[0].Flags & (GIF_FIXEDWIDTH | GIF_FIXEDHEIGHT)) != (GIF_FIXEDWIDTH | GIF_FIXEDHEIGHT))
+ {
+ wud->Flags |= (WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM);
+ wud->IDCMPFlags |= IDCMP_NEWSIZE;
+ }
+
+ /* Force minimum size */
+ wud->WindowSize.Width = max (wud->WindowSize.Width, lga.GInfo[0].MinWidth + lga.GInfo[0].LabelWidth + HSPACING * 2);
+ wud->WindowSize.Height = max (wud->WindowSize.Height, lga.GInfo[0].MinHeight + VSPACING * 2);
+ }
+ else
+ /* Reuse previous GInfo array */
+ lga.GInfo = wud->GInfo;
+
+
+ /* Layout Phase 2 */
+ {
+ ULONG SpecialTags[20];
+
+ SpecialTags[0] = GT_Underscore;
+ SpecialTags[1] = (ULONG) '_';
+
+ if (!CreateContext (&wud->GList))
+ return NULL;
+
+ lga.Args = wud->LayoutArgs;
+ lga.VInfo = VisualInfo;
+ lga.PrevGad = wud->GList;
+ lga.Count = 0;
+ lga.SpecialTags = SpecialTags;
+
+ if (!CreateGadgets (&lga, LAYOUTMODE_V, OffX + lga.GInfo[0].LabelWidth + HSPACING, OffY + VSPACING,
+ wud->WindowSize.Width - lga.GInfo[0].LabelWidth - HSPACING * 2,
+ wud->WindowSize.Height - VSPACING * 2))
+ return NULL;
+ }
+ }
+ }
+
+ if (!(wdescr->Flags & XMWINF_LOCALEDONE))
+ {
+ if (wud->NewMenu)
+ {
+ /* Localize the MenuStrip */
+ struct NewMenu *nm = wud->NewMenu;
+
+ while (nm->nm_Type != NM_END)
+ {
+ if (nm->nm_Label != (STRPTR)NM_BARLABEL)
+ nm->nm_Label = STR ((ULONG)nm->nm_Label);
+
+ nm++;
+ }
+ }
+
+ wdescr->Flags |= XMWINF_LOCALEDONE;
+ }
+
+
+ if (wud->NewMenu && (!wud->MenuStrip))
+ {
+ if (!(wud->MenuStrip = CreateMenusA (wud->NewMenu, NULL)))
+ return NULL;
+
+ if (!(LayoutMenus (wud->MenuStrip, VisualInfo,
+ GTMN_NewLookMenus, TRUE,
+ TAG_DONE)))
+ return NULL;
+ }
+
+
+ /* Setup zoom size */
+
+ if (wud->Flags & WFLG_SIZEGADGET)
+ {
+ wud->WindowZoom.Left = 0;
+ wud->WindowZoom.Top = 1;
+ wud->WindowZoom.Width = Scr->Width;
+ wud->WindowZoom.Height = Scr->Height;
+ }
+ else
+ {
+ wud->WindowZoom.Left = wud->WindowSize.Left;
+ wud->WindowZoom.Top = wud->WindowSize.Top;
+ if (wud->Title)
+ wud->WindowZoom.Width = TextLength (&Scr->RastPort, wud->Title,
+ strlen (wud->Title)) + 80;
+ else
+ wud->WindowZoom.Width = 80;
+ wud->WindowZoom.Height = Scr->WBorTop + Scr->RastPort.TxHeight + 1;
+
+ /* TODO: Under V39 specifying ~0,~0 as Zoom X,Y, intuition
+ * will only size-zoom the window. Consider implementing it...
+ */
+ }
+
+ return wud;
+}
+
+
+
+GLOBALCALL void DeleteLayoutInfo (struct WinUserData *wud)
+{
+ if (!wud) return;
+
+ DeleteGadgets (wud);
+
+ if (wud->LayoutCleanupFunc)
+ wud->LayoutCleanupFunc (wud);
+
+ if (wud->Keys)
+ {
+ FreePooled (Pool, wud->Keys, wud->GCount);
+ wud->Keys = NULL;
+ }
+
+ if (wud->Gadgets)
+ {
+ if (wud->GInfo)
+ {
+ ULONG i;
+
+ /* Free gadget associated resources */
+
+ for (i = 0; i < wud->GCount; i++)
+ {
+ if (wud->Gadgets[i])
+ {
+ switch (wud->GInfo[i].GKind)
+ {
+ case MX_KIND:
+ case CYCLE_KIND:
+ FreeVecPooled (Pool, wud->GInfo[i].SpecialStorage);
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ FreePooled (Pool, wud->Gadgets, wud->GCount * sizeof (struct Gadget *));
+ wud->Gadgets = NULL;
+ }
+
+ if (wud->GInfo)
+ {
+ FreePooled (Pool, wud->GInfo, wud->GCount * sizeof (struct GInfo));
+ wud->GInfo = NULL;
+ }
+
+ if (wud->MenuStrip)
+ {
+ FreeMenus (wud->MenuStrip);
+ wud->MenuStrip = NULL;
+ }
+}
+
+
+
+GLOBALCALL struct Window *NewWindow (ULONG id)
+
+/* Create a new window using the given Window ID. */
+{
+ struct WDescr *wdescr = &WDescr[id];
+ struct WinUserData *wud;
+
+ if (!Scr) return NULL;
+
+ /* Some windows can not be opened multiple times */
+
+ if (!(wud = wdescr->Wud) || (wdescr->Flags & XMWINF_OPENMULTI))
+ {
+ if (!(wud = CreateWUD (id)))
+ return NULL;
+ }
+
+ return MyOpenWindow (wud);
+}
+
+
+
+GLOBALCALL struct Window *MyOpenWindow (struct WinUserData *wud)
+
+/* Open & setup a window using the given WinUserData structure. */
+/* This function is now OBSOLETE and should be merged with NewWindow() */
+{
+ if (!Scr) return NULL;
+
+ if (wud->PreOpenFunc)
+ if (wud->PreOpenFunc (wud))
+ {
+ if (wud->PreCloseFunc) wud->PreCloseFunc (wud);
+ return NULL;
+ }
+
+ if (wud->Win)
+ {
+ RevealWindow (wud);
+ return wud->Win;
+ }
+
+ if (!wud->Gadgets && wud->LayoutArgs)
+ if (!CreateLayoutInfo (wud))
+ return NULL;
+
+
+ /* Open the Window */
+
+ if (wud->Win = OpenWindowTags (NULL,
+ WA_Left, wud->WindowSize.Left,
+ WA_Top, wud->WindowSize.Top,
+ WA_InnerWidth, wud->WindowSize.Width,
+ WA_InnerHeight, wud->WindowSize.Height,
+ WA_Gadgets, wud->GList,
+ WA_Title, wud->Title,
+ WA_Flags, wud->Flags,
+ WA_Zoom, &(wud->WindowZoom),
+ WA_PubScreen, Scr,
+ WA_HelpGroup, UniqueID,
+ /* Set user preferred refresh method unless this window
+ * requests simple refresh explicitly.
+ */
+ GuiSwitches.SmartRefresh ? WA_SmartRefresh : WA_SimpleRefresh, TRUE,
+ WA_Activate, !ScreenReopening,
+
+ /* Set minimum and maximum size according to the main gadget group
+ * properties.
+ */
+ (wud->Flags & WFLG_SIZEGADGET) ? WA_MinWidth : TAG_IGNORE,
+ wud->GInfo ? (wud->GInfo[0].MinWidth + Scr->WBorLeft + Scr->WBorRight) : wud->WindowSize.Width,
+ (wud->Flags & WFLG_SIZEGADGET) ? WA_MinHeight : TAG_IGNORE,
+ wud->GInfo ? (wud->GInfo[0].MinHeight + Scr->WBorTop + Scr->WBorBottom + Scr->Font->ta_YSize + SizeHeight + 1) : wud->WindowSize.Height,
+/**/ (wud->Flags & WFLG_SIZEGADGET) ? WA_MaxWidth : TAG_IGNORE,
+ wud->GInfo ? (wud->GInfo[0].Flags & GIF_FIXEDWIDTH ? 0 : -1) : -1,
+ (wud->Flags & WFLG_SIZEGADGET) ? WA_MaxHeight : TAG_IGNORE,
+ wud->GInfo ? (wud->GInfo[0].Flags & GIF_FIXEDHEIGHT ? wud->GInfo[0].MinHeight : -1) : -1,
+
+ /* Constant tags */
+ WA_ScreenTitle, (ULONG)Version+6,
+ WA_AutoAdjust, TRUE,
+ WA_MenuHelp, TRUE,
+ // WA_MouseQueue, 2,
+ // WA_RptQueue, 2,
+ WA_NewLookMenus, TRUE,
+
+ /* Add user tags */
+ WDescr[wud->WindowID].CreationTags ? TAG_MORE : TAG_DONE, WDescr[wud->WindowID].CreationTags
+ ))
+ {
+ wud->Win->UserData = (BYTE *) wud;
+ wud->Win->UserPort = WinPort;
+
+ if (ModifyIDCMP (wud->Win, wud->IDCMPFlags))
+ {
+ /* Set default font for this window */
+ if (wud->Font) SetFont (wud->Win->RPort, wud->Font);
+
+ if (wud->MenuStrip) SetMenuStrip (wud->Win, wud->MenuStrip);
+
+ /* Do initial refresh */
+ GT_RefreshWindow (wud->Win, NULL);
+ RenderWindowBorders (wud);
+
+ /* Make the window visible on the screen */
+ if (!ScreenReopening
+#ifndef OS30_ONLY
+ && (IntuitionBase->LibNode.lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ )
+ ScreenPosition (Scr, SPOS_MAKEVISIBLE,
+ wud->Win->LeftEdge, wud->Win->TopEdge,
+ wud->Win->LeftEdge + wud->Win->Width - 1,
+ wud->Win->TopEdge + wud->Win->Height - 1);
+
+ /* Make it an AppWindow if it is requested */
+ if (wud->DropIcon) AddAppWin (wud);
+
+ if (wud->PostOpenFunc)
+ wud->PostOpenFunc (wud);
+
+ wud->WUDFlags &= ~WUDF_REOPENME;
+
+ return wud->Win;
+ }
+
+ /* ClearMenuStrip() -- no need to call it... */
+ CloseWindow (wud->Win); wud->Win = NULL;
+ }
+
+ return NULL;
+}
+
+
+
+/* Close a window and all related resources */
+GLOBALCALL void MyCloseWindow (struct WinUserData *wud)
+{
+ struct Window *win = wud->Win;
+ struct Requester *req;
+
+ if (win)
+ {
+ DeselectButton();
+
+ if (wud->PreCloseFunc)
+ if (wud->PreCloseFunc (wud))
+ return;
+
+ /* Cleanup locked window */
+ if (req = win->FirstRequest)
+ {
+ EndRequest (req, win);
+ FreePooled (Pool, req, sizeof (struct WindowLock));
+ }
+
+ /* Remove AppWindow */
+ if (wud->AppWin) RemAppWin (wud);
+
+
+ /* Free MenuStrip */
+ if (win->MenuStrip)
+ {
+ ClearMenuStrip (win);
+ DoNextSelect = 0; /* Do not loop any more on this window's MenuStrip */
+ }
+
+ /* Now remove any pending message from the shared IDCMP port */
+
+ Forbid();
+ {
+ struct Node *succ;
+ struct Message *msg = (struct Message *) win->UserPort->mp_MsgList.lh_Head;
+
+ while (succ = msg->mn_Node.ln_Succ)
+ {
+ if (((struct IntuiMessage *)msg)->IDCMPWindow == win)
+ {
+ REMOVE ((struct Node *)msg);
+ ReplyMsg (msg);
+ }
+ msg = (struct Message *) succ;
+ }
+
+ win->UserPort = NULL; /* Keep intuition from freeing our port... */
+ ModifyIDCMP (win, 0L); /* ...and from sending us any more messages. */
+ }
+ Permit();
+
+
+ /* Save Window position and clear window pointer */
+
+ wud->WindowSize.Left = win->LeftEdge;
+ wud->WindowSize.Top = win->TopEdge;
+
+ if (win->Flags & WFLG_SIZEGADGET)
+ {
+ wud->WindowSize.Width = win->Width - win->BorderLeft - win->BorderRight;
+ wud->WindowSize.Height = win->Height - win->BorderTop - win->BorderBottom;
+ }
+
+ CloseWindow (win); wud->Win = NULL;
+
+ if (wud->PostCloseFunc)
+ wud->PostCloseFunc (wud);
+ }
+
+ if (wud->Font)
+ { CloseFont (wud->Font); wud->Font = NULL; }
+
+ if ((WDescr[wud->WindowID].Flags & XMWINF_OPENMULTI)
+ && (WDescr[wud->WindowID].UseCnt > 1))
+ DeleteWUD (wud);
+}
+
+
+
+GLOBALCALL void ReopenWindows (void)
+
+/* Reopen windows that were previously open */
+{
+ struct WinUserData *wud;
+
+ ScreenReopening = TRUE;
+
+ /* Find window */
+ for (wud = (struct WinUserData *)WindowList.lh_Head; wud->Link.mln_Succ;
+ wud = (struct WinUserData *)wud->Link.mln_Succ)
+ {
+ if (wud->WUDFlags & WUDF_REOPENME)
+ MyOpenWindow (wud);
+ }
+
+ ScreenReopening = FALSE;
+}
+
+
+
+GLOBALCALL LONG SetupScreen (void)
+{
+ struct Screen *DefScr;
+ struct DrawInfo *DefDri;
+
+ static LONG ExtraScreenTags[] =
+ {
+ SA_SysFont, 1,
+ SA_FullPalette, TRUE,
+ SA_SharePens, TRUE, /* These three Tags are valid only under
+ SA_LikeWorkbench, TRUE, * V39 and are properly ignored by V37.
+ SA_MinimizeISG, TRUE, */
+ SA_Interleaved, TRUE,
+ TAG_DONE
+ };
+
+ if (Scr)
+ {
+ /* If screen is already open, pop it to front and activate
+ * the main window.
+ */
+ ScreenToFront (Scr);
+ if (ThisTask->pr_WindowPtr)
+ RevealWindow ((struct WinUserData *)(((struct Window *)(ThisTask->pr_WindowPtr))->UserData));
+
+ return RETURN_OK;
+ }
+
+
+ if (!(ScrollButtonClass = InitScrollButtonClass()))
+ return ERROR_NO_FREE_STORE;
+
+
+ /* Try the user selected Public Screen */
+
+ if (!(Scr = LockPubScreen (ScrInfo.PubScreenName[0] ? ScrInfo.PubScreenName : NULL)))
+ {
+ /* Try to open own screen */
+
+ if (ScrInfo.DisplayID)
+ {
+ static UWORD PensArray[1] = {(UWORD)~0};
+ ULONG *ColorTable = NULL;
+#ifndef OS30_ONLY
+ struct ColorSpec *ColorSpec = NULL;
+#endif /* !OS30_ONLY */
+ ULONG i;
+
+ /* Color map translation */
+
+ if (ScrInfo.OwnPalette)
+ {
+#ifndef OS30_ONLY
+ if (IntuitionBase->LibNode.lib_Version >= 39)
+ {
+#endif /* !OS30_ONLY */
+ if (ColorTable = AllocPooled (Pool, ((32 * 3) + 2) * sizeof (LONG)))
+ {
+ ULONG *col = ColorTable,
+ tmp;
+
+ *col++ = 32 << 16;
+
+ for (i = 0; i < 32; i++)
+ {
+ tmp = (ScrInfo.Colors[i] >> 16); /* Red */
+ tmp |= tmp << 8 | tmp << 16 | tmp << 24;
+ *col++ = tmp;
+
+ tmp = (ScrInfo.Colors[i] >> 8) & 0xFF; /* Green */
+ tmp |= tmp << 8 | tmp << 16 | tmp << 24;
+ *col++ = tmp;
+
+ tmp = ScrInfo.Colors[i] & 0xFF; /* Blue */
+ tmp |= tmp << 8 | tmp << 16 | tmp << 24;
+ *col++ = tmp;
+ }
+
+ *col = 0;
+ }
+ }
+#ifndef OS30_ONLY
+ else /* V37 */
+ {
+ if (ColorSpec = AllocPooled (Pool, 33 * sizeof (struct ColorSpec)))
+ {
+ for (i = 0; i < 32; i++)
+ {
+ ColorSpec[i].ColorIndex = i;
+ ColorSpec[i].Red = ScrInfo.Colors[i] >> 20;
+ ColorSpec[i].Green = ScrInfo.Colors[i] >> 12 & 0xF;
+ ColorSpec[i].Blue = ScrInfo.Colors[i] >> 4 & 0xF;
+ }
+
+ ColorSpec[i].ColorIndex = -1;
+ }
+ }
+ }
+#endif /* !OS30_ONLY */
+
+
+ /* Use user requested attributes for screen */
+
+ Scr = OpenScreenTags (NULL,
+ SA_Width, ScrInfo.Width,
+ SA_Height, ScrInfo.Height,
+ SA_Depth, ScrInfo.Depth,
+ SA_DisplayID, ScrInfo.DisplayID,
+ SA_Overscan, ScrInfo.OverscanType,
+ SA_AutoScroll, ScrInfo.AutoScroll,
+ SA_Title, ScrInfo.PubScreenName,
+ SA_PubName, ScrInfo.PubScreenName,
+ SA_Font, ScreenAttr.ta_Name ? &ScreenAttr : NULL,
+ SA_Pens, PensArray,
+ ScrInfo.OwnPalette ?
+#ifndef OS30_ONLY
+ ((IntuitionBase->LibNode.lib_Version >= 39) ?
+ SA_Colors32 : SA_Colors) : TAG_IGNORE,
+ (IntuitionBase->LibNode.lib_Version >= 39) ?
+ (ULONG)ColorTable : (ULONG)ColorSpec,
+#else
+ SA_Colors32 : TAG_IGNORE, ColorTable,
+#endif /* !OS30_ONLY */
+
+ TAG_MORE, ExtraScreenTags);
+
+ if (ColorTable) FreePooled (Pool, ColorTable, ((32 * 3) + 2) * sizeof (LONG));
+#ifndef OS30_ONLY
+ if (ColorSpec) FreePooled (Pool, ColorSpec, 33 * sizeof (struct ColorSpec));
+#endif /* !OS30_ONLY */
+ if (Scr) OwnScreen = TRUE;
+ }
+
+ if (!Scr)
+ {
+ /* Try to clone the Default (Workbench) Screen */
+
+ if (!(DefScr = LockPubScreen (NULL)))
+ {
+ CloseDownScreen();
+ return ERROR_OBJECT_NOT_FOUND;
+ }
+
+ DefDri = GetScreenDrawInfo (DefScr);
+
+ if (Scr = OpenScreenTags (NULL,
+ SA_Depth, DefDri->dri_Depth,
+ SA_DisplayID, GetVPModeID (&DefScr->ViewPort),
+ SA_Overscan, OSCAN_TEXT,
+ SA_AutoScroll, TRUE,
+ SA_Title, ScrInfo.PubScreenName,
+ SA_PubName, ScrInfo.PubScreenName,
+ SA_Pens, DefDri->dri_Pens,
+ TAG_MORE, ExtraScreenTags))
+ {
+ UnlockPubScreen (NULL, DefScr);
+ OwnScreen = TRUE;
+ }
+ else
+ Scr = DefScr;
+
+ FreeScreenDrawInfo (DefScr, DefDri);
+ }
+
+ /* Make our screen really public */
+ if (OwnScreen) PubScreenStatus (Scr, 0);
+ }
+
+ if (!(VisualInfo = GetVisualInfoA (Scr, NULL)))
+ {
+ CloseDownScreen();
+ return ERROR_NO_FREE_STORE;
+ }
+
+ DrawInfo = GetScreenDrawInfo (Scr);
+
+ OffX = Scr->WBorLeft;
+ OffY = Scr->RastPort.TxHeight + Scr->WBorTop + 1;
+
+ /* Setup fonts */
+ if (!WindowAttr.ta_Name) CopyTextAttrPooled (Pool, Scr->Font, &WindowAttr);
+ if (!ListAttr.ta_Name) CopyTextAttrPooled (Pool, Scr->Font, &ListAttr);
+ if (!EditorAttr.ta_Name) CopyTextAttrPooled (Pool, &TopazAttr, &EditorAttr);
+
+ if (!(TopazFont = OpenFont (&TopazAttr)))
+ {
+ CantOpenLib (TopazAttr.ta_Name, 0);
+ CloseDownScreen();
+ return ERROR_NO_FREE_STORE;
+ }
+
+ if (DiskfontBase)
+ {
+ if (!(WindowFont = OpenDiskFont (&WindowAttr)))
+ CantOpenLib (WindowAttr.ta_Name, 0);
+ if (!(ListFont = OpenDiskFont (&WindowAttr)))
+ CantOpenLib (ListAttr.ta_Name, 0);
+ }
+
+ /* Setup windows shared Message Port */
+ if (!(WinPort = CreateMsgPort()))
+ {
+ CloseDownScreen();
+ return ERROR_NO_FREE_STORE;
+ }
+ IDCMPSig = 1 << WinPort->mp_SigBit;
+ Signals |= IDCMPSig;
+
+
+ /* Create a SIZEIMAGE to get the correct size
+ * and position for the slider and arrow buttons.
+ */
+ {
+ struct Image *sizeimage;
+
+ if (sizeimage = NewImageObject (SIZEIMAGE))
+ {
+ SizeWidth = sizeimage->Width;
+ SizeHeight = sizeimage->Height;
+ DisposeObject (sizeimage);
+ }
+ }
+
+
+ if (!NewWindow (WID_TOOLBOX))
+ {
+ CloseDownScreen();
+ return ERROR_NO_FREE_STORE;
+ }
+
+ /* Set Process Window pointer for DOS requesters */
+ OldPrWindowPtr = ThisTask->pr_WindowPtr;
+ ThisTask->pr_WindowPtr = WDescr[WID_TOOLBOX].Wud->Win;
+
+ ReopenWindows();
+
+ /* Bring screen to front in case it was hidden */
+ ScreenToFront (Scr);
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL void CloseDownScreen (void)
+
+/* Free screen and all associated resources */
+{
+ struct WinUserData *wud;
+
+ if (!Scr) return;
+
+ ScreenShutdown = TRUE;
+
+ /* Close AmigaGuide help window */
+ CleanupHelp();
+
+
+ if (Quit)
+ {
+ /* Destroy all windows */
+ while (!IsListEmpty (&WindowList))
+ DeleteWUD ((struct WinUserData *)WindowList.lh_Head);
+ }
+ else
+ {
+ /* Close all windows */
+ for (wud = (struct WinUserData *)WindowList.lh_Head; wud->Link.mln_Succ;
+ wud = (struct WinUserData *)wud->Link.mln_Succ)
+ {
+ MyCloseWindow (wud);
+ DeleteLayoutInfo (wud);
+ wud->WUDFlags |= WUDF_REOPENME;
+ }
+ }
+
+
+ if (WinPort)
+ {
+ Signals &= ~IDCMPSig;
+ IDCMPSig = 0;
+ DeleteMsgPort (WinPort); WinPort = NULL;
+ }
+
+ FreeScreenDrawInfo (Scr, DrawInfo); DrawInfo = NULL;
+ FreeVisualInfo (VisualInfo); VisualInfo = NULL;
+
+ if (OwnScreen)
+ {
+ /* TODO: Use BuildEasyRequest()/SysReqHandler() with SA_PubSig like IPrefs */
+
+ while (!CloseScreen (Scr))
+ ShowRequestArgs (MSG_CLOSE_ALL_WINDOWS, MSG_CONTINUE, NULL);
+ }
+ else UnlockPubScreen (NULL, Scr);
+
+ if (OldPrWindowPtr != (struct Window *)1L)
+ {
+ ThisTask->pr_WindowPtr = OldPrWindowPtr;
+ OldPrWindowPtr = (struct Window *)1L;
+ }
+
+ if (WindowFont)
+ {
+ CloseFont (WindowFont);
+ WindowFont = NULL;
+ }
+
+ if (ListFont)
+ {
+ CloseFont (ListFont);
+ ListFont = NULL;
+ }
+
+ if (TopazFont)
+ {
+ CloseFont (TopazFont);
+ TopazFont = NULL;
+ }
+
+ if (ScrollButtonClass)
+ {
+ FreeScrollButtonClass (ScrollButtonClass);
+ ScrollButtonClass = NULL;
+ }
+
+ Scr = NULL;
+ OwnScreen = FALSE;
+ ScreenShutdown = FALSE;
+}
--- /dev/null
+/*
+** Gui.h
+**
+** Copyright (C) 1993,94,95,96,98 by Bernardo Innocenti
+**
+** Various definitions for the user interface.
+*/
+
+#ifndef INTUITION_INTUITION_H
+#include <intuition/intuition.h>
+#endif
+
+#ifndef XMODULE_PRIV_H
+#include <XModulePriv.h>
+#endif
+
+
+/***************/
+/* WinUserData */
+/***************/
+
+/* This structure holds data needed to handle windows
+ * in a nice Object oriented way (no more endless switches :-)
+ */
+struct WinUserData
+{
+ struct MinNode Link; /* Used to mantain a list of all open windows. */
+
+ struct Window *Win;
+ ULONG GCount;
+ struct Gadget **Gadgets;
+ struct GInfo *GInfo;
+ UBYTE *Keys;
+ struct TextAttr *Attr;
+ struct TextFont *Font;
+ struct NewMenu *NewMenu;
+ struct Menu *MenuStrip;
+ struct Gadget *GList;
+ ULONG *LayoutArgs;
+ struct WindowBorder *Borders;
+ struct AppWindow *AppWin;
+ ULONG Flags;
+ ULONG IDCMPFlags;
+ STRPTR Title;
+
+ ULONG WindowID;
+ struct IBox WindowSize;
+ struct IBox WindowZoom;
+ LONG WUDFlags;
+ APTR UserData;
+ STRPTR HelpNode;
+
+ /* Window specific user functions */
+ LONG (*PreOpenFunc)(struct WinUserData *);
+ void (*PostOpenFunc)(struct WinUserData *);
+ LONG (*PreCloseFunc)(struct WinUserData *);
+ void (*PostCloseFunc)(struct WinUserData *);
+ void (*IDCMPFunc)(struct WinUserData *);
+ void (*DropIcon)(struct AppMessage *);
+ void (*LayoutCleanupFunc)(struct WinUserData *);
+};
+
+
+/* Flags for WinUserData->WUDFlags */
+
+#define WUDF_REOPENME (1<<0) /* Open this window again later */
+#define WUDF_LOCALEDONE (1<<1) /* Set when menustrip has been localized */
+#define WUDF_JUSTRESIZED (1<<3) /* Set just after an IDCMP_NEWSIZE */
+
+#define WUDF_CUSTOMLAYOUT (1<<2)
+/* Do not use the standard layout routines for
+ * this window. If not NULL, wud->LayoutArgs must point to
+ * a user function which should create and position the gadgets.
+ *
+ * struct Gadget *CustomLayoutFunc (struct WinUserData *wud);
+ */
+
+
+
+struct WDescr
+{
+ struct WinUserData *Wud; /* Pointer to WinUserData */
+ UWORD UseCnt; /* Number of WUDs of this kind */
+ UWORD Flags; /* Some flags, see below */
+ struct TagItem *CreationTags; /* See <gui.h> for possible tags */
+};
+
+
+
+/* Flags for WDescr->Flags */
+#define XMWINF_OPENMULTI (1<<0) /* Allow opening multiple times */
+#define XMWINF_LOCALEDONE (1<<1) /* Set when menustrip has been localized */
+
+
+/* Window IDs */
+
+enum
+{
+ WID_TOOLBOX,
+ WID_PREFS,
+ WID_SONGINFO,
+ WID_INSTRUMENTS,
+ WID_SEQUENCE,
+ WID_PATTERN,
+ WID_PLAY,
+ WID_SAMPLE,
+ WID_OPTIMIZATION,
+ WID_SAVERS,
+ WID_PATTPREFS,
+ WID_PATTSIZE,
+ WID_CLEAR,
+ WID_LOG,
+ WID_PROGRESS,
+
+ WID_COUNT
+};
+
+
+
+/* Tags for MyOpenWindow() */
+#define XMWIN_Dummy (TAG_USER + 'WN' << 8)
+#define XMWIN_NewMenu (XMWIN_Dummy + 1) /* Pointer to NewMenu array */
+#define XMWIN_LayoutArgs (XMWIN_Dummy + 2) /* Pointer to layout args array */
+#define XMWIN_GCount (XMWIN_Dummy + 3) /* Number of gadgets in window */
+#define XMWIN_Title (XMWIN_Dummy + 4) /* Window title */
+#define XMWIN_WindowFlags (XMWIN_Dummy + 5) /* Window flags for WA_Flags */
+#define XMWIN_IDCMPFlags (XMWIN_Dummy + 6) /* Flags for WA_IDCMPFlags */
+#define XMWIN_IDCMPFunc (XMWIN_Dummy + 7) /* Custom IDCMP handler */
+#define XMWIN_DropIconFunc (XMWIN_Dummy + 8) /* AppWindow handler */
+#define XMWIN_LayoutFunc (XMWIN_Dummy + 9) /* Your custom layout function */
+#define XMWIN_PreOpenFunc (XMWIN_Dummy + 10) /* Called just before opening win */
+#define XMWIN_PostOpenFunc (XMWIN_Dummy + 11) /* Called just after opening win */
+#define XMWIN_PreCloseFunc (XMWIN_Dummy + 12) /* Called just before closing win */
+#define XMWIN_PostCloseFunc (XMWIN_Dummy + 13) /* Called just after closing win */
+#define XMWIN_HelpNode (XMWIN_Dummy + 14) /* Name of AmigaGuide node for help */
+#define XMWIN_UserData (XMWIN_Dummy + 15) /* Whatever you want... */
+#define XMWIN_LayoutCleanup (XMWIN_Dummy + 16) /* Cleanup for custom layout func */
+
+/* You can also use standard OpenWindow() tags */
+
+
+
+/* This structure is used by LockWindows() to
+ * track modifications made to the windows.
+ */
+struct WindowLock
+{
+ struct Requester Req;
+ ULONG OldIDCMPFlags;
+ UWORD OldMinWidth,
+ OldMinHeight,
+ OldMaxWidth,
+ OldMaxHeight;
+};
+
+
+
+/****************/
+/* WindowBorder */
+/****************/
+
+/* A linked list of these structures is attached to all
+ * WUDs to keep track of the bevel boxes inside the window.
+ */
+struct WindowBorder
+{
+ struct WindowBorder *NextBorder;
+ ULONG Type;
+ struct IBox Size;
+};
+
+
+
+/*********/
+/* GInfo */
+/*********/
+
+/* Each WUD gets an array of GInfo structures to store
+ * some precalculated layout data. Each gadget and group
+ * has a GInfo structure in the array.
+ */
+struct GInfo
+{
+ WORD GKind; /* Object kind (#?_KIND) */
+ UWORD MinWidth, MinHeight; /* Minimum size */
+ UWORD LabelWidth; /* Label width for this object */
+ UWORD Flags; /* See below */
+ UWORD Dummy; /* Keep structure long aligned */
+ union {
+ APTR SpecialStorage; /* For gadget objects */
+ struct {
+ UWORD Width, Height; /* For group objects */
+ } Fixed;
+ };
+};
+
+/* Flags for GInfo->Flags */
+#define GIF_FIXEDWIDTH (1<<0)
+#define GIF_FIXEDHEIGHT (1<<1)
+#define GIF_HASBORDER (1<<4)
+
+
+
+/*********************/
+/* LayoutGadgetsArgs */
+/*********************/
+
+/* LayoutGadgetsArgs is used to pass arguments to LayoutGadgets()
+ * and CreateGadgets().
+ */
+struct LayoutGadgetsArgs
+{
+ ULONG *Args; /* Arguments array */
+ ULONG Count; /* GInfo array counter */
+ struct GInfo *GInfo; /* GInfo array */
+ struct WinUserData *Wud; /* The window we are working for */
+ struct RastPort *DummyRast; /* RastPort initialzied for TextLength() */
+ void *VInfo; /* VisualInfo for GadTools gadgets */
+ struct Gadget *PrevGad; /* Used to link gadgets together */
+ ULONG *SpecialTags; /* Storage for additional tags for gadgets */
+};
+
+
+/* Layout modes for LayoutGadgets and CreateGadgets() */
+#define LAYOUTMODE_V 0
+#define LAYOUTMODE_H 1
+
+#define VSPACING 1 /* Vertical spacing between GUI elements */
+#define HSPACING 2 /* Horizontal spacing between GUI elements */
+#define LABELSPACING 8 /* Extra horizontal spacing for labels */
+
+
+
+/***********/
+/* ScrInfo */
+/***********/
+
+struct ScrInfo
+{
+ ULONG DisplayID; /* Display mode ID */
+ ULONG Width; /* Width of display in pixels */
+ ULONG Height; /* Height of display in pixels */
+ UWORD Depth; /* Number of bit-planes */
+ UWORD OverscanType; /* Type of overscan of display */
+ BOOL AutoScroll; /* Display should auto-scroll? */
+ BOOL OwnPalette;
+ UWORD Colors[32];
+ UBYTE PubScreenName[32];
+};
+
+
+
+/*******************/
+/* File requesters */
+/*******************/
+
+/* This structure is used to reference all the file requesters.
+ */
+struct XMFileReq
+{
+ APTR FReq; /* Real file requester (ASL or ReqTools) */
+ ULONG Title; /* Message number for title */
+ ULONG Flags; /* FRF_DOSAVEMODE, FRF_DOPATTERNS, FRF_DOMULTISELECT... */
+};
+
+enum
+{
+ FREQ_LOADMOD,
+ FREQ_SAVEMOD,
+ FREQ_LOADINST,
+ FREQ_SAVEINST,
+ FREQ_LOADPATT,
+ FREQ_SAVEPATT,
+ FREQ_LOADMISC,
+ FREQ_SAVEMISC,
+
+ FREQ_COUNT
+};
+
+
+
+/************/
+/* Switches */
+/************/
+
+
+struct ClearSwitches
+{
+ BOOL ClearPatt,
+ ClearSeq,
+ ClearInstr;
+};
+
+struct SaveSwitches
+{
+ BOOL SavePatt,
+ SaveSeq,
+ SaveInstr,
+ SaveNames,
+ SaveIcons;
+};
+
+struct OptSwitches
+{
+ BOOL RemPatts,
+ RemDupPatts,
+ RemInstr,
+ RemDupInstr,
+ CutAfterLoop,
+ CutZeroTail,
+ CutPatts,
+ RemapInstr;
+};
+
+struct GuiSwitches
+{
+ BOOL SaveIcons,
+ AskOverwrite,
+ AskExit,
+ ShowAppIcon,
+ UseReqTools,
+ SmartRefresh,
+ UseDataTypes,
+ InstrSaveIcons,
+ AskAutosave,
+ DoBackups,
+ LogToFile;
+ UWORD InstrSaveMode,
+ SampDrawMode,
+ LogLevel,
+ AutosaveTime,
+ BackupVersions;
+ UBYTE BackupTemplate[64];
+ UBYTE LogFile[128];
+};
+
+struct PattSwitches
+{
+ WORD AdvanceTracks, AdvanceLines;
+ ULONG MaxUndoLevels, MaxUndoMem;
+ ULONG Flags; /* See <patteditclass.h> for possible flags */
+ WORD VScrollerPlace, HScrollerPlace;
+ UBYTE ClipboardUnit, Pad0;
+ UWORD TrackChars, Backdrop;
+ ULONG BGPen, TextPen, LinesPen, TinyLinesPen;
+};
+
+
+
+/* Possible values for (H|V)ScrollerPlace */
+#define SCROLLERPLACE_NONE 0
+#define SCROLLERPLACE_RIGHT 1
+#define SCROLLERPLACE_BOTTOM 1
+#define SCROLLERPLACE_LEFT 2
+
+
+
+/* Instrument save modes */
+enum {
+ INST_8SVX,
+ INST_8SVX_FIB,
+ INST_RAW,
+ INST_XPK
+};
+
+
+/* Handy macros to get a gadget string/number */
+
+#define GetString(g) (((struct StringInfo *)g->SpecialInfo)->Buffer)
+#define GetNumber(g) (((struct StringInfo *)g->SpecialInfo)->LongInt)
+
+
+/* Some handy definitions missing in <devices/inputevent.h> */
+
+#define IEQUALIFIER_SHIFT (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
+#define IEQUALIFIER_ALT (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
+#define IEQUALIFIER_COMMAND (IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND)
+
+
+/* Any break flag */
+
+#define SIGBREAKFLAGS (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F)
+
+
+/**************************/
+/* XModule custom gadgets */
+/**************************/
+
+
+#define ENDGROUP_KIND -1 /* End of group */
+#define VGROUP_KIND -2 /* Vertical group */
+#define HGROUP_KIND -3 /* Horizontal group */
+#define IMAGEBUTTON_KIND 20 /* button with vector image label */
+#define BOOPSI_KIND 21 /* BOOPSI gadget */
+
+
+/* ti_Data is a pointer to a setup function for custom gadgets.
+ * It receives the newly allocated Gadget structure and should customize
+ * all fields it is interested in, as well as doing all the allocations
+ * and precalculations which are needed for the custom gadget.
+ *
+ * When the XMGAD_BOOPSI tag is specified, SetupFunc() receves the
+ * tag array pointer and the NewGadget structure associated with the
+ * gadget. SetupFunc() should then return a pointer to the BOOPSI
+ * Gadget it has allocated.
+ */
+#define XMGAD_SetupFunc (TAG_USER+1)
+
+
+/* If ti_Data is TRUE, the layout routines will let the SetupFunc()
+ * allocate a BOOPSI gadget itself, So the gadget is _not_ allocated with
+ * the GadTools function CreateGadget(). When the window is closed with
+ * MyCloseWindow(), or when MyOpenWindow() fails after creating the object,
+ * it is your duty to DisposeObject() on these gadgets.
+ */
+//#define XMGAD_BOOPSI (TAG_USER+2)
+
+
+
+/********************/
+/* External symbols */
+/********************/
+
+XREF struct Screen *Scr;
+XREF struct DrawInfo *DrawInfo;
+XREF APTR VisualInfo;
+XREF struct ScrInfo ScrInfo;
+XREF struct WDescr WDescr[];
+XREF CxObj *MyBroker;
+XREF UWORD __chip *BlockPointer; /* for OS2.0 only */
+XREF Class *ScrollButtonClass;
+XREF UWORD OffX, OffY;
+XREF WORD SizeWidth, SizeHeight;
+XREF ULONG UniqueID;
+
+XREF struct IntuiMessage IntuiMsg;
+
+XREF struct MsgPort
+ *PubPort,
+ *CxPort;
+
+XREF struct TextAttr
+ TopazAttr,
+ ScreenAttr,
+ WindowAttr,
+ ListAttr,
+ EditorAttr;
+
+XREF struct TextFont *TopazFont;
+
+XREF ULONG
+ FileReqSig,
+ AppSig,
+ AudioSig,
+ CxSig,
+ AmigaGuideSig,
+ PubPortSig,
+ Signals; // Global Wait() signals
+
+
+XREF struct XMFileReq FileReqs[FREQ_COUNT];
+
+
+XREF struct List
+ WindowList,
+ InstrList,
+ PatternsList,
+ SequenceList,
+ LogList;
+
+
+XREF struct SaveSwitches SaveSwitches;
+XREF struct ClearSwitches ClearSwitches;
+XREF struct OptSwitches OptSwitches;
+XREF struct GuiSwitches GuiSwitches;
+XREF struct PattSwitches PattSwitches;
+
+XREF BOOL DoNextSelect;
+XREF BOOL ShowRequesters;
+XREF BOOL Iconified;
+XREF BOOL Quit;
+
+XREF LONG
+ ToolBoxWinTags[],
+ PrefsWinTags[],
+ SongInfoWinTags[],
+ InstrumentsWinTags[],
+ SequenceWinTags[],
+ PatternWinTags[],
+ PlayWinTags[],
+ SampleWinTags[],
+ OptimizationWinTags[],
+ SaversWinTags[],
+ PattPrefsWinTags[],
+ PattSizeWinTags[],
+ ClearWinTags[],
+ LogWinTags[],
+ ProgressWinTags[];
+
+
+
+/***********************/
+/* Function prototypes */
+/***********************/
+
+GLOBALCALL void OpenProgressWindow (void);
+GLOBALCALL void CloseProgressWindow (void);
+
+GLOBALCALL void UpdateInstrList (void);
+GLOBALCALL void UpdateInstrInfo (void);
+GLOBALCALL void DetatchSongInfoList (void);
+GLOBALCALL void UpdateSongInfoList (void);
+GLOBALCALL void UpdateSongInfo (void);
+GLOBALCALL void UpdatePatternList (void);
+GLOBALCALL void UpdateSequenceList (void);
+/*
+GLOBALCALL void UpdateSample (void);
+GLOBALCALL void UpdateSampInfo (void);
+GLOBALCALL void UpdateSampGraph (void);
+*/
+GLOBALCALL void UpdateSampleMenu (void);
+GLOBALCALL void UpdateGuiSwitches (void);
+GLOBALCALL void UpdateInstrSwitches (void);
+GLOBALCALL void UpdateClearSwitches (void);
+GLOBALCALL void UpdateSaveSwitches (void);
+GLOBALCALL void UpdateOptSwitches (void);
+GLOBALCALL void UpdatePrefsWindow (void);
+GLOBALCALL void UpdatePattern (void);
+GLOBALCALL void UpdateEditorInst (void);
+//GLOBALCALL void UpdatePlay (void);
+
+GLOBALCALL void UpdatePattSize (void);
+GLOBALCALL void UpdatePattPrefs (void);
+
+GLOBALCALL void ToolBoxDropIcon (struct AppMessage *msg);
+
+GLOBALCALL void HandleIDCMP (void);
+GLOBALCALL void LockWindows (void);
+GLOBALCALL void UnlockWindows (void);
+GLOBALCALL void RevealWindow (struct WinUserData *wud);
+GLOBALCALL void SetGadgets (struct WinUserData *wud, LONG arg, ...);
+
+GLOBALCALL LONG AddListViewNodeA (struct List *lv, CONST_STRPTR label, LONG *args);
+GLOBALCALL LONG AddListViewNode (struct List *lv, CONST_STRPTR label, ...);
+GLOBALCALL void RemListViewNode (struct Node *n);
+GLOBALCALL struct Image *NewImageObject (ULONG which);
+GLOBALCALL struct Window *NewWindow (ULONG id);
+GLOBALCALL struct Window *MyOpenWindow (struct WinUserData *wud);
+GLOBALCALL void MyCloseWindow (struct WinUserData *wud);
+GLOBALCALL struct WinUserData *CreateWUD (ULONG id);
+GLOBALCALL void DeleteLayoutInfo (struct WinUserData *wud);
+
+GLOBALCALL void ReopenWindows (void);
+GLOBALCALL LONG SetupScreen (void);
+GLOBALCALL void CloseDownScreen (void);
+
+GLOBALCALL struct Gadget *CreateUpButton (ULONG id, struct Gadget *target, LONG *map, LONG Place);
+GLOBALCALL struct Gadget *CreateDnButton (ULONG id, struct Gadget *target, LONG *map, LONG Place);
+GLOBALCALL struct Gadget *CreateSxButton (ULONG id, struct Gadget *target, LONG *map);
+GLOBALCALL struct Gadget *CreateDxButton (ULONG id, struct Gadget *target, LONG *map);
+GLOBALCALL struct Gadget *CreateVSlider (ULONG id, struct Gadget *target, LONG *map, LONG ButtonsSpacing, LONG Place);
+GLOBALCALL struct Gadget *CreateHSlider (ULONG id, struct Gadget *target, LONG *map, LONG ButtonsSpacing);
--- /dev/null
+Push macro
+ ifc "\1","All"
+ movem.l d0-a6,-(sp)
+ else
+ movem.l \1,-(sp)
+ endc
+ endm
+
+Pull macro
+ ifc "\1","All"
+ movem.l (sp)+,d0-a6
+ else
+ movem.l (sp)+,\1
+ endc
+ endm
+
+CALL macro
+ ifnd _LVO\1
+ xref _LVO\1
+ endc
+ jsr _LVO\1(a6)
+ endm
+
+CLIB macro
+ xref _LVO\2
+ ifc "\1","Exec"
+ move.l 4.w,a6
+ else
+ move.l _\1Base,a6
+ endc
+ jsr _LVO\2(a6)
+ endm
+
+CPLIB macro
+ xref _LVO\2
+ ifc "\1","Exec"
+ move.l 4.w,a6
+ else
+ move.l _\1Base(pc),a6
+ endc
+ jsr _LVO\2(a6)
+ endm
+
+C5LIB macro
+ ifnd _LVO\2
+ xref _LVO\2
+ endc
+ ifc "\1","Exec"
+ move.l 4.w,a6
+ else
+ move.l _\1Base(a5),a6
+ endc
+ jsr _LVO\2(a6)
+ endm
+
+OLIB macro *LIB_ID,CLEANUP openlib Dos, cleanup
+D\1 set 1
+ move.l 4.w,a6
+ lea _\1Lib(pc),a1
+ moveq #0,d0
+ jsr _LVOOpenLibrary(a6)
+ ifd RELATIVE
+ move.l d0,_\1Base(a5)
+ endc
+ ifnd RELATIVE
+ move.l d0,_\1Base
+ endc
+ ifnc '\2',''
+ beq \2
+ endc
+ endm
+
+CLLIB macro *LIB_ID closlib Dos
+ ifd RELATIVE
+ move.l _\1Base(a5),a1
+ endc
+ ifnd RELATIVE
+ move.l _\1Base(pc),a1
+ endc
+ move.l a1,d0
+ beq cLIB\@
+ move.l 4,a6
+ jsr _LVOCloseLibrary(a6)
+cLIB\@
+ ifd RELATIVE
+ clr.l _\1Base(a5)
+ endc
+ ifnd RELATIVE
+ clr.l _\1Base
+ endc
+ endm
--- /dev/null
+/*
+** Help.c
+**
+** Copyright (C) 1994,95,96 by Bernardo Innocenti
+**
+** Handle on-line, context sensitive, AmigaGuide help.
+*/
+
+#include <libraries/amigaguide.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/amigaguide.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+XDEF ULONG AmigaGuideSig = 0L;
+
+static struct Library *AmigaGuideBase = NULL;
+static struct NewAmigaGuide NewGuide = { 0 };
+static AMIGAGUIDECONTEXT Context = NULL;
+
+static STRPTR ContextList[] =
+{
+ "Main",
+ NULL
+};
+
+
+
+GLOBALCALL void HandleHelp (struct IntuiMessage *msg)
+{
+
+ if (!AmigaGuideBase)
+ {
+ if (!(AmigaGuideBase = OpenLibrary ("amigaguide.library", 33)))
+ {
+ CantOpenLib ("amigaguide.library", 33);
+ return;
+ }
+
+ NewGuide.nag_Name = (STRPTR) PRGNAME ".guide";
+ NewGuide.nag_Node = (STRPTR) "MAIN";
+ NewGuide.nag_BaseName = (STRPTR) PRGNAME;
+ NewGuide.nag_ClientPort = NULL; // (STRPTR) "XMODULE_HELP";
+ NewGuide.nag_Context = ContextList;
+ NewGuide.nag_Screen = Scr;
+
+ if(Context = OpenAmigaGuideAsync (&NewGuide,
+ AGA_HelpGroup, UniqueID,
+ TAG_DONE))
+ {
+ AmigaGuideSig = AmigaGuideSignal (Context);
+ Signals |= AmigaGuideSig;
+
+ /* Get startup message */
+ Wait (AmigaGuideSig);
+ HandleAmigaGuide();
+ }
+ else
+ {
+ LastErr = IoErr();
+ CloseLibrary (AmigaGuideBase); AmigaGuideBase = NULL;
+ return;
+ }
+ }
+
+
+ /* Link with node */
+ {
+ UBYTE cmd[48];
+
+ if (IntuiMsg.Class == IDCMP_RAWKEY || IntuiMsg.Class == IDCMP_MENUHELP)
+ SPrintf (cmd, "LINK \"%s\"", ((struct WinUserData *)IntuiMsg.IDCMPWindow->UserData)->HelpNode);
+ else
+ strcpy (cmd, "LINK Main");
+
+ SendAmigaGuideCmdA (Context, cmd, NULL);
+ }
+}
+
+
+
+GLOBALCALL void HandleAmigaGuide (void)
+{
+ struct AmigaGuideMsg *agm;
+
+ while (agm = GetAmigaGuideMsg (Context))
+ {
+ if (agm->agm_Pri_Ret) /* Error? */
+ {
+ STRPTR reason;
+
+ if (reason = GetAmigaGuideString (agm->agm_Sec_Ret))
+ ShowRequest (MSG_AMIGAGUIDE_ERROR, 0, reason);
+ }
+
+// switch (agm->agm_Type)
+// {
+// case ToolCmdReplyID: /* A command has completed */
+// case ToolStatusID:
+
+// default:
+// break;
+// }
+
+ ReplyAmigaGuideMsg (agm);
+ }
+}
+
+
+
+GLOBALCALL void CleanupHelp (void)
+{
+ if (AmigaGuideBase)
+ {
+ Signals &= ~AmigaGuideSig;
+ AmigaGuideSig = 0;
+ CloseAmigaGuide (Context); Context = NULL;
+ CloseLibrary (AmigaGuideBase); AmigaGuideBase = NULL;
+ }
+}
--- /dev/null
+/*
+** FastTracker2.h
+**
+** Copyright (C) 1994,95 Bernardo Innocenti
+**
+** Definitions for the FastTracker ][ XM format.
+*/
+
+
+
+struct FT2Header
+{
+ UBYTE ID[17]; /* ID text: "Extended module: " */
+ UBYTE Name[20]; /* Module name, padded with zeroes */
+ UBYTE Dummy; /* Constant: $1a */
+ UBYTE Tracker[20]; /* Tracker name */
+ UBYTE Revision; /* minor version number ($03) */
+ UBYTE Version; /* major version number ($01) */
+ ULONG HeaderSize; /* sizeof (struct FT2Header) */
+ UWORD Length; /* Song length (in patten order table) */
+ UWORD Restart; /* Restart position */
+ UWORD Channels; /* Number of channels (2,4,6,8,10,...,32) */
+ UWORD NumPatt; /* Number of patterns (max 256) */
+ UWORD NumInstr; /* Number of instruments (max 128) */
+ UWORD Flags; /* See below... */
+ UWORD DefTempo; /* Default tempo */
+ UWORD DefBPM; /* Default BeatsPerMinute */
+ /* Followed by patterns order table */
+};
+
+
+
+struct FT2Pattern
+{
+ ULONG Size; /* Header size, sizeof (FT2Pattern) */
+ UBYTE Packing; /* Packing type (always 0) */
+
+ /* Two words at odd offsetts! Argh!!! */
+ UBYTE RowsL, RowsH; /* Number of rows in pattern */
+ UBYTE PackSizeL, PackSizeH; /* Packed patterndata size */
+
+ /* Packed pattern data follows, but this structure's size is
+ * variable! Check FT2Pattern->Size to find out...
+ */
+};
+
+
+
+struct FT2Instrument
+{
+ ULONG Size; /* Instrument size */
+ UBYTE Name[22]; /* Instrument name */
+ UBYTE Type; /* Instrument type (always 0) */
+
+ /* Yet another time: words at odd offsetts! */
+ UBYTE SamplesL; /* If the number of samples > 0, */
+ UBYTE SamplesH; /* then an FT2Sample structure will follow */
+};
+
+
+
+struct FT2InstExt
+{
+ ULONG Size; /* Sample size */
+ UBYTE Number[96]; /* Sample number for all notes */
+ UBYTE VolEnv[48]; /* Points for volume envelope */
+ UBYTE PanEnv[48]; /* Points for panning envelope */
+ UBYTE VolCount; /* Number of volume points */
+ UBYTE PanCount; /* Number of panning points */
+ UBYTE VolSustain; /* Volume sustain point */
+ UBYTE VolLoopStart; /* Volume loop start point */
+ UBYTE VolLoopEnd; /* Volume loop end point */
+ UBYTE PanSustain; /* Panning sustain point */
+ UBYTE PanLoopStart; /* Panning loop start point */
+ UBYTE PanLoopEnd; /* Panning loop end point */
+ UBYTE VolType; /* Volume type: bit 0:On; 1:Sustain; 2:Loop */
+ UBYTE PanType; /* Panning type: bit 0:On; 1:Sustain; 2:Loop*/
+ UBYTE VibType; /* Vibrato type */
+ UBYTE VibSweep; /* Vibrato sweep */
+ UBYTE VibDepth; /* Vibrato depth */
+ UBYTE VibRate; /* Vibrato rate */
+ UBYTE VolFadeoutL;
+ UBYTE VolFadeoutH;
+ UBYTE ReservedL;
+ UBYTE ReservedH;
+};
+
+
+
+struct FT2Sample
+{
+ ULONG Length; /* Sample length */
+ ULONG Repeat; /* Sample loop start */
+ ULONG Replen; /* Sample loop length */
+ UBYTE Volume;
+ BYTE Finetune; /* (signed byte -16..+15) */
+ UBYTE Type; /* See below... */
+ UBYTE Panning; /* Panning (0-255) */
+ BYTE RelNote; /* Relative note number */
+ UBYTE Reserved;
+ UBYTE Name[22];
+};
+
+
+
+/* Flags for FT2Sample->Type */
+
+#define STYP_LOOPMASK 3 /* bits 0-1 */
+#define STYPF_FWDLOOP 1 /* Forward loop */
+#define STYPF_PINGLOOP 2 /* Ping-Pong loop */
+#define STYPF_16BIT 16 /* 16-bit sampledata */
+
--- /dev/null
+/*
+** GetFT2.c
+**
+** Copyright (C) 1995 Bernardo Innocenti
+**
+** Load a FastTracker ][ module with any number of tracks.
+*/
+
+#include <exec/memory.h>
+#include <libraries/XModule.h>
+
+#include <clib/exec_protos.h>
+#include <clib/dos_protos.h>
+#include <clib/xmodule_protos.h>
+
+#include <pragmas/exec_sysbase_pragmas.h>
+#include <pragmas/dos_pragmas.h>
+#include <pragmas/xmodule_pragmas.h>
+
+#include "Gui.h"
+#include "FastTracker2.h"
+
+
+/* Convert an Intel style WORD to Motorola format */
+#define I2M(x) ( (UWORD) ( (((UWORD)(x)) >> 8) | (((UWORD)(x)) << 8) ) )
+
+/* Convert an Intel style LONG to Motorola format */
+#define I2ML(x) ( I2M((x)>>16) | (I2M((x))<<16) )
+
+
+
+LONG GetFT2 (struct SongInfo *si, BPTR fh)
+{
+ ULONG i, j; /* Loop counters */
+ struct FT2Header ft2hd;
+
+
+ /* Read module header */
+ if (Read (fh, &ft2hd, sizeof (ft2hd)) != sizeof (ft2hd))
+ return ERR_READWRITE;
+
+ /* Convert from Intel's shitty endian */
+ ft2hd.HeaderSize = I2ML(ft2hd.HeaderSize);
+ ft2hd.Length = I2M(ft2hd.Length);
+ ft2hd.Restart = I2M(ft2hd.Restart);
+ ft2hd.Channels = I2M(ft2hd.Channels);
+ ft2hd.NumPatt = I2M(ft2hd.NumPatt);
+ ft2hd.NumInstr = I2M(ft2hd.NumInstr);
+ ft2hd.Flags = I2M(ft2hd.Flags);
+ ft2hd.DefTempo = I2M(ft2hd.DefTempo);
+ ft2hd.DefBPM = I2M(ft2hd.DefBPM);
+
+ if (!xmSetSongLen (si, ft2hd.Length))
+ return ERROR_NO_FREE_STORE;
+
+ if (Read (fh, si->Sequence, si->Length * sizeof (UWORD)) !=
+ si->Length * sizeof (UWORD))
+ return ERR_READWRITE;
+
+ for (i = 0; i < si->Length; i++)
+ si->Sequence[i] = I2M(si->Sequence[i]);
+
+ si->Restart = ft2hd.Restart;
+ si->GlobalSpeed = ft2hd.DefTempo;
+ si->GlobalTempo = ft2hd.DefBPM;
+
+ return RETURN_OK;
+}
--- /dev/null
+/*
+** MMDHook.c
+**
+** Copyright (C) 1994,95 by Bernardo Innocenti
+**
+** External loader and saver hook for MMD0 or MMD1 modules.
+**
+** Note: Sorry, this source is a bit of a mess, but the MMD
+** format is awfully complex <grin>.
+**
+**
+** Structure of an MMD0/1/2 module as saved by SaveMED():
+**
+** MMD0
+** MMD0song
+** MMD0exp
+** InstrExt[]
+** InstrInfo[]
+** <AnnoTxt>
+** <SongName>
+** NotationInfo
+** MMD0blockarr
+** MMD0samparr
+** <blocks>
+** <BlockInfo> /* Only for MMD1/MMD2 */
+** <BlockName> /* Only for MMD1/MMD2 */
+** <samples>
+*/
+
+#include <exec/types.h>
+
+#include <clib/exec_protos.h>
+#include <clib/dos_protos.h>
+#include <clib/xmodule_protos.h>
+
+#include <pragmas/exec_sysbase_pragmas.h>
+#include <pragmas/dos_pragmas.h>
+#include <pragmas/xmodule_pragmas.h>
+
+#include "XModule.h"
+#include "Gui.h"
+#include "OctaMed.h"
+
+
+#define OFFSET_MMD0song sizeof (struct MMD0)
+#define OFFSET_MMD0exp (sizeof (struct MMD0) + sizeof (struct MMD0song))
+#define OFFSET_InstrExt (sizeof (struct MMD0) + sizeof (struct MMD0song) + sizeof (struct MMD0exp))
+
+
+
+static INLINE UBYTE DecodeEff (UBYTE eff, UBYTE *effval, UBYTE flags, UBYTE flags2);
+
+
+
+/* Effects conversion table */
+static const UBYTE Effects[MAXTABLEEFFECTS] =
+{
+/* MED XModule Val */
+
+ 0x00, // Null effect $00
+
+ 0x01, // Portamento Up $01
+ 0x02, // Portamento Down $02
+ 0x03, // Tone Portamento $03
+ 0x14, // Vibrato $04
+ 0x05, // ToneP + VolSl $05
+ 0x06, // Vibra + VolSl $06
+ 0x07, // Tremolo $07
+ 0x08, // Set Hold/Decay $08
+ 0x19, // Sample Offset $09
+ 0x0A, // Volume Slide $0A
+ 0x0B, // Position Jump $0B
+ 0x0C, // Set Volume $0C
+ 0x1D, // Pattern break $0D
+ 0x00, // Misc $0E
+ 0x09, // Set Speed $0F
+ 0x0F, // Set Tempo $10
+ 0x00, // Arpeggio $11
+
+ 0x00, // Oktalyzer H
+ 0x00, // Oktalyzer L
+};
+
+
+
+LONG GetMED (struct SongInfo *si, BPTR fp)
+{
+ struct Instrument *inst = si->Inst;
+ struct MMD0 mmd0;
+ struct MMD0song song;
+ ULONG i, j, k, mmdtype, len;
+
+ /******************************/
+ /* Read MMD0 header */
+ /******************************/
+
+ if (Read (fp, &mmd0, sizeof (mmd0)) != sizeof (mmd0))
+ return ERR_READWRITE;
+
+ mmdtype = mmd0.id & 0xFF;
+
+ if (GuiSwitches.Verbose)
+ ShowMessage (MSG_READING_MMD, mmdtype);
+
+ mmdtype -= '0';
+
+ if (mmdtype > 1)
+ {
+ ShowMessage (MSG_UNSUPPORTED_MMD_FORMAT);
+ return ERR_NOTMODULE;
+ }
+
+
+ /******************************/
+ /* Read MMD0song structure */
+ /******************************/
+
+ if (Seek (fp, (LONG)mmd0.song, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ if (Read (fp, &song, sizeof (song)) != sizeof (song))
+ return ERR_READWRITE;
+
+
+ /* Set instruments parameters */
+
+ for (i = 0; i <= song.numsamples; i++)
+ {
+ inst[i+1].Volume = song.sample[i].svol;
+
+ if (song.sample[i].rep || song.sample[i].replen != 1)
+ {
+ inst[i+1].Repeat = song.sample[i].rep << 1;
+ inst[i+1].Replen = song.sample[i].replen << 1;
+ }
+ }
+
+ /* Set position table */
+ if (xmSetSongLen (si, song.songlen))
+ for (i = 0; i < si->Length; i++)
+ si->Sequence[i] = song.playseq[i];
+
+
+ if (song.flags2 & FLAG2_BPM)
+ si->GlobalTempo = (song.deftempo * 4) / ((song.flags2 & FLAG2_BMASK) + 1);
+ else
+ si->GlobalTempo = song.deftempo;
+
+ si->GlobalSpeed = song.tempo2;
+
+
+
+ /******************************/
+ /* Read MMD0exp structure */
+ /******************************/
+
+ if (mmd0.expdata)
+ {
+ struct MMD0exp exp;
+
+ if (Seek (fp, (LONG)mmd0.expdata, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ if (Read (fp, &exp, sizeof (exp)) != sizeof (exp))
+ return ERR_READWRITE;
+
+
+ /******************************/
+ /* Read InstrExt structures */
+ /******************************/
+
+ if (exp.exp_smp && (exp.s_ext_entrsz >= 4))
+ {
+ struct InstrExt instrext;
+ ULONG size = min (exp.s_ext_entrsz, sizeof (instrext));
+
+ if (Seek (fp, (LONG)exp.exp_smp, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ for (i = 1; i <= exp.s_ext_entries; i++)
+ {
+ if (Read (fp, &instrext, size) != size)
+ return ERR_READWRITE;
+
+ inst[i].FineTune = instrext.finetune;
+ if (exp.s_ext_entrsz > size)
+ if (Seek (fp, exp.s_ext_entrsz - size, OFFSET_CURRENT) == -1)
+ return ERR_READWRITE;
+ }
+ }
+
+ /******************************/
+ /* Read Annotation */
+ /******************************/
+
+ if (exp.annotxt && exp.annolen > 1)
+ {
+ ULONG len = min (exp.annolen, MAXAUTHNAME-1);
+
+ if (Seek (fp, (LONG)exp.annotxt, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ if (Read (fp, si->Author, len) != len)
+ return ERR_READWRITE;
+ }
+
+
+ /******************************/
+ /* Read InstrInfo structures */
+ /******************************/
+
+ if (exp.iinfo && (exp.i_ext_entrsz >= sizeof (struct MMDInstrInfo)))
+ {
+ struct MMDInstrInfo instrinfo;
+
+ if (Seek (fp, (LONG)exp.iinfo, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ for (i = 1; i <= exp.i_ext_entries; i++)
+ {
+ if (Read (fp, &instrinfo, sizeof (instrinfo)) != sizeof (instrinfo))
+ return ERR_READWRITE;
+
+ strncpy (inst[i].Name, instrinfo.name, MAXINSTNAME - 1);
+
+ if (exp.i_ext_entrsz > sizeof (struct MMDInstrInfo))
+ if (Seek (fp, exp.i_ext_entrsz - sizeof (struct MMDInstrInfo), OFFSET_CURRENT) == -1)
+ return ERR_READWRITE;
+ }
+ }
+
+ /******************************/
+ /* Read SongName */
+ /******************************/
+
+ if (exp.songname && exp.songnamelen > 1)
+ {
+ ULONG size = min (exp.songnamelen, MAXSONGNAME-1);
+
+ if (Seek (fp, (LONG)exp.songname, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ if (Read (fp, si->SongName, size) != size)
+ return ERR_READWRITE;
+ }
+ }
+
+
+ /******************************/
+ /* Read blocarr */
+ /******************************/
+
+ if (mmd0.blockarr)
+ {
+ LONG *blockarr;
+
+ DisplayAction (MSG_READING_PATTS);
+
+ if (Seek (fp, (LONG)mmd0.blockarr, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ if (!(blockarr = AllocMem (song.numblocks * 4, MEMF_ANY)))
+ return ERROR_NO_FREE_STORE;
+
+ if (Read (fp, blockarr, song.numblocks * 4) != song.numblocks * 4)
+ {
+ FreeMem (blockarr, song.numblocks * 4);
+ return ERR_READWRITE;
+ }
+
+
+
+ /***************/
+ /* Read blocks */
+ /***************/
+
+ for (i = 0; i < song.numblocks; i++)
+ {
+ struct Pattern *patt;
+ struct Note *note;
+ struct MMD1block block;
+ struct MMD0block mmd0block;
+ struct BlockInfo blockinfo;
+ ULONG mednote;
+
+ if (DisplayProgress (i, song.numblocks))
+ {
+ FreeMem (blockarr, song.numblocks * 4);
+ return ERROR_BREAK;
+ }
+
+ if (Seek (fp, (LONG)blockarr[i], OFFSET_BEGINNING) == -1)
+ ShowMessage (MSG_ERR_CANT_LOAD_PATT, i);
+ else
+ {
+ if (mmdtype == 0)
+ len = Read (fp, &mmd0block, sizeof (struct MMD0block));
+ else
+ len = Read (fp, &block, sizeof (struct MMD1block));
+
+ if (len != (mmdtype ? sizeof (struct MMD1block) : sizeof (struct MMD0block)))
+ ShowMessage (MSG_ERR_CANT_LOAD_PATT, i);
+ else
+ {
+ if (mmdtype == 0) /* Convert MMD0 block */
+ {
+ block.numtracks = mmd0block.numtracks;
+ block.lines = mmd0block.lines;
+ block.info = 0;
+ }
+
+ if (block.numtracks >= MAXTRACKS)
+ {
+ ShowMessage (MSG_PATT_TOO_MANY_TRACKS, i, MAXTRACKS);
+ continue;
+ }
+
+ if (si->MaxTracks < block.numtracks)
+ si->MaxTracks = block.numtracks;
+
+ if (block.lines > MAXPATTLINES - 1)
+ {
+ ShowMessage (MSG_PATT_TOO_MANY_LINES, i, MAXPATTLINES);
+ block.lines = MAXPATTLINES - 1;
+ }
+
+ if (!(patt = AddPattern (si, block.numtracks, block.lines + 1)))
+ {
+ FreeMem (blockarr, song.numblocks * 4);
+ return ERROR_NO_FREE_STORE;
+ }
+
+ /***************/
+ /* Read Tracks */
+ /***************/
+
+ for (k = 0; k < patt->Lines; k++)
+ {
+ for (j = 0; j < patt->Tracks; j++)
+ {
+ note = &patt->Notes[j][k];
+
+ if (mmdtype == 0)
+ {
+ if (Read (fp, &mednote, MMD0ROW) != MMD0ROW)
+ return ERR_READWRITE;
+
+ mednote >>= 8;
+
+ note->EffVal = mednote & 0xFF;
+ note->EffNum = DecodeEff ((mednote >> 8) & 0x0F, ¬e->EffVal, song.flags, song.flags2);
+ note->Note = ((mednote >> 16) & 0x3F);
+ if (note->Note) note->Note += 12;
+ note->Inst = ((mednote >> 12) & 0xF) |
+ ((mednote & (1<<22)) ? 0x20 : 0) | ((mednote & (1<<23)) ? 0x10 : 0);
+ }
+ else
+ {
+ if (Read (fp, &mednote, MMD1ROW) != MMD1ROW)
+ return ERR_READWRITE;
+
+ note->EffVal = mednote & 0xFF;
+ note->EffNum = DecodeEff ((mednote >> 8) & 0xFF, ¬e->EffVal, song.flags, song.flags2);
+ note->Inst = ((mednote >> 16) & 0x3F);
+ note->Note = ((mednote >> 24) & 0x7F);
+
+ if (note->Note) note->Note += 12;
+ }
+ }
+ } /* End for (patt->Tracks) */
+
+ /******************/
+ /* Read BlockInfo */
+ /******************/
+
+ if (block.info)
+ if (Seek (fp, (LONG)block.info, OFFSET_BEGINNING) != -1);
+ {
+ if (Read (fp, &blockinfo, sizeof (blockinfo)) == sizeof(blockinfo))
+ {
+ if (blockinfo.blockname)
+ if (Seek (fp, (LONG)blockinfo.blockname, OFFSET_BEGINNING) != -1);
+ {
+ Read (fp, patt->PattName, min (blockinfo.blocknamelen, MAXPATTNAME));
+ patt->PattName[MAXPATTNAME-1] = '\0'; /* Ensure NULL-termination */
+ }
+ }
+ }
+ }
+ }
+ } /* End for (song->numblocks) */
+
+ FreeMem (blockarr, song.numblocks * 4);
+ }
+
+
+
+ /******************************/
+ /* Read smplarr */
+ /******************************/
+
+ if (mmd0.smplarr)
+ {
+ LONG *smplarr;
+ struct InstrHdr instrhdr;
+
+ DisplayAction (MSG_READING_INSTS);
+
+ if (Seek (fp, (LONG)mmd0.smplarr, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ if (!(smplarr = AllocMem (song.numsamples * 4, MEMF_ANY)))
+ return ERROR_NO_FREE_STORE;
+
+ if (Read (fp, smplarr, song.numsamples * 4) != song.numsamples * 4)
+ {
+ FreeMem (smplarr, song.numsamples * 4);
+ return ERR_READWRITE;
+ }
+
+
+ /******************************/
+ /* Read samples */
+ /******************************/
+
+ for (i = 1; i <= song.numsamples ; i++)
+ {
+ if (!smplarr[i-1]) continue;
+
+ if (DisplayProgress (i, song.numsamples))
+ {
+ FreeMem (smplarr, song.numsamples * 4);
+ return ERROR_BREAK;
+ }
+
+ if (Seek (fp, smplarr[i-1], OFFSET_BEGINNING) == -1)
+ ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
+ else
+ {
+ if (Read (fp, &instrhdr, sizeof (instrhdr)) != sizeof (instrhdr))
+ ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
+ else
+ {
+ switch (instrhdr.type)
+ {
+ case SAMPLE:
+ inst[i].Length = instrhdr.length;
+
+ if (!(inst[i].SampleData = (UBYTE *) AllocVec (inst[i].Length, MEMF_SAMPLE)))
+ {
+ ShowMessage (MSG_ERR_NO_MEM_FOR_INST, i);
+ break;
+ }
+
+ if (Read (fp, inst[i].SampleData, inst[i].Length) != inst[i].Length)
+ ShowMessage (MSG_ERR_CANT_LOAD_INST, i);
+
+ /* Double sample data */
+ if (song.flags & FLAG_8CHANNEL)
+ {
+ BYTE *data = inst[i].SampleData;
+
+ for (j = 0; j < inst[j].Length; j++)
+ *data++ <<= 1;
+ }
+ break;
+
+ default:
+ ShowMessage (MSG_ERR_NOT_A_SAMPLE, i);
+ break;
+ }
+ }
+ }
+ }
+
+ FreeMem (smplarr, song.numsamples * 4);
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static __inline UBYTE DecodeEff (UBYTE eff, UBYTE *effval, UBYTE flags, UBYTE flags2)
+{
+ UBYTE i;
+
+ switch (eff)
+ {
+ case 0x00:
+ if (*effval) return EFF_ARPEGGIO;
+ else return EFF_NULL;
+
+ case 0x0C: /* Volume */
+ if (!(flags & FLAG_VOLHEX))
+ /* Convert decimal volumes */
+ *effval = (*effval & 0x0F) + (*effval >> 4) * 10;
+ return EFF_SETVOLUME;
+
+ case 0x0F: /* Tempo */
+ if (*effval == 0) return EFF_PATTERNBREAK;
+ if (*effval <= 0x0A) return EFF_SETSPEED;
+ if (*effval <= 0xF0)
+ {
+ *effval = (*effval * 4) / ((flags2 & FLAG2_BMASK) + 1);
+ return EFF_SETTEMPO;
+ }
+ if (*effval == 0xF8)
+ {
+ *effval = 1;
+ return EFF_MISC; /* Filter off */
+ }
+ if (*effval == 0xF9)
+ {
+ *effval = 0;
+ return EFF_MISC; /* Filter on */
+ }
+ return EFF_NULL;
+
+ default:
+ for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
+ if (eff == Effects[i])
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+LONG SaveMED (struct SongInfo *si, BPTR fp, UWORD mmd_type)
+{
+ struct Instrument *inst = si->Inst;
+ struct Pattern *patt = si->PattData;
+ ULONG modlen, blockslen, instlen, blockptr, offset_blockarr,
+ i, j, k, lastinstr;
+ BOOL halve_instr;
+
+ if (GuiSwitches.Verbose)
+ ShowMessage (MSG_WRITING_MMD, mmd_type + '0');
+
+ DisplayAction (MSG_WRITING_HEADER);
+
+
+ /*********************************/
+ /* Calculate total patterns size */
+ /*********************************/
+ {
+ if (mmd_type > 0) /* MMD1 */
+ {
+ for (i = 0, blockslen = 0; i < si->NumPatterns ; i++)
+ {
+ blockslen += sizeof (struct MMD1block) +
+ (MMD1ROW * patt[i].Lines * patt[i].Tracks) +
+ (patt[i].PattName[0] ?
+ (sizeof (struct BlockInfo) + strlen (patt[i].PattName) + 1)
+ : 0);
+
+ if (blockslen & 1) blockslen++; /* Pad to words */
+ }
+ }
+ else /* MMD0 */
+ {
+ for (i = 0, blockslen = 0; i < si->NumPatterns ; i++)
+ {
+ blockslen += sizeof (struct MMD0block) +
+ (MMD0ROW * patt[i].Lines * patt[i].Tracks);
+
+ if (blockslen & 1) blockslen++; /* Pad to words */
+ }
+ }
+ }
+
+ /* Find last used instrument */
+ for (lastinstr = 63; lastinstr > 0 ; lastinstr--)
+ if (inst[lastinstr].Length || inst[lastinstr].Name[0]) break;
+
+ /* Calculate Total instruments size */
+ for (i = 1, instlen = 0; i <= lastinstr; i++)
+ instlen += (inst[i].Length) ? (si->Inst[i].Length + sizeof (struct InstrHdr)) : 0;
+
+ /* Calculate blockarr offset */
+ offset_blockarr = sizeof (struct MMD0) + sizeof (struct MMD0song) + sizeof (struct MMD0exp) +
+ lastinstr * (sizeof (struct InstrExt) + sizeof (struct MMDInstrInfo));
+
+ offset_blockarr += strlen (si->Author) + 1;
+ if (offset_blockarr & 1) offset_blockarr++; /* Pad to word */
+ offset_blockarr += strlen (si->SongName) + 1;
+ if (offset_blockarr & 1) offset_blockarr++; /* Pad to word */
+
+ /* Calculate Total module size */
+ modlen = offset_blockarr +
+ si->NumPatterns * 4 + /* blockarr */
+ lastinstr * 4 + /* samplearr */
+ blockslen +
+ instlen;
+
+ /**********************************/
+ /* Fill-in & write module header */
+ /*********************************/
+ {
+ struct MMD0 mmd0;
+
+ memset (&mmd0, 0, sizeof (mmd0));
+
+ mmd0.id = mmd_type ? ID_MMD1 : ID_MMD0;
+ mmd0.modlen = modlen;
+ mmd0.song = (struct MMD0song *) OFFSET_MMD0song;
+// mmd0.reserved0 = 0;
+ mmd0.blockarr = (struct MMD0Block **) offset_blockarr;
+// mmd0.reserved1 = si->NumPatterns;
+ mmd0.smplarr = (struct InstrHdr **) (offset_blockarr + (si->NumPatterns * 4));
+// mmd0.reserved2 = lastinst * sizeof (LONG);
+ mmd0.expdata = (struct MMD0exp *) OFFSET_MMD0exp;
+// mmd0.reserved3 = 0; /* expsize */
+// mmd0.pstate = 0; /* the state of the player */
+ mmd0.pblock = si->CurrentPatt; /* current block */
+// mmd0.pline = 0; /* current line */
+ mmd0.pseqnum = si->CurrentPos; /* current # of playseqlist */
+ mmd0.actplayline= -1; /* OBSOLETE!! DON'T TOUCH! */
+// mmd0.counter = 0; /* delay between notes */
+// mmd0.extra_songs= 0;
+
+ /* Write file header */
+ if (Write (fp, &mmd0, sizeof (mmd0)) != sizeof (mmd0))
+ return ERR_READWRITE;
+
+ }
+
+ /************************************/
+ /* Fill-in and write Song structure */
+ /************************************/
+ {
+ struct MMD0song song;
+
+ for (i = 0; i < 63 ; i++)
+ {
+ song.sample[i].rep = inst[i+1].Repeat >> 1;
+ song.sample[i].replen = inst[i+1].Replen >> 1;
+ song.sample[i].midich = 0;
+ song.sample[i].midipreset = 0;
+ song.sample[i].svol = inst[i+1].Volume;
+ song.sample[i].strans = 0;
+ }
+
+ song.numblocks = si->NumPatterns;
+ song.songlen = min (si->Length, 256);
+
+ for (i = 0; i < song.songlen; i++)
+ song.playseq[i] = si->Sequence[i];
+
+ song.deftempo = si->GlobalTempo;
+ song.playtransp = 0;
+ halve_instr = si->MaxTracks > 4;
+ song.flags = (halve_instr ? FLAG_8CHANNEL : 0) | FLAG_STSLIDE | FLAG_VOLHEX;
+ song.flags2 = 3 | FLAG2_BPM;
+ song.tempo2 = si->GlobalSpeed;
+
+ for (i = 0; i <16; i++)
+ song.trkvol[i] = 64;
+
+ song.mastervol = 64;
+ song.numsamples = lastinstr;
+
+ if (Write (fp, &song, sizeof (song)) != sizeof (song))
+ return ERR_READWRITE;
+ }
+
+ /*****************************/
+ /* Write extension (MMD0exp) */
+ /*****************************/
+ {
+ struct MMD0exp mmd0exp;
+ LONG len;
+
+ memset (&mmd0exp, 0, sizeof (mmd0exp));
+
+ mmd0exp.exp_smp = (struct InstrExt *) OFFSET_InstrExt;
+ mmd0exp.s_ext_entries = lastinstr;
+ mmd0exp.s_ext_entrsz = sizeof (struct InstrExt);
+ mmd0exp.annotxt = (STRPTR) (OFFSET_InstrExt + lastinstr * (sizeof (struct InstrExt) + sizeof (struct MMDInstrInfo)));
+ mmd0exp.annolen = strlen (si->Author) + 1;
+ mmd0exp.iinfo = (struct MMDInstrInfo *) (OFFSET_InstrExt + lastinstr * sizeof (struct InstrExt));
+ mmd0exp.i_ext_entries = lastinstr;
+ mmd0exp.i_ext_entrsz = sizeof (struct MMDInstrInfo);
+ mmd0exp.songname = mmd0exp.annotxt + mmd0exp.annolen + ((mmd0exp.annolen & 1) ? 1 : 0);
+ mmd0exp.songnamelen = strlen (si->SongName) + 1;
+
+ if (Write (fp, &mmd0exp, sizeof (mmd0exp)) != sizeof (mmd0exp))
+ return ERR_READWRITE;
+
+
+ /************************/
+ /* Write InstrExt array */
+ /************************/
+ {
+ struct InstrExt instrext = { 0 };
+
+ for (i = 1; i <= lastinstr; i++)
+ {
+ // instrext.hold = 0;
+ // instrext.decay = 0;
+ // instrext.suppress_midi_off = 0;
+ instrext.finetune = si->Inst[i].FineTune;
+ // instrext.default_pitch = 0;
+ // instrext.instr_flags = 0;
+ // instrext.long_midi_preset = 0;
+ // instrext.output_device = 0;
+ // instrext.reserved = 0;
+
+ if (Write (fp, &instrext, sizeof (instrext)) != sizeof (instrext))
+ return ERR_READWRITE;
+ }
+ }
+
+ /*************************/
+ /* Write InstrInfo array */
+ /*************************/
+ {
+ struct MMDInstrInfo instrinfo = { 0 };
+
+ for (i = 1; i <= lastinstr; i++)
+ {
+ strcpy (instrinfo.name, si->Inst[i].Name);
+
+ if (Write (fp, &instrinfo, sizeof (instrinfo)) != sizeof (instrinfo))
+ return ERR_READWRITE;
+ }
+ }
+
+ /* Write AnnoTxt */
+
+ len = strlen (si->Author) + 1;
+ if (len & 1) len++; /* Pad to WORD */
+ if (Write (fp, si->Author, len) != len)
+ return ERR_READWRITE;
+
+ /* Write SongName */
+
+ len = strlen (si->SongName) + 1;
+ if (len & 1) len++; /* Pad to WORD */
+ if (Write (fp, si->SongName, len) != len)
+ return ERR_READWRITE;
+ }
+
+ /*************************************/
+ /* Write pattern pointers (blockarr) */
+ /*************************************/
+ {
+ blockptr = offset_blockarr + (si->NumPatterns * 4) + (lastinstr * 4);
+
+ for (i = 0; i < si->NumPatterns; i++)
+ {
+ if (Write (fp, &blockptr, 4) != 4)
+ return ERR_READWRITE;
+
+ if (mmd_type) /* MMD1 and up */
+ {
+ blockptr += sizeof (struct MMD1block) +
+ (MMD1ROW * patt[i].Tracks * patt[i].Lines) +
+ ((patt[i].PattName[0]) ?
+ (sizeof (struct BlockInfo) + strlen (patt[i].PattName) + 1)
+ : 0);
+ }
+ else /* MMD0 */
+ {
+ blockptr += sizeof (struct MMD0block) +
+ (MMD0ROW * patt[i].Tracks * patt[i].Lines);
+ }
+
+ /* Pad to words */
+ if (blockptr & 1) blockptr++;
+ }
+ }
+
+ /*************************************/
+ /* Write sample pointers (samplearr) */
+ /*************************************/
+ {
+ ULONG sampleptr = blockptr, x;
+
+ for (i = 1; i <= lastinstr; i++)
+ {
+ x = ((inst[i].Length && inst[i].SampleData) ? sampleptr : 0);
+
+ if (Write (fp, &x, 4) != 4)
+ return ERR_READWRITE;
+
+ if (x) sampleptr += inst[i].Length + sizeof (struct InstrHdr);
+ }
+ }
+
+ /********************************/
+ /* Write patterns (blocks) data */
+ /********************************/
+ {
+ struct Note *note;
+ ULONG mednote;
+ LONG blockinfoptr = offset_blockarr + (si->NumPatterns * 4) + (lastinstr * 4);
+ UBYTE eff, effval;
+ BOOL quiet = FALSE;
+
+
+ DisplayAction (MSG_WRITING_PATTS);
+
+
+ for (i = 0; i < si->NumPatterns; i++)
+ {
+ if (DisplayProgress (i, si->NumPatterns))
+ return ERROR_BREAK;
+
+ /* Write Header */
+
+ if (mmd_type) /* MMD1 and higher */
+ {
+ struct MMD1block block;
+
+ blockinfoptr += sizeof (struct MMD1block) +
+ (MMD1ROW * patt[i].Tracks * patt[i].Lines);
+
+ block.numtracks = patt[i].Tracks;
+ block.lines = patt[i].Lines - 1;
+
+ /* Write BlockInfo only if this pattern has a name */
+ block.info = (struct BlockInfo *)(patt[i].PattName[0] ? blockinfoptr : 0);
+
+ if (Write (fp, &block, sizeof (block)) != sizeof (block))
+ return ERR_READWRITE;
+ }
+ else /* MMD0 */
+ {
+ struct MMD0block block;
+
+ block.numtracks = patt[i].Tracks;
+ block.lines = patt[i].Lines - 1;
+
+ if (Write (fp, &block, sizeof (block)) != sizeof (block))
+ return ERR_READWRITE;
+ }
+
+
+ /* Write Tracks */
+ for (k = 0; k < patt[i].Lines; k++)
+ {
+ for (j = 0; j < patt[i].Tracks; j++)
+ {
+ note = &patt[i].Notes[j][k];
+ eff = Effects[note->EffNum];
+
+ if (note->EffNum == EFF_MISC)
+ {
+/*
+ pt module xmod bad conversion octamed right conversion
+
+ E01 006 0FF8 (Led OFF)
+ E11 not converted 1101 (Slide pitch up once)
+ EC3 not converted 1803 (Cut note)
+ E62 not converted 1602 (Loop)
+ 601 1601 0601 (Vibrato and fade)
+ E93 not converted 1F03 (Note delay and retrigger)
+ ED2 ???? 1F20 (Note delay and retrigger)
+ EB1 not converted 1B01 (Slide volume down once)
+*/
+
+ switch (note->EffNum >> 4)
+ {
+ case 0x6: /* Loop */
+ eff = 0x16;
+ effval = note->EffNum & 0x0F;
+ break;
+ case 0xC: /* Note Cut */
+ eff = 0x18;
+ break;
+ case 0xE: /* Replay line */
+ eff = 0x1E;
+ effval = note->EffNum & 0x0F;
+ break;
+ }
+ }
+ else if (note->EffNum == EFF_PATTERNBREAK)
+ effval = 0;
+ else
+ effval = note->EffVal;
+
+ // if (note->EffNum == EFF_SETTEMPO)
+ // effval /= 4;
+
+ if (mmd_type) /* MMD1 and up */
+ {
+ mednote = effval |
+ ((ULONG)eff << 8) |
+ ((ULONG)(note->Inst & 0x3F) << 16) |
+ ((ULONG)((note->Note ? (note->Note - 12) : 0) & 0x7F) << 24);
+
+ if (Write (fp, &mednote, MMD1ROW) != MMD1ROW)
+ return ERR_READWRITE;
+ }
+ else
+ {
+ if ((eff > 0x0F) && !quiet)
+ {
+ ShowMessage (MSG_WRONG_EFFECT_IN_MMD0, eff);
+ quiet = TRUE;
+ }
+
+ mednote = (effval |
+ ((ULONG)(eff & 0x0F) << 8) |
+ ((ULONG)(note->Inst & 0x0F) << 12) |
+ ((ULONG)((note->Note ? (note->Note - 12) : 0) & 0x3F) << 16) |
+ ((ULONG)(note->Inst & 0x20) << 17) |
+ ((ULONG)(note->Inst & 0x10) << 19)) << 8;
+
+ if (Write (fp, &mednote, MMD0ROW) != MMD0ROW)
+ return ERR_READWRITE;
+ }
+ }
+ }
+
+ if (mmd_type)
+ {
+ if (patt[i].PattName[0])
+ {
+ /*****************************/
+ /* Write BlockInfo structure */
+ /*****************************/
+
+ struct BlockInfo blockinfo;
+
+ blockinfoptr += sizeof (blockinfo);
+
+ memset (&blockinfo, 0, sizeof (blockinfo));
+ blockinfo.blockname = (UBYTE *)blockinfoptr;
+ blockinfo.blocknamelen = strlen (patt[i].PattName) + 1;
+
+ if (Write (fp, &blockinfo, sizeof (blockinfo)) != sizeof (blockinfo))
+ return ERR_READWRITE;
+
+ if (Write (fp, patt[i].PattName, blockinfo.blocknamelen) != blockinfo.blocknamelen)
+ return ERR_READWRITE;
+
+ blockinfoptr += sizeof (struct BlockInfo) + blockinfo.blocknamelen;
+
+ /* Pad to words */
+ if (blockinfoptr & 1)
+ {
+ UBYTE dummy;
+
+ if (Write (fp, &dummy, 1) != 1)
+ return ERR_READWRITE;
+
+ blockinfoptr++;
+ }
+ }
+ }
+ else /* MMD0 */
+ {
+ /* Add a pad byte when length is not even */
+ if ((patt[i].Lines * patt[i].Tracks) & 1)
+ {
+ UBYTE dummy = 0;
+
+ if (Write (fp, &dummy, 1) != 1)
+ return ERR_READWRITE;
+ }
+ }
+ } /* End for (si->NumPatterns) */
+ }
+
+
+ /**********************/
+ /* Write samples data */
+ /**********************/
+ {
+ struct InstrHdr instrhdr;
+ UBYTE *samp;
+ LONG l;
+ BOOL free_samp;
+
+ DisplayAction (MSG_WRITING_INSTDATA);
+
+ for (i = 1; i <= lastinstr; i++)
+ {
+ free_samp = FALSE;
+
+ l = inst[i].Length;
+ if (!l || (!inst[i].SampleData)) continue;
+
+ if (DisplayProgress (i, lastinstr))
+ return ERROR_BREAK;
+
+ if (halve_instr)
+ {
+ if (samp = AllocVec (l, MEMF_ANY))
+ {
+ /* Halve Volume */
+ for (k = 0; k < l; k++)
+ samp[k] = inst[i].SampleData[k] >> 1;
+
+ free_samp = TRUE; /* Free this buffer when you are done */
+ }
+ else
+ {
+ ShowMessage (MSG_NO_MEM_TO_HALVE, i);
+ samp = inst[i].SampleData;
+ }
+ }
+ else samp = inst[i].SampleData;
+
+ instrhdr.length = l;
+ instrhdr.type = SAMPLE;
+
+ if (Write (fp, &instrhdr, sizeof (instrhdr)) != sizeof (instrhdr))
+ return ERR_READWRITE;
+
+ if (l)
+ if (Write (fp, samp, l) != l)
+ return ERR_READWRITE;
+
+ if (free_samp)
+ FreeVec (samp);
+ }
+ }
+
+ return RETURN_OK;
+}
--- /dev/null
+##
+## Make the stuff in the Hooks/ subdirectory
+##
+
+include /config.mk
+
+ALL_HOOKS := oktalyzer.xmhook screamtracker.xmhook
+
+HOOK_CFLAGS := CODE=NEAR DATA=FARONLY STRSECT=CODE
+
+OKTA_VERSION := 1
+OKTA_REVISION := 0
+
+S3M_VERSION := 1
+S3M_REVISION := 0
+
+
+all: $(ALL_HOOKS)
+
+clean:
+ -Delete *.map *.o *.gst *.xref *.xmhook
+
+.PHONY: all clean
+
+
+OktalyzerHookInit.o: $(TOP)/RomTag.asm
+ $(AS) $(ASFLAGS) $(TOP)/RomTag.asm TO $@ \
+ SET "LIBVERSION=$(OKTA_VERSION),LIBREVISION=$(OKTA_REVISION),XMHOOK"
+
+OktalyzerHook.o: OktalyzerHook.c
+ $(CC) OktalyzerHook.c $(CFLAGS) $(HOOK_CFLAGS)
+
+oktalyzer.xmhook: OktalyzerHook.o OktalyzerHookInit.o
+ $(LD) $(LDFLAGS) FROM OktalyzerHookInit.o OktalyzerHook.o $(LIBS) TO $@
+ @Protect $@ -e
+
+
+ScreamTrackerHookInit.o: $(TOP)/RomTag.asm
+ $(AS) $(ASFLAGS) $(TOP)/RomTag.asm TO $@ \
+ SET "LIBVERSION=$(S3M_VERSION),LIBREVISION=$(S3M_REVISION),XMHOOK"
+
+ScreamTrackerHook.o: ScreamTrackerHook.c
+ $(CC) ScreamTrackerHook.c $(CFLAGS) $(HOOK_CFLAGS)
+
+screamtracker.xmhook: ScreamTrackerHook.o ScreamTrackerHookInit.o
+ $(LD) $(LDFLAGS) FROM ScreamTrackerHookInit.o ScreamTrackerHook.o $(LIBS) TO $@
+ @Protect $@ -e
--- /dev/null
+/*
+** OctaMed.h
+**
+** Copyright (C) 1994,95 Bernardo Innocenti
+**
+** Use 4 chars wide TABs to read this source
+**
+** Structure definition for MED/OctaMED file formats.
+** Based on Teijo Kinnunen's format description rev 2 (30.5.93)
+*/
+
+
+struct MMD0sample
+{
+ UWORD rep, replen; /* offs: 0(s), 2(s) */
+ UBYTE midich; /* offs: 4(s) */
+ UBYTE midipreset; /* offs: 5(s) */
+ UBYTE svol; /* offs: 6(s) */
+ BYTE strans; /* offs: 7(s) */
+};
+
+
+struct MMD0
+{
+ ULONG id;
+ ULONG modlen;
+ struct MMD0song *song;
+ ULONG reserved0;
+ struct MMD0Block **blockarr;
+ ULONG reserved1;
+ struct InstrHdr **smplarr;
+ ULONG reserved2;
+ struct MMD0exp *expdata;
+ ULONG reserved3;
+ UWORD pstate; /* some data for the player routine */
+ UWORD pblock;
+ UWORD pline;
+ UWORD pseqnum;
+ WORD actplayline;
+ UBYTE counter;
+ UBYTE extra_songs; /* number of songs - 1 */
+}; /* length = 52 bytes */
+
+
+
+struct MMD0song
+{
+ struct MMD0sample sample[63];
+ UWORD numblocks;
+ UWORD songlen; /* NOTE: number of sections in MMD2 */
+ union {
+ UBYTE playseq[256]; /* MMD0/MMD1 only */
+ struct { /* MMD2 only */
+ struct PlaySeq **playseqtable;
+ UWORD *sectiontable; /* UWORD section numbers */
+ UBYTE *trackvols; /* UBYTE track volumes */
+ UWORD numtracks; /* max. number of tracks in the song (also
+ * the number of entries in 'trackvols' table)
+ */
+ UWORD numpseqs; /* number of PlaySeqs in 'playseqtable' */
+ UBYTE pad0[240]; /* reserved for future expansion */
+ };
+ };
+ UWORD deftempo;
+ BYTE playtransp;
+ UBYTE flags;
+ UBYTE flags2;
+ UBYTE tempo2;
+ UBYTE trkvol[16]; /* Unused in MMD2 */
+ UBYTE mastervol;
+ UBYTE numsamples;
+}; /* length = 788 bytes */
+
+
+struct PlaySeq {
+ char name[32]; /* 31 chars + \0 */
+ ULONG reserved[2]; /* for possible extensions */
+ UWORD length; /* # of entries */
+ UWORD seq[0]; /* block numbers.. */
+};
+
+
+/* Flags for MMD0song->Flags */
+
+#define FLAG_FILTERON 0x1 /* hardware low-pass filter */
+#define FLAG_JUMPINGON 0x2 /* jumping.. */
+#define FLAG_JUMP8TH 0x4 /* jump 8th.. */
+#define FLAG_INSTRSATT 0x8 /* instruments are attached (sng+samples)
+ used only in saved MED-songs */
+#define FLAG_VOLHEX 0x10 /* volumes are represented as hex */
+#define FLAG_STSLIDE 0x20 /* no effects on 1st timing pulse (STS) */
+#define FLAG_8CHANNEL 0x40 /* OctaMED 8 channel song, examine this bit
+ to find out which routine to use */
+
+/* Flags for MMD0song->Flags2 */
+
+#define FLAG2_BMASK 0x1F /* (bits 0-4) BPM beat length (in lines)
+ 0 = 1 line, $1F = 32 lines.0
+ (The rightmost slider in OctaMED Pro
+ BPM mode.) */
+#define FLAG2_BPM 0x20 /* BPM mode on */
+
+
+
+struct MMD0block
+{
+ UBYTE numtracks,
+ lines;
+};
+
+
+
+struct MMD1block
+{
+ UWORD numtracks;
+ UWORD lines;
+ struct BlockInfo *info;
+};
+
+
+
+struct BlockInfo
+{
+ ULONG *hlmask;
+ UBYTE *blockname;
+ ULONG blocknamelen;
+ ULONG reserved[6];
+};
+
+
+
+struct InstrHdr {
+ ULONG length;
+ WORD type;
+ /* Followed by actual data */
+};
+
+
+/* Values for InstrHdr.type */
+
+#define HYBRID -2
+#define SYNTHETIC -1
+#define SAMPLE 0 /* ordinary 1 octave sample */
+#define IFF5OCT 1 /* 5 octaves */
+#define IFF3OCT 2 /* 3 octaves */
+
+/* The following ones are recognized by OctaMED Pro only */
+#define IFF2OCT 3 /* 2 octaves */
+#define IFF4OCT 4 /* 4 octaves */
+#define IFF6OCT 5 /* 6 octaves */
+#define IFF7OCT 6 /* 7 octaves */
+
+
+
+struct SynthInstr {
+ ULONG length; /* length of this struct */
+ WORD type; /* -1 or -2 */
+ UBYTE defaultdecay;
+ UBYTE reserved[3];
+ UWORD rep;
+ UWORD replen;
+ UWORD voltbllen;
+ UWORD wftbllen;
+ UBYTE volspeed;
+ UBYTE wfspeed;
+ UWORD wforms;
+ UBYTE voltbl[128];
+ UBYTE wftbl[128];
+ struct SynthWF *wf[64];
+};
+
+
+
+struct InstrExt {
+ UBYTE hold; /* hold/decay values of the instrument */
+ UBYTE decay;
+ UBYTE suppress_midi_off; /* 0 (FALSE) or not (TRUE) */
+ BYTE finetune; /* instrument finetune (-8-+7) */
+ /* length = 4 bytes */
+
+ /* Below fields saved by >= V5 */
+ UBYTE default_pitch;
+ UBYTE instr_flags;
+ UWORD long_midi_preset;
+ /* length = 8 bytes */
+
+ /* Below fields saved by >= V5.02 */
+ UBYTE output_device;
+ UBYTE reserved;
+ /* length = 10 bytes */
+};
+
+
+
+struct MMDInstrInfo {
+ UBYTE name[40]; /* null-terminated instrument name. */
+}; /* length = 40 bytes */
+
+
+
+struct MMD0exp {
+ struct MMD0 *nextmod; /* Pointer to the next module (or zero). */
+ struct InstrExt *exp_smp;
+ UWORD s_ext_entries; /* The size of InstrExt structure array
+ * (i.e. the number of InstrExt structures).
+ */
+ UWORD s_ext_entrsz; /* Size of each InstrExt structure. */
+ UBYTE *annotxt; /* Pointer to the null-terminated annotation text. */
+ ULONG annolen; /* Length of 'annotxt', including the terminating \0. */
+ struct MMDInstrInfo *iinfo;
+ UWORD i_ext_entries; /* Size of the MMDInstrInfo struct array
+ * (i.e. the number of MMDInstrInfo structures).
+ */
+ UWORD i_ext_entrsz; /* Size of each MMDInstrInfo struct */
+ ULONG jumpmask; /* OBSOLETE */
+ UWORD *rgbtable; /* Pointer to eight UWORDs (screen colors)
+ * to be passed to LoadRGB4() routine.
+ */
+ UBYTE channelsplit[4]; /* this longword is divided to four boolean bytes,
+ * controlling channel splitting in OctaMED 5 - 8 chnl
+ * modes. (A non-zero byte means that the channel is
+ * NOT splitted.) Currently only the following
+ * combinations should be used:
+ *
+ * 0x00000000 (8 channels (or normal 4 channel mode))
+ * 0x000000FF (7 channels)
+ * 0x0000FFFF (6 channels)
+ * 0x00FFFFFF (5 channels)
+ */
+ struct NotationInfo *n_info;/* pointer to NotationInfo structure (used only in
+ * OctaMED V2.0 and later). It contains some info for
+ * the notation editor.
+ */
+ UBYTE *songname; /* song name of the current song (0-terminated).
+ * Each song of a multi-module can have a different
+ * name.
+ */
+
+ ULONG songnamelen; /* song name length (including the \0). */
+ struct MMDDumpData *dumps;
+ ULONG reserved2[7]; /* future expansion fields, that MUST be zero now. */
+};
+
+
+
+struct NotationInfo
+{
+ UBYTE n_of_sharps; /* number of sharps or flats (0 - 6). */
+ UBYTE flags; /* See below. */
+ WORD trksel[5]; /* The number of the selected track,
+ * for each display preset
+ * (-1 = no track selected)
+ */
+ UBYTE trkshow[16]; /* tracks shown (five bits used in
+ * each byte, bit #0 = preset 1, etc.)
+ */
+ UBYTE trkghost[16]; /* tracks ghosted (as in 'trkshow') */
+ BYTE notetr[63]; /* note transpose value for each
+ * instrument (-24 - +24). If bit 6 is
+ * negated, the instrument is hidden.
+ */
+ UBYTE pad;
+};
+
+/* flags for NotationInfo->flags */
+#define NFLG_FLAT 1
+#define NFLG_3_4 2
+
+
+/* MIDI dump data (created using OctaMED Pro MIDI
+ * message editor).
+ */
+
+struct MMDDumpData {
+ UWORD numdumps;
+ UWORD reserved[3];
+ /* Immediately after this struct, there are 'numdumps'
+ * pointers to MMDDump structures.
+ */
+};
+
+struct MMDDump {
+ ULONG length; /* length of the MIDI message dump. */
+ UBYTE *data; /* pointer to the actual MIDI dump data. */
+ UWORD ext_len; /* MMDDump struct extension length. */
+
+ /* (if ext_len >= 20, the following fields exist) */
+ UBYTE name[20]; /* name of the dump. */
+};
+
+
+
+#define MMD0ROW 3 /* Size of an MMD0 pattern row */
+#define MMD1ROW 4 /* Size of an MMD1/MMD2 pattern row */
+
+#define ID_MMD0 0x4D4D4430
+#define ID_MMD1 0x4D4D4431
+#define ID_MMD2 0x4D4D4432
--- /dev/null
+/*
+** OktalyzerHook.c
+**
+** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
+**
+** Originally based on Gerardo Iula's Tracker sources.
+**
+** External hook for Oktalyzer 1.1-1.57 module format.
+*/
+
+
+#include <exec/memory.h>
+#include <dos/stdio.h>
+#include <libraries/xmodule.h>
+#include <libraries/songclass.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Local functions prototypes */
+
+HOOKCALL APTR _GetEngine (
+ REG(a6, struct Library *MyBase));
+HOOKCALL APTR _SetupXMHook (
+ REG(a0, struct XModuleBase *_XModuleBase),
+ REG(a6, struct Library *mybase));
+static HOOKCALL struct XMHook *IdentifyOktalyzer (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags));
+static HOOKCALL LONG LoadOktalyzer (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags));
+static HOOKCALL LONG SaveOktalyzer (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *saver),
+ REG(a2, ULONG *tags));
+
+INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval, UWORD patt, UWORD line, UWORD track);
+
+
+
+#define OKT_MODE4 1 /* Mode 4: play 8 bit instruments */
+#define OKT_MODE8 0 /* Mode 8: play 7 bit instruments */
+#define OKT_MODEB 2 /* Mode B: play both 8 & 7 bit instruments */
+
+
+/* Oktalyzer chunk IDs */
+
+#define ID_OKTA 'OKTA'
+#define ID_CMOD 'CMOD'
+#define ID_SAMP 'SAMP'
+#define ID_SPEE 'SPEE'
+#define ID_SLEN 'SLEN'
+#define ID_PLEN 'PLEN'
+#define ID_PBOD 'PBOD'
+#define ID_SBOD 'SBOD'
+
+#ifndef ID_SONG
+#define ID_SONG 'SONG'
+#endif /* !ID_SONG */
+
+#ifndef ID_PATT
+#define ID_PATT 'PATT'
+#endif /* !ID_PATT */
+
+
+
+/* Library data */
+
+const UBYTE LibName[] = "oktalyzer.xmhook";
+const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
+const UBYTE LibId[] = "oktalyzer.xmhook 1.0 (28.1.96) © 1993-96 by Bernardo Innocenti";
+
+
+
+/* Effects conversion table
+ * Originally based on Gerardo Iula's "Tracker" source.
+ */
+static const UBYTE Effects[MAXTABLEEFFECTS] =
+{
+/* OKTA XModule Val */
+
+ 0x00, /* Null effect $00 */
+
+ 0x01, /* Portamento Up $01 */
+ 0x02, /* Portamento Down $02 */
+ 0x00, /* Tone Portamento $03 */
+ 0x00, /* Vibrato $04 */
+ 0x00, /* ToneP + VolSl $05 */
+ 0x00, /* Vibra + VolSl $06 */
+ 0x00, /* Tremolo $07 */
+ 0x00, /* Set Hold/Decay $08 */
+ 0x00, /* Sample Offset $09 */
+ 0x1E, /* Volume Slide $0A */
+ 0x19, /* Position Jump $0B */
+ 0x1F, /* Set Volume $0C */
+ 0x00, /* Pattern break $0D */
+ 0x00, /* Misc $0E */
+ 0x1C, /* Set Speed $0F */
+ 0x00, /* Set Tempo $10 */
+ 0x1A, /* Arpeggio $11 */
+
+ 0x11, /* Oktalyzer H */
+ 0x15 /* Oktalyzer L */
+};
+
+
+
+/* Get around a SAS/C bug which causes some annoying warnings
+ * with the library bases defined below. The problem happens
+ * when the GST has been compiled with the CODE=FAR switch and
+ * the source is being compiled with CODE=NEAR.
+ */
+#ifdef __SASC
+ #pragma msg 72 ignore push
+#endif /* __SASC */
+
+struct ExecBase *SysBase = NULL;
+struct XModuleBase *XModuleBase = NULL;
+struct DosLibrary *DOSBase = NULL;
+struct IntuitionBase *IntuitionBase = NULL;
+
+#ifdef __SASC
+#pragma msg 72 pop
+#endif /* __SASC */
+
+
+
+static HOOKCALL struct XMHook *IdentifyOktalyzer (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags))
+
+/* Determine if the given file is an Oktalyzer module.
+ * Note: the file position will be changed on exit.
+ */
+{
+ ULONG id[3];
+
+ Seek (fh, 0, OFFSET_BEGINNING);
+ if (FRead (fh, &id, 12, 1) != 1)
+ return NULL;
+
+ if ((id[0] == ID_OKTA) && (id[1] == ID_SONG) && (id[2] == ID_CMOD))
+ return loader;
+
+ return NULL;
+}
+
+
+
+INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval, UWORD patt, UWORD line, UWORD track)
+
+/* Inputs: old effect & old type.
+ * Output: new effect in requested newtype.
+ */
+{
+ UBYTE i;
+
+ for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
+ if (eff == Effects[i])
+ return i;
+
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_UNKNOWN_EFF, 5, eff, patt, track, line);
+ return 0;
+}
+
+
+
+static HOOKCALL LONG LoadOktalyzer (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags))
+{
+ struct Instrument *instr;
+ struct Pattern *patt;
+ ULONG i, j, k; /* Loop counters */
+ ULONG size; /* Read buffer */
+ ULONG l; /* Read buffer */
+ UWORD voices, numpatts, songlen, instr_mode[37];
+ UWORD w;
+ UBYTE oktanote[4];
+
+
+ /* Check file header OKTASONGCMOD */
+ {
+ LONG id[3];
+
+ if (FRead (fh, id, 12, 1) != 1) return ERROR_IOERR;
+ if ((id[0] != ID_OKTA) || (id[1] != ID_SONG) || (id[2] != ID_CMOD))
+ return ERROR_NOTMODULE;
+ }
+
+
+ /* TODO: set si->SongName */
+
+
+ /* CMOD Chunk size ($0000 0008) */
+ Seek (fh, 4, OFFSET_CURRENT); /* Skip 4 bytes */
+
+ voices = 4; /* Set minimum voices and check others */
+ for (i = 0 ; i < 4 ; i++)
+ {
+ if (FRead (fh, &w, 2, 1) != 1) return ERROR_IOERR;
+ if (w) voices++;
+ }
+
+ /* Get Sample Name, Length, Repeat Replen, Volume for each instr. */
+
+ /* Check header SAMP */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+ if (l != ID_SAMP) return ERROR_NOTMODULE;
+
+ /* SAMP Chunk Size ($0000 0480) */
+ Seek (fh, 4, OFFSET_CURRENT); /* Skip 4 bytes */
+
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_READING_INSTS_INFO, NULL);
+
+ /* Read in 36 instruments */
+ for ( j = 1 ; j <= 36 ; j++ )
+ {
+ struct
+ {
+ UBYTE name[20];
+ ULONG size;
+ UWORD repeat,
+ replen,
+ volume,
+ mode;
+ } oktainstr;
+
+ /* Get instrument data */
+ if (FRead (fh, &oktainstr, sizeof (oktainstr), 1) != 1)
+ return ERROR_IOERR;
+
+ oktainstr.name[19] = '\0';
+
+ /* Oktalyzer sometimes saves odd lengths for instruments,
+ * but the data saved in SBOD is always rounded _down_ to an even
+ * number! It took me two weeks to find and kill this bug :-(
+ */
+ oktainstr.size &= (~1);
+
+ /* Oktalyzer instrument modes:
+ * mode 8 $00 7 bit instruments
+ * mode 4 $01 normal instruments
+ * mode B $02 both 8bit & 7bit
+ */
+ instr_mode[j] = oktainstr.mode;
+
+ if (oktainstr.size)
+ if (!(xmAddInstrument (si, j,
+ INSTRA_Name, oktainstr.name,
+ INSTRA_Length, oktainstr.size,
+ INSTRA_Repeat, oktainstr.repeat << 1,
+ INSTRA_Replen, oktainstr.replen << 1,
+ INSTRA_Volume, oktainstr.volume,
+ TAG_DONE)))
+ return ERROR_NO_FREE_STORE;
+ }
+
+
+ /* Get global song speed */
+
+ /* Check speed header "SPEE" */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+ if (l != ID_SPEE) return ERROR_NOTMODULE;
+
+ /* SPEE Chunk size ($0000 0002) */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ /* Get Song Global Speed */
+ if (FRead (fh, &w, 2, 1) != 1) return ERROR_IOERR;
+
+ SetAttrs (si,
+ SNGA_GlobalSpeed, w,
+ TAG_DONE);
+
+
+ /* Get number of patterns */
+
+ /* Check header SLEN */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+ if (l != ID_SLEN) return ERROR_NOTMODULE;
+
+ /* SLEN Chunk size ($0000 0002) */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ /* Get number of patterns */
+ if (FRead (fh, &numpatts, 2, 1) != 1) return ERROR_IOERR;
+
+ /* Check value */
+ if (numpatts > MAXPATTERNS)
+ {
+ xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_HAS_TOO_MANY_PATT, NULL);
+ numpatts = MAXPATTERNS - 1;
+ }
+
+
+ /* Get number of positions in song (Length) */
+
+ /* Check header PLEN */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+ if (l != ID_PLEN) return ERROR_NOTMODULE;
+
+ /* Chunk size ($0000 0002) */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ /* Get number of patterns */
+ if (FRead (fh, &songlen, 2, 1) != 1) return ERROR_IOERR;
+
+ /* Check value */
+ if (songlen > MAXPOSITIONS)
+ {
+ xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_TOO_LONG, NULL);
+ songlen = MAXPOSITIONS;
+ }
+
+
+ /* Get position table */
+
+ /* Check header PATT */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+ if (l != ID_PATT) return ERROR_NOTMODULE;
+
+ /* PATT Chunk size ($0000 0080) */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+
+ /* Read song sequence */
+ {
+ UBYTE postable[128];
+
+ if (FRead (fh, postable, 1, 128) != 128) return ERROR_IOERR;
+
+ if (!(xmSetSongLen (si, songlen)))
+ return ERROR_NO_FREE_STORE;
+
+ for (i = 0; i < si->Length; i++)
+ si->Sequence[i] = postable[i];
+ }
+
+
+ /* Get pattern bodies and convert them */
+
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_READING_PATTS, NULL);
+
+ for (j = 0; j < numpatts; j++)
+ {
+ if (xmDisplayProgress (j + 1, numpatts))
+ return ERROR_BREAK;
+
+ /* Check header "PBOD" */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+ if (l != ID_PBOD) return ERROR_NOTMODULE;
+
+ /* Skip Chunk Length (Lines * Tracks * 4 + 2) */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ /* Get pattern length */
+ if (FRead (fh, &w, 2, 1) != 1) return ERROR_IOERR;
+
+ /* Allocate memory for all tracks */
+ if (!(patt = xmAddPattern (si,
+ PATTA_Tracks, voices,
+ PATTA_Lines, w,
+ TAG_DONE)))
+ return ERROR_NO_FREE_STORE;
+
+ for ( k = 0 ; k < patt->Lines ; k++)
+ {
+ for (i = 0; i < voices; i++)
+ {
+ struct Note *n = &patt->Notes[i][k];
+
+ /* Read a whole track row */
+ if (FRead (fh, oktanote, 4, 1) != 1) return ERROR_IOERR;
+
+ /* Convert Note:
+ *
+ * Oktalyzer supports 3 octaves (1 to 3).
+ * Notes are numbered from 1 (C-1) to 36 (B-3).
+ * 0 means no note.
+ */
+ if (oktanote[0] <= 36)
+ n->Note = (oktanote[0] ? (oktanote[0] + 12) : 0); /* Add one octave */
+ else
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_INVALID_NOTE, oktanote[0], j, i, k);
+
+ /* Store Instrument Number */
+ n->Inst = (n->Note ? (oktanote[1] + 1) : 0);
+
+ /* Convert Effect */
+ n->EffNum = DecodeEff (oktanote[2], oktanote[3], j, k, i);
+
+ /* Store Effect Value */
+ n->EffVal = oktanote[3];
+
+ /* Effect Exceptions */
+ if (n->EffNum == EFF_MISC)
+ {
+ /* Oktalyzer has SetFilter values inverted! */
+ if ((n->EffVal >> 4) == 1)
+ n->EffVal = 0x10 | (n->EffVal & 0x0F ? 0 : 1);
+ }
+ } /* end for (i) */
+ } /* End for (k) */
+ } /* End for (j) */
+
+
+ /* Load instruments */
+
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_READING_INSTS, NULL);
+
+ for (j = 1 ; j <= 36 ; j++)
+ {
+ BYTE *sample;
+
+ if (!(instr = si->Instr[j])) continue;
+
+ if (xmDisplayProgress(j, 36))
+ return ERROR_BREAK;
+
+ /* Check header SBOD */
+ if (FRead (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+ if (l != ID_SBOD) return ERROR_NOTMODULE;
+
+ /* Read chunk size (length of instrument) & check it */
+ if (FRead (fh, &size, 4, 1) != 1) return ERROR_IOERR;
+ if (size != instr->Length) return ERROR_NOTMODULE;
+
+ if (!(sample = AllocVec (instr->Length, MEMF_SAMPLE)))
+ return ERROR_NO_FREE_STORE;
+
+ /* Read instrument body */
+ if (FRead (fh, sample, 1, instr->Length) != instr->Length)
+ {
+ FreeVec (sample);
+ return ERROR_IOERR;
+ }
+
+ /* Double sample data */
+ if (instr_mode[j] == OKT_MODE8 || instr_mode[j] == OKT_MODEB)
+ {
+ for (i = 0; i < instr->Length; i++)
+ *sample++ <<= 1;
+ }
+
+ xmSetInstrument (si, j,
+ INSTRA_Sample, sample,
+ TAG_DONE);
+ }
+
+ /* Check for extra data following the module */
+ if (FGetC (fh) != ENDSTREAMCH)
+ xmDisplayMessageA (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_EXTRA_DATA_AFTER_MOD, NULL);
+
+ return 0;
+}
+
+
+
+static HOOKCALL LONG SaveOktalyzer (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *saver),
+ REG(a2, ULONG *tags))
+{
+ struct Instrument *instr;
+ ULONG i, j, k;
+ UWORD voices, instr_mode, songlen;
+ ULONG l; /* Write buffers */
+ UWORD w;
+ UBYTE oktanote[4];
+
+ /* Check number of tracks and fix data */
+ voices = si->MaxTracks;
+ if (voices < 4) voices = 4;
+ if (voices > 8) voices = 8;
+
+
+ /* Oktalyzer does not support pattern break (D) command */
+
+ xmProcessSong (si, NULL,
+ XMSNG_Optimize, XMOF_CUT_PATTERNS,
+ TAG_DONE);
+
+
+ /* Write file header */
+ if (FWrite (fh, "OKTASONGCMOD", 12, 1) != 1) return ERROR_IOERR;
+
+
+ /* Write maximum number of tracks */
+ l = 8;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ /* Write active tracks (only 5 to 8: tracks 1..4 are always on)
+ * TODO: Ask user what tracks should be made active, or loadnig and
+ * saving back the same module will result in the lost of the original
+ * track order.
+ */
+ for (i = 5 ; i <= 8 ; i++)
+ {
+ if (voices >= i) w = 1;
+ else w = 0;
+ if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
+ }
+
+ /* Choose mode for instruments.
+ * When the module is 4 channels, we can always use mode 4.
+ * On modules with 5-7 channels, it is hard to guess which instruments
+ * could be made mode 4, 8 or B, so we always choose mode B.
+ * for 8 channels modules, we just use mode 8 for all instruments.
+ */
+ switch (voices)
+ {
+ case 4:
+ instr_mode = OKT_MODE4;
+ break;
+
+ case 8:
+ instr_mode = OKT_MODE8;
+ break;
+
+ default:
+ instr_mode = OKT_MODEB;
+ }
+
+
+
+ /* Write sample names, length, effects, volume */
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_WRITING_INSTINFO, NULL);
+
+ if (FWrite (fh, "SAMP", 4, 1) != 1) return ERROR_IOERR;
+
+ /* Write chunk length */
+ l = 0x480;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ for ( j = 1 ; j <= 36 ; j++ )
+ {
+ struct
+ {
+ UBYTE name[20];
+ ULONG size;
+ UWORD repeat,
+ replen,
+ volume,
+ mode;
+ } oktainstr = { 0 };
+
+
+ if (instr = si->Instr[j])
+ {
+ strncpy (oktainstr.name, instr->Name, 19);
+ oktainstr.size = instr->Length;
+ oktainstr.repeat = instr->Repeat >> 1;
+ oktainstr.replen = instr->Replen >> 1;
+ oktainstr.volume = instr->Volume;
+ oktainstr.mode = instr->Length ? instr_mode : 0;
+ }
+
+ /* Write instrument data */
+ if (FWrite (fh, &oktainstr, sizeof (oktainstr), 1) != 1)
+ return ERROR_IOERR;
+ }
+
+
+ /* Write global song speed */
+
+ if (FWrite (fh, "SPEE", 4, 1) != 1) return ERROR_IOERR;
+ l = 2;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ w = si->GlobalSpeed;
+ if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
+
+
+ if (FWrite (fh, "SLEN", 4, 1) != 1) return ERROR_IOERR;
+ l = 2;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ w = si->NumPatterns;
+ if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
+
+
+ /* Write patterns number */
+
+ if (FWrite (fh, "PLEN", 4, 1) != 1) return ERROR_IOERR;
+ l = 2;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ songlen = min (si->Length, 128);
+ if (FWrite (fh, &songlen, 2, 1) != 1) return ERROR_IOERR;
+
+
+ /* Write patterns sequence */
+
+ if (FWrite (fh, "PATT", 4, 1) != 1) return ERROR_IOERR;
+ l = 128;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ {
+ UBYTE postable[128];
+
+ memset (postable, 0, 128);
+
+ for (i = 0; i < songlen; i++)
+ postable[i] = si->Sequence[i];
+
+ if (FWrite (fh, postable, 1, 128) != 128) return ERROR_IOERR;
+ }
+
+
+ /* Write patterns */
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_WRITING_PATTS, NULL);
+
+ for ( j = 0 ; j < si->NumPatterns ; j++)
+ {
+ if (xmDisplayProgress (j + 1, si->NumPatterns))
+ return ERROR_BREAK;
+
+ if (FWrite (fh, "PBOD", 4, 1) != 1) return ERROR_IOERR;
+ l = si->Patt[j]->Lines * si->MaxTracks * 4 + 2;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ /* Write pattern length (WORD) */
+ w = si->Patt[j]->Lines;
+ if (FWrite (fh, &w, 2, 1) != 1) return ERROR_IOERR;
+
+ for (k = 0 ; k < si->Patt[j]->Lines ; k++)
+ {
+ for ( i = 0 ; i < voices ; i++)
+ {
+ struct Note *n = &(si->Patt[j]->Notes[i][k]);
+
+ if (n->Note)
+ {
+ if (n->Note < 13)
+ {
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_NOTE_TOO_LOW, j, i, k);
+ oktanote[0] = n->Note;
+ }
+ else
+ {
+ if (n->Note > 48)
+ {
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_NOTE_TOO_HIGH, j, i, k);
+ oktanote[0] = n->Note - 24;
+ }
+ else
+ oktanote[0] = n->Note - 12;
+ }
+ }
+ else oktanote[0] = 0;
+
+ oktanote[1] = (n->Inst ? (n->Inst-1) : 0);
+ oktanote[2] = Effects[n->EffNum];
+
+ /* Effect Exceptions */
+ switch (n->EffNum)
+ {
+ case EFF_MISC:
+ if ((n->EffVal >> 4) == 1) /* Filter On/Off */
+ oktanote[3] = (n->EffVal ? 0 : 1);
+ break;
+
+ case EFF_SETSPEED:
+ if (n->EffVal > 0x0F)
+ oktanote[3] = 0x0F;
+ else
+ oktanote[3] = n->EffVal;
+ break;
+
+ default:
+ oktanote[3] = n->EffVal;
+ break;
+ }
+
+ if (FWrite (fh, oktanote, 4, 1) != 1) return ERROR_IOERR;
+
+ } /* end for(i) */
+ } /* End for(k) */
+ } /* End for(j) */
+
+
+ /* Write Instruments Data */
+
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_WRITING_INSTDATA, NULL);
+
+ for (j = 1 ; j <= 36 ; j++)
+ {
+ BYTE *samp;
+ BOOL free_samp = FALSE;
+
+ /* Skip empty instruments slots */
+
+ if (!(instr = si->Instr[j]))
+ continue;
+ if (!(l = instr->Length))
+ continue;
+
+ if (xmDisplayProgress(j, 36))
+ return ERROR_BREAK;
+
+
+ if (instr_mode == OKT_MODE8 || instr_mode == OKT_MODEB)
+ {
+ if (samp = AllocVec (l, MEMF_ANY))
+ {
+ /* Halve volume */
+ for (i = 0; i < l; i++)
+ samp[i] = instr->Sample[i] >> 1;
+
+ free_samp = TRUE; /* Free this when done */
+ }
+ else
+ {
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_NO_MEM_TO_HALVE, j);
+ samp = instr->Sample;
+ }
+ }
+ else samp = instr->Sample;
+
+
+ if (!samp) l = 0;
+
+ if (FWrite (fh, "SBOD", 4, 1) != 1) return ERROR_IOERR;
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+ if (l)
+ {
+ Flush (fh);
+ if (Write (fh, samp, l) != l)
+ return ERROR_IOERR;
+ }
+
+ if (free_samp) FreeVec (samp);
+ }
+
+ return 0;
+}
+
+
+
+HOOKCALL struct Library * _UserLibInit (REG(a6, struct Library *mybase))
+{
+ return mybase;
+}
+
+
+
+HOOKCALL struct Library * _UserLibCleanup (REG(a6, struct Library *mybase))
+{
+ return mybase;
+}
+
+
+
+HOOKCALL APTR _GetEngine (REG(a6, struct Library *mybase))
+{
+ return NULL;
+}
+
+
+
+HOOKCALL APTR _SetupXMHook (
+ REG(a0, struct XModuleBase *_XModuleBase),
+ REG(a6, struct Library *mybase))
+{
+ if (!(strcmp (_XModuleBase->xm_Library.lib_Node.ln_Name, "xmodule.library")))
+ {
+ SysBase = *((struct ExecBase **)4);
+ XModuleBase = _XModuleBase;
+ IntuitionBase = XModuleBase->xm_IntuitionBase;
+ DOSBase = XModuleBase->xm_DOSBase;
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMLOADER,
+ XMHOOK_Name, (LONG)"Oktalyzer",
+ XMHOOK_Priority, 0,
+ XMHOOK_Descr, (LONG)"Oktalyzer 1.57",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_OKTA,
+ XMHOOK_LibraryBase, mybase,
+ XMHOOK_LoadModFunc, LoadOktalyzer,
+ XMHOOK_IdentifyModFunc, IdentifyOktalyzer,
+ XMHOOK_MaxTracks, 8,
+ XMHOOK_MaxPatterns, 64,
+ XMHOOK_MaxInstruments, 36,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 131070,
+ XMHOOK_MaxPattLen, 128,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"Oktalyzer",
+ XMHOOK_Priority, 0,
+ XMHOOK_Descr, (LONG)"Oktalyzer 1.57",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_OKTA,
+ XMHOOK_LibraryBase, mybase,
+ XMHOOK_SaveModFunc, SaveOktalyzer,
+ XMHOOK_MaxTracks, 8,
+ XMHOOK_MaxPatterns, 64,
+ XMHOOK_MaxInstruments, 36,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 131070,
+ XMHOOK_MaxPattLen, 128,
+ TAG_DONE);
+ }
+
+ return NULL;
+}
--- /dev/null
+CODE=FAR
+PARAMETERS=REGISTERS
+NOSTACKCHECK
+STRINGMERGE
+NOCHECKABORT
+COMMENTNEST
+ERRORREXX
+NOMULTIPLEINCLUDES
+OPTIMIZERINLINELOCAL
+SMALLCODE
+SMALLDATA
+NOVERSION
+ABSFUNCPOINTER
+UTILITYLIBRARY
+NOICONS
+MEMORYSIZE=HUGE
+NOERRORHIGHLIGHT
+ONERROR=CONTINUE
+OPTIMIZERTIME
+MULTIPLECHARACTERCONSTANTS
+DEFINE NO_REQTOOLS_OBSOLETE
+DEFINE ASL_V38_NAMES_ONLY
+DEFINE INTUI_V36_NAMES_ONLY
+DEFINE IFFPARSE_V37_NAMES_ONLY
+DEFINE USE_BUILTIN_MATH
+DEFINE __USE_SYSBASE
+IGNORE=193
+IGNORE=306
+PUBSCREEN=Workbench
--- /dev/null
+/*
+** SaveMIDI.c
+**
+** Copyright (C) 1994 Bernardo Innocenti
+**
+** Original MOD2MIDI PC code (C) 1993 Andrew Scott
+** Amiga Mod2Midi port (C) 1994 Raul Sobon
+**
+** Save internal data to a MIDI type 1 file.
+*/
+
+#include <exec/types.h>
+
+#include <clib/dos_protos.h>
+
+#include <pragmas/dos_pragmas.h>
+
+#include "XModule.h"
+#include "Gui.h"
+
+
+#define ID_MThd 0x4D546864 /* "MThd", Midi Track HeaDer */
+#define ID_MTrk 0x4D54726B /* "MTrk", Midi TRacK */
+
+
+
+#define DRUMCHANNEL 9
+
+/* Returns a volume in the range 0..127 */
+#define RESTRICTVOL(v) (((v) < 0) ? 0 : (((v) > 127) ? 127 : (v)))
+
+/* Convert XModule note -> MIDI note */
+#define NOTEVALUE(n) ((n)+36)
+
+#define EVOL(ie,x) (((x) + (ie)->VolShift[0]) * (ie)->VolShift[1] / (ie)->VolShift[2])
+
+#define ANOTE(x) (((x) < 0) ? (-x) : NOTEVALUE(x))
+
+#define ENOTE(ie,x,y) (((ie)->MidiCh > 127) ? ((ie)->MidiCh - 128) : (ANOTE(x) + (ie)->Transpose[y]))
+
+
+
+struct MThd
+{
+ UWORD unknown1; /* Set to 1 */
+ UWORD Tracks;
+ UWORD unknown2; /* Set to 192 */
+};
+
+struct TRKInfo
+{
+ UBYTE unknown1[63];
+ UBYTE NameLen;
+ /* SongName follows */
+};
+
+struct InstrExtra
+{
+ UBYTE MidiCh;
+ UBYTE MidiPr;
+ UBYTE VolShift[3];
+ UBYTE Transpose[3];
+};
+
+
+/* Local functions prototypes */
+
+static UWORD ChooseChannels (struct Instrument *instr, struct InstrExtra *ie, UBYTE DrumChann);
+static ULONG WriteTrack0Info (BPTR fp, struct SongInfo *si);
+static UWORD WriteMIDIHeader (BPTR fp, UWORD numofchannels);
+static LONG WriteVLQ (BPTR fp, ULONG i);
+static ULONG StopNote (BPTR fp, struct InstrExtra *ie, UWORD channel, UWORD note, UWORD vol, ULONG timer);
+static ULONG NoteLength (UBYTE note, ULONG lenght, UWORD beats);
+
+
+
+UWORD SaveMIDI (struct SongInfo *si, BPTR fp)
+{
+ UWORD numofchannels, err,
+ i, j, k;
+ struct Instrument *instr = &si->Inst[0], *inst1;
+ ULONG l;
+ BOOL tempodone = FALSE;
+
+ struct InstrExtra iext[MAXINSTRUMENTS], *ie1;
+
+ DisplayAction (MSG_CHOOSING_CHANNELS);
+
+ /* Setup InstrExtra structures */
+ for (i = 0; i < MAXINSTRUMENTS; i++)
+ {
+ iext[i].VolShift[0] = 0;
+ iext[i].VolShift[1] = 1;
+ iext[i].VolShift[2] = 1;
+ iext[i].Transpose[0] = 0;
+ iext[i].Transpose[1] = 0;
+ iext[i].Transpose[2] = 0;
+ }
+
+
+ /* Get required number of channels */
+ if ((numofchannels = ChooseChannels (instr, iext, DRUMCHANNEL)) > 16)
+ {
+ ShowMessage (MSG_TOO_MANY_CHANNELS);
+ return RETURN_FAIL;
+ }
+
+ /* Write Header */
+ if (err = WriteMIDIHeader (fp, numofchannels))
+ return err;
+
+ DisplayAction (MSG_WRITING_MIDI_TRACKS);
+
+ for (i = 0, inst1 = instr, ie1 = iext ; i < MAXINSTRUMENTS; inst1++, ie1++, i++)
+ {
+ ULONG count, mktrlenpos, timer, delay[MAXTRACKS];
+ UBYTE c;
+
+ if (!i || inst1->Length)
+ {
+ if (DisplayProgress (i, MAXINSTRUMENTS))
+ return ERROR_BREAK;
+
+ /* Write MIDI Track */
+ l = ID_MTrk;
+ if (!FWrite (fp, &l, 4, 1)) return ERR_READWRITE;
+
+ /* Write chunk length (set to 0 now...) */
+
+ mktrlenpos = Seek (fp, 0, OFFSET_CURRENT); /* Write it later */
+ l = 0;
+ if (!FWrite (fp, &l, 4, 1)) return ERR_READWRITE;
+
+ if (!i)
+ {
+ if (!(count = WriteTrack0Info (fp, si)))
+ return ERR_READWRITE;
+ }
+ else
+ {
+ static UBYTE unknown[4] = {0, 255, 3, 0};
+ static struct
+ {
+ UBYTE unknown_zero;
+ UBYTE channel;
+ UBYTE preset;
+ } instrinfo;
+
+ /* Write some unknown header */
+ unknown[3] = strlen (inst1->Name);
+ if (!FWrite (fp, unknown, 4, 1)) return ERR_READWRITE;
+
+ /* Write instrument name */
+ if (!FWrite (fp, inst1->Name, unknown[3], 1)) return ERR_READWRITE;
+
+ instrinfo.unknown_zero = 0;
+ instrinfo.channel = c = 0xC0 + ie1->MidiCh;
+ instrinfo.unknown_zero = (ie1->MidiPr > 127) ? 126 : ie1->MidiPr;
+ if (!FWrite (fp, &instrinfo, sizeof (instrinfo), 1))
+ return ERR_READWRITE;
+
+ count = sizeof (unknown) + sizeof (instrinfo) + unknown[3];
+ }
+ }
+
+ timer = 0;
+ if (!i || inst1->Length)
+ {
+ UWORD bpm, ticks, l, h;
+ UBYTE sampnum, effnum, effval, lastsam[MAXTRACKS] = {0}, vol[MAXTRACKS];
+ BYTE patbreak;
+ ULONG x, pause;
+ UWORD note, lastslide, slideto;
+ UWORD n[MAXTRACKS][48][2]; /* Note data for a song position */
+ UWORD lastn[MAXTRACKS]; /* Last note on a particular channel */
+
+ memset (lastn, 0, MAXTRACKS * sizeof (UWORD));
+ memset (vol, 0, MAXTRACKS);
+ memset (delay, 0, MAXTRACKS * sizeof (ULONG));
+
+ bpm = si->GlobalTempo;
+ ticks = si->GlobalSpeed;
+ lastslide = slideto = 0;
+ patbreak = 0;
+
+ for (h = 48; h--; )
+ for (k = MAXTRACKS; k--; )
+ n[k][h][0] = 0;
+
+ for (l = 0; l < si->Length; l++)
+ {
+ struct Pattern *patt = &si->PattData[si->Sequence[l]];
+
+ /* ??? */
+ if (patbreak > 0)
+ patbreak = 1 - patbreak;
+
+ for (j = 0; j < patt->Lines; j++)
+ {
+ pause = 0;
+ if (!patbreak)
+ {
+ for (k = 0; k < patt->Tracks; k++)
+ {
+ n[k][0][1] = inst1->Volume;
+
+ sampnum = patt->Notes[k]->Inst;
+ note = patt->Notes[k]->Note;
+ effnum = patt->Notes[k]->EffNum;
+ effval = patt->Notes[k]->EffVal;
+
+ if (!i) note = 0;
+
+ if ((note || sampnum) && delay[k])
+ {
+ count += StopNote (fp, ie1, c, lastn[k], vol[k], timer);
+ timer = 0;
+ delay[k] = 0;
+ }
+
+ if (!note && sampnum == i) /* check "defaults" */
+ note = lastn[k];
+ else
+ {
+ if (!sampnum)
+ {
+ if (lastsam[k] == i)
+ sampnum = i;
+ else
+ note = 0;
+ }
+ else
+ {
+ if (sampnum != i)
+ note = 0;
+ lastsam[k] = sampnum;
+ }
+ n[k][0][0] = note;
+ }
+
+ /* Do Effects */
+ switch (effnum)
+ {
+ case 0x0: /* Arpeggio */
+ {
+ UWORD nv;
+
+ if (!i || !effval || ie1->MidiCh > 127)
+ break;
+ if (!note)
+ {
+ if (!delay[k])
+ break;
+ else
+ {
+ nv = NOTEVALUE(lastn[k]);
+ n[k][47][0] = lastn[k];
+ n[k][47][1] = vol[k];
+ if (effval & 0xF0)
+ n[k][16][0] = -(nv + ((effval & 0xF0) >> 4));
+ n[k][16][1] = vol[k];
+ if (effval & 0x0F)
+ n[k][32][0] = -(nv + (effval & 0x0F));
+ n[k][32][1] = vol[k];
+ }
+ }
+ else
+ {
+ nv = NOTEVALUE(note);
+ n[k][47][0] = note;
+ n[k][47][1] = inst1->Volume;
+ if (effval & 0xF0)
+ n[k][16][0] = -(nv + ((effval & 0xF0) >> 4));
+ n[k][16][1] = inst1->Volume;
+ if (effval & 0x0F)
+ n[k][32][0] = -(nv + (effval & 0x0F));
+ n[k][32][1] = inst1->Volume;
+ }
+ break;
+ }
+
+ case 0x1: /* Slide Up */
+ case 0x2: /* Slide Down */
+ if (!(effval & 0xFF) || ie1->MidiCh > 127)
+ break;
+ if (effnum == 0x2)
+ lastslide = effval;
+ else
+ lastslide = -effval;
+ if (!note)
+ if (!delay[k])
+ break;
+ else
+ {
+ n[k][0][0] = lastn[k] + lastslide;
+ n[k][0][1] = vol[k];
+ }
+ else
+ n[k][0][0] += lastslide;
+ if (n[k][0][0] < 13)
+ n[k][0][0] = 13; /* C-1 */
+ else if (n[k][0][0] > 48)
+ n[k][0][0] = 48; /* B#3 */
+ break;
+
+ case 0x3: /* Slide To */
+ if (!note && !slideto || note == lastn[k] || ie1->MidiCh > 127)
+ break;
+ if (effval & 0xFF)
+ lastslide = effval;
+ else
+ lastslide = abs (lastslide);
+ if (note)
+ slideto = note;
+ if (slideto > lastn[k])
+ {
+ n[k][0][0] = lastn[k] + lastslide * (ticks-1);
+ if (n[k][0][0] < 13)
+ n[k][0][0] = 13; /* C-1 */
+ if (n[k][0][0] > slideto)
+ n[k][0][0] = slideto;
+ }
+ else
+ {
+ n[k][0][0] = lastn[k] - lastslide*(ticks-1);
+ if (n[k][0][0] > 48)
+ n[k][0][0] = 48; /* B#3 */
+ if (n[k][0][0] < slideto)
+ n[k][0][0] = slideto;
+ }
+ n[k][0][1] = vol[k];
+ break;
+
+ case 0x4: /* Vibrato */
+ case 0x7: /* Tremolo */
+ /* ignore these effects.. not convertable */
+ break;
+
+ case 0x5: /* Slide To + Volume Slide */
+ if ((note || slideto) && note!=lastn[k] && ie1->MidiCh < 128)
+ {
+ if (note)
+ slideto = note;
+ if (slideto > lastn[k])
+ {
+ n[k][0][0] = lastn[k] + lastslide*(ticks-1);
+ if (n[k][0][0] < 13)
+ n[k][0][0] = 13; /* C-1 */
+ if (n[k][0][0] > slideto)
+ n[k][0][0] = slideto;
+ }
+ else
+ {
+ n[k][0][0] = lastn[k] - lastslide*(ticks-1);
+ if (n[k][0][0] > 48)
+ n[k][0][0] = 48; /* B#3 */
+ if (n[k][0][0] < slideto)
+ n[k][0][0] = slideto;
+ }
+ }
+ else
+ n[k][0][0] = 0;
+ note = 0;
+
+ /* We do not break here: the next case block (0xA)
+ * will slide volume for us
+ */
+
+ case 0x6: /* Vibrato & Volume Slide */
+ /* Ignore Vibrato; do Volume Slide only */
+
+ case 0xA: /* Volume Slide */
+ {
+ UWORD v;
+
+ if (!note)
+ v = vol[k];
+ else
+ v = inst1->Volume;
+ v += (ticks-1)*(effval & 0xF0); /* Can't really slide */
+ v -= (ticks-1)*(effval & 0x0F);
+ if (v > 127)
+ v = 127;
+ else if (v < 0)
+ v = 0;
+ n[k][0][1] = v;
+ break;
+ }
+
+ case 0x9: /* Set offset: pretend it's retrigger */
+ if ((!n[k][0][0] || !sampnum) && delay[k])
+ {
+ n[k][0][0] = lastn[k];
+ n[k][0][1] = vol[k];
+ }
+ break;
+
+ case 0xB: /* Position Jump */
+ patbreak = 1; /* Ignore, but break anyway */
+ break;
+
+ case 0xD: /* Pattern Break */
+ patbreak = 1 + 10 * (effval & 0xF0) + (effval & 0x0F);
+ break;
+
+ case 0xC: /* Set Volume */
+ {
+ UWORD vol = effval;
+
+ if (vol == 0x40) vol=0x3F;
+ vol = vol & 0x3F;
+ n[k][0][1] = vol << 1;
+ }
+ break;
+
+ case 0xF: /* Set Speed/Tempo */
+ {
+ UWORD temp;
+
+ temp = effval;
+
+ if (!temp)
+ temp = 1;
+ if (temp < 32)
+ {
+ ticks = temp;
+ // if (TempoType) /* Tempos act strangely so .. */
+ {
+ bpm = 750 / temp;
+ x = 80000 * temp;
+ }
+ }
+ else
+ {
+ bpm = temp;
+ x = 60000000 / temp;
+ }
+
+ if (i) /* Only write tempo on track 0 */
+ break;
+
+ count += 6 + WriteVLQ (fp, timer);
+ timer = 0;
+ FPutC (fp, 255); /* Meta-Event */
+ FPutC (fp, 81); /* Set Tempo */
+ FPutC (fp, 3);
+ FPutC (fp, x >> 16);
+ FPutC (fp, (x >> 8) & 0xFF);
+ FPutC (fp, x & 0xFF);
+ tempodone = TRUE;
+
+ break;
+ }
+
+ case 0xE: /* Extended Effects */
+ switch (effval & 0xF0)
+ {
+ case 0x10: /* Fine Slide Up */
+ if (!(effval & 0x0F) || ie1->MidiCh > 127)
+ break;
+ if (!note)
+ {
+ if (!delay[k])
+ break;
+ else
+ {
+ n[k][h][0] = lastn[k] + (effval & 0x0F);
+ n[k][h][1] = vol[k];
+ }
+ }
+ else
+ n[k][h][0] += effval & 0x0F;
+ break;
+
+ case 0x020: /* Fine Slide Down */
+ if (!(effval & 0x0F) || ie1->MidiCh > 127)
+ break;
+ if (!note)
+ if (!delay[k])
+ break;
+ else {
+ n[k][h][0] = lastn[k] - (effval & 0x0F);
+ n[k][h][1] = vol[k];
+ }
+ else
+ n[k][h][0] -= effval & 0x0F;
+ break;
+ case 0x00: /* set filter on/off */
+ case 0x30: /* glissando on/off */
+ case 0x40: /* set vibrato wave */
+ case 0x50: /* set finetune */
+ case 0x60: /* pattern loop */
+ case 0x70: /* set tremolo wave */
+ case 0x80: /* un-used */
+ case 0xF0: /* invert loop */
+ /* Can't do these in MIDI.. ignore */
+ break;
+
+ case 0x0A0: /* Fine volume slide up */
+ case 0x0B0: /* Fine volume slide down */
+ {
+ UWORD v;
+
+ v = inst1->Volume;
+ if (effval & 0xA0)
+ v += effval & 0x0F;
+ else
+ v -= effval & 0x0F;
+ if (v < 0)
+ v = 0;
+ else if (v>127)
+ v = 127;
+ n[k][0][1] = v;
+ break;
+ }
+
+ case 0x90: /* Retrigger sample */
+ {
+ UWORD a, b, c;
+
+ if (!note && !delay[k] || !(effval & 0x0F))
+ break;
+ a = effval & 0x0F;
+ if (!(ticks / a))
+ break;
+ if (!note)
+ {
+ n[k][0][0] = lastn[k];
+ n[k][0][1] = vol[k];
+ }
+ c = 0;
+ b = 1;
+ a *= 48;
+ while (c < 48)
+ {
+ n[k][c][0] = note;
+ n[k][c][1] = n[k][0][1];
+ c = b * a / ticks;
+ b++;
+ }
+
+ break;
+ }
+
+ case 0xC0: /* Cut sample */
+ {
+ UWORD a;
+
+ if (!note && !delay[k])
+ break;
+ a = 48 * (effval & 0x0F) / ticks;
+ if (a > 47)
+ break;
+ if (note)
+ n[k][a][0] = note;
+ else
+ n[k][a][0] = lastn[k];
+ n[k][a][1] = 0;
+ break;
+ }
+
+ case 0xD0: /* Delay Sample */
+ {
+ UWORD a;
+
+ if (!note || !(effval & 0x0F))
+ break;
+ a = 48 * (effval & 0x0F) / ticks;
+ n[k][0][0] = 0;
+ if (a > 47)
+ break;
+ n[k][a][0] = note;
+ n[k][a][1] = n[k][a][0];
+ break;
+ }
+
+ case 0xE0: /* Pattern Pause */
+ pause = 48 * (effval & 0x0F);
+ break;
+
+ } /* End Switch (E effects) */
+
+ break;
+ /* else dunno what it does.. disbelieve it ;) */
+
+ } /* End switch (effnum) */
+
+ } /* End for (Tracks) */
+ } /* End if (!pattnreak) */
+
+ for (h = 0; h<48; h++)
+ {
+ for (k = 0; k < patt->Tracks; k++)
+ {
+ if (n[k][h][0])
+ {
+ if (delay[k]) /* Turn off old note on same channel */
+ {
+ count += StopNote (fp, ie1, c, lastn[k], vol[k], timer);
+ timer = 0;
+ delay[k] = 0;
+ }
+ lastn[k] = n[k][h][0];
+ n[k][h][0] = 0;
+ vol[k] = n[k][h][1];
+ count += 3 + WriteVLQ(fp, timer);
+ timer = 0;
+ FPutC (fp, 0x90 + c); /* Note On */
+ FPutC (fp, ENOTE(ie1, lastn[k], 0));
+ FPutC (fp, RESTRICTVOL(EVOL(ie1,vol[k])));
+ if (ie1->Transpose[1])
+ {
+ count += 4;
+ FPutC (fp, 0);
+ FPutC (fp, 0x90 + c);
+ FPutC (fp, ENOTE(ie1, lastn[k], 1));
+ FPutC (fp, RESTRICTVOL(EVOL(ie1, vol[k])));
+ if (ie1->Transpose[2])
+ {
+ count += 4;
+ FPutC (fp, 0);
+ FPutC (fp, 0x90 + c);
+ FPutC (fp, ENOTE(ie1, lastn[k], 2));
+ FPutC (fp, RESTRICTVOL(EVOL(ie1, vol[k])));
+ }
+ }
+ delay[k] = NoteLength (ANOTE(lastn[k]), inst1->Length, bpm);
+ }
+ else if (delay[k]==1)
+ {
+ delay[k] = 0;
+ count += StopNote (fp, ie1, c, lastn[k], vol[k], timer);
+ timer = 0;
+ }
+ else if (delay[k]>0)
+ delay[k]--;
+ }
+ timer++;
+ }
+ timer += pause;
+ if (patbreak<0)
+ patbreak++;
+ else if (patbreak>0)
+ {
+ patbreak = 1 - patbreak;
+ j = 0;
+ }
+ } /* End for (Lines) */
+ } /* End for (si->Length) */
+
+ for (k = 0; k < si->MaxTracks; k++)
+ if (delay[k])
+ {
+ count += StopNote (fp, ie1, c, lastn[k], vol[k], timer);
+ timer = 0;
+ }
+ }
+
+ if(!i && !tempodone)
+ {
+ count += 7;
+ FPutC (fp, 0); /* Give the default 128 bpm if none done yet */
+ FPutC (fp, 255);
+ FPutC (fp, 81);
+ FPutC (fp, 3);
+ FPutC (fp, 7);
+ FPutC (fp, 39);
+ FPutC (fp, 14);
+ }
+
+ if(inst1->Length || !i) // RAUL addition
+ {
+ count += 3 + WriteVLQ (fp, timer);
+ FPutC (fp, 255);
+ FPutC (fp, 47);
+ FPutC (fp, 0);
+
+ /* Write total chunk length */
+
+ if (Seek (fp, mktrlenpos, OFFSET_BEGINNING) == -1)
+ return ERR_READWRITE;
+
+ if (!(FWrite (fp, &count, 4, 1)))
+ return ERR_READWRITE;
+
+ if (Seek (fp, 0, OFFSET_END) == -1)
+ return ERR_READWRITE;
+ }
+
+ } /* End for (instruments) */
+
+ return RETURN_OK;
+}
+
+
+
+static UWORD ChooseChannels (struct Instrument *instr, struct InstrExtra *ie, UBYTE DrumChann)
+
+/* Returns: The number of different channels needed to play instruments.
+ * If that number is not greater than 16, upto 16 channels will
+ * be allocated to the samples.
+ */
+{
+ UBYTE c, Presets[128], m, n, numchan;
+ UBYTE DrumUsed = 0;
+ struct Instrument *instr1, *instr2;
+ struct InstrExtra *ie1, *ie2;
+
+ /* Preset all presets !!! */
+ for (n = 0; n < MAXINSTRUMENTS; n++)
+ ie[n].MidiPr = n;
+
+ memset (Presets, 0, 128);
+
+ for (n = MAXINSTRUMENTS, instr1 = &instr[1], ie1 = &ie[1]; n--; instr1++, ie1++)
+ {
+ ie->MidiCh = 255;
+ if (instr1->Length)
+ {
+ if (ie1->MidiPr > 127)
+ {
+ DrumUsed = 1;
+ ie1->MidiCh = DrumChann;
+ }
+ else
+ Presets[ie1->MidiPr] = 1;
+ }
+ else
+ ie1->MidiPr = 0;
+ }
+
+ for (numchan = DrumUsed, n = 128; n--; numchan += Presets[n]);
+
+ if (numchan > 16)
+ return numchan;
+
+ /* Go through and set channels appropriately */
+ m = MAXINSTRUMENTS;
+ instr1 = &instr[1];
+ ie1 = &ie[1];
+ c = 0;
+ while (--m)
+ {
+ if (ie1->MidiCh < 0)
+ {
+ ie1->MidiCh = c;
+ n = m;
+ ie2 = ie1 + 1;
+ instr2 = instr1 + 1;
+
+ /* Search for other instruments with the same preset and set
+ * all them to the same MIDI channel.
+ */
+ while (n--)
+ {
+ if (ie2->MidiCh < 0)
+ if (ie2->MidiPr == ie1->MidiPr || !instr2->Length)
+ ie2->MidiCh = c;
+ instr2++;
+ ie2++;
+ }
+ if (++c == DrumChann && DrumUsed)
+ c++;
+ }
+ ie1++;
+ instr1++;
+ }
+ return numchan;
+}
+
+
+
+static UWORD WriteMIDIHeader (BPTR fp, UWORD numofchannels)
+{
+ ULONG l;
+
+ /* Write MIDI header */
+ l = ID_MThd;
+ if (!FWrite (fp, &l, 4, 1)) return ERR_READWRITE;
+
+ /* Write chunk length */
+ l = sizeof (struct MThd);
+ if (!FWrite (fp, &l, 4, 1)) return ERR_READWRITE;
+
+ /* Write header chunk */
+ {
+ struct MThd mthd;
+
+ mthd.unknown1 = 1;
+ mthd.Tracks = numofchannels;
+ mthd.unknown2 = 192;
+
+ if (!FWrite (fp, &mthd, sizeof (mthd), 1)) return ERR_READWRITE;
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static ULONG WriteTrack0Info (BPTR fp, struct SongInfo *si)
+
+/* Write info for track 0.
+ * Return actual number of bytes written, or 0 for failure.
+ */
+{
+ static UBYTE TRK0I[63] =
+ {
+ 0, 255, 2, 42, 70, 105, 108, 101, 32, 67, 111, 112, 121, 114, 105, 103,
+ 104, 116, 32, 40, 99, 41, 32, 49 ,57, 57, 51, 32, 65, 100, 114, 101, 110,
+ 97, 108, 105, 110, 32, 83, 111, 102, 116, 119, 97, 114, 101,
+ 0, 255, 88, 4, 3, 2, 24, 8,
+ 0, 255, 89, 2, 0, 0,
+ 0, 255, 3
+ }; /* standard header + copyright message */
+
+ struct TRKInfo trkinfo;
+
+ memcpy (trkinfo.unknown1, TRK0I, 63);
+ trkinfo.NameLen = strlen (si->SongName);
+
+ if (!FWrite (fp, &trkinfo, sizeof (trkinfo), 1))
+ return 0;
+
+ if (!FWrite (fp, si->SongName, trkinfo.NameLen, 1))
+ return 0;
+
+ return (sizeof (trkinfo) + trkinfo.NameLen);
+}
+
+
+
+static LONG WriteVLQ (BPTR fp, ULONG i)
+
+/* Writes a stream of bytes, each with the msb (bit 7) set to 1 to mean
+ * continuation and 0 to end the byte sequnce. Note that the data is
+ * put in reverse order.
+ *
+ * Returns: # of bytes written after a variable-length-quantity equivalent
+ * of i has been written to the file f, or 0 for failure.
+ */
+{
+ LONG x = 0;
+ ULONG buffer;
+
+ buffer = i & 0x7F;
+ while ((i >>= 7) > 0)
+ buffer = ((buffer << 8) | 0x80) + (i & 0x7F);
+ while (1)
+ {
+ FPutC (fp, buffer & 0xFF);
+
+ x++;
+
+ if (buffer & 0x80)
+ buffer >>= 8;
+ else
+ return x;
+ }
+}
+
+
+
+static ULONG StopNote (BPTR fp, struct InstrExtra *ie, UWORD channel, UWORD note, UWORD vol, ULONG timer)
+
+/* stop old note */
+{
+ UWORD count = 3 + WriteVLQ (fp, timer);
+
+ FPutC (fp, 0x80 + channel); /* note off */
+ FPutC (fp, ENOTE(ie, note, 0));
+ FPutC (fp, RESTRICTVOL(EVOL(ie, vol)));
+ if (ie->Transpose[1])
+ {
+ count += 4;
+ FPutC (fp, 0);
+ FPutC (fp, 0x80 + channel);
+ FPutC (fp, ENOTE(ie, note, 1));
+ FPutC (fp, RESTRICTVOL(EVOL(ie, vol)));
+ if (ie->Transpose[2])
+ {
+ count += 4;
+ FPutC (fp, 0);
+ FPutC (fp, 0x80 + channel);
+ FPutC (fp, ENOTE(ie, note, 2));
+ FPutC (fp, RESTRICTVOL(EVOL(ie, vol)));
+ }
+ }
+
+ return count;
+}
+
+
+
+static ULONG NoteLength (UBYTE note, ULONG lenght, UWORD beats)
+
+{
+ ULONG idx;
+
+ /* Note conversion table for Sound/Noise/ProTracker */
+ static const UWORD TrackerNotes[] =
+ {
+ 0x000, /* Null note */
+
+ 0x6B0, 0x650, 0x5F5, 0x5A0, 0x54D, 0x501, /* Octave 0 */
+ 0x4B9, 0x475, 0x435, 0x3F9, 0x3C1, 0x38B,
+
+ 0x358, 0x328, 0x2FA, 0x2D0, 0x2A6, 0x280, /* Octave 1 */
+ 0x25C, 0x23A, 0x21A, 0x1FC, 0x1E0, 0x1C5,
+
+ 0x1AC, 0x194, 0x17D, 0x168, 0x153, 0x140, /* Octave 2 */
+ 0x12E, 0x11d, 0x10D, 0x0FE, 0x0F0, 0x0E2,
+
+ 0x0D6, 0x0CA, 0x0BE, 0x0B4, 0x0AA, 0x0A0, /* Octave 3 */
+ 0x097, 0x08F, 0x087, 0x07F, 0x078, 0x071,
+
+ 0x06B, 0x065, 0x05F, 0x05A, 0x055, 0x050, /* Octave 4 */
+ 0x04C, 0x047, 0x043, 0x040, 0x03C, 0x039,
+
+ 0x035, 0x032, 0x030, 0x02D, 0x02A, 0x028, /* Octave 5 */
+ 0x026, 0x024, 0x022, 0x020, 0x01E, 0x01C
+ };
+
+/* static float t[84] =
+ {
+ 3.200e-3, 3.020e-3, 2.851e-3, 2.691e-3, 2.540e-3, 2.397e-3,
+ 2.263e-3, 2.136e-3, 2.016e-3, 1.903e-3, 1.796e-3, 1.695e-3,
+ 1.600e-3, 1.510e-3, 1.425e-3, 1.345e-3, 1.270e-3, 1.197e-3,
+ 1.131e-3, 1.068e-3, 1.008e-3, 9.514e-4, 8.980e-4, 8.476e-4,
+ 8.000e-4, 7.551e-4, 7.127e-4, 6.727e-4, 6.350e-4, 5.993e-4,
+ 5.657e-4, 5.339e-4, 5.040e-4, 4.757e-4, 4.490e-4, 4.238e-4,
+ 4.000e-4, 3.775e-4, 3.564e-4, 3.364e-4, 3.175e-4, 2.997e-4,
+ 2.828e-4, 2.670e-4, 2.520e-4, 2.378e-4, 2.245e-4, 2.119e-4,
+ 2.000e-4, 1.888e-4, 1.782e-4, 1.682e-4, 1.587e-4, 1.498e-4,
+ 1.414e-4, 1.335e-4, 1.260e-4, 1.189e-4, 1.122e-4, 1.059e-4,
+ 1.000e-4, 9.439e-5, 8.909e-5, 8.409e-5, 7.937e-5, 7.492e-5,
+ 7.071e-5, 6.674e-5, 6.300e-5, 5.946e-5, 5.612e-5, 5.297e-5,
+ 5.000e-5, 4.719e-5, 4.454e-5, 4.204e-5, 3.969e-5, 3.746e-5,
+ 3.536e-5, 3.337e-5, 3.150e-5, 2.973e-5, 2.806e-5, 2.649e-5
+ }; */ /* multipliers for each pitch: 12th roots of 2 apart */
+
+ idx = note - 36;
+ if (idx < 0) idx = 0;
+ if (idx > 84) idx = 84;
+
+ return (TrackerNotes[idx] * beats * lenght); /* better not slide out of this range :( */
+}
--- /dev/null
+/*
+** ScreamTrackerHook.c
+**
+** Copyright (C) 1995,96,97 Bernardo Innocenti
+**
+** Load a ScreamTracker 3.01 module with any number of tracks.
+** Only sample instruments are supported.
+*/
+
+
+#include <exec/memory.h>
+#include <libraries/xmodule.h>
+#include <libraries/songclass.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+
+
+/* Local function prototypes */
+
+APTR HOOKCALL _GetEngine (
+ REG(a6, struct Library *MyBase));
+APTR HOOKCALL _SetupXMHook (
+ REG(a0, struct XModuleBase *_XModuleBase),
+ REG(a6, struct Library *mybase));
+static HOOKCALL struct XMHook *IdentifyScreamTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags));
+static HOOKCALL LONG LoadScreamTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags));
+INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval);
+
+
+#ifndef MAKE_ID
+#define MAKE_ID(a,b,c,d) \
+ ((ULONG) (a)<<24 | (ULONG) (b)<<16 | (ULONG) (c)<<8 | (ULONG) (d))
+#endif
+
+/* Convert an Intel style WORD to Motorola format */
+#define I2M(x) ( (UWORD) ( (((UWORD)(x)) >> 8) | (((UWORD)(x)) << 8) ) )
+
+/* Convert an Intel style LONG to Motorola format */
+#define I2ML(x) ( I2M((x)>>16) | (I2M((x))<<16) )
+
+/* Convert a ParaPointer to a normal file offset */
+#define PARA(x) (((ULONG)I2M(x)) << 4)
+
+/* Convert a 3-byte ParaPointer to a normal file offset */
+#define PARAL(x) (( (ULONG)x[0]<<16 | (ULONG)x[2]<<8 | (ULONG)x[1] ) << 4)
+
+
+struct S3MHeader
+{
+ UBYTE SongName[28];
+ UBYTE Constant;
+ UBYTE Type;
+ UWORD Pad0;
+ UWORD OrdNum; /* Number of positions */
+ UWORD InsNum;
+ UWORD PatNum;
+ UWORD Flags;
+ UWORD CreatedWith;
+ UWORD FileFormat; /* See below */
+ ULONG ID; /* Should be ID_SCRM */
+ UBYTE GlobalVolume;
+ UBYTE InitialSpeed;
+ UBYTE InitialTempo;
+ UBYTE MasterVolume;
+ UBYTE Pad1[10];
+ UWORD Special;
+ UBYTE Channels[32];
+};
+
+
+
+/* Values for S3MHeader->FileFormat */
+
+#define S3MFF_SIGNEDSAMPLES 1
+#define S3MFF_UNSIGNEDSAMPLES 2
+
+
+
+struct S3MSamp
+{
+ UBYTE Type; /* See ITYPE_#? definitions below. */
+ UBYTE DosName[12];
+ UBYTE MemSeg[3]; /* :-))) Parapointer to sample data */
+ ULONG Length;
+ ULONG LoopBeg;
+ ULONG LoopEnd;
+ UBYTE Volume;
+ UBYTE Pad0;
+ UBYTE Pack; /* See SPACK_#? definitions below. */
+ UBYTE Flags; /* See SFLAG_#? definitions below. */
+ ULONG C2Spd;
+ ULONG Pad1;
+ UWORD Reserved0[2];
+ ULONG Reserved1[1];
+ UBYTE SampleName[28];
+ ULONG SampID; /* Should be ID_SCRS */
+};
+
+
+/* S3M Instrument types */
+
+#define ITYPE_NONE 0
+#define ITYPE_SAMPLE 1
+#define ITYPE_ADLIB_MELODY 2
+#define ITYPE_ADLIB_SNARE 3
+#define ITYPE_ADLIB_TOM 4
+#define ITYPE_ADLIB_CYMBAL 5
+#define ITYPE_ADLIB_HIHAT 6
+
+
+/* S3M Sample packing */
+
+#define SPACK_NONE 0
+#define SPACK_ADPCM 1 /* Unsupported by ST3.01 */
+
+
+/* S3M Sample Flags */
+#define SFLAG_LOOP 1
+#define SFLAG_STEREO 2 /* Unsupported by ST3.01 */
+#define SFLAG_16BIT 4 /* Unsupported by ST3.01 */
+
+
+/* S3M IDs */
+
+#define ID_S3M MAKE_ID('S','3','M','\0') /* ScreamTracker ID */
+#define ID_SCRM MAKE_ID('S','C','R','M') /* ScreamTracker Module */
+#define ID_SCRS MAKE_ID('S','C','R','S') /* ScreamTracker Sample */
+#define ID_SCRI MAKE_ID('S','C','R','I') /* ScreamTracker Instr */
+
+
+
+/* Effects conversion table */
+static const UBYTE Effects[MAXTABLEEFFECTS] =
+{
+/* S3M XModule Val */
+
+ 0x00, /* Null effect $00 */
+
+ 0x05, /* Portamento Up $01 */
+ 0x04, /* Portamento Down $02 */
+ 0x06, /* Tone Portamento $03 */
+ 0x07, /* Vibrato $04 */
+ 0x0B, /* ToneP + VolSl $05 */
+ 0x0A, /* Vibra + VolSl $06 */
+ 0x08, /* Tremolo $07 */
+ 0x00, /* Set Hold/Decay $08 */
+ 0x0E, /* Sample Offset $09 */
+ 0x03, /* Volume Slide $0A */
+ 0x01, /* Position Jump $0B */
+ 0x12, /* Set Volume $0C */
+ 0x02, /* Pattern break $0D */
+ 0x10, /* Misc $0E */
+ 0x00, /* Set Speed $0F */
+ 0x11, /* Set Tempo $10 */
+ 0x09, /* Arpeggio $11 */
+
+ 0x03, /* Oktalyzer H */
+ 0x03 /* Oktalyzer L */
+};
+
+
+
+/* Library data */
+
+const UBYTE LibName[] = "screamtracker.xmhook";
+const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
+const UBYTE LibId[] = "screamtracker.xmhook 1.0 (17.3.96) © 1995-96 by Bernardo Innocenti";
+
+
+/* Get around a SAS/C bug which causes some annoying warnings
+ * with the library bases defined below. The problem happens
+ * when the GST has been compiled with the CODE=FAR switch and
+ * the source is being compiled with CODE=NEAR.
+ */
+#ifdef __SASC
+#pragma msg 72 ignore push
+#endif /* __SASC */
+
+struct ExecBase *SysBase = NULL;
+struct XModuleBase *XModuleBase = NULL;
+struct DosLibrary *DOSBase = NULL;
+struct IntuitionBase *IntuitionBase = NULL;
+
+#ifdef __SASC
+#pragma msg 72 ignore pop
+#endif /* __SASC */
+
+
+static HOOKCALL struct XMHook *IdentifyScreamTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags))
+
+/* Determine if the given file is a ScreamTracker 3.0 module.
+ * Note: the file position will be changed on exit.
+ */
+{
+ ULONG id;
+
+ Seek (fh, 0x2C, OFFSET_BEGINNING);
+ if (FRead (fh, &id, 4, 1) != 1)
+ return NULL;
+
+ return ((id == ID_SCRM) ? loader : NULL);
+}
+
+
+
+static HOOKCALL LONG LoadScreamTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags))
+{
+ UWORD *ParaInst,
+ *ParaPatt;
+ struct Note *note;
+ struct Pattern *patt;
+ struct S3MHeader s3mhd;
+
+ ULONG i, j; /* Loop counters */
+ LONG err = 0;
+ UWORD numchannels = 0, w;
+ LONG l; /* Dummy read buffer */
+
+
+ /* Read module header */
+ if (FRead (fh, &s3mhd, sizeof (s3mhd), 1) != 1)
+ return ERROR_IOERR;
+
+ /* Fix Intel WORD format */
+ s3mhd.OrdNum = I2M (s3mhd.OrdNum);
+ s3mhd.InsNum = I2M (s3mhd.InsNum);
+ s3mhd.PatNum = I2M (s3mhd.PatNum);
+ s3mhd.Flags = I2M (s3mhd.Flags);
+ s3mhd.CreatedWith = I2M (s3mhd.CreatedWith);
+ s3mhd.FileFormat = I2M (s3mhd.FileFormat);
+ s3mhd.Special = I2M (s3mhd.Special);
+
+
+ for (i = 0; i < 32; i++)
+ if (s3mhd.Channels[i] != 255) numchannels++;
+
+ s3mhd.SongName[27] = '\0'; /* Ensure Null-termination */
+
+
+ /* Read the pattern sequence */
+
+ if (!xmSetSongLen (si, s3mhd.OrdNum))
+ return ERROR_NO_FREE_STORE;
+
+ for (i = 0; i < s3mhd.OrdNum; i++)
+ {
+ if ((l = FGetC (fh)) != -1L)
+ si->Sequence[i] = l;
+ else
+ return ERROR_IOERR;
+ }
+
+ if (s3mhd.OrdNum & 1) FGetC (fh); /* Keep WORD alignament */
+
+
+
+ /*******************/
+ /* Get Instruments */
+ /*******************/
+
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_READING_INSTS, NULL);
+
+ /* Get parapointers to instruments */
+
+ if (!(ParaInst = AllocVec (s3mhd.InsNum * sizeof (UWORD), MEMF_PUBLIC)))
+ return ERROR_NO_FREE_STORE;
+
+ if (!(ParaPatt = AllocVec (s3mhd.PatNum * sizeof (UWORD), MEMF_PUBLIC)))
+ {
+ FreeVec (ParaInst);
+ return ERROR_NO_FREE_STORE;
+ }
+
+ if (FRead (fh, ParaInst, sizeof (UWORD), s3mhd.InsNum) == s3mhd.InsNum)
+ {
+ if (FRead (fh, ParaPatt, sizeof (UWORD), s3mhd.PatNum) == s3mhd.PatNum)
+ {
+ struct S3MSamp samp;
+
+ /* Note: Need to Flush() here? Hmm... I think not. */
+
+ for (i = 0; i < s3mhd.InsNum; i++)
+ {
+ if (xmDisplayProgress (i + 1, s3mhd.InsNum))
+ {
+ err = ERROR_BREAK;
+ break;
+ }
+
+ if (Seek (fh, PARA(ParaInst[i]), OFFSET_BEGINNING) == -1)
+ {
+ err = ERROR_IOERR;
+ break;
+ }
+ else
+ {
+ struct Instrument *inst;
+ BYTE *sample;
+
+ if (Read (fh, &samp, sizeof (samp)) != sizeof (samp))
+ {
+ err = ERROR_IOERR;
+ break;
+ }
+
+ samp.SampleName[27] = '\0'; /* Ensure NULL termination */
+
+ if (samp.Type == 0)
+ ; /* Do nothing */
+ else if (samp.Type == ITYPE_SAMPLE)
+ {
+ if (samp.Pack != SPACK_NONE)
+ xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_UNKNOWN_SAMPLE_COMPRESSION, i + 1);
+
+ if (samp.Flags & SFLAG_STEREO)
+ xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_INST_IS_STEREO, i + 1);
+
+ if (samp.Flags & SFLAG_16BIT)
+ {
+ xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_INST_IS_16BIT, i + 1);
+ continue;
+ }
+
+ if (!(inst = xmAddInstrument (si, i + 1,
+ INSTRA_Name, samp.SampleName,
+ INSTRA_Length, I2ML(samp.Length),
+ INSTRA_Volume, (samp.Volume > 64) ? 64 : samp.Volume,
+ (samp.Flags & SFLAG_LOOP) ? INSTRA_LoopStart : TAG_IGNORE, I2ML(samp.LoopBeg),
+ (samp.Flags & SFLAG_LOOP) ? INSTRA_LoopEnd : TAG_IGNORE, I2ML(samp.LoopEnd),
+ TAG_DONE)))
+ return ERROR_NO_FREE_STORE;
+
+
+ /* Seek where the sample is stored */
+ if (Seek (fh, PARAL(samp.MemSeg), OFFSET_BEGINNING) == -1)
+ {
+ err = ERROR_IOERR;
+ break;
+ }
+
+ /* Allocate memory for sample */
+
+ if (!(sample = AllocVec (inst->Length, MEMF_SAMPLE)))
+ {
+ err = ERROR_NO_FREE_STORE;
+ break;
+ }
+
+ /* Load sample data */
+
+ if (Read (fh, sample, inst->Length) != inst->Length)
+ {
+ FreeVec (sample);
+ err = ERROR_IOERR;
+ break;
+ }
+
+ if (s3mhd.FileFormat >= S3MFF_UNSIGNEDSAMPLES)
+ {
+ /* unsigned -> signed conversion */
+ for (j = 0; j < inst->Length; j++)
+ sample[j] ^= 0x80;
+ }
+
+ xmSetInstrument (si, i,
+ INSTRA_Sample, sample,
+ TAG_DONE);
+ }
+ else if (samp.Type <= ITYPE_ADLIB_HIHAT)
+ {
+ static UBYTE *ADLibNames[] =
+ {
+ "Melody",
+ "Snare Drum",
+ "Tom",
+ "Cymbal",
+ "Hihat"
+ };
+
+ xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_ADLIB_INSTR, i + 1, ADLibNames[samp.Type - 2]);
+ }
+ else
+ xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_ERR_NOT_A_SAMPLE, i + 1);
+ }
+ }
+ }
+ else err = ERROR_IOERR;
+ }
+ else err = ERROR_IOERR;
+
+ FreeVec (ParaInst);
+
+ SetAttrs (si,
+ SNGA_Title, s3mhd.SongName,
+ SNGA_Author, -1,
+ SNGA_GlobalSpeed, s3mhd.InitialSpeed,
+ SNGA_GlobalTempo, s3mhd.InitialTempo,
+ TAG_DONE);
+
+ if (err)
+ {
+ FreeVec (ParaPatt);
+ return err;
+ }
+
+
+ /****************/
+ /* Get Patterns */
+ /****************/
+
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
+ (APTR)MSG_READING_PATTS, NULL);
+
+ for (i = 0; i < s3mhd.PatNum; i++)
+ {
+ if (xmDisplayProgress (i + 1, s3mhd.PatNum))
+ {
+ err = ERROR_BREAK;
+ break;
+ }
+
+ if (Seek (fh, PARA(ParaPatt[i]), OFFSET_BEGINNING) == -1)
+ {
+ err = ERROR_IOERR;
+ break;
+ }
+ else
+ {
+ LONG ch = 0; /* Channel number */
+
+ /* Read size */
+
+ if (Read (fh, &w, sizeof (UWORD)) != sizeof (UWORD))
+ {
+ err = ERROR_IOERR;
+ break;
+ }
+
+ if (patt = xmAddPattern (si,
+ PATTA_Tracks, numchannels,
+ PATTA_Lines, 64,
+ TAG_DONE))
+ {
+ /* Loop on rows */
+ for (j = 0; (j < 64) && (ch != -1L); j++)
+ {
+ while ((ch = FGetC (fh)) != -1L)
+ {
+ if (ch == 0) /* End of row */
+ break;
+
+ if ((ch & 31) >= numchannels)
+ {
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_TRACK_OUT_OF_RANGE, ch & 31);
+ note = &patt->Notes[0][j];
+ }
+ else
+ note = &patt->Notes[ch & 31][j];
+
+ if (ch & 32) /* Note and instrument follows */
+ {
+ l = FGetC (fh);
+ if (l != 255)
+ note->Note = ((((l >> 4) - 2) * 12) | (l & 0x0F)) + 1;
+ note->Inst = FGetC (fh);
+ }
+ if (ch & 64) /* Volume */
+ {
+ l = FGetC (fh);
+ note->EffNum = EFF_SETVOLUME;
+ note->EffVal = l;
+ }
+
+ if (ch & 128) /* Command and Info */
+ {
+ note->EffNum = FGetC (fh);
+ note->EffVal = FGetC (fh);
+ note->EffNum = DecodeEff (note->EffNum, note->EffVal);
+ }
+ }
+ }
+
+ if (ch == -1)
+ {
+ err = ERROR_IOERR;
+ break;
+ }
+ }
+ else
+ {
+ err = ERROR_NO_FREE_STORE;
+ break;
+ }
+
+ /* Flush (fh); */
+ }
+ }
+
+ FreeVec (ParaPatt);
+
+ return err;
+}
+
+
+
+INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval)
+{
+ UBYTE i;
+
+ if ((eff == 0) && effval) /* Speed/Tempo */
+ {
+ if (effval < 0x20)
+ return EFF_SETSPEED;
+ else
+ return EFF_SETTEMPO;
+ }
+
+ for (i = 0 ; i < MAXTABLEEFFECTS; i++)
+ if (eff == Effects[i])
+ return i;
+
+ return 0;
+}
+
+
+
+HOOKCALL struct Library * _UserLibInit (REG(a6, struct Library *mybase))
+{
+ return mybase;
+}
+
+
+
+HOOKCALL struct Library * _UserLibCleanup (REG(a6, struct Library *mybase))
+{
+ return mybase;
+}
+
+
+
+HOOKCALL APTR _GetEngine (REG(a6, struct Library *mybase))
+{
+ return NULL;
+}
+
+
+
+HOOKCALL APTR _SetupXMHook (
+ REG(a0, struct XModuleBase *_XModuleBase),
+ REG(a6, struct Library *mybase))
+{
+ if (!(strcmp (_XModuleBase->xm_Library.lib_Node.ln_Name, "xmodule.library")))
+ {
+ SysBase = *((struct ExecBase **)4);
+ XModuleBase = _XModuleBase;
+ IntuitionBase = XModuleBase->xm_IntuitionBase;
+ DOSBase = XModuleBase->xm_DOSBase;
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMLOADER,
+ XMHOOK_Name, (LONG)"ScreamTracker",
+ XMHOOK_Priority, 0,
+ XMHOOK_Descr, (LONG)"ScreamTracker 3.01",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_S3M,
+ XMHOOK_Flags, 0,
+ XMHOOK_LibraryBase, mybase,
+ XMHOOK_LoadModFunc, LoadScreamTracker,
+ XMHOOK_IdentifyModFunc, IdentifyScreamTracker,
+ XMHOOK_MaxTracks, 16,
+ XMHOOK_MaxPatterns, 100,
+ XMHOOK_MaxInstruments, 99,
+ XMHOOK_MaxLength, 255,
+ XMHOOK_MaxSampleLen, 64000,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+ }
+
+ return NULL;
+}
--- /dev/null
+/*
+** Instr.c
+**
+** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+**
+** Instrument loading/saving/handling routines.
+*/
+
+#define IFFPARSE_V37_NAMES_ONLY
+
+#include <exec/libraries.h>
+#include <exec/memory.h>
+#include <datatypes/soundclass.h>
+#include <libraries/iffparse.h>
+#include <libraries/maud.h>
+#include <libraries/toccata.h>
+#include <libraries/xmoduleclass.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/iffparse.h>
+#include <proto/datatypes.h>
+#include <proto/toccata.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+#define UNITY 0x10000L
+
+
+/* Local function prototypes */
+static LONG DTLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename);
+static LONG LoadMAUDInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename);
+static LONG RawLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename, UWORD mode);
+static BYTE D1Unpack (UBYTE source[], LONG n, BYTE dest[], BYTE x);
+static void DUnpack (UBYTE source[], LONG n, BYTE dest[]);
+
+
+
+struct Library *DataTypesBase = NULL;
+struct Library *ToccataBase = NULL;
+
+
+
+GLOBALCALL LONG LoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename)
+
+/* Load an instrument file to the instrument slot <num> in the passed song.
+ * Will use DataTypes if available, otherwise it will use the built-in loaders.
+ */
+{
+ LONG err = IFFERR_NOTIFF;
+
+ /* Try loading with DataTypes */
+
+ if (GuiSwitches.UseDataTypes)
+ if (DataTypesBase = OpenLibrary ("datatypes.library", 39L))
+ {
+ err = DTLoadInstrument (si, num, filename);
+ CloseLibrary (DataTypesBase); DataTypesBase = NULL;
+ }
+
+
+ /* Try again using built-in loaders */
+
+ if (err)
+ {
+ struct IFFHandle *iff;
+
+ if (iff = AllocIFF())
+ {
+ if (iff->iff_Stream = (ULONG) Open (filename, MODE_OLDFILE))
+ {
+ InitIFFasDOS (iff);
+
+ if (!(err = OpenIFF (iff, IFFF_READ)))
+ {
+ if (!(err = ParseIFF (iff, IFFPARSE_RAWSTEP)))
+ {
+ LONG type = CurrentChunk (iff)->cn_Type;
+ switch (type)
+ {
+ case ID_8SVX:
+ err = Load8SVXInstrument (si, num, iff, filename);
+ break;
+
+ case ID_MAUD:
+ err = LoadMAUDInstrument (si, num, iff, filename);
+ break;
+
+ default:
+ {
+ UBYTE buf[5];
+
+ IDtoStr (type, buf);
+ ShowMessage (MSG_UNKNOWN_IFF, buf);
+ err = ERROR_OBJECT_WRONG_TYPE;
+ break;
+ }
+ }
+ }
+ }
+
+ Close (iff->iff_Stream);
+ }
+ else err = IoErr();
+
+ FreeIFF (iff);
+ }
+ else err = ERROR_NO_FREE_STORE;
+ }
+
+ if (err == IFFERR_MANGLED || err == IFFERR_SYNTAX)
+ ShowMessage (MSG_ILLEGAL_IFF_STRUCTURE);
+
+ if (err == IFFERR_NOTIFF)
+ {
+ LONG mode;
+
+ if (mode = ShowRequestArgs (MSG_SELECT_RAW_MODE,
+ MSG_RAW_MODES, NULL))
+ err = RawLoadInstrument (si, num, filename, mode);
+ else err = 0;
+ }
+
+ UpdateInstrList();
+
+ if (err) LastErr = err;
+ return err;
+}
+
+
+
+static LONG DTLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename)
+{
+ Object *dto;
+ UBYTE *instname;
+ LONG err = 0;
+
+ if (dto = NewDTObject (filename,
+ DTA_GroupID, GID_SOUND,
+ TAG_DONE))
+ {
+ struct VoiceHeader *vhdr;
+ LONG Len, Vol;
+ UBYTE *Sample;
+ UBYTE *errorstring;
+
+ if (GetDTAttrs (dto,
+ SDTA_VoiceHeader, &vhdr,
+ SDTA_SampleLength, &Len,
+ SDTA_Volume, &Vol,
+ SDTA_Sample, &Sample,
+ DTA_Title, &instname,
+ TAG_DONE) == 5)
+ {
+ /* Detach sample from DataType Object, so it won't
+ * be freed when we dispose the object.
+ */
+ SetDTAttrs (dto, NULL, NULL,
+ SDTA_Sample, NULL,
+ TAG_DONE);
+
+ /* Create new instrument */
+
+ if (!xmAddInstrument (si, num,
+ INSTRA_Name, instname,
+ INSTRA_Sample, Sample,
+ INSTRA_Length, Len,
+ INSTRA_Repeat, vhdr->vh_OneShotHiSamples,
+ INSTRA_Replen, vhdr->vh_RepeatHiSamples,
+
+ /* The sound.datatype _should_ return
+ * volumes in the normal Amiga range 0-64.
+ * However, the 8svx.datatype behaves
+ * differently: it returns the volume in the
+ * standard 8SVX format, where the maximum
+ * volume is $10000. Here is a good
+ * workaround to this bug.
+ */
+ INSTRA_Volume, (vhdr->vh_Volume > 64) ?
+ ((vhdr->vh_Volume * 64) / UNITY) : vhdr->vh_Volume,
+ TAG_DONE))
+ err = ERROR_NO_FREE_STORE;
+
+ DB(kprintf ("Vol: %ld InstVol: %ld\n", Vol, si->Instr[num]->Volume));
+ }
+ else err = RETURN_FAIL;
+
+
+ if (GetDTAttrs (dto,
+ DTA_ErrorString, &errorstring,
+ TAG_DONE) == 1)
+ ShowMessage (MSG_DATATYPES_ERROR, errorstring);
+
+ DisposeDTObject (dto);
+ }
+ else err = IoErr();
+
+ return err;
+}
+
+
+
+GLOBALCALL LONG Load8SVXInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename)
+
+/* Load an IFF 8SVX file to the instrument slot <num>. If <num> is 0, it requires
+ * the INST chunk and it uses the num stored there.
+ * Can decode Fibonacci Delta encoded samples.
+ */
+{
+ struct ContextNode *cn;
+ struct VoiceHeader vhdr;
+ struct InstrumentHeader insthdr;
+ LONG err;
+ UBYTE name[64];
+ BOOL is_valid_8svx = FALSE,
+ insthdr_loaded = FALSE;
+
+ static LONG stopchunks[] =
+ {
+ ID_8SVX, ID_INST,
+ ID_8SVX, ID_VHDR,
+ ID_8SVX, ID_BODY,
+ ID_8SVX, ID_NAME
+ };
+
+ /* Put the file name if the optional NAME propriety is missing */
+ if (filename)
+ {
+ strncpy (name, FilePart (filename), 63);
+ name[63] = '\0';
+ }
+ else name[0] = '\0';
+
+ if (err = StopChunks (iff, stopchunks, 4))
+ return err;
+
+ if (err = StopOnExit (iff, ID_8SVX, ID_FORM))
+ return err;
+
+ while (1)
+ {
+ if (err = ParseIFF (iff, IFFPARSE_SCAN))
+ {
+ if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
+ break; /* Free resources & exit */
+ }
+
+ if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_8SVX))
+ {
+ switch (cn->cn_ID)
+ {
+ case ID_INST:
+ {
+ if ((err = ReadChunkBytes (iff, &insthdr, sizeof (insthdr))) !=
+ sizeof (insthdr)) return err;
+
+ if (!num) num = insthdr.Num;
+ }
+
+ case ID_VHDR:
+ {
+ if ((err = ReadChunkBytes (iff, &vhdr, sizeof (vhdr))) !=
+ sizeof (vhdr)) return err;
+
+ if (!is_valid_8svx)
+ {
+ if (!xmAddInstrumentA (si, num, NULL))
+ return ERROR_NO_FREE_STORE;
+
+ if (!num) num = si->LastInstrument;
+ }
+
+ xmSetInstrument (si, num,
+ INSTRA_Repeat, (vhdr.vh_RepeatHiSamples ? vhdr.vh_OneShotHiSamples : 0),
+ INSTRA_Replen, vhdr.vh_RepeatHiSamples,
+ INSTRA_Volume, (vhdr.vh_Volume * 64) / UNITY,
+ INSTRA_FineTune, insthdr_loaded ? insthdr.FineTune : 0,
+ TAG_DONE);
+
+ is_valid_8svx = TRUE;
+
+ break;
+ }
+
+ case ID_BODY:
+ {
+ struct Instrument *instr;
+ BYTE *sample;
+
+ if (!is_valid_8svx)
+ {
+ xmAddInstrumentA (si, num, NULL);
+ if (!num) num = si->LastInstrument;
+ }
+
+ if (!(instr = si->Instr[num]))
+ return ERROR_NO_FREE_STORE;
+
+ if (!(sample = AllocVec (cn->cn_Size, MEMF_SAMPLE)))
+ return ERROR_NO_FREE_STORE;
+
+ xmSetInstrument (si, num,
+ INSTRA_Sample, sample,
+ INSTRA_Length, cn->cn_Size,
+ TAG_DONE);
+
+ /* We only require that at least some data is
+ * read. This way if, say, you have a corrupted
+ * 8SVX file, you can still load part of the
+ * instrument data.
+ */
+ if ((err = ReadChunkBytes (iff, instr->Sample,
+ cn->cn_Size)) < 0)
+ return err;
+
+ is_valid_8svx = TRUE;
+ break;
+ }
+
+ case ID_NAME:
+ ReadChunkBytes (iff, name, min(cn->cn_Size, 63));
+ name[63] = '\0'; /* Ensure string termination */
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (is_valid_8svx)
+ {
+ xmSetInstrument (si, num,
+ INSTRA_Name, name,
+ TAG_DONE);
+
+ if (!err)
+ {
+ if (vhdr.vh_Compression == CMP_FIBDELTA)
+ {
+ BYTE *buf;
+ struct Instrument *instr = si->Instr[num];
+
+ if (buf = AllocVec (instr->Length * 2, MEMF_SAMPLE))
+ {
+ DUnpack (instr->Sample, instr->Length + 2, buf);
+ FreeVec (instr->Sample);
+
+ xmSetInstrument (si, num,
+ INSTRA_Sample, buf,
+ INSTRA_Length, instr->Length * 2,
+ TAG_DONE);
+ }
+ }
+ else if (vhdr.vh_Compression != CMP_NONE)
+ ShowMessage (MSG_UNKNOWN_COMPRESSION);
+ }
+ }
+ else err = IFFERR_MANGLED;
+
+ return err;
+}
+
+
+
+/* Number of samples loaded & processed at one time */
+#define MAUDBLOCKSIZE 32768
+
+static LONG LoadMAUDInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename)
+
+/* Load an IFF MAUD file to the instrument slot <num> of the passed song.
+ * MAUD is the standard file format for Macrosystem's audio boards
+ * Toccata and Maestro.
+ */
+{
+ struct ContextNode *cn;
+ struct MaudHeader mhdr;
+ LONG err;
+ BOOL is_valid_maud = FALSE;
+ UBYTE name[64];
+
+ static LONG stopchunks[] =
+ {
+ ID_MAUD, ID_MHDR,
+ ID_MAUD, ID_MDAT,
+ ID_MAUD, ID_NAME
+ };
+
+
+ /* Put the file name if the optional NAME propriety is missing */
+
+ if (filename)
+ {
+ strncpy (name, FilePart (filename), 63);
+ name[63] = '\0';
+ }
+ else name[0] = '\0';
+
+ if (err = StopChunks (iff, stopchunks, 3))
+ return err;
+
+ if (err = StopOnExit (iff, ID_MAUD, ID_FORM))
+ return err;
+
+
+ while (1)
+ {
+ if (err = ParseIFF (iff, IFFPARSE_SCAN))
+ {
+ if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
+ break; /* Free resources & exit */
+ }
+
+ if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_MAUD))
+ {
+ switch (cn->cn_ID)
+ {
+ case ID_MHDR:
+ if ((err = ReadChunkBytes (iff, &mhdr, sizeof (mhdr))) !=
+ sizeof (mhdr)) return err;
+
+ if ((mhdr.mhdr_SampleSizeU != 8) && (mhdr.mhdr_SampleSizeU != 16))
+ {
+ ShowMessage (MSG_SAMPLE_WRONG_SIZE, mhdr.mhdr_SampleSizeU);
+ return IFFERR_SYNTAX;
+ }
+
+ if (mhdr.mhdr_ChannelInfo != MCI_MONO)
+ {
+ ShowMessage (MSG_SAMPLE_NOT_MONO, mhdr.mhdr_ChannelInfo);
+ return IFFERR_SYNTAX;
+ }
+
+ if (mhdr.mhdr_Channels != 1)
+ {
+ ShowMessage (MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS, mhdr.mhdr_Channels);
+ return IFFERR_SYNTAX;
+ }
+
+ is_valid_maud = TRUE;
+ break;
+
+ case ID_MDAT:
+ {
+ ULONG i;
+ struct Instrument *instr;
+ BYTE *sample;
+
+ if (!is_valid_maud)
+ return IFFERR_SYNTAX;
+
+ if (!(sample = AllocVec ((mhdr.mhdr_Samples + 1) & (~1), MEMF_SAMPLE)))
+ return ERROR_NO_FREE_STORE;
+
+ if (!(instr = xmAddInstrument (si, num,
+ INSTRA_Sample, sample,
+ INSTRA_Length, (mhdr.mhdr_Samples + 1) & (~1),
+ TAG_DONE)))
+ {
+ FreeVec (sample);
+ return ERROR_NO_FREE_STORE;
+ }
+
+ if (mhdr.mhdr_SampleSizeU == 8) /* 8 bit */
+ {
+ /* We only require that at least some data is
+ * read. This way if, say, you have a corrupted
+ * MAUD file, you can still load part of the
+ * sample data.
+ */
+ if ((err = ReadChunkBytes (iff, instr->Sample,
+ instr->Length)) == 0) return err;
+
+ SampChangeSign8 (instr->Sample, instr->Length);
+
+ }
+ else if (mhdr.mhdr_SampleSizeU == 16) /* 16 bit */
+ {
+ WORD *tmp;
+ ULONG actual, current = 0;
+
+ if (!(tmp = AllocPooled (Pool, MAUDBLOCKSIZE * ((mhdr.mhdr_SampleSizeC + 7) / 8))))
+ return ERROR_NO_FREE_STORE;
+
+ if (mhdr.mhdr_Compression != MCOMP_NONE)
+ if (!(ToccataBase = MyOpenLibrary ("toccata.library", 0L)))
+ {
+ FreePooled (Pool, tmp, MAUDBLOCKSIZE * sizeof (WORD));
+ CantOpenLib ("toccata.library", 0L);
+ return ERROR_INVALID_RESIDENT_LIBRARY;
+ }
+
+ for (;;)
+ {
+ actual = ReadChunkBytes (iff, tmp, MAUDBLOCKSIZE * ((mhdr.mhdr_SampleSizeC + 7) / 8));
+
+ if (actual == 0) break;
+
+ /* Filter (tmp, actual); */
+
+ switch (mhdr.mhdr_Compression)
+ {
+ case MCOMP_ALAW:
+ /* Convert 8bit A-Law data to 8bit signed data */
+ T_Convert (tmp, instr->Sample + current, actual, TMODE_ALAW, TMODE_LINEAR_8);
+ current += actual;
+ break;
+
+ case MCOMP_ULAW:
+ /* Convert 8bit µ-Law data to 8bit signed data */
+ T_Convert (tmp, instr->Sample + current, actual, TMODE_ULAW, TMODE_LINEAR_8);
+ current += actual;
+ break;
+
+ default:
+ /* Convert 16bit signed data to 8bit signed data */
+ actual >>= 1;
+ for (i = 0; (i < actual) && (current < mhdr.mhdr_Samples); i++, current++)
+ instr->Sample[current] = tmp[i] >> 8;
+ break;
+ }
+ }
+
+ SampChangeSign8 (instr->Sample, instr->Length);
+ FreePooled (Pool, tmp, MAUDBLOCKSIZE * sizeof (WORD));
+ CloseLibrary (ToccataBase);
+ ToccataBase = NULL;
+ }
+
+ is_valid_maud = TRUE;
+ break;
+ }
+
+ case ID_NAME:
+ ReadChunkBytes (iff, name, min(cn->cn_Size, 63));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (is_valid_maud)
+ {
+ xmSetInstrument (si, num,
+ INSTRA_Name, name,
+ TAG_DONE);
+
+ if (!err)
+ {
+ if (mhdr.mhdr_Compression == CMP_FIBDELTA)
+ {
+ BYTE *buf;
+ struct Instrument *instr = si->Instr[num];
+
+ if (buf = AllocVec (instr->Length * 2, MEMF_SAMPLE))
+ {
+ DUnpack (instr->Sample, instr->Length + 2, buf);
+ FreeVec (instr->Sample);
+
+ xmSetInstrument (si, num,
+ INSTRA_Sample, buf,
+ INSTRA_Length, instr->Length * 2,
+ TAG_DONE);
+ }
+ }
+ else if (mhdr.mhdr_Compression != CMP_NONE)
+ ShowMessage (MSG_UNKNOWN_COMPRESSION);
+ }
+ }
+ else err = IFFERR_NOTIFF;
+
+ return err;
+}
+
+
+
+static LONG RawLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename, UWORD mode)
+
+/* Load a raw file to the instrument slot pointed by inst.
+ * mode 1 - signed 8bit
+ * 2 - unsigned 8bit
+ */
+{
+ BPTR lock, fh;
+ struct FileInfoBlock *fib;
+ struct Instrument *instr;
+ LONG err = 0;
+ ULONG len;
+
+ if (lock = Lock (filename, ACCESS_READ))
+ {
+ /* Get file size */
+ if (fib = AllocDosObject (DOS_FIB, NULL))
+ {
+ if (Examine (lock, fib))
+ len = fib->fib_Size;
+ else
+ err = IoErr();
+ FreeDosObject (DOS_FIB, fib);
+ }
+ else err = ERROR_NO_FREE_STORE;
+
+ if (!err)
+ {
+ if (fh = OpenFromLock (lock))
+ {
+ BYTE *sample;
+
+ lock = NULL; /* OpenFromLock() relinquished our lock! */
+
+ if (sample = AllocVec (len, MEMF_SAMPLE))
+ {
+ if (instr = xmAddInstrument (si, num,
+ INSTRA_Sample, sample,
+ INSTRA_Length, len,
+ INSTRA_Volume, 64,
+ INSTRA_Name, FilePart (filename),
+ TAG_DONE))
+ {
+ /* We do not check for failure here to
+ * be more error tolerant. This way you
+ * can load at least part of an instrument
+ * from a corrupted file ;-)
+ */
+ Read (fh, sample, len);
+
+ if (mode == 2)
+ SampChangeSign8 (sample, instr->Length);
+ }
+ else
+ {
+ FreeVec (sample);
+ err = ERROR_NO_FREE_STORE;
+ }
+ }
+ else err = ERROR_NO_FREE_STORE;
+
+ Close (fh);
+ }
+ else err = IoErr();
+ }
+
+ UnLock (lock); /* Will be NULL if OpenFromLock() was successful */
+ }
+ else err = IoErr();
+
+ return err;
+}
+
+
+
+GLOBALCALL LONG SaveInstrument (struct Instrument *inst, CONST_STRPTR filename)
+{
+ LONG err;
+ struct IFFHandle *iff;
+
+
+ if (iff = AllocIFF())
+ {
+ if (iff->iff_Stream = (ULONG) Open (filename, MODE_NEWFILE))
+ {
+ InitIFFasDOS (iff);
+
+ if (!(err = OpenIFF (iff, IFFF_WRITE)))
+ {
+ err = Save8SVXInstrument (inst, 0, iff);
+ CloseIFF (iff);
+ }
+
+ Close (iff->iff_Stream);
+ }
+ else err = IoErr();
+
+ FreeIFF (iff);
+ }
+ else return ERROR_NO_FREE_STORE;
+
+ if (!err)
+ {
+ if (GuiSwitches.InstrSaveIcons)
+ /* Write icon */
+ PutIcon ("def_Instrument", filename);
+ }
+ else
+ {
+ /* Remove incomplete file */
+ LastErr = err;
+ DeleteFile (filename);
+ }
+
+ return (err);
+}
+
+
+
+GLOBALCALL LONG Save8SVXInstrument (struct Instrument *instr, ULONG num, struct IFFHandle *iff)
+
+/* Save the instrument pointed by inst to a standard IFF 8SVX file.
+ */
+{
+ struct VoiceHeader vhdr;
+ LONG err;
+
+
+ /* Write 8SVX */
+
+ if (err = PushChunk (iff, ID_8SVX, ID_FORM, IFFSIZE_UNKNOWN))
+ return err;
+
+
+ /* Write INST */
+ if (num)
+ {
+ struct InstrumentHeader insthdr;
+
+ insthdr.Num = num;
+ insthdr.Type = ITYPE_SAMPLE8;
+ insthdr.FineTune = instr->FineTune;
+
+ if (err = PushChunk (iff, ID_8SVX, ID_INST, sizeof (insthdr)))
+ return err;
+ if ((err = WriteChunkBytes (iff, &insthdr, sizeof (insthdr))) !=
+ sizeof (insthdr))
+ return err;
+ if (err = PopChunk (iff)) return err; /* Pop INST */
+ }
+
+ /* Write VHDR */
+
+ if (vhdr.vh_RepeatHiSamples = instr->Replen)
+ /* Loop */
+ vhdr.vh_OneShotHiSamples = instr->Repeat;
+ else
+ /* No Loop */
+ vhdr.vh_OneShotHiSamples = instr->Length;
+
+ vhdr.vh_SamplesPerHiCycle = 0;
+ vhdr.vh_SamplesPerSec = 8363;
+ vhdr.vh_Octaves = 1;
+ vhdr.vh_Compression = CMP_NONE;
+ vhdr.vh_Volume = (instr->Volume * UNITY) / 64;
+
+ if (err = PushChunk (iff, ID_8SVX, ID_VHDR, sizeof (vhdr)))
+ return err;
+ if ((err = WriteChunkBytes (iff, &vhdr, sizeof (vhdr))) !=
+ sizeof (vhdr))
+ return err;
+ if (err = PopChunk (iff)) return err; /* Pop VHDR */
+
+
+ /* Write NAME */
+ {
+ ULONG l = strlen (instr->Name) + 1;
+
+ if (err = PushChunk (iff, ID_8SVX, ID_NAME, l))
+ return err;
+ if ((err = WriteChunkBytes (iff, instr->Name, l)) != l)
+ return err;
+ if (err = PopChunk (iff)) return err; /* Pop NAME */
+ }
+
+ /* Write BODY */
+
+ if (instr->Sample)
+ {
+ if (PushChunk (iff, ID_8SVX, ID_BODY, instr->Length))
+ return err;
+ if ((err = WriteChunkBytes (iff, instr->Sample, instr->Length)) !=
+ instr->Length) return err;
+ if (err = PopChunk (iff)) return err; /* Pop BODY */
+ }
+
+ PopChunk (iff); /* Pop 8SVX */
+
+ return err;
+}
+
+
+
+GLOBALCALL void OptimizeInstruments (struct SongInfo *si)
+
+/* Remove useless sample data (cut beyond loops and zero-tails) */
+{
+ UWORD i;
+ ULONG newlen;
+ struct Instrument *instr;
+
+ for (i = 1 ; i <= si->LastInstrument ; i++)
+ {
+ if (!(instr = si->Instr[i])) continue;
+ if (!instr->Sample) continue;
+
+ newlen = instr->Length;
+
+ if (instr->Replen)
+ {
+ if (instr->Length > instr->Repeat + instr->Replen)
+ newlen = instr->Repeat + instr->Replen; /* Cut instrument after loop */
+ }
+ else
+ {
+ BYTE *tail;
+
+ instr->Repeat = 0; /* Kill null loops */
+
+ /* Kill instrument zero-tail.
+ * In order to reduce the instrument even more,
+ * 1 & -1 are treated the same as zero.
+ */
+
+ tail = instr->Sample + instr->Length - 1;
+ while ((*tail < 1) && (*tail > -1) && (tail > instr->Sample))
+ tail--;
+
+ newlen = tail - instr->Sample;
+ if (newlen & 1) newlen++; /* Pad instrument size to words */
+
+ /* leave 2 end zeroes to prevent an audible end-of-instrument click. */
+ if (newlen) newlen += 2;
+ }
+
+ if (newlen == 0)
+ xmRemInstrument (si, i); /* This instrument is mute! Free it... */
+ else if (newlen < instr->Length)
+ {
+ /* Resize the instrument if necessary */
+ BYTE *newinstr;
+
+ /* Allocate memory for optimized instrument */
+ if (!(newinstr = AllocVec (newlen, MEMF_SAMPLE)))
+ {
+ ShowMessage (MSG_NO_MEMORY_TO_OPTIMIZE_INSTR, i);
+ continue; /* Better luck with next instrument :) */
+ }
+
+ ShowMessage (MSG_INSTR_WILL_SHRINK, i, instr->Length, newlen);
+
+ /* Copy first part of instrument */
+ CopyMem (instr->Sample, newinstr, newlen);
+
+ /* Free old instrument */
+ FreeVec (instr->Sample);
+
+ /* Replace with new instrument */
+ xmSetInstrument (si, i,
+ INSTRA_Sample, newinstr,
+ INSTRA_Length, newlen,
+ TAG_DONE);
+
+ }
+ }
+}
+
+
+
+GLOBALCALL void RemDupInstruments (struct SongInfo *si)
+/* Find out identical patterns and cut them out */
+{
+ ULONG i, j, k, w, v;
+ struct Instrument *insta, *instb;
+ struct Pattern *patt;
+ struct Note *note;
+
+
+ for (i = 1; i < si->LastInstrument; i++) /* '<' instead of '<=' is ok here... */
+ {
+ if (!(insta = si->Instr[i])) continue;
+ if (!insta->Length) continue;
+
+ for (j = i + 1; j <= si->LastInstrument ; j++)
+ {
+ if (!(instb = si->Instr[j])) continue;
+
+ if (insta->Length == instb->Length &&
+ insta->Repeat == instb->Repeat &&
+ insta->Replen == instb->Replen &&
+ insta->Volume == instb->Volume &&
+ insta->Type == instb->Type &&
+ insta->FineTune == instb->FineTune)
+ {
+ if (!memcmp (insta->Sample, instb->Sample, insta->Length))
+ {
+ xmRemInstrument (si, j);
+
+ for (k = 0; k < si->NumPatterns; k++)
+ {
+ if (!(patt = si->Patt[k])) continue;
+
+ for (w = 0; w < patt->Tracks; w++)
+ {
+ note = patt->Notes[w];
+ for (v = 0; v < patt->Lines; v++, note++)
+ if (note->Inst == j) note->Inst = i;
+ }
+ }
+
+ ShowMessage (MSG_INSTR_DUPES_REMOVED, i, j);
+ }
+ }
+ }
+ }
+
+}
+
+
+
+GLOBALCALL void RemapInstruments (struct SongInfo *si)
+
+/* Remove empty slots between instruments, to allow those module formats
+ * that support less instruments to use even the last instruments.
+ */
+{
+ UWORD i, j, k;
+ UBYTE newpos[MAXINSTRUMENTS] = { 0 };
+ struct Instrument *instr;
+ struct Pattern *patt;
+ struct Note *note;
+
+ /* newpos[0] = 0; */
+
+ DB (kprintf ("Before - LastInstrument = %ld", si->LastInstrument));
+
+ /* Build instrument remap table & compress instrument slots */
+ for (i = 1, j = 0; i <= si->LastInstrument; i++)
+ {
+ if (!(instr = si->Instr[i])) continue;
+
+ if (instr->Length)
+ {
+ j++;
+ newpos[i] = j;
+
+ if (j != i) DoMethod ((Object *)si, SNGM_SWAPINSTRUMENTS, i, j);
+ }
+ else newpos[i] = 0;
+ }
+
+
+ /* Update score */
+ for (i = 0 ; i < si->NumPatterns ; i++)
+ {
+ patt = si->Patt[i];
+
+ for (j = 0 ; j < patt->Tracks ; j++)
+ {
+ note = patt->Notes[j];
+
+ for (k = 0; k < patt->Lines ; k++, note++)
+ if (note->Note)
+ {
+ note->Inst = newpos[note->Inst];
+ if (!note->Inst)
+ note->Note = 0;
+ }
+ }
+ }
+
+ DB (kprintf ("After - LastInstrument = %ld", si->LastInstrument));
+}
+
+
+
+GLOBALCALL void RemUnusedInstruments (struct SongInfo *si)
+{
+ ULONG usecount[MAXINSTRUMENTS] = { 0 };
+ struct Pattern *patt;
+ struct Note *note;
+ UWORD i, j, k;
+
+ for (i = 0; i < si->NumPatterns; i++)
+ {
+ if (!(patt = si->Patt[i])) continue;
+
+ for (j = 0; j < patt->Tracks; j++)
+ {
+ note = patt->Notes[j];
+
+ for (k = 0; k < patt->Lines; k++, note++)
+ usecount[note->Inst]++;
+ }
+ }
+
+ for (i = 1; i <= si->LastInstrument; i++)
+ {
+ if ((usecount[i] == 0) && si->Instr[i])
+ {
+ ShowMessage (MSG_INSTR_UNUSED, i);
+ xmRemInstrument (si, i);
+ }
+ }
+}
+
+
+
+/* DUnpack.c --- Fibonacci Delta decompression by Steve Hayes */
+
+/* Fibonacci delta encoding for sound data */
+static const BYTE codeToDelta[16] = {-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};
+
+
+static BYTE D1Unpack (UBYTE source[], LONG n, BYTE dest[], BYTE x)
+
+/* Unpack Fibonacci-delta encoded data from n byte source
+ * buffer into 2*n byte dest buffer, given initial data
+ * value x. It returns the last data value x so you can
+ * call it several times to incrementally decompress the data.
+ */
+{
+ UBYTE d;
+ LONG i, lim;
+
+ lim = n << 1;
+ for (i = 0; i < lim; ++i)
+ {
+ /* Decode a data nibble, high nibble then low nibble */
+ d = source[i >> 1]; /* get a pair of nibbles */
+ if (i & 1) /* select low or high nibble */
+ d &= 0xf; /* mask to get the low nibble */
+ else
+ d >>= 4; /* shift to get the high nibble */
+ x += codeToDelta[d]; /* add in the decoded delta */
+ dest[i] = x; /* store a 1 byte sample */
+ }
+ return x;
+}
+
+
+static void DUnpack (UBYTE source[], LONG n, BYTE dest[])
+
+/* Unpack Fibonacci-delta encoded data from n byte
+ * source buffer into 2*(n-2) byte dest buffer.
+ * Source buffer has a pad byte, an 8-bit initial
+ * value, followed by n-2 bytes comprising 2*(n-2)
+ * 4-bit encoded samples.
+ */
+{
+ D1Unpack (source+2, n-2, dest, (BYTE)source[1]);
+}
+
+
+
+GLOBALCALL void SampChangeSign8 (UBYTE *samp, ULONG len)
+
+/* Performs a sign conversion on a 8bit sample. The same function can be
+ * used to convert a signed sample into an unsigned one and vice versa.
+ *
+ * TODO: optimize with a LONG oriented loop
+ */
+{
+ while (len)
+ samp[--len] ^= 0x80;
+}
--- /dev/null
+/*
+** InstrumentsWin.c
+**
+** Copyright (C) 1994,95,96 Bernardo Innocenti
+**
+** Instruments editor handling functions.
+*/
+
+#include <exec/nodes.h>
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+#include <libraries/asl.h>
+#include <workbench/workbench.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Gadgets IDs */
+
+enum
+{
+ GD_InstrGroup0,
+ GD_InstrGroup1,
+ GD_InstrGroup2,
+ GD_InstrList,
+ GD_InstrName,
+ GD_InstrGroup3,
+ GD_InstrNew,
+ GD_InstrOpen,
+ GD_InstrDel,
+ GD_InstrUp,
+ GD_InstrDown,
+ GD_InstrGroup4,
+ GD_InstrVolume,
+ GD_InstrFineTune,
+ GD_InstrLen,
+ GD_InstrKind,
+ GD_InstrEdit,
+
+ Instruments_CNT
+};
+
+
+
+/*****************************/
+/* Local function prototypes */
+/*****************************/
+
+static void InstrumentsDropIcon (struct AppMessage *msg);
+static void InstrumentsPostOpen (void);
+static void InstrumentsPostClose (void);
+void UpdateInstrInfo (void);
+void UpdateInstrList (void);
+
+static void InstrNewClicked (void);
+static void InstrOpenClicked (void);
+static void InstrDelClicked (void);
+static void InstrUpClicked (void);
+// TODO static void InstrSwapClicked (void);
+static void InstrDownClicked (void);
+static void InstrNameClicked (struct WinUserData *wud);
+static void InstrListClicked (void);
+static void InstrVolumeClicked (void);
+static void InstrFineTuneClicked (void);
+static void InstrEditClicked (void);
+static void InstrKindClicked (void);
+
+static void InstrumentsMiLoad (void);
+static void InstrumentsMiSave (void);
+static void InstrumentsMiSaveAs (void);
+static void InstrumentsMiRemap (void);
+static void InstrumentsMiSaveIcons (void);
+static void InstrumentsMiSaveCompressed (void);
+static void InstrumentsMiSaveRaw (void);
+
+
+XDEF struct List InstrList;
+static ULONG InstrSecs = 0, InstrMicros = 0;
+
+
+static struct NewMenu InstrumentsNewMenu[] = {
+ NM_TITLE, (STRPTR)MSG_INSTRUMENTS_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_LOAD_MEN, (STRPTR)"L", 0, 0L, (APTR)InstrumentsMiLoad,
+ NM_ITEM, (STRPTR)MSG_SAVE_MEN, (STRPTR)"S", 0, 0L, (APTR)InstrumentsMiSave,
+ NM_ITEM, (STRPTR)MSG_SAVE_AS_MEN, (STRPTR)"A", 0, 0L, (APTR)InstrumentsMiSaveAs,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_REMAP_MEN, (STRPTR)"R", 0, 0L, (APTR)InstrumentsMiRemap,
+ NM_TITLE, (STRPTR)MSG_SETTINGS_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_SAVE_ICONS_MEN, NULL, CHECKIT|MENUTOGGLE, 0L, (APTR)InstrumentsMiSaveIcons,
+ NM_ITEM, (STRPTR)MSG_SAVE_COMPRESSED_MEN, NULL, CHECKIT|MENUTOGGLE, 4L, (APTR)InstrumentsMiSaveCompressed,
+ NM_ITEM, (STRPTR)MSG_SAVE_RAW_MEN, NULL, CHECKIT|MENUTOGGLE, 2L, (APTR)InstrumentsMiSaveRaw,
+ NM_END, NULL, NULL, 0, 0L, NULL };
+
+
+
+static STRPTR InstrKindLabels[] = {
+ (STRPTR)MSG_SAMPLE_GAD,
+ (STRPTR)MSG_SYNTH_GAD,
+ (STRPTR)MSG_HYBRID_GAD,
+ (STRPTR)0
+};
+
+
+
+static ULONG InstrumentsArgs[] =
+{
+ HGROUP_KIND, BBFT_RIDGE,
+ VGROUP_KIND, 0,
+ LISTVIEW_KIND, (ULONG)InstrListClicked, 0, (ULONG)&InstrList, TAG_DONE,
+ STRING_KIND, (ULONG)InstrNameClicked, 0, 64, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)InstrNewClicked, MSG_UNDERSCORE_NEW_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)InstrOpenClicked, MSG_OPEN_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)InstrDelClicked, MSG_DEL_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)InstrUpClicked, MSG_UNDERSCORE_UP_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)InstrDownClicked, MSG_UNDERSCORE_DOWN_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ VGROUP_KIND, BBFT_RIDGE,
+ SLIDER_KIND, (ULONG)InstrVolumeClicked, MSG_VOLUME_GAD, 0, 64, (ULONG)"%lu", 3, TAG_DONE,
+ SLIDER_KIND, (ULONG)InstrFineTuneClicked,MSG_FINETUNE_GAD, -8, +7, (ULONG)"%ld", 3, TAG_DONE,
+ NUMBER_KIND, MSG_LENGHT_GAD, 7, TAG_DONE,
+ CYCLE_KIND, (ULONG)InstrKindClicked, MSG_KIND_GAD, (ULONG)InstrKindLabels, TAG_DONE,
+ BUTTON_KIND, (ULONG)InstrEditClicked, MSG_EDIT_DOTS_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG InstrumentsWinTags[] =
+{
+ XMWIN_NewMenu, (LONG)InstrumentsNewMenu,
+ XMWIN_LayoutArgs, (LONG)InstrumentsArgs,
+ XMWIN_GCount, Instruments_CNT,
+ XMWIN_Title, MSG_INSTRUMENTS_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, BUTTONIDCMP|STRINGIDCMP|LISTVIEWIDCMP|INTEGERIDCMP|NUMBERIDCMP|IDCMP_MENUPICK|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
+ XMWIN_PostOpenFunc, (LONG)InstrumentsPostOpen,
+ XMWIN_PostCloseFunc,(LONG)InstrumentsPostClose,
+ XMWIN_DropIconFunc, (LONG)InstrumentsDropIcon,
+ XMWIN_HelpNode, (LONG)"Instruments",
+ TAG_DONE
+};
+
+
+
+static void InstrumentsPostOpen (void)
+{
+ UpdateInstrSwitches();
+ UpdateInstrList();
+}
+
+static void InstrumentsPostClose (void)
+{
+ while (!IsListEmpty(&InstrList))
+ RemListViewNode (InstrList.lh_Head);
+}
+
+
+
+static void InstrumentsDropIcon (struct AppMessage *msg)
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ struct WBArg *wba = msg->am_ArgList;
+ BPTR olddir;
+ UWORD i, j;
+
+ olddir = CurrentDir (wba->wa_Lock);
+
+ for (i = 0, j = si->CurrentInst;
+ (i < msg->am_NumArgs) && (j < MAXINSTRUMENTS);
+ i++, j++)
+ {
+ CurrentDir (msg->am_ArgList->wa_Lock);
+
+ if (j == 0) j++;
+
+ if (LastErr = LoadInstrument (si, j, wba->wa_Name))
+ break;
+
+ wba++;
+ }
+
+ ReleaseSemaphore (&si->Lock);
+
+ if (si->CurrentInst == 0)
+ SetAttrs (si,
+ SNGA_CurrentInst, 1,
+ TAG_DONE);
+
+ CurrentDir (olddir);
+ }
+
+ UnlockWindows();
+}
+
+
+
+/*************************/
+/* Instruments Functions */
+/*************************/
+
+GLOBALCALL void UpdateInstrList (void)
+{
+ struct SongInfo *si;
+ struct WinUserData *wud = WDescr[WID_INSTRUMENTS].Wud;
+ ULONG i, curr = 0;
+
+ if (wud && wud->Win)
+ {
+ GT_SetGadgetAttrs (wud->Gadgets[GD_InstrList], wud->Win, NULL,
+ GTLV_Labels, ~0,
+ TAG_DONE);
+
+ /* Empty previous list */
+ while (!IsListEmpty (&InstrList))
+ RemListViewNode (InstrList.lh_Head);
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ for (i = 1 ; i <= si->LastInstrument; i++)
+ {
+ AddListViewNode (&InstrList, "%02lx %s", i,
+ si->Instr[i] ?
+ (si->Instr[i]->Name ? si->Instr[i]->Name : STR(MSG_UNNAMED))
+ : STR(MSG_EMPTY));
+ }
+
+ curr = si->CurrentInst - 1;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_InstrList], wud->Win, NULL,
+ GTLV_Labels, &InstrList,
+ GTLV_MakeVisible, curr,
+ TAG_DONE);
+
+ UpdateInstrInfo();
+ }
+}
+
+
+
+GLOBALCALL void UpdateInstrInfo (void)
+{
+ struct SongInfo *si;
+ struct WinUserData *wud;
+
+ if (!(wud = WDescr[WID_INSTRUMENTS].Wud)) return;
+ if (!(wud->Win)) return;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ struct Instrument *instr;
+
+ if (si->CurrentInst && (instr = si->Instr[si->CurrentInst]))
+ SetGadgets (wud,
+ GD_InstrName, instr->Name,
+ GD_InstrVolume, instr->Volume,
+ GD_InstrFineTune, instr->FineTune,
+ GD_InstrLen, instr->Length,
+ GD_InstrList, si->CurrentInst - 1,
+ -1);
+ else
+ SetGadgets (wud,
+ GD_InstrName, NULL,
+ GD_InstrVolume, 0,
+ GD_InstrFineTune, 0,
+ GD_InstrLen, 0,
+ GD_InstrList, si->CurrentInst - 1,
+ -1);
+
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ DB(kprintf("remember to uncomment updatesample!\n"));
+ //UpdateSample();
+ UpdateEditorInst();
+}
+
+
+
+GLOBALCALL void UpdateInstrSwitches (void)
+{
+ struct MenuItem *item;
+ struct WinUserData *wud = WDescr[WID_INSTRUMENTS].Wud;
+
+ if (wud && wud->MenuStrip)
+ {
+ if (wud->Win) ClearMenuStrip (wud->Win);
+
+ item = ItemAddress (wud->MenuStrip, SHIFTMENU(1) | SHIFTITEM(0) );
+
+ /* Save Icons? */
+ if (GuiSwitches.InstrSaveIcons)
+ item->Flags |= CHECKED;
+ else
+ item->Flags &= ~CHECKED;
+
+ if (wud->Win) ResetMenuStrip (wud->Win, wud->MenuStrip);
+ }
+}
+
+
+
+static void InstrumentsLoad (STRPTR name, ULONG num, ULONG count)
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ UWORD instnum;
+
+ if (si->CurrentInst == 0)
+ instnum = 1;
+ else
+ instnum = si->CurrentInst + num;
+
+ if (instnum < MAXINSTRUMENTS)
+ LoadInstrument (si, instnum, name);
+
+ if (si->CurrentInst == 0)
+ SetAttrs (si,
+ SNGA_CurrentInst, 1,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UnlockWindows();
+}
+
+
+
+/***********************/
+/* Instruments Gadgets */
+/***********************/
+
+static void InstrNewClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ xmAddInstrumentA (si, -1, NULL);
+
+ SetAttrs (si,
+ SNGA_CurrentInst, si->LastInstrument,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSongInfo();
+}
+
+
+
+static void InstrOpenClicked (void)
+{
+ StartFileRequest (FREQ_LOADINST, InstrumentsLoad);
+}
+
+
+
+static void InstrDelClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ xmRemInstrument (si, si->CurrentInst);
+
+ SetAttrs (si,
+ SNGA_CurrentInst, si->CurrentInst + 1,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSongInfo();
+}
+
+
+
+static void InstrUpClicked (void)
+{
+ struct Instrument *tmp;
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ /* Swap actual instrument with the previous one */
+ if (si->CurrentInst <= 1) return;
+
+ tmp = si->Instr[si->CurrentInst];
+ si->Instr[si->CurrentInst] = si->Instr[si->CurrentInst-1];
+ si->Instr[si->CurrentInst-1] = tmp;
+
+ SetAttrs (si,
+ SNGA_CurrentInst, si->CurrentInst-1,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+
+ UpdateInstrList();
+ }
+}
+
+
+
+static void InstrDownClicked (void)
+{
+ struct SongInfo *si;
+ struct Instrument *tmp;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ /* Swap actual instrument with the next one */
+ if (si->CurrentInst >= MAXINSTRUMENTS-1) DisplayBeep (Scr);
+ else
+ {
+ /**/ /* Use some OO-legal way to do this!! :) */
+ tmp = si->Instr[si->CurrentInst];
+ si->Instr[si->CurrentInst] = si->Instr[si->CurrentInst+1];
+ si->Instr[si->CurrentInst+1] = tmp;
+ if (si->CurrentInst + 1 >= si->LastInstrument)
+ si->LastInstrument = si->CurrentInst + 1;
+
+ SetAttrs (si,
+ SNGA_CurrentInst, si->CurrentInst + 1,
+ TAG_DONE);
+ }
+
+ ReleaseSemaphore (&si->Lock);
+
+ UpdateInstrList();
+ }
+}
+
+
+
+/*
+static void InstrSwapClicked (void)
+{
+ TODO
+}
+*/
+
+
+static void InstrNameClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ xmSetInstrument (si, si->CurrentInst,
+ INSTRA_Name, GetString (wud->Gadgets[GD_InstrName]),
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+
+ UpdateInstrList();
+ }
+}
+
+
+
+static void InstrListClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ if (si->CurrentInst != IntuiMsg.Code + 1)
+ {
+ si->CurrentInst = IntuiMsg.Code + 1;
+ UpdateInstrInfo();
+ }
+ else
+ {
+ /* Check Double Click */
+ if (DoubleClick (InstrSecs, InstrMicros, IntuiMsg.Seconds, IntuiMsg.Micros))
+ NewWindow (WID_SAMPLE);
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ InstrSecs = IntuiMsg.Seconds;
+ InstrMicros = IntuiMsg.Micros;
+}
+
+
+
+static void InstrVolumeClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ xmSetInstrument (si, si->CurrentInst,
+ INSTRA_Volume, IntuiMsg.Code,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void InstrFineTuneClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ xmSetInstrument (si, si->CurrentInst,
+ INSTRA_FineTune, IntuiMsg.Code,
+ TAG_DONE);
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void InstrKindClicked (void)
+{
+ /**/
+}
+
+
+
+static void InstrEditClicked (void)
+{
+ NewWindow (WID_SAMPLE);
+}
+
+
+
+/**************/
+/* Menu Items */
+/**************/
+
+static void InstrumentsMiLoad (void)
+{
+ StartFileRequest (FREQ_LOADINST, InstrumentsLoad);
+}
+
+
+
+static void InstrumentsMiSave (void)
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ if (si->Instr[si->CurrentInst])
+ LastErr = SaveInstrument (si->Instr[si->CurrentInst],
+ si->Instr[si->CurrentInst]->Name);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UnlockWindows();
+}
+
+
+
+static void InstrumentsMiSaveAs (void)
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ struct Instrument *instr;
+ UBYTE name[PATHNAME_MAX];
+
+ if (instr = si->Instr[si->CurrentInst])
+ {
+ strncpy (name, instr->Name, PATHNAME_MAX-1);
+ name[PATHNAME_MAX-1] = '\0';
+
+ if (FileRequest (FREQ_SAVEINST, name))
+ {
+ xmSetInstrument (si, si->CurrentInst,
+ INSTRA_Name, FilePart (name),
+ TAG_DONE);
+
+ LastErr = SaveInstrument (instr, name);
+ }
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UnlockWindows();
+}
+
+
+
+static void InstrumentsMiRemap (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ RemapInstruments (si);
+ UpdateSongInfo();
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void InstrumentsMiSaveIcons (void)
+{
+ GuiSwitches.InstrSaveIcons ^= 1;
+}
+
+
+
+static void InstrumentsMiSaveCompressed (void)
+{
+ /**/ /* TODO */
+}
+
+
+
+static void InstrumentsMiSaveRaw (void)
+{
+ /**/ /* TODO */
+}
--- /dev/null
+/*
+** Library.c
+**
+** Copyright (C) 1995,96,97 Bernardo Innocenti
+**
+** xmodule.library functions
+*/
+
+#include <exec/libraries.h>
+#include <exec/memory.h>
+#include <dos/dos.h>
+#include <dos/stdio.h>
+#include <libraries/xmodule.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/utility.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+#include <proto/iffparse.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/****** xmodule/--background-- *********************************************
+*
+* INTRODUCTION
+* The xmodule.library is an API that provides access to the XModule
+* song management engine, as well as other general pourpose
+* services. Hooks and external applications can use this library
+* to create, load, save and modify songs.
+*
+* The xmodule.library does not exist as a stand alone shared libray.
+* Instead, it's code is contained inside the main XModule executable
+* (altrough it's logically independant from the rest of the XModule
+* code).
+*
+* XModule adds the xmodule.library to the exec libray list at
+* startup time, unless it finds one already in the list (this might
+* happen when multiple copies of XModule are running at the same
+* time).
+*
+* PUBLIC SONGS
+* The xmodule.library maintains a list of public songs which can be
+* used by all applications which opened the library. Each song in
+* the list is protected from multiple task access by a
+* SignalSemaphore, and whole list is also protected by another
+* semaphore. The locking mechanism is very simple: before you can
+* access a song you must obtain its semaphore, and you must release
+* it as soon as you are done with it. If you are locking a song to
+* just to read some information (i.e.: you don't want to modify
+* anything), you should obtain a shared lock instead of an exclusive
+* one. The list must be locked whenever you want to add or remove
+* a song, or when you scan it in any way.
+*
+* Since public songs could be modified or even deleted by other
+* tasks, do not make your songs public unless your code is smart
+* enough to handle all possible situations.
+*
+*
+* HOOKS
+* Actually, most modular programs call them `modules', but it would
+* have created a lot of confusion with a program like XModule :-).
+*
+* Hooks are programs that add some kind of functionality
+* to XModule. External hook files are standard shared libraries
+* which will open the xmodule.library when they are loaded and
+* call xmAddHookA() to add themselves to a list of hooks maintained
+* by the library.
+*
+* Currently, XModule supports two kinds of hooks: loaders and
+* savers. Loader hooks can also provide a function which
+* identifies a particular module format.
+*
+* An external hook libray may also contain several hooks.
+* Putting a loader and a saver for one particolar format together
+* in one libray is generally a good idea, while making a hook
+* with hundereds of loaders and savers isn't a good move because
+* it makes the whole concept of external hooks quite useless.
+* Grouping different versions or variants of one format together
+* in one external hook is acceptable.
+*
+* SEE ALSO
+* songclass/--background--, exec/ObtainSemaphore()
+*
+****************************************************************************
+*/
+
+
+
+/* Library function prototypes */
+
+static LIBCALL struct SongInfo * XModuleCreateSong (
+ REG(a0, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleDeleteSong (
+ REG(a0, struct SongInfo *si),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL ULONG XModuleAddSong (
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct SongInfo *position),
+ REG(a2, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL ULONG XModuleRemSong (
+ REG(a0, struct SongInfo *si),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL ULONG XModuleActivateSong (
+ REG(a0, struct SongInfo *si),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL struct SongInfo *XModuleLockActiveSong (
+ REG(d0, UWORD mode),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL struct XMHook *XModuleAddHook (
+ REG(a0, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleRemHook (
+ REG(a0, struct XMHook *hook),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL struct XMHook *XModuleIdentifyModule(
+ REG(d0, BPTR fh),
+ REG(a0, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL struct SongInfo * XModuleLoadModule (
+ REG(a0, CONST_STRPTR filename),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL LONG XModuleSaveModule (
+ REG(a0, struct SongInfo *si),
+ REG(a1, CONST_STRPTR filename),
+ REG(a2, struct XMHook *saver),
+ REG(a3, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL UWORD *XModuleSetSongLen(
+ REG(a0, struct SongInfo *si),
+ REG(d0, UWORD length),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleAddPattern (
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleSetPattern (
+ REG(a0, struct SongInfo *si),
+ REG(d0, ULONG pattNum),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleRemPattern (
+ REG(a0, struct SongInfo *si),
+ REG(d0, ULONG pattNum),
+ REG(d1, ULONG replaceWith));
+static LIBCALL void XModuleAddInstrument (
+ REG(a0, struct SongInfo *si),
+ REG(d0, LONG num),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleSetInstrument (
+ REG(a0, struct SongInfo *si),
+ REG(d0, LONG num),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleRemInstrument (
+ REG(a0, struct SongInfo *si),
+ REG(d0, ULONG num),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL LONG XModuleProcessSong (
+ REG(a0, struct SongInfo *si),
+ REG(a1, void *reserved),
+ REG(a2, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleDisplayMessage (
+ REG(d0, ULONG level),
+ REG(a0, CONST_STRPTR msg),
+ REG(a1, LONG *args),
+ REG(a6, struct XModuleBase *XModuleBase));
+static LIBCALL void XModuleDisplayProgress (
+ REG(d0, ULONG actual),
+ REG(d1, ULONG max),
+ REG(a6, struct XModuleBase *XModuleBase));
+
+
+
+/* Local function prototypes */
+
+static LIBCALL void XModuleLibOpen (void);
+static LIBCALL void XModuleLibClose (void);
+static LIBCALL void XModuleLibExpunge (void);
+static LIBCALL void XModuleLibExtFunc (void);
+static LIBCALL LONG XModuleRexxServer (void);
+
+
+
+/* Library vector table */
+
+static APTR XModuleVectors[] =
+{
+ XModuleLibOpen,
+ XModuleLibClose,
+ XModuleLibExpunge,
+ XModuleLibExtFunc,
+ XModuleRexxServer,
+ XModuleRexxServer,
+ XModuleRexxServer,
+ XModuleRexxServer,
+
+ XModuleCreateSong,
+ XModuleDeleteSong,
+ XModuleAddSong,
+ XModuleRemSong,
+ XModuleActivateSong,
+ XModuleLockActiveSong,
+ XModuleAddHook,
+ XModuleRemHook,
+ XModuleIdentifyModule,
+ XModuleLoadModule,
+ XModuleSaveModule,
+ XModuleSetSongLen,
+ XModuleAddPattern,
+ XModuleSetPattern,
+ XModuleRemPattern,
+ XModuleAddInstrument,
+ XModuleSetInstrument,
+ XModuleRemInstrument,
+ XModuleProcessSong,
+ XModuleDisplayMessage,
+ XModuleDisplayProgress,
+
+ (APTR)-1
+};
+
+
+
+GLOBALCALL ULONG MakeXModuleLibrary (void)
+{
+ if (XModuleBase = (struct XModuleBase *)MakeLibrary (XModuleVectors,
+ NULL, NULL, sizeof (struct XModuleBase), NULL))
+ {
+ /* Clear base variables following the library node
+ * (InitSemaphore reqires a clean memory block!)
+ *
+ * GOOD NEWS: Clearing the library base is not required because
+ * MakeLibrary() allocates a MEMF_CLEAR memory block.
+ *
+ * memset (((UBYTE *)XModuleBase) + sizeof (struct Library), 0,
+ * sizeof (struct XModuleBase) - sizeof (struct Library));
+ */
+
+ XModuleBase->xm_Library.lib_Node.ln_Name = (STRPTR)"xmodule.library";
+ XModuleBase->xm_Library.lib_Version = VERSION;
+ XModuleBase->xm_Library.lib_Revision = REVISION;
+ XModuleBase->xm_Library.lib_IdString = (STRPTR)Version + 6;
+
+ XModuleBase->xm_DOSBase = DOSBase;
+ XModuleBase->xm_UtilityBase = UtilityBase;
+ XModuleBase->xm_IFFParseBase = IFFParseBase;
+ XModuleBase->xm_IntuitionBase = IntuitionBase;
+
+ NEWLIST ((struct List *)&XModuleBase->xm_Songs);
+ NEWLIST ((struct List *)&XModuleBase->xm_Loaders);
+ NEWLIST ((struct List *)&XModuleBase->xm_Savers);
+ InitSemaphore (&XModuleBase->xm_BaseLock);
+
+ /* Create global memory pool */
+ if (!(XModuleBase->xm_Pool = CreatePool (MEMF_ANY, 16*1024, 4*1024)))
+ return ERROR_NO_FREE_STORE;
+
+ if (!(XModuleBase->xm_SongClass = InitSongClass()))
+ return ERROR_NO_FREE_STORE;
+
+ return RETURN_OK;
+ }
+
+ return ERROR_NO_FREE_STORE;
+}
+
+
+
+GLOBALCALL void DisposeXModuleLibrary (void)
+{
+ if (XModuleBase)
+ {
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+
+ /* Free all songs in SongList */
+ while (!IsListEmpty ((struct List *)&XModuleBase->xm_Songs))
+ xmDeleteSong ((struct SongInfo *)XModuleBase->xm_Songs.mlh_Head);
+
+ /* Remove all loaders */
+ while (!IsListEmpty ((struct List *)&XModuleBase->xm_Loaders))
+ xmRemHook ((struct XMHook *)XModuleBase->xm_Loaders.mlh_Head);
+
+ /* Remove all savers */
+ while (!IsListEmpty ((struct List *)&XModuleBase->xm_Savers))
+ xmRemHook ((struct XMHook *)XModuleBase->xm_Savers.mlh_Head);
+
+ /* Free the songclass */
+ FreeSongClass (XModuleBase->xm_SongClass);
+
+ /* Dispose the global memory pool */
+ if (XModuleBase->xm_Pool) DeletePool (XModuleBase->xm_Pool);
+
+ FreeMem (((UBYTE *)XModuleBase) - XModuleBase->xm_Library.lib_NegSize,
+ XModuleBase->xm_Library.lib_NegSize + XModuleBase->xm_Library.lib_PosSize);
+ XModuleBase = NULL;
+ }
+}
+
+
+
+static LIBCALL void XModuleLibOpen (void)
+{
+}
+
+
+
+static LIBCALL void XModuleLibClose (void)
+{
+}
+
+
+
+static LIBCALL void XModuleLibExpunge (void)
+{
+}
+
+
+
+static LIBCALL void XModuleLibExtFunc (void)
+{
+}
+
+
+
+static LIBCALL LONG XModuleRexxServer (void)
+{
+ return 0;
+}
+
+
+
+/****** xmodule/xmAddHook **************************************************
+*
+* NAME
+* xmAddHookA -- Creates a new XModule Hook
+* xmAddHook -- Varargs stub for xmAddHookA
+*
+* SYNOPSIS
+* hook = xmAddHookA(tagList)
+* D0 A0
+*
+* struct XMHook *xmAddHookA(struct TagItem *);
+*
+*
+* hook = xmAddHook(Tag1,...)
+*
+* struct XMHook *xmAddHook(ULONG,...);
+*
+* FUNCTION
+* Creates a new XMHook structure and fills it in with data supplied
+* with the TagList. Adds the newly created Hook to the appropriate
+* list.
+*
+* INPUTS
+* tagList - pointer to a tag list specifying how to initialize the
+* XMHook structure.
+*
+* TAGS
+* XMHOOK_Type - (ULONG) Defines the pourpose of this hook. Possible
+* values are currently NT_XMLOADER and NT_XMSAVER. (This
+* tag is REQUIRED).
+*
+* XMHOOK_Name - (STRPTR) ti_Data contains a short name for the
+* hook (e.g: "SoundTracker"). This name will appear in the
+* Savers list if this hook is a saver and will be passed as an
+* argument for some ARexx commands, so please use a single
+* word name. (This tag is REQUIRED).
+*
+* XMHOOK_Priority - (BYTE) Priority to give to this hook. Hooks
+* with higher priorities will be used before lower priority
+* ones and will come first in lists shown to the user. Valid
+* range is -128..+127, but please restrict to a -20..+20
+* interval for normal cases. (Defaults to 0).
+*
+* XMHOOK_Descr - (STRPTR) Verbose description of the hook
+* (without newlines). (Defaults to NULL).
+*
+* XMHOOK_Author - (STRPTR) Author's name. Please, just put
+* your full name here; no greetings, copyright notices,
+* etc. (Defaults to NULL).
+*
+* XMHOOK_ID - (ULONG) This is a unique, IFF-style identifier for
+* the format. If the format is an IFF format, it must be the
+* same of the FORM ID. (Defaults to 0, this tag is required
+* for IFF loaders and savers).
+*
+* XMHOOK_Flags - (ULONG) Sets miscellaneous flags for this hook.
+* See xmodule.h for possible flags.
+*
+* XMHOOK_LibraryBase - (struct Library *) Pointer to the library
+* base for external hooks. This pointer will be used to open
+* the library when the hook is created and to close it when
+* the hook is deleted. If you do not pass this tag, you must
+* find some other way to keep your code in memory until one
+* or more of your hooks are active. XModule will close your
+* library just after calling the SetupXMHook() function.
+* (Defaults to NULL).
+*
+* XMHOOK_UserData - (APTR) ti_Data will be stored in the
+* xmh_UserData field of the XMHook structure. This field can
+* come andy to store private data. (Defaults to NULL).
+*
+* XMHOOK_RemoveHookFunc - (APTR) Pointer to a function which will be
+* called upon removing the hook. This function can be used to
+* free any private resources allocated by the hook when it was
+* created. The template for the function is:
+*
+* void RemoveHookFunc (struct XMHook *hook);
+* A0
+*
+* XMHOOK_LoadModFunc - (APTR) Pointer to the hook function which
+* loads a module. The template for the function is:
+*
+* LONG LoadModFunc (BPTR fileHandle, struct SongInfo *song,
+* D0 A0
+* struct XMHook *loader);
+* A1
+*
+* `fileHandle' is an open file to load the module from. The
+* caller will take care to close it for you. The loader should
+* return RETURN_OK for success, or any other AmigaDOS error
+* code to mean failure. In particular, RETURN_WARN indicates
+* that something went wrong, but the song is ok. The special
+* error code ERROR_IOERR can be used when some dos.library
+* call (e.g.: Read()) failed. In the latter case, the
+* AmigaDOS IoErr value should be set to explain the specific
+* cause of the problem.
+* (This tag is required by all NT_XMLOADER type hooks).
+*
+* XMHOOK_SaveModFunc - (APTR) Pointer to the hook function which
+* saves a module. The template for the function is:
+*
+* LONG SaveModFunc (BPTR fileHandle, struct SongInfo *song,
+* D0 A0
+* struct XMHook *saver);
+* A1
+*
+* fileHandle is an open file to save the module to. The caller
+* will take care to close it for you. The saver should return
+* RETURN_OK for success, or any other AmigaDOS error code to
+* mean failure. In particular, RETURN_WARN indicates that
+* something went wrong, but the song is ok. The special
+* error code ERROR_IOERR can be used when some dos.library
+* call (e.g.: Read()) failed. In the latter case, the
+* AmigaDOS IoErr value should be set to explain the specific
+* cause of the problem.
+* (This tag is required by all NT_XMSAVER type hooks).
+*
+*
+* XMHOOK_IdentifyModFunc - (APTR) Pointer to the hook function
+* which identifies a module format. The template for the
+* function is:
+*
+* struct XMHook *IdentifyModFunc (BPTR fileHandle,
+* D0
+* struct XMHook *hook,struct TagItem *tagList);
+* A0 A1
+*
+* fileHandle is an open file to try the identification on. The
+* caller will take care to close it. NOTE: Do not make assumptions
+* on the initial file position (e.g: seek yourself to position 0
+* if you need to). This funtion should return a pointer to a valid
+* loader (usually the one passed in) if the format is recognized,
+* NULL otherwhise. (This tag is required by all NT_XMLOADER type
+* hooks).
+*
+* RESULT
+* hook - Pointer to the newly allocated XMHook structure, or
+* NULL for failure.
+*
+* SEE ALSO
+* xmRemHook(), xmIdentifyModule(), xmLoadSong(), xmSaveSong()
+*
+****************************************************************************
+*/
+static LIBCALL struct XMHook *XModuleAddHook (
+ REG(a0, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ struct XMHook *hook;
+
+ if (hook = AllocPooled (XModuleBase->xm_Pool, sizeof (struct XMHook)))
+ {
+ hook->xmh_Link.ln_Name = (UBYTE *) GetTagData (XMHOOK_Name, NULL, tags);
+ hook->xmh_Link.ln_Type = (UBYTE) GetTagData (XMHOOK_Type, 0, tags);
+ hook->xmh_Link.ln_Pri = (BYTE) GetTagData (XMHOOK_Priority, 0, tags);
+ hook->xmh_Descr = (STRPTR) GetTagData (XMHOOK_Descr, 0, tags);
+ hook->xmh_Author = (STRPTR) GetTagData (XMHOOK_Author, 0, tags);
+ hook->xmh_ID = (ULONG) GetTagData (XMHOOK_ID, 0, tags);
+ hook->xmh_Flags = (ULONG) GetTagData (XMHOOK_Flags, 0, tags);
+ hook->xmh_UserData = (APTR) GetTagData (XMHOOK_UserData, NULL, tags);
+ hook->xmh_LibraryBase = (APTR) GetTagData (XMHOOK_LibraryBase, NULL, tags);
+ hook->xmh_RemoveHook = (void (* ASMCALL)()) GetTagData (XMHOOK_RemoveFunc, NULL, tags);
+ hook->xmh_LoadMod = (LONG (* ASMCALL)()) GetTagData (XMHOOK_LoadModFunc, NULL, tags);
+ hook->xmh_SaveMod = (LONG (* ASMCALL)()) GetTagData (XMHOOK_SaveModFunc, NULL, tags);
+ hook->xmh_IdentifyMod = (struct XMHook * (* ASMCALL)()) GetTagData (XMHOOK_IdentifyModFunc, NULL, tags);
+ hook->xmh_MaxTracks = GetTagData (XMHOOK_MaxTracks, MAXTRACKS, tags);
+ hook->xmh_MaxPatterns = GetTagData (XMHOOK_MaxPatterns, MAXPATTERNS, tags);
+ hook->xmh_MaxInstruments= GetTagData (XMHOOK_MaxInstruments, MAXINSTRUMENTS, tags);
+ hook->xmh_MaxLength = GetTagData (XMHOOK_MaxLength, MAXPOSITIONS, tags);
+ hook->xmh_MaxSampleLen = GetTagData (XMHOOK_MaxSampleLen, ((2<<31)-1), tags);
+ hook->xmh_MaxPattLen = GetTagData (XMHOOK_MaxPattLen, MAXPATTLINES, tags);
+ hook->xmh_Version = XMHOOK_VERSION;
+
+
+ if (hook->xmh_LibraryBase)
+ if (!(OpenLibrary (hook->xmh_LibraryBase->lib_Node.ln_Name, 0)))
+ return NULL;
+
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+ Enqueue ((struct List *)((hook->xmh_Link.ln_Type == NT_XMSAVER) ?
+ &XModuleBase->xm_Savers : &XModuleBase->xm_Loaders),
+ (struct Node *)hook);
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ }
+
+ return hook;
+}
+
+
+
+/****** xmodule/xmRemHook **************************************************
+*
+* NAME
+* xmRemHook -- Removes an XModule Hook
+*
+* SYNOPSIS
+* xmRemHookA(xmHook)
+* A0
+*
+* void xmRemHook(struct XMHook *);
+*
+* FUNCTION
+* Removes an XModule Hook, calls its RemoveHookFunc() and then
+* frees its memory.
+*
+* INPUTS
+* xmHook - pointer to the hook to be removed.
+*
+* SEE ALSO
+* xmAddHook()
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleRemHook (
+ REG(a0, struct XMHook *hook),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+ REMOVE ((struct Node *)hook);
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ if (hook->xmh_RemoveHook)
+ hook->xmh_RemoveHook (hook);
+
+ if (hook->xmh_LibraryBase) CloseLibrary (hook->xmh_LibraryBase);
+
+ FreePooled (XModuleBase->xm_Pool, hook, sizeof (struct XMHook));
+}
+
+
+
+
+/****** xmodule/xmCreateSong ***********************************************
+*
+* NAME
+* xmCreateSongA -- Create and initialize a new SongInfo structure
+* xmCreateSong -- Varargs stub for xmCreateSongA
+*
+* SYNOPSIS
+* songInfo = xmCreateSongA(tagList);
+* D0 A0
+*
+* struct SongInfo *xmCreateSongA(struct TagItem *);
+*
+*
+* songInfo = xmCreateSong(Tag1,...);
+*
+* struct SongInfo *xmCreateSong(ULONG,...);
+*
+* FUNCTION
+* Allocates and initializes a new SongInfo structure. The song
+* can then be added to the song list via xmAddSongA(), in which
+* case, it is no longer required to free it with xmDeleteSong().
+*
+* INPUTS
+* tagList - pointer to an optional tag list specifying how to
+* initialize the SongInfo structure.
+*
+* TAGS
+* SNGA_DefaultTracks - Sets the default number of pattern tracks for
+* the song. Defaults to 4.
+*
+* SNGA_DefaultPattLen - Sets the default number of pattern lines for
+* the song. Defaults to 64.
+*
+* SNGA_ReadyToUse - Adds one pattern and one position to the song.
+* Defaults to FALSE.
+*
+* XMSNG_AddToList - (struct SongInfo *) Add the song to the song list.
+* When a song is being added to the list, a shared lock is
+* obtained on it to prevent other tasks from modifying (or even
+* remove) the song before you can do your job on it. IT IS YOUR
+* DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
+* Passing NULL adds the song on the head of the list, while -1
+* (~0) will add it to the end of the SongList. Passing in a
+* pointer to another song which is already in the list, will
+* add the new song _after_ the latter.
+* (Default is not adding the song).
+*
+* XMSNG_Active - (BOOL) Makes this song the active one. This tag is
+* considered only if the XMSNG_AddToList tag is also passed.
+*
+* RESULT
+* songInfo - pointer to the newly allocated SongInfo structure, or
+* NULL for failure.
+*
+* SEE ALSO
+* xmDeleteSong(), xmAddSongA()
+*
+****************************************************************************
+*/
+static LIBCALL struct SongInfo * XModuleCreateSong (
+ REG(a0, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ struct SongInfo *si;
+ struct TagItem *tag;
+
+ if (si = NewObjectA (XModuleBase->xm_SongClass, NULL, tags))
+ {
+ if (tag = FindTagItem (XMSNG_AddToList, tags))
+ {
+ ObtainSemaphore (&si->Lock);
+ xmAddSongA (si, (struct SongInfo *)tag->ti_Data, tags);
+ }
+ }
+
+ return si;
+}
+
+
+
+/****** xmodule/xmDeleteSong ***********************************************
+*
+* NAME
+* xmDeleteSong -- Deletes a song
+*
+* SYNOPSIS
+* xmDeleteSong(songInfo)
+* A0
+*
+* void xmDeleteSong(struct SongInfo *);
+*
+* FUNCTION
+* Deletes a song and all the items attached to it (patterns,
+* instruments, etc.). This call will also remove the song from
+* the song list if it had been added to it.
+*
+* INPUTS
+* songInfo - pointer to the SongInfo structure to be deleted.
+* Passing a NULL pointer is harmless.
+*
+* NOTE
+* In order to delete a song which has been added to the public
+* song list, you must first obtain an exclusive lock on it to
+* prevent other tasks from walking on it while you are freeing
+* it's data structures. The semaphore does NOT need to be
+* relinquished, because the SongInfo structure won't exist any
+* more when this call returns.
+*
+* SEE ALSO
+* xmCreateSong(), xmRemSong()
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleDeleteSong (
+ REG(a0, struct SongInfo *si),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ xmRemSong (si);
+ DisposeObject (si);
+}
+
+
+
+/****** xmodule/xmAddSongA *************************************************
+*
+* NAME
+* xmAddSongA -- Add a song to the song list
+* xmAddSong -- Varargs stub for xmAddSongA
+*
+* SYNOPSIS
+* success = xmAddSongA(songInfo,position,tagList);
+* D0 A0 A1 A2
+*
+* ULONG xmAddSongA(struct SongInfo *,struct SongInfo *,
+* struct TagItem *);
+*
+*
+* success = xmAddSong(songInfo,position,tag1,...);
+*
+* ULONG xmAddSong(struct SongInfo *,struct SongInfo *,
+* LONG,...);
+*
+* FUNCTION
+* Adds a song to the public song list. Trying to add a song
+* twice is harmless.
+*
+* INPUTS
+* songInfo - song to be added. Passing a NULL pointer is safe.
+* position - Position to add the song in. Passing NULL adds the
+* song in the head of the list, while ~0 adds it in the tail.
+* passing a pointer to a SongInfo already in the list inserts
+* the first song _after_ the other one.
+* tagList - pointer to a TagList for more arguments.
+*
+* TAGS
+* XMSNG_Active - (BOOL) Makes this song the active one. The
+* default is to just add the song without activating it.
+*
+* RESULT
+* success - Will be 0 for failure, in which case the song will
+* not be added to the songs list. Currently, xmAddSongA()
+* will never fail.
+*
+* NOTE
+* The song list is protected from multiple tasks access by a
+* SignalSemaphore in XModuleBase. This call will wait if the
+* song list has been locked by another task. Beware of deadlock
+* conditions!
+*
+* SEE ALSO
+* xmRemSong()
+*
+****************************************************************************
+*/
+static LIBCALL ULONG XModuleAddSong (
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct SongInfo *position),
+ REG(a2, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ if (!si) return FALSE;
+
+ if (!si->Link.ln_Type) /* Is it already in the list? */
+ {
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+ DetatchSongInfoList();
+
+ if ((ULONG)position == ~0)
+ ADDTAIL ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si);
+ else
+ Insert ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si, (struct Node *)position);
+
+ si->Link.ln_Type = NT_XMSONG; /* Mark this song */
+
+ if (GetTagData (XMSNG_Active, FALSE, tags))
+ xmActivateSong (si);
+
+ UpdateSongInfoList();
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ }
+
+ return TRUE;
+}
+
+
+
+/****** xmodule/xmRemSong **************************************************
+*
+* NAME
+* xmRemSong -- Remove a song from the song list
+*
+* SYNOPSIS
+* success = xmRemSong(songInfo);
+* D0 A0
+*
+* ULONG xmRemSong(struct SongInfo *);
+*
+* FUNCTION
+* Removes a song from the public song list. It is safe to call this
+* function even if the song has not been added to the song list. If
+* the passed SongInfo is the active one, another song will be
+* selected automatically.
+*
+* INPUTS
+* songInfo - song to be removed. If NULL, this function will take
+* no action.
+*
+* RESULT
+* success - Will be 0 for failure, in which case the song will
+* not be removed from the song list. Currently,
+* xmRemSong() will never fail.
+*
+* NOTE
+* In order to remove a song, you must first obtain an exclusive
+* lock on it.
+*
+* The song list is also protected from multiple task access by
+* a SignalSemaphore. This call will wait if the song list has
+* been locked by another task. Beware of deadlock conditions!
+*
+* SEE ALSO
+* xmAddSongA()
+*
+****************************************************************************
+*/
+static LIBCALL ULONG XModuleRemSong (
+ REG(a0, struct SongInfo *si),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ if (!si) return FALSE;
+
+ if (!si->Link.ln_Type) return TRUE; /* Not in the list */
+
+
+ /* Brutal but effective way to obtain two locks at the same time,
+ * avoiding the risk of deadlock conditions. We can't just use
+ * ObtainSemaphoreList() here because the two semaphores can
+ * not be linked into a list.
+ */
+/* while (1)
+ {
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+
+ if (AttemptSemaphore (&si->Lock))
+ break;
+
+ ReleaseSemaphore (&XModuleBase->xm_SongsLock);
+ ObtainSemaphore (&si->Lock);
+
+ if (AttemptSemaphore (&XModuleBase->xm_SongsLock))
+ break;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+*/
+
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+ DetatchSongInfoList();
+
+ REMOVE ((struct Node *)si);
+ si->Link.ln_Type = 0; /* Mark song outside list */
+
+ if (XModuleBase->xm_CurrentSong == si)
+ {
+ if (IsListEmpty ((struct List *)&XModuleBase->xm_Songs))
+ XModuleBase->xm_CurrentSong = NULL;
+ else
+ XModuleBase->xm_CurrentSong = (struct SongInfo *)
+ XModuleBase->xm_Songs.mlh_TailPred;
+
+ UpdateSongInfoList();
+ UpdateSongInfo();
+ }
+ else UpdateSongInfoList();
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ return TRUE;
+}
+
+
+
+/****** xmodule/xmActivateSong *********************************************
+*
+* NAME
+* xmActivateSong -- Makes a song the active one
+*
+* SYNOPSIS
+* success = xmActivateSong(songInfo);
+* D0 A0
+*
+* ULONG xmActivateSong(struct SongInfo *);
+*
+* FUNCTION
+* Makes the passed song the currently active one. It's pointer
+* will be stored in the public song list and most actions
+* will happen on this song by default.
+*
+* INPUTS
+* songInfo - song to be activated. If NULL, this function will
+* take no action.
+*
+* RESULT
+* success - Will be 0 for failure, in which case the song will
+* not be removed from the song list. Currently,
+* xmActivateSong() will never fail.
+*
+* NOTE
+* In order to activate a song, you must own a shared lock
+* on it. Please do not hog this lock for a long time when
+* xmActivateSong() returns, because most internal routines
+* try to lock the current song before taking any action.
+*
+* SEE ALSO
+*
+****************************************************************************
+*/
+static LIBCALL ULONG XModuleActivateSong (
+ REG(a0, struct SongInfo *si),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ if (!si) return FALSE;
+ if (!si->Link.ln_Type) return FALSE; /* Not in the list */
+
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+
+ if (XModuleBase->xm_CurrentSong != si)
+ {
+ XModuleBase->xm_CurrentSong = si;
+ UpdateSongInfo(); /**/
+ }
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ return TRUE;
+}
+
+
+
+/****** xmodule/xmLockActiveSong *******************************************
+*
+* NAME
+* xmLockActiveSong -- Obtains an lock on the active song
+*
+* SYNOPSIS
+* song = xmLockActiveSong(mode);
+* D0 D0:16
+*
+* struct SongInfo *xmActivateSong(UWORD);
+*
+* FUNCTION
+* Obtains an exclusive or shared lock on the active song,
+* waiting if needed. This call is a shortcut to:
+*
+* ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+* ObtainSemaphore[Shared] (&XModuleBase->xm_CurrentSong.Lock);
+* ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+*
+* To unlock a song obtained in this way, you just need to
+* ReleaseSemaphore() it.
+*
+* You MUST always lock a song before you even think to
+* read from -or write to- its data!
+*
+* INPUTS
+* mode - one of SM_SHARED or SM_EXCLUSIVE.
+*
+* RESULT
+* song - Pointer to the song which *was* active at the time
+* you called xmLockActiveSong(). The song will be
+* locked for you. The result will be NULL if no song
+* is active.
+*
+* NOTE
+* Please be careful if you call this function while holding
+* locks on other songs. Doing so, you run the risk of causing
+* deadlock condition.
+* This function does only lock the song; it is NOT guaranteed
+* that it will remain the active one.
+*
+* SEE ALSO
+*
+****************************************************************************
+*/
+static LIBCALL struct SongInfo *XModuleLockActiveSong (
+ REG(d0, UWORD mode),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ struct SongInfo *si;
+
+
+#ifdef OS30_ONLY
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#else
+ /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
+
+ /* Try to get the shared semaphore */
+ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
+ /* Check if we can get the exclusive version */
+ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
+ /* Oh well, wait for the shared lock */
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#endif /* OS30_ONLY */
+
+ if (si = XModuleBase->xm_CurrentSong)
+ {
+ if (mode == SM_SHARED)
+ {
+#ifdef OS30_ONLY
+ ObtainSemaphoreShared (&si->Lock);
+#else
+ /* Try to get the shared semaphore */
+ if (!AttemptSemaphoreShared (&si->Lock))
+ /* Check if we can get the exclusive version */
+ if (!AttemptSemaphore (&si->Lock))
+ /* Oh well, wait for the shared lock */
+ ObtainSemaphoreShared (&si->Lock);
+#endif /* OS30_ONLY */
+ }
+ else ObtainSemaphore (&si->Lock);
+ }
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ return si;
+}
+
+
+
+/****** xmodule/xmLoadModuleA **********************************************
+*
+* NAME
+* xmLoadModuleA -- Loads a module and converts it in XModule format
+* xmLoadModule -- Varargs stub for xmLoadModuleA
+*
+* SYNOPSIS
+* songInfo = xmLoadModuleA(fileName,tagList)
+* D0 A0 A1
+*
+* struct SongInfo *xmLoadModuleA(CONST_STRPTR,struct TagItem *);
+*
+*
+* songInfo = xmLoadModule(fileName,tag,...)
+*
+* struct SongInfo *xmLoadModule(CONST_STRPTR,LONG,...);
+*
+* FUNCTION
+* Loads fileName using the correct module loader.
+*
+* INPUTS
+* fileName - File to read. Can be NULL if the XMSNG_FileHandle
+* tag is passed.
+* tagList - Additional parameters (see below). Can be NULL.
+*
+* TAGS
+* XMSNG_OldSong - ti_Data is the pointer to a SongInfo which will be
+* freed as soon as the module format has been determined. This
+* is useful when the user wants to replace a song with another
+* one. Passing NULL uses the currently active song.
+*
+* XMSNG_AddToList - (struct SongInfo *) Add the song to the song list.
+* When a song is being added to the list, a shared lock is
+* obtained on it to prevent other tasks from modifying (or even
+* remove) the song before you can do your job on it. IT IS YOUR
+* DUTY TO UNLOCK THE SONG as soon as you are done modifying it.
+* Passing NULL adds the song on the head of the list, while -1
+* (~0) will add it to the end of the SongList. Passing in a
+* pointer to another song which is already in the list, will
+* add the new song _after_ the latter.
+* (Default is not adding the song).
+*
+* XMSNG_Loader - (struct XMHook *) Disables automatic format
+* checking and forces loading the module with the given
+* loader. (Defaults to NULL).
+*
+* XMSNG_FileHandle - (BPTR) pointer to an open AmigaDOS
+* FileHandle to read the module from. The file must
+* already be positioned over the beginning of the module data.
+* NOTE: Even if this tag is passed, the fileName parameter is
+* still used to fill in the SongName field in case it is
+* missing inside the module AND the filesystem does not
+* support the ACTION_EXAMINE_FH packet.
+* NOTE: Some loaders may require a fully seekable file, so be
+* careful when using pipes.
+* NOTE: automatic data decompression is not possible when
+* XMSNG_FileHandle is passed.
+* (Defaults to NULL).
+*
+* XMSNG_IFFHandle - (BPTR) pointer to an already initialized
+* IFFHandle to read the module from. The IFF must
+* already be positioned over the beginning of the module FORM.
+* Even if this tag is passed, the fileName parameter is still
+* used to fill in the SongName field in case it is missing
+* inside the module.
+* NOTE: The iffparse.library can deal with non-seekable files,
+* but some loaders may require a fully seekable file, so be
+* careful when using pipes.
+* NOTE: automatic file decompression is not possible when
+* XMSNG_IFFHandle is passed.
+* (Defaults to NULL).
+*
+* XMSNG_Active - (BOOL) Makes this song the active one. This tag is
+* considered only if the XMSNG_AddToList tag is also passed.
+*
+* RESULT
+* songInfo - Pointer to the newly allocated SongInfo structure, or
+* NULL for failure. This function will not fail if the module is
+* partially corrupted. Anyway, the returned SongInfo will always
+* be valid. You can check IoErr() to know if there were problems.
+*
+* EXAMPLE
+* \* Load a song and add it to the SongList *\
+* si = xmLoadSongTags (file, NULL,
+* XMSNG_AddToList, (struct SongInfo *)~0,
+* TAG_DONE);
+*
+* \* Check for errors even if si is not NULL *\
+* error = IoErr();
+*
+* \* Release Semaphore got by xmLoadSong() *\
+* if (si) ReleaseSemaphore (&si->Lock);
+*
+* SEE ALSO
+* xmAddSongA(), xmIdentifyModule()
+*
+****************************************************************************
+*/
+static LIBCALL struct SongInfo * XModuleLoadModule (
+ REG(a0, CONST_STRPTR filename),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ struct XMHook *loader;
+ struct SongInfo *si = NULL;
+ UBYTE fullpath[PATHNAME_MAX];
+ ULONG err;
+ BPTR fh, compressed = 0;
+ UWORD type;
+
+
+ loader = (struct XMHook *)GetTagData (XMSNG_Loader, NULL, tags);
+
+
+ /* Get source file or open it */
+
+ if (fh = (BPTR) GetTagData (XMSNG_FileHandle, NULL, tags))
+ {
+ /* Get the full pathname */
+
+ if (!NameFromFH (fh, fullpath, PATHNAME_MAX))
+ fullpath[0] = '\0';
+ }
+ else
+ {
+ if (!(fh = Open (filename, MODE_OLDFILE)))
+ {
+ UBYTE buf[FAULT_MAX];
+
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
+ (APTR)MSG_ERR_LOAD, filename, buf);
+ return NULL;
+ }
+
+ /* Optimize file buffering for faster I/O.
+ *
+ * NOTE: SetVBuf() was introduced in V39, but V37 has a
+ * no-op vector for it, so it is safe to call SetVBuf()
+ * without checking the OS version.
+ * NOTE: Due to a bug in dos.library V40, SetVBuf() works
+ * only when called right after Open() (before doing any I/O).
+ */
+ SetVBuf (fh, NULL, BUF_FULL, 16*1024);
+
+
+ /* Get the full pathname */
+
+ if (!NameFromFH (fh, fullpath, PATHNAME_MAX))
+ fullpath[0] = '\0';
+
+
+ /* Check wether the file is compressed */
+
+ if (type = CruncherType (fh))
+ {
+ Close (fh);
+
+ if (compressed = DecompressFile (filename, type))
+ {
+ if (!(fh = OpenFromLock (compressed)))
+ {
+ err = IoErr();
+ UnLock (compressed);
+ DecompressFileDone();
+ SetIoErr (LastErr = err);
+ return NULL;
+ }
+ }
+ else return NULL;
+ }
+ }
+
+
+
+ /* Lock loaders list */
+
+#ifdef OS30_ONLY
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#else
+ /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
+
+ /* Try to get the shared semaphore */
+ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
+ /* Check if we can get the exclusive version */
+ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
+ /* Oh well, wait for the shared lock */
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#endif /* OS30_ONLY */
+
+
+ /* Find out what the heck this file format is */
+
+ if (!loader)
+ loader = xmIdentifyModule (fh, tags);
+
+ if (loader)
+ {
+ if (loader->xmh_LoadMod)
+ {
+ Seek (fh, 0, OFFSET_BEGINNING); /* Reset file */
+
+
+ /* Free old song */
+ {
+ struct TagItem *tag;
+
+ if (tag = FindTagItem (XMSNG_OldSong, tags))
+ xmDeleteSong (tag->ti_Data ?
+ (struct SongInfo *)tag->ti_Data : XModuleBase->xm_CurrentSong);
+ }
+
+
+ /* Create a new song and set its title and path.
+ * File name will be replaced by the real song name if the
+ * load format supports embedded song names (e.g.: SoundTracker).
+ */
+ if (si = xmCreateSong (
+ SNGA_Path, fullpath,
+ SNGA_Title, FilePart (filename),
+ TAG_DONE))
+ {
+ OpenProgressWindow();
+
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
+ (APTR)MSG_READING_TYPE_MODULE, loader->xmh_Link.ln_Name);
+
+
+ /* Call loader hook */
+ err = loader->xmh_LoadMod (fh, si, loader, tags);
+
+ if (err == ERROR_IOERR)
+ {
+ if (err = IoErr())
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT | err,
+ (APTR)MSG_ERROR_READING, FilePart (filename));
+ else
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_ERROR,
+ (APTR)MSG_UNESPECTED_EOF);
+ }
+
+ FixSong (si);
+
+ CloseProgressWindow();
+ }
+ else err = ERROR_NO_FREE_STORE;
+ }
+ else err = ERROR_ACTION_NOT_KNOWN;
+ }
+ else err = ERROR_OBJECT_WRONG_TYPE;
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ Close (fh);
+
+ if (compressed) DecompressFileDone ();
+
+ if (!err)
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG,
+ (APTR)MSG_MODULE_LOADED_OK, NULL);
+ else
+ SetIoErr (LastErr = err);
+
+ if (si)
+ {
+ struct TagItem *tag;
+
+ if (tag = FindTagItem (XMSNG_AddToList, tags))
+ {
+ ObtainSemaphore (&si->Lock);
+ xmAddSongA (si, (struct SongInfo *)tag->ti_Data, NULL);
+ if (GetTagData (XMSNG_Active, FALSE, tags))
+ xmActivateSong (si);
+ }
+ }
+
+ return si;
+}
+
+
+
+/****** xmodule/xmSaveModuleA **********************************************
+*
+* NAME
+* xmSaveModuleA -- Saves a module to the specified file format
+* xmSaveModule -- Varargs stub for xmSaveModuleA
+*
+* SYNOPSIS
+* error = xmSaveModuleA(songInfo, fileName, saver, tagList)
+* D0 A0 A1 A2 A3
+*
+* LONG xmSaveModuleA(struct SongInfo *,CONST_STRPTR,struct XMHook *,
+* struct TagItem *);
+*
+*
+* error = xmSaveModule(songInfo, fileName, saver, tag1,...)
+*
+* LONG xmSaveModule(struct SongInfo *,CONST_STRPTR,struct XMHook *,
+* LONG,...);
+*
+* FUNCTION
+* Saves songInfo to fileName using the specified saver.
+*
+* INPUTS
+* songInfo - Song to save.
+* fileName - AmigaDOS filename to write the song to.
+* saver - Pointer to the saver to use. Pass NULL to use
+* the default saver.
+*
+* TAGS
+* No tags are defined for this call.
+*
+* RESULT
+* error - RETURN_OK for success, or any other AmigaDOS error code
+* to mean failure. In particular, RETURN_WARN indicates that
+* something went wrong, but the song is still ok. The special
+* error code ERROR_IOERR means that some dos.library
+* call (e.g.: Read()) failed. In the latter case, the
+* AmigaDOS IoErr() value will be set to explain the specific
+* cause of the problem.
+*
+* SEE ALSO
+*
+****************************************************************************
+*/
+static LIBCALL LONG XModuleSaveModule (
+ REG(a0, struct SongInfo *si),
+ REG(a1, CONST_STRPTR filename),
+ REG(a2, struct XMHook *saver),
+ REG(a3, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ LONG err;
+ BPTR fh;
+ BOOL releasebase = FALSE;
+
+
+ /* Use the default saver if not specified */
+ if (!saver)
+ {
+#ifdef OS30_ONLY
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#else
+ /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
+
+ /* Try to get the shared semaphore */
+ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
+ /* Check if we can get the exclusive version */
+ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
+ /* Oh well, wait for the shared lock */
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#endif /* OS30_ONLY */
+
+ if (!(saver = XModuleBase->xm_DefaultSaver))
+ {
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ return ERROR_ACTION_NOT_KNOWN;
+ }
+
+ releasebase = TRUE;
+ }
+
+
+ if (saver->xmh_SaveMod)
+ {
+ ULONG i;
+
+ /* Make some checks before actually saving */
+
+
+ if (si->LastInstrument > saver->xmh_MaxInstruments)
+ {
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_TOO_MANY_INSTRS, si->LastInstrument, saver->xmh_MaxInstruments);
+
+ if (ShowRequest (MSG_TRY_REMAPPING_INSTRUMENTS, MSG_PROCEED_OR_CANCEL))
+ xmProcessSong (si, NULL,
+ XMSNG_RemapInstruments, TRUE,
+ TAG_DONE);
+ }
+
+ for (i = 1; i <= si->LastInstrument; i++)
+ if (si->Instr[i] && (si->Instr[i]->Length > saver->xmh_MaxSampleLen))
+ {
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_INSTR_TOO_LONG, i, saver->xmh_MaxSampleLen);
+ }
+
+ for (i = 0; i < si->NumPatterns; i++)
+ {
+ if (si->Patt[i])
+ if ((si->Patt[i]->Lines > saver->xmh_MaxPattLen) ||
+ ( (saver->xmh_Flags & XMHF_FIXED_PATT_LEN) &&
+ (si->Patt[i]->Lines != saver->xmh_MaxPattLen) ) )
+ {
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_PATT_LENGTH_INVALID, i, si->Patt[i]->Lines);
+
+ if (ShowRequest (MSG_WILL_MODIFY_SONG, MSG_PROCEED_OR_CANCEL))
+ {
+ xmProcessSong (si, NULL,
+ XMSNG_LimitPatterns, saver->xmh_MaxPattLen |
+ ((saver->xmh_Flags & XMHF_FIXED_PATT_LEN) ? (saver->xmh_MaxPattLen << 16) : 0),
+ TAG_DONE);
+ break;
+ }
+ else
+ {
+ if (releasebase)
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ return ERROR_BREAK;
+ }
+ }
+ }
+
+ if (si->MaxTracks > saver->xmh_MaxTracks)
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_TOO_MANY_TRACKS, saver->xmh_MaxTracks, si->MaxTracks);
+
+ if (si->NumPatterns > saver->xmh_MaxPatterns)
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_TOO_MANY_PATTS, saver->xmh_MaxPatterns, si->NumPatterns);
+
+ if (si->Length > saver->xmh_MaxLength)
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_TOO_MANY_POS, saver->xmh_MaxLength, si->Length);
+
+
+ /* Now make a backup of the old file before overwriting this */
+
+ if (GuiSwitches.DoBackups)
+ BackupFile (filename, GuiSwitches.BackupTemplate, GuiSwitches.BackupVersions);
+
+
+ if (fh = Open (filename, MODE_NEWFILE))
+ {
+ OpenProgressWindow();
+
+ /* Optimize file buffering for faster I/O.
+ *
+ * NOTE: SetVBuf() was introduced in V39, but V37 has a
+ * no-op vector for it, so it is safe to call SetVBuf()
+ * without checking the OS version.
+ * NOTE: Due to a bug in dos.library V40, SetVBuf() works
+ * only when called right after Open() (before doing any I/O).
+ */
+ SetVBuf (fh, NULL, BUF_FULL, 16*1024);
+
+
+ xmDisplayMessage (XMDMF_INFORMATION | XMDMF_USECATALOG,
+ (APTR)MSG_SAVING_MODULE, saver->xmh_Link.ln_Name,
+ si->Title ? si->Title : STR(MSG_SONG_UNTITLED));
+
+
+ err = saver->xmh_SaveMod (fh, si, saver, tags);
+
+ Close (fh);
+ SetComment (filename, VERS " by Bernardo Innocenti");
+
+ if (err)
+ {
+ /* Delete incomplete file */
+ DeleteFile (filename);
+
+ if (err == ERROR_IOERR)
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
+ (APTR)MSG_ERROR_WRITING, filename);
+ }
+ else
+ {
+ if (SaveSwitches.SaveIcons)
+ PutIcon ("def_Module", filename);
+ xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG,
+ (APTR)MSG_MODULE_SAVED_OK, NULL);
+
+ }
+
+ CloseProgressWindow();
+ }
+ else /* Open failed */
+ {
+ err = IoErr();
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT,
+ (APTR)MSG_CANT_OPEN, filename);
+ }
+ }
+ else err = ERROR_ACTION_NOT_KNOWN;
+
+ if (releasebase)
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ return err;
+}
+
+
+
+/****** xmodule/xmIdentifyModule *******************************************
+*
+* NAME
+* xmIdentifyModule -- Returns the type of a module
+*
+* SYNOPSIS
+* loader = xmIdentifyModule(fh, tagList)
+* D0 D0 A0
+*
+* struct XMHook *xmIdentifyModule(BPTR,struct TagItem *);
+*
+* FUNCTION
+* Finds out a loader which is able to read the given module.
+*
+* INPUTS
+* fh - AmigaDOS FileHandle to examine.
+* tagList - Additional parameters. Leave it NULL for now.
+*
+* RESULT
+* loader - Pointer to the first loader which knows how to load this
+* module, or NULL otherwise.
+*
+* NOTE
+* Before you call this function, you must first obtain a lock on the
+* loaders list to protect yourself from other tasks which might remove
+* the returned loader before you can actually use it.
+*
+* SEE ALSO
+*
+****************************************************************************
+*/
+static LIBCALL struct XMHook *XModuleIdentifyModule(
+ REG(d0, BPTR fh),
+ REG(a0, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ struct XMHook *loader, *ret = NULL;
+
+ loader = (struct XMHook *) XModuleBase->xm_Loaders.mlh_Head;
+
+ while (loader->xmh_Link.ln_Succ)
+ {
+ if (ret = loader->xmh_IdentifyMod (fh, loader, tags))
+ break;
+
+ loader = (struct XMHook *)loader->xmh_Link.ln_Succ;
+ }
+
+ Seek (fh, 0, OFFSET_BEGINNING);
+
+ return ret;
+}
+
+
+
+/****** xmodule/xmSetSongLen ***********************************************
+*
+* NAME
+* xmSetSongLen -- Set the number of song positions
+*
+* SYNOPSIS
+* sequence = xmSetSongLen(songInfo, length)
+* D0 A0 D0:16
+*
+* UWORD *xmSetSongLen(struct SongInfo *, UWORD);
+*
+* FUNCTION
+* Allocates space in the song for a sequence table of the given
+* length. If no sequence exists yet, a new one is allocated.
+* Increasing the song length may require the sequence table to be
+* expanded, in which case it will be re-allocated and the old sequence
+* entries will be copied. Decreasing the song length could also cause
+* a reallocation of the sequence table if the size drops down enough.
+* Setting the song length to 0 will free the sequence table and return
+* NULL.
+*
+* INPUTS
+* songInfo - pointer to a SongInfo structure.
+* length - new length of song sequence table.
+*
+* NOTE
+* This function will also adjust the CurrentPos field if it is found
+* beyond the song end
+*
+* BUGS
+* This funtion is quite useless because all it does is setting the
+* SNGA_Length attribute in the song object. Setting the SNGA_Length
+* attribute yourself is easier and faster if you are already setting
+* other attributes in the same song.
+*
+* RESULT
+* Pointer to the newly allocated sequence table or NULL for failure,
+* in which case the previous sequence table is left untouched.
+*
+****************************************************************************
+*/
+static LIBCALL UWORD *XModuleSetSongLen (
+ REG(a0, struct SongInfo *si),
+ REG(d0, UWORD len),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ SetAttrs (si, SNGA_Length, len, TAG_DONE);
+ return si->Sequence;
+}
+
+
+
+/****** xmodule/xmAddPatternA **********************************************
+*
+* NAME
+* xmAddPatternA -- Adds a pattern to a song
+* xmAddPattern -- Varargs stub for xmAddPatternA
+*
+* SYNOPSIS
+* pattern = xmAddPatternA(si, tagList)
+* D0 A0 A1
+*
+* struct Pattern *xmAddPatternA(struct SongInfo *,struct TagItem *);
+*
+*
+* pattern = xmAddPattern (si,tag1,...)
+*
+* struct Pattern *xmAddPattern(struct SongInfo *,LONG Tag1,...);
+*
+* FUNCTION
+* Adds a pattern to a song by calling the SNGM_ADDPATTERN method.
+*
+* INPUTS
+* si - pointer to the song to which the pattern should be added.
+* tagList - optional TagList. See SNGM_ADDPATTERN for possible tags.
+*
+* RESULT
+* Pointer to the new pattern or NULL for failure.
+*
+* NOTE
+* In order to add patterns, you should have exclusive access to
+* the song. Always obtain a lock before you call this function on
+* public songs.
+*
+* SEE ALSO
+* xmRemPattern(), songclass/SNGM_ADDPATTERN
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleAddPattern (
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ DoMethod ((Object *)si, SNGM_ADDPATTERN, tags);
+}
+
+
+
+/****** xmodule/xmSetPatternA **********************************************
+*
+* NAME
+* xmSetPatternA -- Sets pattern attributes
+* xmSetPattern -- Varargs stub for xmSetPatternA
+*
+* SYNOPSIS
+* success = xmSetPatternA(si, pattNum, tagList)
+* D0 A0 D0 A1
+*
+* ULONG xmSetPatternA(struct SongInfo *,ULONG,struct TagItem *);
+*
+*
+* success = xmSetPattern (si,pattNum,tag1,...)
+*
+* ULONG xmSetPattern(struct SongInfo *,ULONG,LONG Tag1,...);
+*
+* FUNCTION
+* Sets attributes of a pattern by calling the SNGM_SETPATTERN method.
+*
+* INPUTS
+* si - pointer to the song which contains the pattern to be set.
+* tagList - list of attributes to set. See SNGM_SETPATTERN for
+* possible tags.
+*
+* RESULT
+* Non zero for success
+*
+* NOTE
+* In order to set patterns attributes, you should have exclusive
+* access to the song. Always obtain a lock before you call this
+* function on public songs.
+*
+* SEE ALSO
+* xmAddPattern(), songclass/SNGM_SETPATTERN
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleSetPattern (
+ REG(a0, struct SongInfo *si),
+ REG(d0, ULONG pattNum),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ DoMethod ((Object *)si, SNGM_SETPATTERN, pattNum, tags);
+}
+
+
+
+/****** xmodule/xmRemPattern ***********************************************
+*
+* NAME
+* xmRemPattern -- Removes a pattern from a song
+*
+* SYNOPSIS
+* xmRemPattern(si, pattNum, replaceWith)
+* A0 D0, D1
+*
+* void xmRemPattern(struct SongInfo *,LONG,LONG);
+*
+* FUNCTION
+* Removes a pattern from a song by calling the SNGM_REMPATTERN method.
+*
+* INPUTS
+* si - pointer to the song to which the pattern should be removed.
+* mum - Number of the pattern to be removed.
+* replaceWith - What to put in the song sequence in place of the
+* deleted pattern.
+*
+* NOTE
+* In order to remove patterns, you should have exclusive access to
+* the song. Always obtain a lock before you call this function on
+* public songs.
+*
+* SEE ALSO
+* xmAddPatternA(), songclass/SNGM_REMPATTERN
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleRemPattern (
+ REG(a0, struct SongInfo *si),
+ REG(d0, ULONG pattNum),
+ REG(d1, ULONG replaceWith))
+{
+ DoMethod ((Object *)si, SNGM_REMPATTERN, pattNum, replaceWith);
+}
+
+
+
+/****** xmodule/xmAddInstrumentA *******************************************
+*
+* NAME
+* xmAddInstrumentA -- Adds a instrument to a song
+* xmAddInstrument -- Varargs stub for xmAddInstrumentA
+*
+* SYNOPSIS
+* instrument = xmAddInstrumentA(si, instrNum, tagList)
+* D0 A0 D0 A1
+*
+* struct Instrument *xmAddInstrumentA(struct SongInfo *,LONG,
+* struct TagItem *);
+*
+*
+* instrument = xmAddInstrument(si,instrNum,tag1,...)
+*
+* struct Instrument *xmAddInstrument(struct SongInfo *,LONG,LONG,...);
+*
+* FUNCTION
+* Adds an instrument to a song by calling the SNGM_ADDINSTRUMENT method.
+*
+* INPUTS
+* si - pointer to the song to which the instrument should be added.
+* tagList - optional TagList. See SNGM_ADDINSTRUMENT for possible tags.
+*
+* RESULT
+* Pointer to the new instrument or NULL for failure.
+*
+* NOTE
+* In order to add instruments, you should have exclusive access to
+* the song. Always obtain a lock before you call this function on
+* public songs.
+*
+* SEE ALSO
+* xmRemInstrument(), songclass/SNGM_REMINSTRUMENT
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleAddInstrument (
+ REG(a0, struct SongInfo *si),
+ REG(d0, LONG num),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ DoMethod ((Object *)si, SNGM_ADDINSTRUMENT, num, tags);
+}
+
+
+
+/****** xmodule/xmSetInstrumentA *******************************************
+*
+* NAME
+* xmSetInstrumentA -- Sets an instrument's attributes
+* xmSetInstrument -- Varargs stub for xmSetInstrumentA
+*
+* SYNOPSIS
+* success = xmSetInstrumentA(si, instrNum, tagList)
+* D0 A0 D0 A1
+*
+* ULONG xmSetInstrumentA(struct SongInfo *,LONG,
+* struct TagItem *);
+*
+*
+* success = xmSetInstrument(si,instrNum,tag1,...)
+*
+* ULONG xmSetInstrument(struct SongInfo *,LONG,LONG,...);
+*
+* FUNCTION
+* Sets an instrument's attributes by calling the SNGM_SETINSTRUMENT
+* method.
+*
+* INPUTS
+* si - pointer to the song which contains the instrument to be set.
+* tagList - instrument attributes to set. See SNGM_SETINSTRUMENT
+* for possible tags.
+*
+* RESULT
+* non zero for success.
+*
+* NOTE
+* In order to set instruments' attributes, you should have
+* exclusive access to the song. Always obtain a lock before
+* you call this function on public songs.
+*
+* SEE ALSO
+* xmAddInstrument(), songclass/SNGM_SETINSTRUMENT
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleSetInstrument (
+ REG(a0, struct SongInfo *si),
+ REG(d0, LONG num),
+ REG(a1, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ DoMethod ((Object *)si, SNGM_SETINSTRUMENT, num, tags);
+}
+
+
+
+/****** xmodule/xmRemInstrument ***********************************************
+*
+* NAME
+* xmRemInstrument -- Removes an instrument from a song
+*
+* SYNOPSIS
+* xmRemInstrument(si, instrNum)
+* A0 D0
+*
+* void xmRemInstrument(struct SongInfo *,LONG);
+*
+* FUNCTION
+* Removes an instrument from a song by calling the SNGM_REMINSTRUMENT
+* method.
+*
+* INPUTS
+* si - pointer to the song to which the instrument should be removed.
+* mum - Number of the instrument to be removed.
+*
+* NOTE
+* In order to remove instruments, you should have exclusive access to
+* the song. Always obtain a lock before you call this function on
+* public songs.
+*
+* SEE ALSO
+* xmAddInstrumentA(), songclass/SNGM_REMINSTRUMENT
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleRemInstrument (
+ REG(a0, struct SongInfo *si),
+ REG(d0, ULONG num),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ DoMethod ((Object *)si, SNGM_REMINSTRUMENT, num);
+}
+
+
+
+/****** xmodule/xmProcessSongA *********************************************
+*
+* NAME
+* xmProcessSongA -- Performs complex processing on a song
+* xmProcessSong -- Varargs stub for xmProcessSongA()
+*
+* SYNOPSIS
+* result = xmProcessSongA(si, reserved, tagList)
+* A0 A1, A2
+*
+* LONG xmProcessSongA(struct SongInfo *,void *,struct TagItem *);
+*
+*
+* result = xmProcessSong(si, reserved, tag1, ...)
+*
+* LONG xmProcessSongA(struct SongInfo *,void *,LONG,...);
+*
+* FUNCTION
+* Performs complex processing operations on a song.
+*
+* INPUTS
+* si - pointer to the song to be processed.
+* reserved - Reserved for future use.
+* tagList - List of arguments. See below for possible tags.
+*
+* RESULT
+* The result depends on the kind of operation done. For most
+* operations, it's value will be 0 to indicate success and
+* an AmigaDOS error code which indicates the cause for failure.
+*
+* TAGS
+* XMSNG_Optimize - (LONG). Tries to reduce the song size by removing
+* all unused data. Possible optimizations are:
+* - XMOF_REM_UNUSED_PATTERNS
+* - XMOF_REM_DUP_PATTERNS
+* - XMOF_CUT_PATTERNS
+* - XMOF_REM_UNUSED_INSTRS
+* - XMOF_CUT_INSTR_LOOPS
+* - XMOF_CUT_INSTR_TAILS
+* - XMOF_DEFAULT
+* XMOF_DEFAULT will select all the optimizations choosen by
+* the user in addition to the ones specified with the other flags.
+*
+* XMSNG_RemapInstruments - (BOOL) Performs instruments remapping.
+*
+* XMSNG_LimitPatterns - (UWORD,UWORD) Limits the length all the
+* patterns inside a minimum and maximum value. The upper 16bits
+* of the argument are the minimum value, the lower ones are
+* the maximum value. Patterns outside this limits will be grown
+* or split as appropriate.
+*
+* XMSNG_Join - (struct SongInfo *) - Joins the song with another one.
+*
+* XMSNG_Merge - (struct SongInfo *) - Merges the song with another one.
+*
+* NOTE
+* In order to process a song, you should have exclusive access to
+* it. Always obtain a lock before you call this function on
+* public songs.
+*
+****************************************************************************
+*/
+static LIBCALL LONG XModuleProcessSong (
+ REG(a0, struct SongInfo *si),
+ REG(a1, void *reserved),
+ REG(a2, struct TagItem *tags),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ struct TagItem *ti, *tstate = tags;
+ LONG result = 0;
+
+ while (ti = NextTagItem(&tstate))
+ {
+ switch (ti->ti_Tag)
+ {
+ case XMSNG_Optimize:
+ {
+ ULONG flags = ti->ti_Data;
+
+ if (flags & XMOF_DEFAULT)
+ {
+ if (OptSwitches.RemPatts) flags |= XMOF_REM_UNUSED_PATTERNS;
+ if (OptSwitches.RemDupPatts) flags |= XMOF_REM_DUP_PATTERNS;
+ if (OptSwitches.RemInstr) flags |= XMOF_REM_UNUSED_INSTRS;
+ if (OptSwitches.RemDupInstr) flags |= XMOF_REM_DUP_INSTRS;
+ if (OptSwitches.CutAfterLoop) flags |= XMOF_CUT_INSTR_LOOPS;
+ if (OptSwitches.CutZeroTail) flags |= XMOF_CUT_INSTR_TAILS;
+ if (OptSwitches.CutPatts) flags |= XMOF_CUT_PATTERNS;
+ if (OptSwitches.RemapInstr) flags |= XMOF_REMAP_INSTRS;
+ }
+
+ if (flags & XMOF_REM_UNUSED_PATTERNS) DiscardPatterns (si);
+ if (flags & XMOF_REM_DUP_PATTERNS) RemDupPatterns (si);
+ if (flags & XMOF_CUT_PATTERNS) CutPatterns (si);
+ if (flags & XMOF_REM_UNUSED_INSTRS) RemUnusedInstruments (si);
+ if (flags & XMOF_REM_DUP_INSTRS) RemDupInstruments (si);
+ if (flags & XMOF_CUT_INSTR_LOOPS) OptimizeInstruments (si);
+ if (flags & XMOF_CUT_INSTR_TAILS) OptimizeInstruments (si);
+ if (flags & XMOF_REMAP_INSTRS) RemapInstruments (si);
+
+ break;
+ }
+
+ case XMSNG_RemapInstruments:
+ RemapInstruments (si);
+ result = 0;
+ break;
+
+ case XMSNG_LimitPatterns:
+ result = ResizePatterns (si, ti->ti_Data >> 16, ti->ti_Data & 0xFFFF);
+ break;
+
+ case XMSNG_Join:
+ result = NULL;
+ break;
+
+ case XMSNG_Merge:
+ result = NULL;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+
+/****** xmodule/xmDisplayMessage *******************************************
+*
+* NAME
+* xmDisplayMessageA -- Displays a message to the user
+* xmDisplayMessage -- Varargs stub for xmDisplayMessageA()
+*
+* SYNOPSIS
+* xmDisplayMessageA(level, message, args)
+* DO A0 A1
+*
+* void xmDisplayMessageA(ULONG,APTR,LONG *);
+*
+*
+* xmDisplayMessage(level, message, ...)
+*
+* void xmDisplayMessage(ULONG,APTR,...);
+*
+* FUNCTION
+* Outputs a string in the XModule log window or in the 'action' field
+* of the progress indicator. The string is printf-formatted before
+* being output.
+*
+* INPUTS
+* level - a number from 0 to 7 which indicates the importance of the
+* message. 7 is used for very annoying messages (eg: debug output),
+* 0 for very important things (eg: disaster).
+* If you set the XMDMF_USECATALOG bit in the level parameter, you
+* can pass a catalog string number instead of a pointer to a string.
+* Specifying XMDMF_ACTION, the message will be put in the 'action'
+* field of the progress indicator.
+* If the flag XMDMF_DOSFAULT is specified, a Fault() output is
+* formatted using the message as an header. In this case, the
+* level parameter takes another meaing: The lower word can contain
+* an AmigaDOS error code. If it is 0, IoErr() will be used to get
+* the error code.
+* message - pointer to a string or catalog message number,
+* when the XMDMF_USECATALOG flag is set.
+* args - arguments for formatting.
+*
+* EXAMPLES
+* xmDisplayMessage (XMDMF_ALERT,
+* "The application `%s' fucked up Windoze95 because %s.",
+* "(unknown name)", "an error occurred");
+*
+* xmDisplayMessage (XMDMF_USE_CATALOG | XMDMF_COMMENT,
+* MSG_LIFE_UNIVERSE_AND_ALLTHAT, 42, "Fortytwo", "For tea too");
+*
+* xmDisplayMessageA (XMDMF_ACTION | XMDMF_USECATALOG,
+* MSG_READING_COMICS, NULL);
+*
+* xmDisplayMessage (XMDMF_DOSFAULT | XMDMF_USECATALOG,
+* MSG_CANT_LOAD_MODULE, ModuleName);
+*
+* SEE ALSO
+* xmDisplayProgress()
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleDisplayMessage (
+ REG(d0, ULONG level),
+ REG(a0, CONST_STRPTR msg),
+ REG(a1, LONG *args),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ if (level & XMDMF_USECATALOG)
+ msg = STR((ULONG)msg);
+
+ if (level & XMDMF_DOSFAULT)
+ {
+ UBYTE buf[FAULT_MAX + 100];
+ UBYTE buf2[100];
+
+ if (GuiSwitches.LogLevel >= XMDMF_ERROR)
+ {
+ if (args) VSPrintf (buf2, msg, args);
+
+ if (Fault ((level & XMDMF_DOSERRORMASK) ? (level & XMDMF_DOSERRORMASK) : IoErr(),
+ args ? buf2 : msg, buf, FAULT_MAX + 100))
+ {
+ if (level & XMDMF_USEREQUESTER) ShowRequestStr (buf, NULL, args);
+ else ShowString (buf, args);
+ }
+ }
+ }
+ else if (level & XMDMF_ACTION)
+ {
+ DisplayActionStr (msg);
+ if (GuiSwitches.LogLevel > XMDMF_INFORMATION)
+ ShowString (msg, args);
+ }
+ else if (level & XMDMF_USEREQUESTER)
+ ShowRequestStr (msg, NULL, args);
+ else if (GuiSwitches.LogLevel > (level & XMDMF_LEVELMASK))
+ ShowString (msg, args);
+}
+
+
+
+/****** xmodule/xmDisplayProgress ******************************************
+*
+* NAME
+* xmDisplayProgress -- Uptdates the progress bar indicator
+*
+* SYNOPSIS
+* abort = xmDisplayProgress(done, total)
+* D0 D1
+*
+* LONG xmDisplayMessageA(ULONG,ULONG);
+*
+* FUNCTION
+* Updates the position of the fuel gauge in the progress window to
+* show the progress of an operation. Additionally, it checks for
+* user abort.
+*
+* INPUTS
+* done - a number which indicates how much of the work has
+* been already completed
+* total - Total number of operations to do.
+*
+* RESULT
+* 0 for success, ERROR_BREAK if user abort was detected. You should
+* always check this return code to abort what you were doing.
+*
+****************************************************************************
+*/
+static LIBCALL void XModuleDisplayProgress (
+ REG(d0, ULONG done),
+ REG(d1, ULONG total),
+ REG(a6, struct XModuleBase *XModuleBase))
+{
+ DisplayProgress (done, total);
+}
--- /dev/null
+#ifndef LISTMACROS_H
+#define LISTMACROS_H
+/*
+** $VER: ListMacros.h 2.1 (1.9.97)
+**
+** Copyright (C) 1996,97 Bernardo Innocenti. All rights reserved.
+**
+** Use 4 chars wide TABs to read this source
+**
+** Some handy macros for list operations. Using these macros is faster
+** than calling their exec.library equivalents, but they will eventually
+** make your code a little bigger and are also subject to common macro
+** side effects.
+*/
+
+
+#define NEWLIST(l) ( (l)->lh_TailPred = (struct Node *)(l), \
+ (l)->lh_Tail = 0, \
+ (l)->lh_Head = (struct Node *)(&((l)->lh_Tail)) )
+
+#define ADDHEAD(l,n) ( (n)->ln_Pred = (struct Node *)(l), \
+ (n)->ln_Succ = (l)->lh_Head, \
+ (l)->lh_Head->ln_Pred = (n), \
+ (l)->lh_Head = (n) )
+
+#define ADDTAIL(l,n) ( (n)->ln_Succ = (struct Node *)(&((l)->lh_Tail)), \
+ (n)->ln_Pred = (l)->lh_TailPred, \
+ (l)->lh_TailPred->ln_Succ = (n), \
+ (l)->lh_TailPred = (n) )
+
+#define REMOVE(n) ( (n)->ln_Succ->ln_Pred = (n)->ln_Pred, \
+ (n)->ln_Pred->ln_Succ = (n)->ln_Succ )
+
+#define GETHEAD(l) ( (l)->lh_Head->ln_Succ ? (l)->lh_Head : (struct Node *)NULL )
+
+#define GETTAIL(l) ( (l)->lh_TailPred->ln_Succ ? (l)->lh_TailPred : (struct Node *)NULL )
+
+#define GETSUCC(n) ( (n)->ln_Succ->ln_Succ ? (n)->ln_Succ : (struct Node *)NULL )
+
+#define GETPRED(n) ( (n)->ln_Pred->ln_Pred ? (n)->ln_Pred : (struct Node *)NULL )
+
+
+#ifdef __GNUC__
+
+#define REMHEAD(l) \
+({ \
+ struct Node *n = (l)->lh_Head; \
+ n->ln_Succ ? \
+ (l)->lh_Head = n->ln_Succ, \
+ (l)->lh_Head->ln_Pred = (struct Node *)(l), \
+ n : \
+ NULL; \
+})
+
+#define REMTAIL(l) \
+({ \
+ struct Node *n = (l)->lh_TailPred; \
+ n->ln_Pred ? \
+ (l)->lh_TailPred = n->ln_Pred, \
+ (l)->lh_TailPred->ln_Succ = (struct Node *)(&((l)->lh_Tail)), \
+ n : \
+ NULL; \
+})
+
+
+#else
+
+/* These two can't be implemented as macros without the GCC ({...}) language extension */
+
+INLINE struct Node *REMHEAD(struct List *l)
+{
+ struct Node *n = l->lh_Head;
+
+ if (n->ln_Succ)
+ {
+ l->lh_Head = n->ln_Succ;
+ l->lh_Head->ln_Pred = (struct Node *)l;
+ return n;
+ }
+ return NULL;
+}
+
+INLINE struct Node *REMTAIL(struct List *l)
+{
+ struct Node *n = l->lh_TailPred;
+
+ if (n->ln_Pred)
+ {
+ l->lh_TailPred = n->ln_Pred;
+ l->lh_TailPred->ln_Succ = (struct Node *)(&(l->lh_Tail));
+ return n;
+ }
+ return NULL;
+}
+
+#endif
+
+#endif /* !LISTMACROS_H */
--- /dev/null
+/*
+** Locale.c
+**
+** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+**
+** Routines to handle localization.
+**
+** NOTE: The Locale.c file is generated automatically by `FlexCat 2.1'
+** using "Catalogs/XModule.cd". Do NOT edit by hand!
+*/
+
+#include <libraries/locale.h>
+
+#include <proto/exec.h>
+#include <proto/locale.h>
+
+#include "XModulePriv.h"
+
+
+#define CATALOGVERSION 1
+#define CATALOGNAME "XModule.catalog"
+#define MSG_COUNT 311
+
+
+STRPTR AppStrings[] =
+{
+ "",
+ "Ok",
+ "_Use",
+ "_Ok",
+ "_Cancel",
+ "Yes|No",
+ "Retry|Cancel",
+ "Proceed|Cancel",
+ "Continue",
+ "Insufficient memory.",
+ "Aborted.",
+ "Unable to load \"%s\"",
+ "Cannot open file \"%s\"",
+ "Error reading \"%s\"",
+ "Error writing \"%s\"",
+ "Decrunching...",
+ "Nothing found in archive \"%s\".",
+ "Unable to load compressed file.",
+ "Error decompressing file \"%s\": %s.",
+ "Bad Commodity HotKey description.",
+ "Reading Patterns...",
+ "Reading Instruments Info...",
+ "Reading Instruments...",
+ "ERROR: Couldn't load pattern %ld.",
+ "ERROR: Couldn't load instrument %lx.",
+ "ERROR: Not enough memory for instrument %lx.",
+ "ERROR: Instrument %lx is not a sample.",
+ "WARNING: Song length exceeds maximum. Will be truncated.",
+ "WARNING: Song exceeds maximum number of patterns.",
+ "WARNING: Pattern %ld has too many tracks. Cropping to %ld tracks.",
+ "WARNING: Pattern %ld has too many lines. Cropping to %ld lines.",
+ "WARNING: Invalid note %ld (Patt %ld Track %ld Line %ld).",
+ "Unknown effect: $%lx (Patt %ld Track %ld Line %ld).",
+ "WARNING: Extra data found after valid module: Will be ignored.",
+ "Writing Header...",
+ "Writing Patterns...",
+ "Writing Instruments...",
+ "Writing Instruments Info...",
+ "Writing Instruments Data...",
+ "WARNING: Note at Patt %ld Track %ld Line %ld is too low.",
+ "WARNING: Note at Patt %ld Track %ld Line %ld is too high.",
+ "WARNING: Not enough memory to halve volume of instrument %lx.",
+ "Loading MMD%lc module...",
+ "ERROR: Unsupported OctaMED format.",
+ "WARNING: Effect %lx is not supported in MMD0 format. Use MMD1 or better.",
+ "ERROR: Instrument %lx is an ADLib %s.",
+ "WARNING: Track %lx is out of range.",
+ "WARNING: Unknown sample compression for instrument %lx.",
+ "WARNING: Instrument %lx is a stereo sample.",
+ "WARNING: Instrument %lx is 16bit: unable to load it.",
+ "Reading %s module...",
+ "NOTE: Module exceeds 64 patterns. You need ProTracker 2.3 to play it.",
+ "Tracker ID: \"%s\"",
+ "Module has %lu tracks...",
+ "This file is a song and doesn't contain instruments.",
+ "WARNING: Module execeeds %ld patterns.",
+ "Pattern %ld will grow to 64 lines (was %ld lines long).",
+ "Splitting pattern %ld (was %ld lines long).",
+ "Choosing Channels...",
+ "Writing MIDI Tracks...",
+ "ERROR: Song requires too many MIDI channels.",
+ "Really Quit XModule?",
+ "Please close all visitor windows\n"\
+ "and then select `Continue'.",
+ "Unknown IFF format %s.",
+ "Illegal IFF structure.",
+ "Unrecognized instrument format.\n"\
+ "Please select RAW mode.",
+ "Signed 8bit|Unsigned 8bit|Cancel",
+ "DataTypes error: %s.",
+ "Unknown compression type.",
+ "%lu bit samples are not supported.",
+ "Samples other than MONO are not supported.",
+ "Samples with %ld channels are not supported.",
+ "WARNING: insufficient memory to optimize instrument %lx.",
+ "Instrument %lx will shrink from %ld to %ld.",
+ "Duplicate instruments found and removed: %lx == %lx.",
+ "Instrument %lx was never used and it's being removed.",
+ "Couldn't open \"%s\" version %ld or greater.",
+ "Couldn't open \"%s\".",
+ "Unable to insert pattern: Maximum number of patterns reached.",
+ "Pattern %ld is not used and is beeing deleted.",
+ "Pattern %ld will be cut at line %ld.",
+ "Duplicate patterns found and removed: %ld == %ld.",
+ "WARNING: Song lengths are different. Using shorter one.",
+ "WARNING: Different pattern lengths at position %ld. Using shorter one.",
+ "ERROR: Instruments overflow. Try remapping the instruments.",
+ "Incorrect version of preferences file",
+ "XModule Request",
+ "Clone Workbench Screen",
+ "Please close FileRequester\n and then select `Continue'.",
+ "Select Module(s)...",
+ "Select Instrument(s)...",
+ "Select Pattern...",
+ "Save Module...",
+ "Save Instrument...",
+ "Save Pattern...",
+ "File \"%s\"\nalready exists.",
+ "Overwrite|Choose Another|Abort",
+ "Please close all open songs\n"\
+ "and then select `Continue'.",
+ "Unespected end of file.",
+ "Module loaded OK.",
+ "Module saved OK.",
+ "ERROR: Unrecognized save format.",
+ "Removed invalid loop for instrument %lx.",
+ "Fixed invalid loop for instrument %lx.",
+ "WARNING: Song has no patterns.",
+ "WARNING: Song has no sequence.",
+ "WARNING: Song position %ld references pattern %ld, which doesn't exist.",
+ "Unable to identify module format.\n"\
+ "(Loading a data file as a module is unwise)",
+ "SoundTracker 15|ProTracker|Cancel",
+ "Unknown",
+ "Untitled",
+ "AmigaGuide error:",
+ "Pattern Editor Settings",
+ "Advance _Tracks",
+ "Advance _Lines",
+ "Max _Undo Levels",
+ "Max Undo _Memory",
+ "Cli_pboard Unit",
+ "_Scroller Position",
+ "_Vertical Wrap",
+ "_Horizontal Wrap",
+ "He_x Line Numbers",
+ "Blank _Zero Digits",
+ "Backdrop _Window",
+ "Ho_rizontal Scroller",
+ "Display Cursor Ruler",
+ "Display Tiny Lines",
+ "_Editor Font",
+ "Background",
+ "T_ext",
+ "Li_nes",
+ "Tin_y Lines",
+ "Right",
+ "Left",
+ "Off",
+ "ERROR: Pattern would exceed the maximum number of lines.",
+ "Sequence Editor",
+ "Sequence",
+ "_Add",
+ "Up",
+ "Down",
+ "_Name",
+ "Patterns",
+ "_Up",
+ "_Down",
+ "_Ins",
+ "-- unnamed --",
+ "Instruments",
+ "_Volume",
+ "_FineTune",
+ "Length",
+ "_Kind",
+ "_Edit...",
+ "Sample",
+ "Synth",
+ "Hybrid",
+ "Instruments",
+ "Load...",
+ "Remap",
+ "Save Compressed",
+ "Save Raw",
+ "-- empty --",
+ "Play",
+ "_Vol",
+ "Pos",
+ "Time",
+ "_Rst",
+ "Player initialization error: %ld.",
+ "XModule is working...",
+ "XModule Log",
+ "_Abort",
+ "%ld of %ld (%ld%% done)",
+ "Song Information",
+ "Ne_w",
+ "_Open...",
+ "_Save",
+ "Del",
+ "Song _Name",
+ "_Author",
+ "_Tempo",
+ "S_peed",
+ "_Restart",
+ "Length",
+ "Num Patterns",
+ "Tracks",
+ "Total Module Size",
+ "Total Instruments Size",
+ "Song",
+ "Merge Songs",
+ "Join Songs",
+ "ERROR: Join requires two songs.",
+ "ERROR: Merge requires two songs.",
+ "Discard current song?",
+ "ToolBox",
+ "Play...",
+ "_Songs...",
+ "_Patterns...",
+ "_Instruments...",
+ "Se_quence...",
+ "_Optimization...",
+ "Project",
+ "New",
+ "Open...",
+ "Open New...",
+ "Save",
+ "Save As...",
+ "Clear...",
+ "About...",
+ "Help...",
+ "Iconify...",
+ "Quit",
+ "Settings",
+ "Save Format...",
+ "User Interface...",
+ "Save Icons",
+ "Confirm Overwrite",
+ "Confirm Exit",
+ "Verbose Log",
+ "Open Settings...",
+ "Save Settings",
+ "Save Settings As...",
+ "%s\n"\
+ "A Music Module Processing Utility\n\n"\
+ "%s\n"\
+ "All rights reserved.\n\n"\
+ "Internet: bernie@shock.cosmos.it\n\n"\
+ "FidoNet: 2:332/118.4\n"\
+ "Free CHIP Memory: %ldKB\n"\
+ "Free FAST Memory: %ldKB\n\n"\
+ "Public Screen: %s\n"\
+ "ARexx Port: %s\n"\
+ "Cx HotKey: %s\n"\
+ "Language: %s",
+ "-- Default --",
+ "-- Disabled --",
+ "Module Optimization",
+ "_Optimize",
+ "Remove Unused _Patterns",
+ "Remove _Duplicate Patterns",
+ "Remove Unused _Instruments",
+ "Remove Duplicate I_nstruments",
+ "Cut Instruments After _Loop",
+ "Cut Instrument _Zero Tails",
+ "Cut _Breaked Patterns",
+ "_Remap Instruments",
+ "Saved %ld bytes (%ld%%)",
+ "User Interface Settings",
+ "_Public Screen",
+ "Display _Mode",
+ "_Window Font",
+ "_ListView Font",
+ "_Requesters",
+ "Use _DataTypes",
+ "Put App_Icon",
+ "Refres_h",
+ "Log To File",
+ "_Log Level",
+ "Confirm _Autosave",
+ "Autosave Mi_nutes",
+ "Create _Backups",
+ "Bac_kup Template",
+ "Backup _Versions",
+ "Asl",
+ "ReqTools",
+ "Smart",
+ "Simple",
+ "--Clone Default Screen--",
+ "Pattern Editor",
+ "Patterns",
+ "Size...",
+ "Edit",
+ "Mark",
+ "Cut",
+ "Copy",
+ "Paste",
+ "Erase",
+ "Undo",
+ "Redo",
+ "Editor Settings...",
+ "Savers",
+ "Se_quence",
+ "_Instruments",
+ "_Patterns",
+ "Names",
+ "Add I_con",
+ "_Mode",
+ "Options...",
+ "None",
+ "XPK",
+ "LhA",
+ "Description",
+ "Author",
+ "Max Length",
+ "Max Tracks",
+ "Max Instruments",
+ "Max Patterns",
+ "Max Pattern Length",
+ "Max Sample Length",
+ "Clear Module",
+ "_Sequence",
+ "_Instruments",
+ "_Patterns",
+ "_Clear",
+ "Sample Editor",
+ "Render",
+ "Points",
+ "Lines",
+ "Filled",
+ "Pattern Attributes",
+ "_Lines",
+ "_Tracks",
+ "_Double",
+ "_Halve",
+ "WARNING: The selected saver supports max %lu tracks, but the song has %lu tracks.",
+ "WARNING: The selected saver supports max %lu patterns; the song has %lu patterns.",
+ "WARNING: The selected saver supports max %lu instruments, last instrument is %lu.",
+ "WARNING: The selected saver supports max %lu positions, but the song has %lu positions.",
+ "WARNING: The length of pattern %ld ($%lx lines) isn't allowed with the selected saver.",
+ "WARNING: Instrument %lx is too long and will be cropped to %lu bytes.",
+ "Some modifications need to be performed on this song\n"\
+ "in order to adapt it to the limitations of the\n"\
+ "destination format.",
+ "Some instruments are placed beyond the limit for\n"\
+ "the selected format.\n"\
+ "Remapping the instruments now could help saving\n"\
+ "all them along with the selected format.",
+ "Saving %s module \"%s\"..."
+};
+
+static struct Library *LocaleBase = NULL;
+XDEF struct Catalog *Catalog = NULL;
+
+
+
+GLOBALCALL void SetupLocale (void)
+{
+ /* Try to open locale.library */
+ if (LocaleBase = OpenLibrary ("locale.library", 38L))
+ {
+ /* Try to get catalog for current language */
+ if (Catalog = OpenCatalog (NULL, CATALOGNAME,
+ OC_BuiltInLanguage, "english",
+ OC_Version, CATALOGVERSION,
+ TAG_DONE))
+ {
+ /* Read in locale language strings */
+ UBYTE **as = AppStrings;
+ ULONG i;
+
+ /* Get translation strings */
+ for (i = 1; i < MSG_COUNT; i++, as++)
+ *as = GetCatalogStr (Catalog, i, *as);
+ }
+ }
+
+ /* Fix for a limitation of FlexCat: MSG_NULL should translate
+ * to a NULL pointer, not a pointer to an empty string :)
+ */
+ AppStrings[0] = NULL;
+}
+
+
+
+GLOBALCALL void CleanupLocale (void)
+{
+ if (LocaleBase)
+ {
+ /* No need to check for NULL */
+ CloseCatalog (Catalog); Catalog = NULL;
+ CloseLibrary (LocaleBase); LocaleBase = NULL;
+ }
+}
--- /dev/null
+/*
+** NOTE: The LocaleStrings.h file is generated automatically by `FlexCat 2.1'
+** using "Catalogs/XModule.cd". Do NOT edit by hand!
+*/
+
+#ifndef XModule_CAT_H
+#define XModule_CAT_H
+
+
+/* Message Definitions */
+#define MSG_NULL 0
+#define MSG_OK 1
+#define MSG_UNDERSCORE_USE_GAD 2
+#define MSG_UNDERSCORE_OK_GAD 3
+#define MSG_UNDERSCORE_CANCEL_GAD 4
+#define MSG_YES_OR_NO 5
+#define MSG_RETRY_OR_CANCEL 6
+#define MSG_PROCEED_OR_CANCEL 7
+#define MSG_CONTINUE 8
+#define MSG_NO_FREE_STORE 9
+#define MSG_BREAK 10
+#define MSG_ERR_LOAD 11
+#define MSG_CANT_OPEN 12
+#define MSG_ERROR_READING 13
+#define MSG_ERROR_WRITING 14
+#define MSG_DECRUNCHING 15
+#define MSG_NOTHING_IN_ARC 16
+#define MSG_CANT_LOAD_COMPRESSED 17
+#define MSG_ERROR_DECOMPRESSING 18
+#define MSG_BAD_HOTKEY 19
+#define MSG_READING_PATTS 20
+#define MSG_READING_INSTS_INFO 21
+#define MSG_READING_INSTS 22
+#define MSG_ERR_CANT_LOAD_PATT 23
+#define MSG_ERR_CANT_LOAD_INST 24
+#define MSG_ERR_NO_MEM_FOR_INST 25
+#define MSG_ERR_NOT_A_SAMPLE 26
+#define MSG_SONG_TOO_LONG 27
+#define MSG_SONG_HAS_TOO_MANY_PATT 28
+#define MSG_PATT_TOO_MANY_TRACKS 29
+#define MSG_PATT_TOO_MANY_LINES 30
+#define MSG_INVALID_NOTE 31
+#define MSG_UNKNOWN_EFF 32
+#define MSG_EXTRA_DATA_AFTER_MOD 33
+#define MSG_WRITING_HEADER 34
+#define MSG_WRITING_PATTS 35
+#define MSG_WRITING_INSTS 36
+#define MSG_WRITING_INSTINFO 37
+#define MSG_WRITING_INSTDATA 38
+#define MSG_NOTE_TOO_LOW 39
+#define MSG_NOTE_TOO_HIGH 40
+#define MSG_NO_MEM_TO_HALVE 41
+#define MSG_READING_MMD 42
+#define MSG_UNSUPPORTED_MMD_FORMAT 43
+#define MSG_WRONG_EFFECT_IN_MMD0 44
+#define MSG_ADLIB_INSTR 45
+#define MSG_TRACK_OUT_OF_RANGE 46
+#define MSG_UNKNOWN_SAMPLE_COMPRESSION 47
+#define MSG_INST_IS_STEREO 48
+#define MSG_INST_IS_16BIT 49
+#define MSG_READING_TYPE_MODULE 50
+#define MSG_EXCEEDS_64_PATTS 51
+#define MSG_MODULE_ID 52
+#define MSG_MODULE_HAS_N_CHN 53
+#define MSG_SONG_HAS_NO_INSTS 54
+#define MSG_EXCEEDS_MAXPAATTS 55
+#define MSG_PATT_WILL_GROW 56
+#define MSG_SPLITTING_PATT 57
+#define MSG_CHOOSING_CHANNELS 58
+#define MSG_WRITING_MIDI_TRACKS 59
+#define MSG_TOO_MANY_CHANNELS 60
+#define MSG_REALLY_QUIT_XMODULE 61
+#define MSG_CLOSE_ALL_WINDOWS 62
+#define MSG_UNKNOWN_IFF 63
+#define MSG_ILLEGAL_IFF_STRUCTURE 64
+#define MSG_SELECT_RAW_MODE 65
+#define MSG_RAW_MODES 66
+#define MSG_DATATYPES_ERROR 67
+#define MSG_UNKNOWN_COMPRESSION 68
+#define MSG_SAMPLE_WRONG_SIZE 69
+#define MSG_SAMPLE_NOT_MONO 70
+#define MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS 71
+#define MSG_NO_MEMORY_TO_OPTIMIZE_INSTR 72
+#define MSG_INSTR_WILL_SHRINK 73
+#define MSG_INSTR_DUPES_REMOVED 74
+#define MSG_INSTR_UNUSED 75
+#define MSG_OPENLIB_VER_FAIL 76
+#define MSG_OPENLIB_FAIL 77
+#define MSG_CANT_INSERT_PATT 78
+#define MSG_PATT_UNUSED 79
+#define MSG_PATT_CUT 80
+#define MSG_PATT_DUPE 81
+#define MSG_SONG_LEN_DIFFERENT 82
+#define MSG_PATT_LEN_DIFFERENT 83
+#define MSG_ERR_INSTR_OVERFLOW 84
+#define MSG_BAD_PREFS_VERSION 85
+#define MSG_XMODULE_REQUEST 86
+#define MSG_CLONE_WB 87
+#define MSG_CLOSE_FILEREQUESTER 88
+#define MSG_SELECT_MODULES 89
+#define MSG_SELECT_INSTRUMENTS 90
+#define MSG_SELECT_PATTERN 91
+#define MSG_SAVE_MODULE 92
+#define MSG_SAVE_INSTRUMENT 93
+#define MSG_SAVE_PATTERN 94
+#define MSG_FILE_EXISTS 95
+#define MSG_OVERWRITE 96
+#define MSG_CLOSE_ALL_SONGS 97
+#define MSG_UNESPECTED_EOF 98
+#define MSG_MODULE_LOADED_OK 99
+#define MSG_MODULE_SAVED_OK 100
+#define MSG_UNKNOWN_SAVE_FORMAT 101
+#define MSG_INVALID_LOOP_REMOVED 102
+#define MSG_INVALID_LOOP_FIXED 103
+#define MSG_SONG_HAS_NO_PATTS 104
+#define MSG_SONG_HAS_NO_SEQ 105
+#define MSG_INVALID_SONG_POS 106
+#define MSG_UNKNOWN_MOD_FORMAT 107
+#define MSG_SOUND_PRO_CANCEL 108
+#define MSG_AUTHOR_UNKNOWN 109
+#define MSG_SONG_UNTITLED 110
+#define MSG_AMIGAGUIDE_ERROR 111
+#define MSG_PATTPREFS_TITLE 112
+#define MSG_ADVANCE_TRACKS_GAD 113
+#define MSG_ADVANCE_LINES_GAD 114
+#define MSG_MAX_UNDO_LEVELS_GAD 115
+#define MSG_MAX_UNDO_MEM_GAD 116
+#define MSG_CLIPBOARD_UNIT_GAD 117
+#define MSG_SCROLLER_POS_GAD 118
+#define MSG_VERT_WRAP_GAD 119
+#define MSG_HORIZ_WRAP_GAD 120
+#define MSG_HEX_LINE_NUMBERS_GAD 121
+#define MSG_BLANK_ZERO_GAD 122
+#define MSG_BACKDROP_GAD 123
+#define MSG_HORIZ_SCROLLER_GAD 124
+#define MSG_DO_RULER_GAD 125
+#define MSG_DO_TINY_LINES_GAD 126
+#define MSG_EDITOR_FONT_GAD 127
+#define MSG_BACKGROUND_PEN_GAD 128
+#define MSG_TEXT_PEN_GAD 129
+#define MSG_LINES_PEN_GAD 130
+#define MSG_TINY_LINES_PEN_GAD 131
+#define MSG_RIGHT_GAD 132
+#define MSG_LEFT_GAD 133
+#define MSG_OFF_GAD 134
+#define MSG_PATT_TOO_LONG 135
+#define MSG_SEQUENCE_TITLE 136
+#define MSG_SEQUENCE_GAD 137
+#define MSG_UNDERSCORE_ADD_GAD 138
+#define MSG_UP_GAD 139
+#define MSG_DOWN_GAD 140
+#define MSG_UNDERSCORE_NAME_GAD 141
+#define MSG_PATTERNS_GAD 142
+#define MSG_UNDERSCORE_UP_GAD 143
+#define MSG_UNDERSCORE_DOWN_GAD 144
+#define MSG_UNDERSCORE_INS_GAD 145
+#define MSG_UNNAMED 146
+#define MSG_INSTRUMENTS_TITLE 147
+#define MSG_VOLUME_GAD 148
+#define MSG_FINETUNE_GAD 149
+#define MSG_LENGTH_GAD 150
+#define MSG_KIND_GAD 151
+#define MSG_EDIT_DOTS_GAD 152
+#define MSG_SAMPLE_GAD 153
+#define MSG_SYNTH_GAD 154
+#define MSG_HYBRID_GAD 155
+#define MSG_INSTRUMENTS_MEN 156
+#define MSG_LOAD_MEN 157
+#define MSG_REMAP_MEN 158
+#define MSG_SAVE_COMPRESSED_MEN 159
+#define MSG_SAVE_RAW_MEN 160
+#define MSG_EMPTY 161
+#define MSG_PLAY_TITLE 162
+#define MSG_VOL_GAD 163
+#define MSG_POS_GAD 164
+#define MSG_TIME_GAD 165
+#define MSG_RST_GAD 166
+#define MSG_PLAYER_INIT_ERR 167
+#define MSG_PROGRESS_TITLE 168
+#define MSG_LOG_TITLE 169
+#define MSG_UNDERSCORE_ABORT_GAD 170
+#define MSG_PERCENT_DONE 171
+#define MSG_SONGINFO_TITLE 172
+#define MSG_UNDERSCORE_NEW_GAD 173
+#define MSG_OPEN_GAD 174
+#define MSG_SAVE_GAD 175
+#define MSG_DEL_GAD 176
+#define MSG_SONG_NAME_GAD 177
+#define MSG_AUTHOR_NAME_GAD 178
+#define MSG_DEF_TEMPO_GAD 179
+#define MSG_DEF_SPEED_GAD 180
+#define MSG_RESTART_GAD 181
+#define MSG_LENGHT_GAD 182
+#define MSG_NUM_PATTS_GAD 183
+#define MSG_NUM_TRACKS_GAD 184
+#define MSG_TOT_MOD_SIZE_GAD 185
+#define MSG_TOT_INST_SIZE_GAD 186
+#define MSG_SONG_MEN 187
+#define MSG_MERGE_SONGS_MEN 188
+#define MSG_JOIN_SONGS_MEN 189
+#define MSG_JOIN_REQUIRES_TWO_SONGS 190
+#define MSG_MERGE_REQUIRES_TWO_SONGS 191
+#define MSG_DISCARD_CURRENT_SONG 192
+#define MSG_TOOLBOX_TITLE 193
+#define MSG_PLAY_GAD 194
+#define MSG_SONGS_GAD 195
+#define MSG_PATTERNS_DOTS_GAD 196
+#define MSG_INSTRUMENTS_GAD 197
+#define MSG_SEQUENCE_DOTS_GAD 198
+#define MSG_OPTIMIZATION_GAD 199
+#define MSG_PROJECT_MEN 200
+#define MSG_NEW_MEN 201
+#define MSG_OPEN_MEN 202
+#define MSG_OPEN_NEW_MEN 203
+#define MSG_SAVE_MEN 204
+#define MSG_SAVE_AS_MEN 205
+#define MSG_CLEAR_MEN 206
+#define MSG_ABOUT_MEN 207
+#define MSG_HELP_MEN 208
+#define MSG_ICONIFY_MEN 209
+#define MSG_QUIT_MEN 210
+#define MSG_SETTINGS_MEN 211
+#define MSG_SAVE_FORMAT_MEN 212
+#define MSG_USER_INTERFACE_MEN 213
+#define MSG_SAVE_ICONS_MEN 214
+#define MSG_CONFIRM_OVERWRITE_MEN 215
+#define MSG_CONFIRM_EXIT_MEN 216
+#define MSG_VERBOSE_MEN 217
+#define MSG_OPEN_SETTINGS_MEN 218
+#define MSG_SAVE_SETTINGS_MEN 219
+#define MSG_SAVE_SETTINGS_AS_MEN 220
+#define MSG_ABOUT_TEXT 221
+#define MSG_DEFAULT 222
+#define MSG_DISABLED 223
+#define MSG_OPTIMIZATION_TITLE 224
+#define MSG_OPTIMIZE_GAD 225
+#define MSG_REM_UNUSED_PATTS_GAD 226
+#define MSG_REM_DUPLICATE_PATTS_GAD 227
+#define MSG_REM_UNUSED_INSTR_GAD 228
+#define MSG_REM_DUP_INSTR_GAD 229
+#define MSG_CUT_AFTER_LOOP_GAD 230
+#define MSG_CUT_ZERO_TAILS_GAD 231
+#define MSG_CUT_PATTERNS_GAD 232
+#define MSG_REMAP_INSTRUMENTS_GAD 233
+#define MSG_SAVED_X_BYTES 234
+#define MSG_PREFS_TITLE 235
+#define MSG_PUBLIC_SCREEN_GAD 236
+#define MSG_DISPLAY_MODE_GAD 237
+#define MSG_WINDOW_FONT_GAD 238
+#define MSG_LISTVIEW_FONT_GAD 239
+#define MSG_REQUESTERS_GAD 240
+#define MSG_USE_DATATYPES_GAD 241
+#define MSG_APPICON_GAD 242
+#define MSG_REFRESH_GAD 243
+#define MSG_LOG_TO_FILE_GAD 244
+#define MSG_LOG_LEVEL_GAD 245
+#define MSG_ASK_AUTOSAVE_GAD 246
+#define MSG_AUTOSAVE_TIME_GAD 247
+#define MSG_DO_BACKUPS_GAD 248
+#define MSG_BACKUP_TEMPLATE_GAD 249
+#define MSG_BACKUP_VERSIONS_GAD 250
+#define MSG_ASL_GAD 251
+#define MSG_REQTOOLS_GAD 252
+#define MSG_SMART_GAD 253
+#define MSG_SIMPLE_GAD 254
+#define MSG_CLONE_DEF_SCREEN 255
+#define MSG_PATTERN_TITLE 256
+#define MSG_PATTERNS_MEN 257
+#define MSG_SIZE_MEN 258
+#define MSG_EDIT_MEN 259
+#define MSG_MARK_MEN 260
+#define MSG_CUT_MEN 261
+#define MSG_COPY_MEN 262
+#define MSG_PASTE_MEN 263
+#define MSG_ERASE_MEN 264
+#define MSG_UNDO_MEN 265
+#define MSG_REDO_MEN 266
+#define MSG_EDITOR_SETTINGS_MEN 267
+#define MSG_SAVERS_TITLE 268
+#define MSG_SF_SEQUENCE_GAD 269
+#define MSG_SF_INSTRUMENTS_GAD 270
+#define MSG_SF_PATTERNS_GAD 271
+#define MSG_SF_NAMES_GAD 272
+#define MSG_ADD_ICON_GAD 273
+#define MSG_MODE_GAD 274
+#define MSG_OPTIONS_DOTS_GAD 275
+#define MSG_NONE_GAD 276
+#define MSG_XPK_GAD 277
+#define MSG_LHA_GAD 278
+#define MSG_DESCRIPTION 279
+#define MSG_AUTHOR 280
+#define MSG_MAXLENGTH 281
+#define MSG_MAXTRACKS 282
+#define MSG_MAXINSTRUMENTS 283
+#define MSG_MAXPATTERNS 284
+#define MSG_MAXPATTLEN 285
+#define MSG_MAXSAMPLELEN 286
+#define MSG_CLEAR_TITLE 287
+#define MSG_CLR_SEQUENCE_GAD 288
+#define MSG_CLR_INSTRUMENTS_GAD 289
+#define MSG_CLR_PATTERNS_GAD 290
+#define MSG_CLEARMOD_GAD 291
+#define MSG_SAMPLE_TITLE 292
+#define MSG_RENDER_MEN 293
+#define MSG_POINTS_MEN 294
+#define MSG_LINES_MEN 295
+#define MSG_FILLED_MEN 296
+#define MSG_PATTSIZE_TITLE 297
+#define MSG_LINES_GAD 298
+#define MSG_TRACKS_GAD 299
+#define MSG_DOUBLE_GAD 300
+#define MSG_HALVE_GAD 301
+#define MSG_SONG_TOO_MANY_TRACKS 302
+#define MSG_SONG_TOO_MANY_PATTS 303
+#define MSG_SONG_TOO_MANY_INSTRS 304
+#define MSG_SONG_TOO_MANY_POS 305
+#define MSG_PATT_LENGTH_INVALID 306
+#define MSG_INSTR_TOO_LONG 307
+#define MSG_WILL_MODIFY_SONG 308
+#define MSG_TRY_REMAPPING_INSTRUMENTS 309
+#define MSG_SAVING_MODULE 310
+
+/* Strings Definitions */
+#define STR(x) (AppStrings[x])
+
+
+#endif /* !XModule_CAT_H */
--- /dev/null
+##stringtype C
+##shortstrings
+/*
+** NOTE: The LocaleStrings.h file is generated automatically by `%fv'
+** using "%f0". Do NOT edit by hand!
+*/
+
+#ifndef %b_CAT_H
+#define %b_CAT_H
+
+
+/* Message Definitions */
+#define %i %d
+
+/* Strings Definitions */
+#define STR(x) (AppStrings[x])
+
+
+#endif /* !%b_CAT_H */
--- /dev/null
+##stringtype C
+##shortstrings
+/*
+** Locale.c
+**
+** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+**
+** Routines to handle localization.
+**
+** NOTE: The Locale.c file is generated automatically by `%fv'
+** using "%f0". Do NOT edit by hand!
+*/
+
+#include <libraries/locale.h>
+
+#include <proto/exec.h>
+#include <proto/locale.h>
+
+#include "XModulePriv.h"
+
+
+#define CATALOGVERSION %v
+#define CATALOGNAME "%b.catalog"
+#define MSG_COUNT %n
+
+
+STRPTR AppStrings[] =
+{
+ %s%(,)
+};
+
+static struct Library *LocaleBase = NULL;
+XDEF struct Catalog *Catalog = NULL;
+
+
+
+GLOBALCALL void SetupLocale (void)
+{
+ /* Try to open locale.library */
+ if (LocaleBase = OpenLibrary ("locale.library", 38L))
+ {
+ /* Try to get catalog for current language */
+ if (Catalog = OpenCatalog (NULL, CATALOGNAME,
+ OC_BuiltInLanguage, "english",
+ OC_Version, CATALOGVERSION,
+ TAG_DONE))
+ {
+ /* Read in locale language strings */
+ UBYTE **as = AppStrings;
+ ULONG i;
+
+ /* Get translation strings */
+ for (i = 1; i < MSG_COUNT; i++, as++)
+ *as = GetCatalogStr (Catalog, i, *as);
+ }
+ }
+
+ /* Fix for a limitation of FlexCat: MSG_NULL should translate
+ * to a NULL pointer, not a pointer to an empty string :)
+ */
+ AppStrings[0] = NULL;
+}
+
+
+
+GLOBALCALL void CleanupLocale (void)
+{
+ if (LocaleBase)
+ {
+ /* No need to check for NULL */
+ CloseCatalog (Catalog); Catalog = NULL;
+ CloseLibrary (LocaleBase); LocaleBase = NULL;
+ }
+}
--- /dev/null
+/*
+** $VER: XModule 3.9 (16.8.97) Copyright (C) 1993,94,95,96 Bernardo Innocenti
+**
+** This source code is provided "AS-IS", without warranties of any kind and
+** it is subject to change without notice. All usage is at your own risk.
+** No liability or responsibility is assumed by the author.
+**
+** Use 4 chars wide TABs to read this file
+*/
+
+#include <exec/execbase.h>
+#include <exec/lists.h>
+#include <exec/memory.h>
+#include <dos/dos.h>
+#include <dos/dostags.h>
+#include <libraries/gadtools.h>
+#include <utility/utility.h>
+#include <workbench/startup.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/icon.h>
+#include <proto/utility.h>
+#include <proto/xmodule.h>
+
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Local functions prototypes */
+
+static LONG GetShellArgs (void);
+static LONG GetWBArgs (void);
+static void DisposeArgs (void);
+static void HandleFrom (void);
+static void SetupHooks (void);
+static void Cleanup (LONG err);
+static LONG Setup (void);
+
+
+
+/* Memory pool for use by main program only */
+XDEF void *Pool = NULL;
+
+
+
+/* Version tag */
+XDEF const UBYTE Version[] = "$VER: " VSTR " " BUILDMODE " " XMODULECOPY;
+XDEF const UBYTE BaseName[] = BASENAME;
+XDEF const UBYTE PrgName[] = PRGNAME;
+
+
+
+/* Library bases */
+XDEF struct IntuitionBase *IntuitionBase = NULL;
+XDEF struct GfxBase *GfxBase = NULL;
+XDEF struct Library *LayersBase = NULL;
+XDEF struct UtilityBase *UtilityBase = NULL;
+XDEF struct Library *GadToolsBase = NULL;
+XDEF struct Library *DiskfontBase = NULL;
+XDEF struct Library *AslBase = NULL;
+XDEF struct Library *IFFParseBase = NULL;
+XDEF struct Library *WorkbenchBase = NULL;
+XDEF struct Library *IconBase = NULL;
+XDEF struct ReqToolsBase *ReqToolsBase = NULL;
+XDEF struct Library *CxBase = NULL;
+XDEF struct Library *KeymapBase = NULL;
+XDEF struct XModuleBase *XModuleBase = NULL;
+
+
+
+/* This structure holds data to open required libraries automatically. */
+
+struct OpenLibs
+{
+ struct Library **Base;
+ UBYTE *Name;
+ LONG Version;
+};
+
+
+#ifdef OS30_ONLY
+ #define OSLIBVER 39
+#else
+ #define OSLIBVER 37
+#endif /* !OS30_ONLY */
+
+static const struct OpenLibs openlibs[] =
+{
+ { (struct Library **)&IntuitionBase, "intuition.library", OSLIBVER },
+ { (struct Library **)&GfxBase, "graphics.library", OSLIBVER },
+ { (struct Library **)&LayersBase, "layers.library", OSLIBVER },
+ { (struct Library **)&UtilityBase, "utility.library", OSLIBVER },
+ { (struct Library **)&GadToolsBase, "gadtools.library", OSLIBVER },
+ { (struct Library **)&KeymapBase, "keymap.library", 37 },
+ { (struct Library **)&IFFParseBase, "iffparse.library", 37 },
+ { (struct Library **)&WorkbenchBase, "workbench.library", 37 },
+ { (struct Library **)&IconBase, "icon.library", 37 },
+ { (struct Library **)&DiskfontBase, "diskfont.library", 0 },
+
+ { NULL }
+};
+
+
+
+/* Used for argument parsing.
+ * NOTE: All fields must be 4 bytes long.
+ */
+static struct
+{
+ STRPTR *From,
+ PubScreen,
+ PortName,
+ Settings;
+ LONG CxPopup; /* Set to TRUE by default. */
+ STRPTR CxPopKey;
+ LONG *CxPriority,
+ *IconX,
+ *IconY;
+ STRPTR IconName;
+} XMArgs; /* egcs doesn't like this: = { 0 }; */
+
+static struct RDArgs *RDArgs = NULL;
+
+#define ARGS_TEMPLATE "FROM/M,PUBSCREEN/K,PORTNAME/K,SETTINGS/K,CX_POPUP/T,CX_POPKEY/K,CX_PRIORITY/K/N,ICONXPOS/K/N,ICONYPOS/K/N,ICONNAME/K"
+
+
+
+extern LONG STDARGS __main (void)
+
+/* XModule main entry point. Get arguments from CLI/Workbench,
+ * setup environment, open required files and call main event
+ * handling loop.
+ */
+{
+ LONG err;
+
+ DB(kprintf ("\n"));
+ DB(kprintf ((STRPTR)(Version + 6)));
+ DB(kprintf ("\n*** Starting debug session -- Good luck!\n\n"));
+
+
+ if (!(err = Setup())) /* Setup environment */
+ err = HandleGui();
+
+ Cleanup (err);
+
+ return err;
+} /* End main() */
+
+
+
+static LONG GetShellArgs (void)
+
+/* Parse command line arguments */
+{
+ if (!(RDArgs = ReadArgs (ARGS_TEMPLATE, (LONG *)&XMArgs, NULL)))
+ return IoErr();
+
+ return RETURN_OK;
+
+} /* End GetShellArgs() */
+
+
+
+static LONG GetWBArgs (void)
+
+/* Parse Workbench arguments */
+{
+ struct DiskObject *dobj;
+ STRPTR val;
+ UWORD i;
+
+
+ /* Get Multiselect args.
+ * Create a NULL-terminated array of STRPTRs
+ * in the same way ReadArgs() would have done.
+ */
+ if (WBenchMsg->sm_NumArgs > 1)
+ if (!(XMArgs.From = AllocVec (WBenchMsg->sm_NumArgs * sizeof (STRPTR), MEMF_CLEAR)))
+ return RETURN_FAIL;
+
+ for (i = 1; i < WBenchMsg->sm_NumArgs; i++)
+ {
+ UBYTE buf[PATHNAME_MAX];
+
+ if (NameFromLock (WBenchMsg->sm_ArgList[i].wa_Lock, buf, PATHNAME_MAX))
+ if (AddPart (buf, WBenchMsg->sm_ArgList[i].wa_Name, PATHNAME_MAX))
+ {
+ if (XMArgs.From[i-1] = AllocVec (strlen (buf), MEMF_ANY))
+ strcpy (XMArgs.From[i-1], buf);
+ else
+ break;
+ }
+ }
+
+
+ /* Get ToolTypes */
+
+ if (!(dobj = GetProgramIcon()))
+ return RETURN_FAIL;
+
+ if (val = FindToolType (dobj->do_ToolTypes, "PUBSCREEN"))
+ if (XMArgs.PubScreen = AllocVec (strlen (val), MEMF_ANY))
+ strcpy (XMArgs.PubScreen, val);
+
+ if (val = FindToolType (dobj->do_ToolTypes, "PORTNAME"))
+ if (XMArgs.PortName = AllocVec (strlen (val), MEMF_ANY))
+ strcpy (XMArgs.PortName, val);
+
+ if (val = FindToolType (dobj->do_ToolTypes, "SETTINGS"))
+ if (XMArgs.Settings = AllocVec (strlen (val), MEMF_ANY))
+ strcpy (XMArgs.Settings, val);
+
+ if (val = FindToolType (dobj->do_ToolTypes, "CX_POPUP"))
+ XMArgs.CxPopup = MatchToolValue (val, "YES");
+
+ if (val = FindToolType (dobj->do_ToolTypes, "CX_POPKEY"))
+ if (XMArgs.CxPopKey = AllocVec (strlen (val), MEMF_ANY))
+ strcpy (XMArgs.CxPopKey, val);
+
+ if (val = FindToolType (dobj->do_ToolTypes, "CX_PRIORITY"))
+ if (XMArgs.CxPriority = AllocVec (sizeof (LONG), MEMF_ANY))
+ StrToLong (val, XMArgs.CxPriority);
+
+ if (val = FindToolType (dobj->do_ToolTypes, "ICONXPOS"))
+ if (XMArgs.IconX = AllocVec (sizeof (LONG), MEMF_ANY))
+ StrToLong (val, XMArgs.IconX);
+
+ if (val = FindToolType (dobj->do_ToolTypes, "ICONYPOS"))
+ if (XMArgs.IconY = AllocVec (sizeof (LONG), MEMF_ANY))
+ StrToLong (val, XMArgs.IconY);
+
+ if (val = FindToolType (dobj->do_ToolTypes, "ICONNAME"))
+ if (XMArgs.IconName = AllocVec (strlen (val), MEMF_ANY))
+ strcpy (XMArgs.IconName, val);
+
+ FreeDiskObject (dobj);
+
+ return RETURN_OK;
+} /* End GetWBArgs() */
+
+
+
+static void DisposeArgs (void)
+{
+ if (RDArgs)
+ {
+ FreeArgs (RDArgs);
+ RDArgs = NULL;
+ }
+ else /* Workbench */
+ {
+ /* NULL is a valid parameter for FreeVec() */
+ FreeVec (XMArgs.IconName);
+ FreeVec (XMArgs.IconY);
+ FreeVec (XMArgs.IconX);
+ FreeVec (XMArgs.CxPriority);
+ FreeVec (XMArgs.CxPopKey);
+ FreeVec (XMArgs.Settings);
+ FreeVec (XMArgs.PortName);
+ FreeVec (XMArgs.PubScreen);
+
+ if (XMArgs.From)
+ {
+ STRPTR *tmp = XMArgs.From;
+
+ while (*tmp)
+ {
+ FreeVec (*tmp);
+ tmp++;
+ }
+
+ FreeVec (XMArgs.From);
+ }
+ }
+
+ memset (&XMArgs, 0, sizeof (XMArgs));
+}
+
+
+
+static void HandleFrom (void)
+{
+ if (XMArgs.From)
+ {
+ STRPTR *name = XMArgs.From;
+ struct AnchorPath *ap;
+ LONG err;
+
+ if (ap = AllocMem (sizeof (struct AnchorPath) + PATHNAME_MAX, MEMF_CLEAR))
+ {
+ OpenProgressWindow ();
+
+ ap->ap_BreakBits = SIGBREAKF_CTRL_C;
+ ap->ap_Strlen = PATHNAME_MAX;
+
+ while (*name)
+ {
+ err = MatchFirst (*name, ap);
+
+ while (!err)
+ {
+ xmLoadModule (ap->ap_Buf,
+ XMSNG_AddToList, TRUE,
+ TAG_DONE);
+
+ err = MatchNext (ap);
+ }
+
+ if (err != ERROR_NO_MORE_ENTRIES)
+ {
+ UBYTE buf[FAULT_MAX];
+
+ Fault (err, NULL, buf, FAULT_MAX);
+ ShowMessage (MSG_ERR_LOAD, *name, buf);
+ }
+
+ MatchEnd (ap);
+ name++;
+ }
+
+ CloseProgressWindow();
+
+ FreeMem (ap, sizeof (struct AnchorPath) + PATHNAME_MAX);
+ }
+ else LastErr = ERROR_NO_FREE_STORE;
+ }
+}
+
+
+
+static void SetupHooks (void)
+{
+ struct Library *XMHookBase;
+ struct FileInfoBlock *fib;
+ BPTR lock;
+ UBYTE libpath[PATHNAME_MAX];
+
+ /* Built-in hooks */
+ AddXModuleHooks ();
+ AddTrackerHooks ();
+
+
+ if (fib = (struct FileInfoBlock *)AllocDosObject (DOS_FIB, NULL))
+ {
+ if (lock = Lock (DEF_HOOKSDIR, ACCESS_READ))
+ if (Examine (lock, fib))
+ while (ExNext (lock, fib))
+ {
+ strcpy (libpath, DEF_HOOKSDIR);
+ AddPart (libpath, fib->fib_FileName, PATHNAME_MAX);
+
+ if (XMHookBase = OpenLibrary (libpath, 0))
+ {
+ #if defined(__SASC)
+ #pragma libcall XMHookBase SetupXMHook 24 801
+ void SetupXMHook (struct XModuleBase *);
+ #elif defined(__GNUC__)
+ #define SetupXMHook(xmbase) \
+ LP1NR(0x24, SetupXMHook, struct XModuleBase *, xmbase, a0, \
+ , XMHookBase)
+ #else
+ #error Define SetupXMHook() library call for your compiler
+ #endif
+
+ SetupXMHook (XModuleBase);
+ CloseLibrary (XMHookBase);
+ }
+ }
+
+ FreeDosObject (DOS_FIB, fib);
+ }
+
+
+ /* Set default saver */
+
+ if (!IsListEmpty ((struct List *)&XModuleBase->xm_Savers))
+ XModuleBase->xm_DefaultSaver = (struct XMHook *)XModuleBase->xm_Savers.mlh_Head;
+}
+
+
+
+static LONG Setup (void)
+{
+ LONG err;
+ ULONG i;
+
+ SetProgramName (PrgName);
+
+ /* Initialize view lists */
+ NEWLIST (&WindowList);
+ NEWLIST (&LogList);
+ NEWLIST (&PatternsList);
+ NEWLIST (&SequenceList);
+ NEWLIST (&InstrList);
+
+ /* Install graphics function replacements */
+ /* InstallGfxFunctions(); */ /* These are currently never used */
+
+ /* Initialize ScrInfo structure */
+ strcpy (ScrInfo.PubScreenName, BaseName);
+
+ /* Initialize PubPort name */
+ strcpy (PubPortName, BaseName);
+
+ /* Initialize PubPort name */
+ strcpy (IconName, PrgName);
+
+
+ /* Open required libraries */
+ for (i = 0 ; openlibs[i].Base ; i++)
+ if (!(*(openlibs[i].Base) = MyOpenLibrary (openlibs[i].Name, openlibs[i].Version)))
+ if (openlibs[i].Version) return RETURN_FAIL;
+
+#ifndef OS30_ONLY
+ if (UtilityBase->lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ UniqueID = GetUniqueID(); /* Get ID for HelpGroup and other jobs */
+
+ /* Get startup arguments */
+ XMArgs.CxPopup = TRUE;
+
+ if (WBenchMsg)
+ err = GetWBArgs();
+ else
+ err = GetShellArgs();
+
+ if (err) return err;
+
+ SetupLocale();
+
+ /* Create XModule library */
+ if (err = MakeXModuleLibrary ())
+ return err;
+
+ Pool = XModuleBase->xm_Pool;
+
+
+ /* Try to load XModule preferences */
+ if (XMArgs.Settings)
+ {
+ if (LoadPrefs (XMArgs.Settings))
+ {
+ UBYTE buf[FAULT_MAX];
+
+ Fault (IoErr(), NULL, buf, FAULT_MAX);
+ ShowMessage (MSG_ERR_LOAD, XMArgs.Settings, buf);
+ }
+ }
+ else
+ {
+ if (LoadPrefs ("PROGDIR:" PRGNAME ".prefs"))
+ LoadPrefs ("ENV:" PRGNAME ".prefs");
+ }
+
+
+ /* Use startup Arguments */
+
+ if (XMArgs.PubScreen)
+ strncpy (ScrInfo.PubScreenName, XMArgs.PubScreen, 31);
+
+ if (XMArgs.PortName)
+ strncpy (PubPortName, XMArgs.PortName, 15);
+
+ CxPopup = XMArgs.CxPopup;
+
+ if (XMArgs.CxPopKey)
+ strncpy (CxPopKey, XMArgs.CxPopKey, 31);
+
+ if (XMArgs.CxPriority)
+ CxPri = *XMArgs.CxPriority;
+
+ if (XMArgs.IconX)
+ IconX = *XMArgs.IconX;
+
+ if (XMArgs.IconY)
+ IconY = *XMArgs.IconY;
+
+ if (XMArgs.IconName)
+ strncpy (IconName, XMArgs.IconName, 15);
+
+ /* Setup FileRequesters if LoadPrefs() hasn't already done it */
+ if (!AslBase && !ReqToolsBase)
+ if (err = SetupRequesters())
+ return err;
+
+ /* Setup App Message Port */
+ SetupApp();
+
+ /* Setup Rexx Host */
+ CreateRexxPort();
+
+ /* Setup Commodity object */
+ SetupCx();
+
+ /* Add internal hooks and load external ones */
+ SetupHooks();
+
+ /* Allocate a new SongInfo structure */
+ if (!XModuleBase->xm_CurrentSong)
+ {
+ struct SongInfo *si;
+
+ if (si = xmCreateSong (
+ SNGA_ReadyToUse, TRUE,
+ XMSNG_AddToList, -1,
+ XMSNG_Active, TRUE,
+ TAG_DONE))
+ ReleaseSemaphore (&si->Lock);
+ else
+ return ERROR_NO_FREE_STORE;
+ }
+
+ /* Open screen and ToolBox window */
+ if (CxPopup)
+ if (err = SetupScreen())
+ return err;
+
+
+ /* Load modules requested with Shell/Workbench startup arguments */
+
+ HandleFrom();
+
+ DisposeArgs();
+
+ return 0;
+}
+
+
+
+static void Cleanup (LONG err)
+
+/* Cleanup routine. Display error message, free all resources & exit */
+{
+ ULONG i;
+
+ if (err > 100)
+ PrintFault (err, PrgName);
+
+ /* Free all allocated resources */
+
+ CleanupAudio();
+
+ DisposeArgs(); /* Just to be sure */
+
+ FreeFReq();
+ CloseDownScreen();
+
+ if (Pool)
+ {
+ FreeVecPooled (Pool, ScreenAttr.ta_Name);
+ FreeVecPooled (Pool, WindowAttr.ta_Name);
+ FreeVecPooled (Pool, ListAttr.ta_Name);
+ FreeVecPooled (Pool, EditorAttr.ta_Name);
+ }
+
+ /* Dispose XModule library */
+ DisposeXModuleLibrary ();
+
+ /* Remove AppIcons/AppWindows Port */
+ CleanupApp();
+
+ /* Remove Commodity Broker */
+ CleanupCx();
+
+ /* Remove ARexx port */
+ DeleteRexxPort();
+
+ CleanupLocale();
+
+ /* Close all libraries */
+ for (i = 0 ; openlibs[i].Base ; i++)
+ /* starting with V36, NULL is a valid parameter for CloseLibrary(). */
+ CloseLibrary (*(openlibs[i].Base));
+}
+
+
+
+/*!*/
+/* Emergency patch: */
+
+LONG SampleWinTags[1] = { 0 };
+void UpdateSampleMenu (void) {}
--- /dev/null
+##
+## $VER: XModule_Makefile 4.1 (15.8.98)
+##
+## Copyright (C) 1993,94,95,96,97,98 by Bernardo Innocenti
+##
+## This Makefile must be processed with GNU make 3.76.1
+## Use 4 chars wide TABs to read this file
+##
+## Please see below to know what developement tools you need to
+## install in order to build XModule and all related files.
+##
+##
+## Relevant build targets:
+##
+## make all - Make all binaries. This is the default
+## make release - Make distribution archives
+## make bumprev - Bump the revision number of XModule
+## make newctfiles - Make new .ct files for catalog translators
+## make clean - Removes all objects and temporary files
+
+include config.mk
+
+###########################################################
+# Tools used to make this project:
+###########################################################
+#
+# SAS/C 6.58 is a commercial product. Updates are available on Aminet
+# PhxAss 4.38 is on Aminet in the archive "dev/asm/PhxAss438.lha"
+# PhxLnk 4.31 is on Aminet in the archive "dev/asm/PhxLnk431.lha"
+# FlexCat 2.1 is on Aminet in the archive "dev/misc/FlexCat.lha"
+# FD2Pragma 2.52 is on Aminet in the archive "dev/misc/FD2Pragma.lha"
+# ADtoHT 2.1 is on Aminet in the archive "text/hyper/ADtoHT2_1.lha"
+# RevUp 1.4 is on Aminet in the archive "dev/misc/RevUp1_4.lha"
+# GNU Make 3.76.1 is on Aminet in the archive "dev/c/make_bin.lha"
+# GNU MakeInfo 1.64 is part of the GeekGadgets project
+# scompare is part of the SAS/C Developement System and is not freely distributable
+# autodoc is in the 3.1 NDTK or in the Amiga Developer CD 1.2
+#
+# 3.1 Includes is in the 3.1 NDTK or in the Amiga Developer CD 1.2
+# XPK 2.5 DTK is on Aminet in the archive "util/pack/×pk25dev.lha"
+# ReqTools DTK is on Aminet in the archive "util/libs/ReqToolDev.lha"
+#
+#
+# My ethernal gratitude to the clever authors of these useful tools!
+#
+
+###########################################################
+# Additional configuration
+###########################################################
+#
+# Subdirectories to work in
+#
+SUBDIRS := Hooks gadgets manuals
+
+
+###########################################################
+# Source files to compile for the XModule executible
+###########################################################
+#
+#
+
+SRCS := Main.c Misc.c Rexx.c Gui.c Requesters.c Prefs.c Locale.c \
+ Audio.c Compress.c App.c Cx.c Help.c Operators.c Instr.c \
+ SongClass.c Library.c XModuleHook.c TrackerHook.c CustomClasses.c \
+ ToolBoxWin.c OptimizationWin.c ClearWin.c InstrumentsWin.c \
+ SongInfoWin.c SequenceWin.c ProgressWin.c SaversWin.c \
+ PlayWin.c PrefsWin.c PatternWin.c PattPrefsWin.c
+
+# List the objects from the source files
+OBJS := $(SRCS:%.c=$(OBJDIR)/%.o)
+
+
+# NOTE: SampleWin.o removed because still not updated to new GUI system
+
+
+
+###########################################################
+# Targets to make for normal builds. This is the default
+###########################################################
+#
+# NOTE: the GST file is put here to have it remade when needed.
+# the source files do not depend on $(GST) because otherwise
+# make would rebuild all them when the GST is rebuild.
+
+all: $(GST) $(PROJNAME) Players/32Channels.player subdirs
+
+final: $(GST) $(PROJNAME)_final Players/32Channels.player subdirs
+
+subdirs:
+ @$(foreach subdir, $(SUBDIRS), $(MAKE) -w $(MAKEFLAGS) -C $(subdir) @@)
+
+
+
+###########################################################
+# Patch files for special binary versions
+###########################################################
+
+patches: \
+ Patches/XModule_020.pch \
+ Patches/XModule_020_OS30.pch \
+ Patches/XModule.guide_39.pch \
+ Patches/XModule.guide_40.pch \
+ Patches/XModule.doc.pch \
+ Patches/pattedit.gadget_020.pch \
+ Patches/pattedit.gadget_020_OS30.pch
+
+
+###########################################################
+# Files to make for the distribution
+###########################################################
+
+release: all patches catalogs autodocs
+
+catalogs:
+ @$(MAKE) -w $(MAKEFLAGS) -C catalogs
+
+
+###########################################################
+# Make the Global Symbol Table used to speed up SAS/C
+###########################################################
+
+ifneq ($(strip $(GST)),)
+
+$(GST): XModulePriv.h Gui.h LocaleStrings.h Gst.c \
+ include/libraries/xmodule.h include/libraries/xmoduleclass.h \
+ include/libraries/songclass.h include/libraries/patteditclass.h \
+ include/pragmas/xmodule_pragmas.h include/clib/xmodule_protos.h
+ $(CC) FROM Gst.c MakeGst=$(GST) NOOBJNAME $(CFLAGS)
+
+endif
+
+###########################################################
+# Make the main XModule executable
+###########################################################
+#
+# Generic rule for making all the objects
+# NOTE: the `:' path separator must be escaped to avoid confusing GNU make
+#
+$(OBJS): $(subst :,\:,$(OBJDIR))/%.o: %.c LocaleStrings.h
+ $(CC) $< $(CC_OUT) $@ $(CFLAGS) $(MORE_CFLAGS)
+
+$(OBJDIR)/Startup.o: Startup.asm
+ $(AS) Startup.asm TO $@ $(ASFLAGS) $(MORE_ASFLAGS) SET "STKSIZE=$(STACKSIZE)"
+
+# NOTE: the following rule works around the command line length limit of the
+# Amiga native shell by striping away the path component from $(OBJECTS)
+$(PROJNAME): $(OBJDIR)/Startup.o $(OBJS)
+ cd $(OBJDIR) @@\
+ $(LD) FROM $(OBJDIR)/Startup.o $(OBJS:$(OBJDIR)/%=%) TO $@ $(LDFLAGS) $(LIBS) @@\
+ move $@ $(TOP)/$@
+
+# EXPERIMENTAL: to get better code optimization, we compile all the source
+# code modules at once, by including all them in a single dummy source file.
+#
+$(PROJNAME)_final: $(OBJDIR)/Startup.o $(PROJNAME)_final.o
+ $(LD) FROM $(OBJDIR)/Startup.o $(OBJDIR)/$(PROJNAME)_final.o TO $@ $(LDFLAGS) $(LIBS)
+
+$(PROJNAME)_final.o: $(PROJNAME)_final.c
+ $(CC) $(PROJNAME)_final.c $(CC_OUT) $(OBJDIR)/$(PROJNAME)_final.o $(CFLAGS) $(MORE_CFLAGS) \
+ DEF SINGLE_OBJECT
+
+$(PROJNAME)_final.c: $(SRCS)
+ echo "/** This source file is automatically generated - do not edit **/" >$(PROJNAME)_final.c
+ list $(SRCS) LFORMAT "#include *"%p%n*"" >>$(PROJNAME)_final.c
+
+
+###########################################################
+# Make other executable flavors
+###########################################################
+
+XModule_020: XModule
+ $(MAKE) -u "MORE_CFLAGS=CPU 68020" "MORE_ASFLAGS=MACHINE 68020" XModule
+ Move XModule XModule_020
+ $(MAKE) clean
+
+XModule_OS30: XModule
+ $(MAKE) -u "MORE_CFLAGS=DEF OS30_ONLY" "MORE_ASFLAGS=SET OS30_ONLY" $(GST) XModule
+ Move XModule XModule_OS30
+ $(MAKE) clean
+
+XModule_020_OS30: XModule
+ $(MAKE) -u "MORE_CFLAGS=CPU 68020 DEF OS30_ONLY" \
+ "MORE_ASFLAGS=SET OS30_ONLY MACHINE 68020" $(GST) XModule
+ Move XModule XModule_020_OS30
+ $(MAKE) clean
+
+
+
+
+###########################################################
+# Make 32Channels.player
+###########################################################
+
+Player.o: Player.asm
+ $(AS) Player.asm $(ASFLAGS) $(MORE_ASFLAGS)
+
+Players/32Channels.player: Player.o
+ $(LD) Player.o TO Players/32Channels.player LIB:Small.lib NODEBUG SMALLCODE SMALLDATA
+
+
+
+###########################################################
+# Locale related stuff
+###########################################################
+#
+
+LocaleStrings.h Locale.c: LocaleStrings_h.sd Locale_c.sd Catalogs/$(PROJNAME).cd
+ $(FLEXCAT) Catalogs/$(PROJNAME).cd LocaleStrings.h=LocaleStrings_h.sd Locale.c=Locale_c.sd
+
+# Make NewCTFiles to create updated CT files for all supported languages
+
+NewCTFiles:
+ @$(MAKE) -w $(MAKEFLAGS) -C catalogs NewCTFiles
+
+
+
+
+###########################################################
+# Make xmodule_pragmas.h
+###########################################################
+#
+# Do not use SAS/C's fd2pragma! What you need here is FD2Pragma 2.52 or newer.
+# It can be found on Aminet in the archive "dev/misc/FD2Pragma.lha".
+#
+
+include/pragmas/xmodule_pragmas.h: xmodule_lib.fd
+ $(FD2PRAGMA) FDFILE xmodule_lib.fd SPECIAL 6 TO include/pragmas/xmodule_pragmas.h
+ $(FD2PRAGMA) FDFILE xmodule_lib.fd SPECIAL 10 TO include/libraries/xmodule_lib.i
+ $(FD2PRAGMA) FDFILE xmodule_lib.fd SPECIAL 18 TO include/proto/xmodule.h
+
+
+
+###########################################################
+# Make AutoDocs
+###########################################################
+#
+# autodoc is a standard Commodore developer tool. You can
+# find it in the 3.1 NDTK.
+#
+# ADtoHT 2.1 is an AutoDoc to AmigaGuide file conversion
+# utility made by Christian Stieber. It can be found on
+# Aminet in the directory "text/hyper/".
+#
+
+autodocs: Autodocs/xmodule.doc Autodocs/songclass.doc
+
+Autodocs/xmodule.doc Autodocs/songclass.doc: Library.c SongClass.c
+ $(CP) include/libraries/xmodule.h include/libraries/songclass.h Autodocs/
+ autodoc -t4 -C -I -c Library.c >Autodocs/xmodule.doc
+ autodoc -t4 -C -I -c SongClass.c >Autodocs/songclass.doc
+ SC:Doc/ADtoHT Autodocs/ RAM: Autodocs/ Autodocs/ VERSION 39
+ $(RM) Autodocs/xmodule.h
+ $(RM) Autodocs/songclass.h
+
+
+###########################################################
+# Bump the revision
+###########################################################
+#
+# RevUp is a developer tool by Boris Folgmann which is similar to BumpRev.
+# You can find it on Aminet in the archive "dev/misc/RevUp1_4.lha".
+#
+
+bumprev:
+ RevUp 3 XModule NOASM EXTRA TINY
+
+
+###########################################################
+# Update dependencies
+###########################################################
+#
+# EXPERIMENTAL - Do not use
+#
+
+depend:
+ gcc -Iinclude: -Iinclude -MM *.c >depend
+
+# other handy gcc options:
+# -O2 -fomit-frame-pointer -fstrength-reduce -finline-functions -funroll-loops
+# -W -Wunused -Wuninitialized -Wparentheses Wreturn-type -Wshadow -Wcast-align
+# -Wstrict-prototypes
+
+###########################################################
+# Cleanup all mess
+###########################################################
+
+clean:
+ -$(RM) *.pch *.map *.gst *.xref XModule XModule.doc \
+ Locale.c LocaleStrings.h Players/*.player Patches/*.pch $(PROJNAME)_final.c depend
+ -$(RM) $(OBJDIR)/*.o
+ @$(MAKE) -w $(MAKEFLAGS) -C Hooks clean
+ @$(MAKE) -w $(MAKEFLAGS) -C gadgets clean
+ @$(MAKE) -w $(MAKEFLAGS) -C catalogs clean
+
+
+
+##################################################################################
+# Make patches for special executables optimized for different CPU and OS version
+##################################################################################
+
+Patches/XModule_020.pch: XModule_020
+ scompare -o$@ XModule XModule_020
+
+Patches/XModule_020_OS30.pch: XModule_020_OS30
+ scompare -o$@ XModule XModule_020_OS30
+
+Patches/pattedit.gadget_020.pch: Gadgets/pattedit.gadget_020
+ scompare -o$@ Gadgets/pattedit.gadget Gadgets/pattedit.gadget_020
+
+Patches/pattedit.gadget_020_OS30.pch: Gadgets/pattedit.gadget_020_OS30
+ scompare -o$@ Gadgets/pattedit.gadget Gadgets/pattedit.gadget_020_OS30
+
+Patches/XModule.guide_39.pch: manuals/XModule.guide_39
+ scompare -o$@ manuals/XModule.guide_34 manuals/XModule.guide_39
+
+Patches/XModule.guide_40.pch: manuals/XModule.guide_40
+ scompare -o$@ manuals/XModule.guide_34 manuals/XModule.guide_40
+
+Patches/XModule.doc.pch: manuals/XModule.txt
+ scompare -o$@ manuals/XModule.doc manuals/XModule.txt
+
+
+
+###########################################################
+# Make the distribution archives
+###########################################################
+#
+# Note: We can't use the `#?' wildcard because the `#' character
+# is also used to start a comment in a Makefile. This script
+# will work only when the dos wildstar flag is set.
+#
+
+release: distribution_archive source_archive
+
+distribution_archive:
+ Execute <<
+ If NOT EXISTS $(DISTPREFIX)/XModule
+ MakeDir $(DISTPREFIX)/XModule
+ EndIf
+
+ $(CP) XModule $(DISTPREFIX)/XModule/
+ $(CP) manuals/XModule.doc manuals/HISTORY manuals/README $(DISTPREFIX)/XModule/
+ $(CP) manuals/XModule.guide_34 $(DISTPREFIX)/XModule/XModule.guide
+ $(CP) Players/* $(DISTPREFIX)/XModule/Players/
+ $(CP) Hooks/*.xmhook $(DISTPREFIX)/XModule/Hooks/
+ $(CP) Gadgets/pattedit.gadget $(DISTPREFIX)/XModule/Gadgets/
+ $(CP) Patches/*.pch $(DISTPREFIX)/XModule/Patches/
+
+ $(RM) ALL $(DISTPREFIX)/XModule/Catalogs/**
+ $(CP) ALL Catalogs $(DISTPREFIX)/XModule/Catalogs/
+
+
+ CD $(DISTPREFIX)
+ If EXISTS $(ARCNAME)
+ $(RM) $(ARCNAME)
+ EndIf
+ $(ARCHIVER) $(ARCNAME) XModule/** XModule.info
+ <
+
+source_archive:
+ Execute <<
+ If EXISTS $(SRCARCNAME)
+ $(RM) $(SRCARCNAME)
+ EndIf
+
+ $(RM) ALL $(SRCDISTDIR)/XModuleSource/**
+
+ $(CP) *.c $(DISTPREFIX)/XModuleSource/
+ $(CP) *.h $(DISTPREFIX)/XModuleSource/
+ $(CP) *.asm TO $(DISTPREFIX)/XModuleSource/
+ $(CP) ALL Include $(DISTPREFIX)/XModuleSource/Include
+ $(CP) XModule.texi MakeFile XModule_rev.rev LocaleStrings_h.sd Locale_c.sd TODO SCOPTIONS $(DISTPREFIX)/XModuleSource/
+
+ $(CP) FakePlayer.asm $(DISTPREFIX)/XModuleSource/Player.asm
+ $(RM) $(DISTPREFIX)XModuleSource/FakePlayer.asm
+
+ If EXISTS $(SRCARCNAME)
+ $(RM) $(SRCARCNAME)
+ EndIf
+ $(ARCHIVER) $(SRCARCNAME) include
+ CD $(DISTPREFIX)
+ $(ARCHIVER) $(SRCARCNAME) XModuleSource XModuleSource.info
+ <
+
+
+
+###########################################################
+# Phony targets (targets which arn't really files to make)
+###########################################################
+
+.PHONY: all patches release autodocs distribution_archive source_archive \
+ NewCTFiles bumprev depend clean subdirs $(SUBDIRS)
+
+#end
--- /dev/null
+/*
+** Misc.c
+**
+** Copyright (C) 1993,94,95,96 Bernardo Innocenti
+**
+** Parts of this file are:
+**
+** Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
+** All Rights Reserved
+**
+** Miscellaneus useful functions
+*/
+
+#include <exec/ports.h>
+#include <workbench/startup.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/icon.h>
+#include <proto/graphics.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+#if 0 /* --- This part has been removed --- */
+
+/* Function pointers to Kickstart-sensitive RastPort query code */
+ULONG (*ReadAPen)(struct RastPort *RPort);
+ULONG (*ReadBPen)(struct RastPort *RPort);
+ULONG (*ReadDrMd)(struct RastPort *RPort);
+
+
+
+/* Local function prototypes */
+static ULONG OldGetAPen (struct RastPort *RPort);
+static ULONG OldGetBPen (struct RastPort *RPort);
+static ULONG OldGetDrMd (struct RastPort *RPort);
+static ULONG NewGetAPen (struct RastPort *RPort);
+static ULONG NewGetBPen (struct RastPort *RPort);
+static ULONG NewGetDrMd (struct RastPort *RPort);
+
+#endif /* --- This part has been removed --- */
+
+
+
+GLOBALCALL struct DiskObject *GetProgramIcon (void)
+
+/* Get program associated icon.
+ * This function will fail if we are not a son of Workbench.
+ * The returned DiskObject must be freed by FreeDiskObject().
+ */
+{
+ struct DiskObject *dobj;
+ BPTR olddir;
+
+ if (!WBenchMsg) return NULL;
+
+ olddir = CurrentDir (WBenchMsg->sm_ArgList->wa_Lock);
+ dobj = GetDiskObject (WBenchMsg->sm_ArgList->wa_Name);
+ CurrentDir (olddir);
+
+ return dobj;
+}
+
+
+
+GLOBALCALL struct Library *MyOpenLibrary (CONST_STRPTR name, ULONG ver)
+{
+ struct Library *lib;
+ BPTR progdir = NULL;
+
+ while (!(lib = OpenLibrary (name, ver)))
+ {
+ UBYTE path[PATHNAME_MAX];
+
+
+ /* Search the library file in "PROGDIR:"... */
+
+ strcpy (path, "PROGDIR:");
+ AddPart (path, name, PATHNAME_MAX);
+ if (lib = OpenLibrary (path, ver))
+ break;
+
+ strcpy (path, "PROGDIR:Libs");
+ AddPart (path, name, PATHNAME_MAX);
+ if (lib = OpenLibrary (path, ver))
+ break;
+
+ /* Tell the user we can't find this library anywhere... */
+ if (!ShowRequest (MSG_OPENLIB_FAIL, MSG_RETRY_OR_CANCEL, name, ver))
+ break;
+ }
+
+ if (progdir) UnLock (progdir);
+
+ return lib;
+}
+
+
+
+GLOBALCALL void CantOpenLib (CONST_STRPTR name, LONG ver)
+
+/* Notify the user that a library didn't open */
+{
+ if (ver)
+ ShowRequest (MSG_OPENLIB_VER_FAIL, MSG_CONTINUE, name, ver);
+ else
+ ShowRequest (MSG_OPENLIB_FAIL, MSG_CONTINUE, name);
+}
+
+
+
+GLOBALCALL void KillMsgPort (struct MsgPort *mp)
+
+/* Reply all pending messages and call DeletePort() */
+{
+ struct Message *msg;
+
+ Forbid(); /* is this really useful? */
+
+ /* Reply all pending Messages */
+ while (msg = GetMsg (mp))
+ ReplyMsg (msg);
+
+ DeleteMsgPort (mp);
+
+ Permit();
+}
+
+
+
+GLOBALCALL struct TextAttr *CopyTextAttrPooled (void *pool, const struct TextAttr *source, struct TextAttr *dest)
+
+/* Copy <source> textattr structure over <dest>, allocating and copying
+ * the ta_Name field. <dest>->ta_Name if FreeVec()ed before
+ * allocating the new one.
+ *
+ * Returns: <dest> if everything was ok, NULL for failure.
+ */
+
+{
+ FreeVecPooled (pool, dest->ta_Name);
+
+ memcpy (dest, source, sizeof (struct TextAttr));
+
+ if (dest->ta_Name = AllocVecPooled (pool, strlen (source->ta_Name) + 1))
+ {
+ strcpy (dest->ta_Name, source->ta_Name);
+ return dest;
+ }
+ return NULL;
+}
+
+
+
+GLOBALCALL UWORD CmpTextAttr (const struct TextAttr *ta1, const struct TextAttr *ta2)
+
+/* Compares two TextAttr structures and returns 0 if they refer to
+ * the same font, a non-zero value otherwise.
+ */
+{
+ if (ta1->ta_YSize == ta2->ta_YSize && ta1->ta_Style == ta2->ta_Style)
+ {
+ if (!ta1->ta_Name && !ta2->ta_Name)
+ return 0;
+
+ if (!ta1->ta_Name || !ta2->ta_Name)
+ return 1;
+
+ if (!strcmp (ta1->ta_Name, ta2->ta_Name))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+
+/* More memory pools support */
+
+#ifdef PORTABLE
+
+GLOBCALL void *AllocVecPooled (void *pool, ULONG size)
+{
+ void *mem;
+
+ size += 4;
+
+#ifdef OS30_ONLY
+ if (mem = AllocPooled (pool, size))
+#else
+ if (mem = AsmAllocPooled (pool, size))
+#endif
+ *((ULONG *)mem)++ = size;
+
+ return mem;
+}
+
+
+
+GLOBCALL void FreeVecPooled (void *pool, void *memory)
+{
+ if (mem)
+ {
+ --(((ULONG *)mem);
+#ifdef OS30_ONLY
+ FreePooled (pool, mem, *((ULONG *)mem));
+#else
+ AsmFreePooled (pool, mem, *((ULONG *)mem));
+#endif
+ }
+}
+
+
+
+GLOBCALL void *CAllocVecPooled (void *pool, ULONG size)
+
+{
+ void *mem;
+
+ size += 4;
+
+#ifdef OS30_ONLY
+ if (mem = AllocPooled (pool, size))
+#else
+ if (mem = AsmAllocPooled (pool, size))
+#endif
+ {
+ memset (mem, size, 0);
+ *((ULONG *)mem)++ = size;
+ }
+
+ return mem;
+}
+
+#endif /* PORTABLE */
+
+
+
+GLOBALCALL STRPTR DupStringPooled (void *pool, CONST_STRPTR source, STRPTR *dest)
+
+/* Allocates a buffer big enough to fit the <source> string
+ * using AllocVecPooled() and then copies the contents of <source>
+ * there. If the buffer pointed by <dest> is not NULL,
+ * DupStringPooled() will first check to see if <source> and
+ * <dest> are identical, in which case no new buffer will be
+ * allocated and the result will be FALSE. Otherwise, <dest> will
+ * be deallocated and a new buffer will be allocated. Both <source> and <*dest>
+ * can be NULL.
+ *
+ * RESULT
+ * Non-zero if <dest> has been reallocated, FALSE otherwise.
+ */
+{
+ if (*dest)
+ {
+ if (source)
+ {
+ if (!(strcmp (*dest, source)))
+ return NULL;
+ }
+
+ FreeVecPooled (pool, *dest);
+ }
+
+ if (source)
+ {
+ if (*dest = AllocVecPooled (pool, strlen (source) + 1))
+ strcpy (*dest, source);
+ }
+
+ return *dest;
+}
+
+
+
+GLOBALCALL void FilterName (STRPTR name)
+
+/* Finds and blanks out invalid characters in a string.
+ * Will also strip blanks at the end. Passing NULL is safe.
+ */
+{
+ UWORD i = 0;
+
+ if (!name) return;
+
+ while (name[i])
+ {
+ if (name[i] < ' ' ||
+ (name[i] >'~' && name[i] < '¡'))
+ name[i] = ' ';
+
+ i++;
+ }
+
+ /* Kill blanks at the end of the string */
+ for (--i; i > 0 ; i--)
+ if (name[i] == ' ') name[i] = '\0';
+ else break;
+}
+
+
+
+GLOBALCALL LONG PutIcon (CONST_STRPTR source, CONST_STRPTR dest)
+
+/* Add the <source> icon to <dest> file */
+{
+ struct DiskObject *dobj;
+ UBYTE buf[PATHNAME_MAX];
+
+ /* We do not alter existing icons */
+ if (dobj = GetDiskObject (dest))
+ {
+ FreeDiskObject (dobj);
+ return RETURN_WARN;
+ }
+
+ /* Get source icon */
+
+ strcpy (buf, "PROGDIR:Icons");
+ AddPart (buf, source, PATHNAME_MAX);
+ if (!(dobj = GetDiskObject (buf)))
+ {
+ strcpy (buf, "ENV:Sys");
+ AddPart (buf, source, PATHNAME_MAX);
+ if (!(dobj = GetDiskObject (buf)))
+ {
+ /* Get default project icon */
+ dobj = GetDefDiskObject (WBPROJECT);
+ }
+ }
+
+ if (dobj)
+ {
+ dobj->do_CurrentX = dobj->do_CurrentY = NO_ICON_POSITION;
+
+ if (!(dobj->do_DefaultTool[0]))
+ {
+ /* Get program path and store in icon's Default Tool */
+
+ BPTR progdir;
+
+ dobj->do_DefaultTool = NULL;
+
+ if (WBenchMsg) /* WB */
+ progdir = WBenchMsg->sm_ArgList->wa_Lock;
+ else /* CLI */
+ progdir = GetProgramDir();
+
+ if (progdir)
+ {
+ if (NameFromLock (progdir, buf, PATHNAME_MAX))
+ {
+ UBYTE progname[32];
+
+ if (WBenchMsg) /* WB */
+ strncpy (progname, WBenchMsg->sm_ArgList->wa_Name, 32);
+ else /* CLI*/
+ GetProgramName (progname, 32);
+
+ if(AddPart (buf, progname, PATHNAME_MAX))
+ dobj->do_DefaultTool = buf;
+ }
+ }
+ }
+
+ if (!dobj->do_DefaultTool) dobj->do_DefaultTool = BaseName;
+ PutDiskObject (dest, dobj);
+ FreeDiskObject (dobj);
+ return RETURN_OK;
+ }
+
+ return RETURN_FAIL;
+}
+
+
+
+static ULONG MakeBackupName (STRPTR buf, CONST_STRPTR s, CONST_STRPTR tmpl, ULONG n)
+
+/* DESCRIPTION
+ * Subfunction for BackupFile() - Constructs a file name for the backup.
+ * <tmpl> is the template to build the backup name.
+ * Each occurence of the '#' character in the template is replaced with
+ * an ASCII decimal representation of <n>. The '*' character is replaced
+ * with the original filename <s>.
+ *
+ * RESULT
+ * The result is stored in <buf>. Returns TRUE if at least one occurrence
+ * of the '#' wildcard has been replaced, otherwise returns FALSE.
+ *
+ * BUGS
+ * Does not check for buffer overflow.
+ *
+ */
+{
+ BOOL wild_done = FALSE;
+ BOOL num_done = FALSE;
+
+ do
+ {
+ if ((*tmpl == '*') && (!wild_done))
+ {
+ /* Copy file name */
+
+ s = FilePart (s);
+ while (*buf++ = *s++);
+ --buf;
+
+ wild_done = TRUE;
+ }
+ else if (*tmpl == '#')
+ {
+ /* Insert backup number */
+
+ if (n > 9)
+ *buf++ = (n / 10) + '0';
+ *buf++ = (n % 10) + '0';
+
+ num_done = TRUE;
+ }
+ else if (*tmpl == '\\')
+ /* Handle escape character */
+ *buf++ = *++tmpl;
+ else
+ *buf++ = *tmpl;
+ }
+ while (*tmpl++);
+
+ return num_done;
+}
+
+
+
+GLOBALCALL LONG BackupFile (CONST_STRPTR src, CONST_STRPTR template, ULONG versions)
+
+/* Creates backups of the given file */
+{
+ UBYTE buf[PATHNAME_MAX], oldbuf[PATHNAME_MAX];
+ ULONG i;
+ BPTR lock, dir, olddir;
+
+ if (!(lock = Lock (src, ACCESS_READ)))
+ return IoErr();
+
+ /* CD to source directory: needed for relative paths */
+ dir = ParentDir (lock);
+ olddir = CurrentDir (dir);
+
+
+ if (!MakeBackupName (buf, src, template, 1))
+ {
+ /* Simple backup */
+ DeleteFile (buf);
+ }
+ else
+ {
+ /* Delete oldest backup if exists */
+
+ MakeBackupName (buf, src, template, versions);
+ DeleteFile (buf);
+
+ /* Shift all others ahead... */
+
+ for (i = versions; i > 0; --i)
+ {
+ strcpy (oldbuf, buf);
+ MakeBackupName (buf, src, template, i);
+ Rename (buf, oldbuf);
+
+ DB (kprintf ("Backup name is: %s\n", buf));
+ }
+ }
+
+ /* And finally move file to its backup location */
+ Rename (src, buf);
+
+ /* Restore old current directory */
+ CurrentDir (olddir);
+ UnLock (dir);
+ UnLock (lock);
+
+ return RETURN_OK;
+}
+
+
+
+#if 0 /* --- This part has been removed --- */
+
+GLOBALCALL void InstallGfxFunctions (void)
+
+/* Install the correct routines to query
+ * the rendering colours and drawing mode.
+ */
+{
+ if(GfxBase->lib_Version >= 39)
+ {
+ ReadAPen = NewGetAPen;
+ ReadBPen = NewGetBPen;
+ ReadDrMd = NewGetDrMd;
+ }
+ else
+ {
+ ReadAPen = OldGetAPen;
+ ReadBPen = OldGetBPen;
+ ReadDrMd = OldGetDrMd;
+ }
+}
+
+
+
+/* OldGetAPen(struct RastPort *RPort):
+ *
+ * Query the current primary rendering colour (old style).
+ */
+
+static ULONG OldGetAPen (struct RastPort *RPort)
+{
+ return((ULONG)RPort->FgPen);
+}
+
+
+
+/* OldGetBPen(struct RastPort *RPort):
+ *
+ * Query the current seconary rendering colour (old style).
+ */
+
+static ULONG OldGetBPen (struct RastPort *RPort)
+{
+ return((ULONG)RPort->BgPen);
+}
+
+
+
+/* OldGetDrMd(struct RastPort *RPort):
+ *
+ * Query the current drawing mode (old style).
+ */
+
+static ULONG OldGetDrMd (struct RastPort *RPort)
+{
+ return((ULONG)RPort->DrawMode);
+}
+
+
+
+/* NewGetAPen(struct RastPort *RPort):
+ *
+ * Query the current primary rendering colour (new style).
+ */
+
+static ULONG NewGetAPen (struct RastPort *RPort)
+{
+ return(GetAPen (RPort));
+}
+
+
+
+/* NewGetBPen(struct RastPort *RPort):
+ *
+ * Query the current seconary rendering colour (new style).
+ */
+
+static ULONG NewGetBPen (struct RastPort *RPort)
+{
+ return (GetBPen (RPort));
+}
+
+
+
+/* NewGetDrMd(struct RastPort *RPort):
+ *
+ * Query the current drawing mode (new style).
+ */
+
+static ULONG NewGetDrMd (struct RastPort *RPort)
+{
+ return(GetDrMd (RPort));
+}
+
+#endif /* --- This part has been removed --- */
--- /dev/null
+/*
+** Operators.c
+**
+** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
+**
+** General pourpose module handling/processing functions.
+*/
+
+#include <exec/memory.h>
+
+#include <proto/exec.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+/* Local function prototypes */
+
+static void BreakPattern (struct Note **arr, UWORD rows, UWORD tracks);
+
+
+
+GLOBALCALL ULONG InsertPattern (struct SongInfo *si, struct Pattern *patt, UWORD patnum)
+
+/* Inserts a pattern at any song position. Patterns >= patnum will be moved
+ * ahead one slot. The position table is updated inserting references to
+ * the new pattern immediately before each occurence of patnum, so that the
+ * two patterns are always played together.
+ *
+ * RESULT
+ * 0 to mean succes,
+ * any other value means failure.
+ */
+{
+ ULONG i, k;
+
+
+ if (xmAddPattern (si,
+ PATTA_Num, patnum,
+ PATTA_Pattern, patt,
+ TAG_DONE))
+ {
+ xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_CANT_INSERT_PATT, NULL);
+ return 1;
+ }
+
+ /* Adjust position table */
+ for (i = 0 ; i < si->Length ; i++)
+ {
+ /* Song can't grow bigger than MAXPOSITIONS */
+ if (si->Length >= MAXPOSITIONS)
+ return 2; /* TODO: better error handling */
+
+ /* Fix pattern numbers */
+ if (si->Sequence[i] > patnum) si->Sequence[i]++;
+
+ /* Insert references to the new pattern in the position table */
+ if (si->Sequence[i] == patnum)
+ {
+ /* Grow song */
+ if (xmSetSongLen (si, si->Length + 1))
+ {
+ /* Shift subsequent positions ahead 1 slot */
+ for (k = si->Length - 1; k > i ; k--)
+ si->Sequence[k] = si->Sequence[k-1];
+
+ si->Sequence[i+1] = patnum+1; /* Restore old pattern */
+
+ i++; /* Avoid processing this pattern again */
+
+ /* TODO: It would be nice to fix Pattern Jump commands too... */
+ }
+ else return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+GLOBALCALL void DiscardPatterns (struct SongInfo *si)
+
+/* Discard patterns beyond the last pattern referenced
+ * in the song sequence.
+ */
+{
+ ULONG i, j;
+ UBYTE *used;
+ struct Pattern *patt;
+
+ if (!(used = AllocVecPooled (Pool, si->NumPatterns)))
+ return;
+
+ /* Flag patterns REALLY used in the song */
+ for (i = 0; i < si->Length ; i++)
+ used[si->Sequence[i]]++;
+
+
+ for (i = 0; i < si->NumPatterns; i++)
+ {
+ if (!(patt = si->Patt[i])) continue;
+
+ if (!used[i] && patt)
+ {
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_PATT_UNUSED, i);
+
+ xmRemPattern (si, i, 0);
+
+ /* Shift all subsequent patterns one position back */
+ for (j = i; j < si->NumPatterns; j++)
+ used[j] = used[j+1];
+
+ /* Rearrange Position Table */
+ for (j = 0; j < si->Length; j++)
+ if (si->Sequence[j] > i) si->Sequence[j]--;
+
+ /* TODO: It would be nice to fix Pattern Jump commands too... */
+
+ i--; /* Process this pattern # again, since it's now another pattern */
+ }
+ }
+
+ FreeVecPooled (Pool, used);
+}
+
+
+
+GLOBALCALL void CutPatterns (struct SongInfo *si)
+
+/* Find out what patterns are breaked (effect D) and resize them */
+{
+ ULONG i, j, k, l;
+ struct Pattern *patt, *newpatt;
+
+ for (i = 0; i < si->NumPatterns; i++)
+ {
+ if (!(patt = si->Patt[i])) continue;
+
+ for (j = 0; j < patt->Lines; j++)
+ for (k = 0; k < patt->Tracks; k++)
+ if (patt->Notes[k][j].EffNum == EFF_PATTERNBREAK)
+ {
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_PATT_CUT, i, j+1);
+
+ if (newpatt = xmAddPattern (si,
+ PATTA_Name, patt->Name,
+ PATTA_Tracks, patt->Tracks,
+ PATTA_Lines, j + 1,
+ PATTA_Num, -1, /* Do not add it */
+ TAG_DONE))
+ {
+ /* Remove break command */
+ patt->Notes[k][j].EffNum = 0;
+ patt->Notes[k][j].EffVal = 0;
+
+ /* Copy notes */
+ for (l = 0; l < patt->Tracks; l++)
+ CopyMem (patt->Notes[l], newpatt->Notes[l], sizeof (struct Note) * (j+1));
+
+ /* Replace with new pattern */
+ xmAddPattern (si,
+ PATTA_Pattern, patt,
+ PATTA_Num, i,
+ PATTA_Replace, TRUE,
+ TAG_DONE);
+
+ /* stop the loop on this pattern */
+ patt = newpatt;
+ j = patt->Lines;
+ k = patt->Tracks;
+ }
+ }
+ }
+}
+
+
+
+GLOBALCALL void RemDupPatterns (struct SongInfo *si)
+
+/* Find out identical patterns and cut them out */
+{
+ ULONG i, j, k;
+ struct Pattern *patta, *pattb;
+
+ if (si->NumPatterns < 2) return;
+
+ for (i = 0; i < si->NumPatterns-1; i++)
+ {
+ if (!(patta = si->Patt[i])) continue;
+
+ for (j = i+1; j < si->NumPatterns; j++)
+ {
+ if (!(pattb = si->Patt[j])) continue;
+
+ if ((patta->Lines == pattb->Lines) && (patta->Tracks == pattb->Tracks))
+ {
+ for (k = 0; k < patta->Tracks; k++)
+ if (memcmp (patta->Notes[k], pattb->Notes[k], sizeof (struct Note) * patta->Lines))
+ break;
+
+ if (k == patta->Tracks)
+ {
+ xmRemPattern (si, j, i);
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG,
+ (APTR)MSG_PATT_DUPE, i, j);
+ j--;
+ }
+ }
+ }
+ }
+}
+
+
+
+static void BreakPattern (struct Note **arr, UWORD row, UWORD tracks)
+
+/* Put a break command at the end of a pattern */
+{
+ ULONG i;
+
+ /* Try to find a free effect slot in the row... */
+ for (i = 0 ; i < tracks ; i++)
+ if (arr[i][row].EffNum == 0 && arr[i][row].EffVal == 0)
+ break;
+
+ if (i == tracks) i = 0;
+
+ arr[i][row].EffNum = EFF_PATTERNBREAK; /* ...and break the pattern */
+ arr[i][row].EffVal = 0;
+}
+
+
+
+GLOBALCALL LONG ResizePatterns (struct SongInfo *si, ULONG min, ULONG max)
+
+/* Find out what patterns are less than <min> and more than <max>
+ * lines long and either grow them or split them in shorter
+ * patterns. Pattern splitting will automatically recurse when
+ * more than one split is required.
+ *
+ * RESULT
+ * 0 to mean succes, any other value means failure.
+ */
+{
+ struct Pattern *patt, *newpatt, *newpatt2;
+ ULONG i, j;
+ UWORD len;
+ BOOL do_update = FALSE;
+
+ for (i = 0 ; i < si->NumPatterns ; i++)
+ {
+ if (!(patt = si->Patt[i])) continue;
+
+ if ((len = patt->Lines) < min)
+ {
+ /* BREAK PATTERN */
+
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_COMMENT,
+ (APTR)MSG_PATT_WILL_GROW, i, len);
+
+ if (!(newpatt = xmAddPattern (si,
+ PATTA_Num, -1,
+ PATTA_Lines, min,
+ PATTA_Tracks, patt->Tracks,
+ TAG_DONE)))
+ return ERROR_NO_FREE_STORE;
+
+
+ /* Copy the old notes in the new (longer) pattern */
+ for (j = 0 ; j < patt->Tracks ; j++)
+ memcpy (newpatt->Notes[j], patt->Notes[j],
+ (sizeof (struct Note)) * len);
+
+ /* Break the new pattern */
+ BreakPattern (newpatt->Notes, len-1, newpatt->Tracks);
+
+ xmAddPattern (si,
+ PATTA_Num, i,
+ PATTA_Pattern, newpatt,
+ PATTA_Replace, TRUE,
+ TAG_END);
+ }
+ else if (len > max)
+ {
+ /* SPLIT PATTERN */
+
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_COMMENT,
+ (APTR)MSG_SPLITTING_PATT, i, len);
+
+ if (!(newpatt = xmAddPattern (si,
+ PATTA_Num, -1,
+ PATTA_Lines, max,
+ PATTA_Tracks, patt->Tracks,
+ TAG_DONE)))
+ return ERROR_NO_FREE_STORE;
+
+
+ /* If len - max is still above max, this pattern
+ * will be splitted or breaked once again in the
+ * next loop.
+ */
+
+ if (!(newpatt2 = xmAddPattern (si,
+ PATTA_Num, -1,
+ PATTA_Lines, len - max,
+ PATTA_Tracks, patt->Tracks,
+ TAG_DONE)))
+ {
+ xmRemPattern (si, -1, (ULONG)newpatt);
+ return ERROR_NO_FREE_STORE;
+ }
+
+ /* Copy first <max> rows of the pattern */
+ for (j = 0 ; j < si->MaxTracks ; j++)
+ memcpy (newpatt->Notes[j], patt->Notes[j],
+ (sizeof (struct Note)) * max);
+
+ /* Copy the rest of the pattern */
+ for (j = 0 ; j < si->MaxTracks ; j++)
+ memcpy (newpatt2->Notes[j], patt->Notes[j] + max,
+ (sizeof (struct Note)) * (len - max));
+
+
+ /* Make room for the new pattern */
+ if (InsertPattern (si, newpatt, i))
+ {
+ xmRemPattern (si, -1, (ULONG)newpatt);
+ xmRemPattern (si, -1, (ULONG)newpatt2);
+ continue;
+ }
+
+ /* Substitute old pattern */
+ if (!(xmAddPattern (si,
+ PATTA_Num, i + 1,
+ PATTA_Pattern, newpatt2,
+ TAG_DONE)))
+ {
+ xmRemPattern (si, -1, (ULONG)newpatt2);
+ continue;
+ }
+ }
+ } /* End for(i) */
+
+ if (do_update)
+ UpdateSongInfo();
+
+ return 0;
+
+} /* End ResizePatterns */
+
+
+
+GLOBALCALL struct Pattern *CopyPattern (struct SongInfo *si, struct Pattern *src, ULONG destNum)
+
+/* Makes a copy of the notes and attributes of the <src>
+ * pattern to the <dest> pattern. The destination pattern is
+ * created with xmAddPattern().
+ */
+{
+ struct Pattern *dest;
+ ULONG i;
+
+ if (!src) return NULL;
+
+ if (dest = xmAddPattern (si, destNum,
+ PATTA_Lines, src->Lines,
+ PATTA_Tracks, src->Tracks,
+ PATTA_Name, src->Name))
+ {
+ for (i = 0; i < src->Tracks; i++)
+ CopyMem (src->Notes[i], dest->Notes[i],
+ sizeof (struct Note) * src->Lines);
+ }
+
+ return dest;
+}
+
+
+
+GLOBALCALL struct SongInfo *MergeSongs (struct SongInfo *songa, struct SongInfo *songb)
+
+/* Merges <songa> with <songb> in a new song where the notes of the
+ * two sources are played at the same time.
+ */
+{
+ struct SongInfo *songc;
+ struct Pattern *patta, *pattb, *pattc;
+ struct Note *note;
+ struct Instrument *source;
+ BYTE *newsample;
+ LONG slen, plen, ntrk, i, j, k;
+
+
+ if (songa->Length != songb->Length)
+ xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_LEN_DIFFERENT, NULL);
+
+ /* Create new song */
+ {
+ UBYTE newtitle[64], newauthor[64];
+
+ /* Make new title */
+ newtitle[63] = '\0';
+ strncpy (newtitle, songa->Title, 63);
+ strncat (newtitle, " + ", 63);
+ strncat (newtitle, songb->Title, 63);
+
+ /* Make new author */
+
+ newauthor[63] = '\0';
+ if (songa->Author && songa->Author[0])
+ strncpy (newauthor, songa->Author, 63);
+ if (songb->Author && songb->Author[0])
+ {
+ if (songa->Author && songa->Author[0])
+ {
+ strncat (newauthor, " & ", 63);
+ strncat (newauthor, songb->Author, 63);
+ }
+ else strncpy (newauthor, songb->Author, 63);
+ }
+
+ if (!(songc = xmCreateSong (
+ SNGA_GlobalSpeed, songa->GlobalSpeed,
+ SNGA_GlobalTempo, songa->GlobalTempo,
+ SNGA_RestartPos, songa->RestartPos,
+ SNGA_Title, newtitle,
+ SNGA_Author, newauthor,
+ TAG_DONE)))
+ return NULL;
+ }
+
+ slen = min (songa->Length, songb->Length);
+ xmSetSongLen (songc, slen);
+
+
+ for (i = 0; i < slen; i++)
+ {
+ UBYTE pattname[64];
+
+ patta = songa->Patt[songa->Sequence[i]];
+ pattb = songb->Patt[songb->Sequence[i]];
+
+ if (patta->Lines != pattb->Lines)
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_PATT_LEN_DIFFERENT, i);
+
+ plen = min (patta->Lines, pattb->Lines);
+ ntrk = patta->Tracks + pattb->Tracks;
+
+ if (ntrk > MAXTRACKS)
+ {
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_PATT_TOO_MANY_TRACKS, MAXTRACKS);
+
+ ntrk = MAXTRACKS;
+ }
+
+ /* Set pattern name */
+ if (patta->Name && pattb->Name)
+ {
+ pattname[63] = '\0';
+ strncpy (pattname, patta->Name, 63);
+ strncat (pattname, " + ", 63);
+ strncat (pattname, pattb->Name, 63);
+ }
+ else
+ SPrintf (pattname, "%ld + %ld", songa->Sequence[i], songb->Sequence[i]);
+
+ if (!(pattc = xmAddPattern (songc,
+ PATTA_Lines, plen,
+ PATTA_Tracks, ntrk,
+ PATTA_Name, pattname,
+ TAG_DONE)))
+ {
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+
+ songc->Sequence[i] = i;
+
+ /* Copy tracks from source A */
+ for (j = 0; j < patta->Tracks; j++)
+ CopyMem (patta->Notes[j], pattc->Notes[j], sizeof (struct Note) * plen);
+
+ /* Copy tracks from source B */
+ for (j = 0; j < pattb->Tracks; j++)
+ {
+ if (j + patta->Tracks >= MAXTRACKS) break;
+
+ note = pattc->Notes[j + patta->Tracks];
+ CopyMem (pattb->Notes[j], note, sizeof (struct Note) * plen);
+
+ for (k = 0; k < plen; k++)
+ if (note[k].Note)
+ {
+ if ((note[k].Inst + songa->LastInstrument) >= MAXINSTRUMENTS)
+ {
+ xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_ERR_INSTR_OVERFLOW, NULL);
+ xmDeleteSong (songc);
+ return NULL;
+ }
+
+ note[k].Inst += songa->LastInstrument - 1;
+ }
+ }
+ }
+
+
+ /* Copy instruments */
+
+ for (i = 1; i < songa->LastInstrument + songb->LastInstrument; i++)
+ {
+ if (i <= songa->LastInstrument)
+ source = songa->Instr[i];
+ else
+ source = songb->Instr[i - songa->LastInstrument + 1];
+
+ if (source)
+ {
+ if (source->Sample)
+ {
+ if (!(newsample = AllocVec (source->Length, MEMF_SAMPLE)))
+ {
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+ CopyMem (source->Sample, newsample, source->Length);
+ }
+ else
+ newsample = NULL;
+
+ if (!xmAddInstrument (songc, i,
+ INSTRA_Type, source->Type,
+ INSTRA_Name, source->Name,
+ INSTRA_Volume, source->Volume,
+ INSTRA_Sample, newsample,
+ INSTRA_Length, source->Length,
+ INSTRA_LoopStart, source->Repeat,
+ INSTRA_LoopLen, source->Replen,
+ INSTRA_FineTune, source->FineTune,
+ TAG_DONE))
+ {
+ FreeVec (newsample);
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+ }
+ }
+
+ return songc;
+}
+
+
+
+GLOBALCALL struct SongInfo *JoinSongs (struct SongInfo *songa, struct SongInfo *songb)
+
+/* Joins <songa> with <songb> in a new song where the notes of the
+ * two sources are played one after the oder.
+ */
+{
+ struct SongInfo *songc;
+ struct Pattern *patta, *pattb, *pattc;
+ struct Note *note;
+ struct Instrument *inst, *source;
+ BYTE *newsample;
+ ULONG plen, ntrk, i, j, k;
+ UWORD songclen;
+
+
+ /* Check maximum values */
+
+ if ((songa->Length + songb->Length) > MAXPOSITIONS)
+ {
+ xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_TOO_LONG, NULL);
+ songclen = MAXPOSITIONS;
+ }
+ else songclen = songa->Length + songb->Length;
+
+ if ((songa->NumPatterns + songb->NumPatterns) > MAXPATTERNS)
+ xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_HAS_TOO_MANY_PATT, NULL);
+
+
+ /* Create new song */
+ {
+ UBYTE newtitle[64], newauthor[64];
+
+ /* Make new title */
+ newtitle[63] = '\0';
+ strncpy (newtitle, songa->Title, 63);
+ strncat (newtitle, " & ", 63);
+ strncat (newtitle, songb->Title, 63);
+
+ /* Make new author */
+
+ newauthor[63] = '\0';
+ if (songa->Author && songa->Author[0])
+ strncpy (newauthor, songa->Author, 63);
+ if (songb->Author && songb->Author[0])
+ {
+ if (songa->Author && songa->Author[0])
+ {
+ strncat (newauthor, " & ", 63);
+ strncat (newauthor, songb->Author, 63);
+ }
+ else strncpy (newauthor, songb->Author, 63);
+ }
+
+ if (!(songc = xmCreateSong (
+ SNGA_GlobalSpeed, songa->GlobalSpeed,
+ SNGA_GlobalTempo, songa->GlobalTempo,
+ SNGA_RestartPos, songa->RestartPos,
+ SNGA_Title, newtitle,
+ SNGA_Author, newauthor,
+ TAG_DONE)))
+ return NULL;
+ }
+
+ if (xmSetSongLen (songc, songclen))
+ {
+ /* Copy position table of song A */
+ memcpy (songc->Sequence, songa->Sequence, songa->Length * sizeof (UWORD));
+
+ /* Append position table of song B */
+ for (i = 0; i < songb->Length; i++)
+ {
+ if (i + songa->Length >= songc->Length) break;
+ songc->Sequence[i + songa->Length] = (songb->Sequence[i] + songa->NumPatterns > MAXPATTERNS) ?
+ songb->Sequence[i] : (songb->Sequence[i] + songa->NumPatterns);
+ }
+ }
+ else
+ {
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+
+
+ /* Copy song A patterns */
+
+ for (i = 0; i < songa->NumPatterns; i++)
+ {
+ patta = songa->Patt[i];
+ plen = patta->Lines;
+ ntrk = patta->Tracks;
+
+ if (!(pattc = xmAddPattern (songc,
+ PATTA_Lines, plen,
+ PATTA_Tracks, ntrk,
+ PATTA_Name, patta->Name,
+ TAG_DONE)))
+ {
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+
+ /* Copy tracks from source A */
+
+ for (j = 0; j < ntrk; j++)
+ CopyMem (patta->Notes[j], pattc->Notes[j], sizeof (struct Note) * plen);
+ }
+
+
+ /* Append song B patterns */
+
+ for (i = 0; i < songb->NumPatterns; i++)
+ {
+ pattb = songb->Patt[i];
+ plen = pattb->Lines;
+ ntrk = pattb->Tracks;
+
+ if (!(pattc = xmAddPattern (songc,
+ PATTA_Lines, plen,
+ PATTA_Tracks, ntrk,
+ PATTA_Name, pattb->Name,
+ TAG_DONE)))
+ {
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+
+ /* Copy tracks from source B */
+
+ for (j = 0; j < ntrk; j++)
+ CopyMem (pattb->Notes[j], pattc->Notes[j], sizeof (struct Note) * plen);
+
+ /* Adjust instruments references */
+
+ for (j = 0; j < ntrk; j++)
+ for (k = 0; k < plen; k++)
+ {
+ note = &pattc->Notes[j][k];
+
+ if (note->Inst)
+ {
+ if ((note->Inst + songa->LastInstrument) >= MAXINSTRUMENTS)
+ {
+ xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_ERR_INSTR_OVERFLOW, NULL);
+ xmDeleteSong (songc);
+ return NULL;
+ }
+
+ note->Inst += songa->LastInstrument - 1;
+ }
+ }
+ }
+
+
+
+ /* Copy instruments */
+
+ for (i = 1; i < songa->LastInstrument + songb->LastInstrument; i++)
+ {
+ if (i <= songa->LastInstrument)
+ source = songa->Instr[i];
+ else
+ source = songb->Instr[i - songa->LastInstrument + 1];
+
+ if (source)
+ {
+ if (source->Sample)
+ {
+ if (!(newsample = AllocVec (source->Length, MEMF_ANY)))
+ {
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+ CopyMem (source->Sample, newsample, source->Length);
+ }
+ else
+ newsample = NULL;
+
+ if (!(inst = xmAddInstrument (songc, i,
+ INSTRA_Type, source->Type,
+ INSTRA_Name, source->Name,
+ INSTRA_Volume, source->Volume,
+ INSTRA_Sample, newsample,
+ INSTRA_Length, source->Length,
+ INSTRA_Repeat, source->Repeat,
+ INSTRA_Replen, source->Replen,
+ INSTRA_FineTune, source->FineTune,
+ TAG_DONE)))
+ {
+ FreeVec (newsample);
+ xmDeleteSong (songc);
+ LastErr = ERROR_NO_FREE_STORE;
+ return NULL;
+ }
+ }
+ }
+
+ return songc;
+}
+
+
+
+GLOBALCALL void FixSong (struct SongInfo *si)
+
+/* Fixes dangerous errors in song structure */
+{
+ struct Instrument *instr;
+ ULONG i;
+
+ /* Check instruments */
+
+ for (i = 1 ; i < si->LastInstrument ; i++)
+ {
+ if (!(instr = si->Instr[i])) continue;
+
+ /* Filter instrument names */
+ FilterName (instr->Name);
+
+ /* Some sensible sanity checks on loops */
+
+ if (instr->Repeat && (instr->Repeat >= instr->Length))
+ {
+ instr->Repeat = instr->Replen = 0;
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
+ (APTR)MSG_INVALID_LOOP_REMOVED, i);
+ }
+ else if (instr->Repeat + instr->Replen > instr->Length)
+ {
+ instr->Replen = instr->Length - instr->Repeat;
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
+ (APTR)MSG_INVALID_LOOP_FIXED, i);
+ }
+ }
+
+ /* Check patterns */
+ for (i = 0; i < si->NumPatterns; i++)
+ if (si->Patt[i]) FilterName (si->Patt[i]->Name);
+
+ /* Can't have a song with no patterns!!! */
+ if (si->NumPatterns == 0)
+ {
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_NOTE,
+ (APTR)MSG_SONG_HAS_NO_PATTS, NULL);
+ xmAddPattern (si, NULL);
+ }
+
+ /* Better forcing the song to a minimum length of 1. */
+ if (si->Length == 0)
+ {
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_NOTE,
+ (APTR)MSG_SONG_HAS_NO_SEQ, NULL);
+ xmSetSongLen (si, 1);
+ }
+
+ /* Check sequence */
+ for (i = 0; i < si->Length; i++)
+ {
+ if (si->Sequence[i] >= si->NumPatterns)
+ {
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
+ (APTR)MSG_INVALID_SONG_POS, i, si->Sequence[i]);
+ si->Sequence[i] = si->NumPatterns - 1;
+ }
+ }
+
+ FilterName (si->Title);
+ FilterName (si->Author);
+}
--- /dev/null
+/*
+** OptimizationWin.c
+**
+** Copyright (C) 1994,95,96 Bernardo Innocenti
+**
+** Song optimization panel.
+*/
+
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Local functions prototypes */
+
+static void OptPerformClicked (struct WinUserData *wud);
+
+
+
+/* Gadgets IDs */
+
+enum
+{
+ GD_OptimizationGroup0,
+ GD_OptimizationGroup1,
+ GD_RemPatts,
+ GD_RemDupPatts,
+ GD_RemInstr,
+ GD_RemDupInstr,
+ GD_CutAfterLoop,
+ GD_CutZeroTail,
+ GD_CutPatterns,
+ GD_RemapInstr,
+ GD_OptPerform,
+
+ Optimization_CNT
+};
+
+
+
+
+XDEF struct OptSwitches OptSwitches = { 1, 1, 1, 1, 1, 1, 0, 0 };
+
+
+
+
+static ULONG OptimizationArgs[] =
+{
+ VGROUP_KIND, BBFT_RIDGE,
+ CHECKBOX_KIND, NULL, MSG_REM_UNUSED_PATTS_GAD, (ULONG)&OptSwitches.RemPatts, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_REM_DUPLICATE_PATTS_GAD,(ULONG)&OptSwitches.RemDupPatts, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_REM_UNUSED_INSTR_GAD, (ULONG)&OptSwitches.RemInstr, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_REM_DUP_INSTR_GAD, (ULONG)&OptSwitches.RemDupInstr, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_CUT_AFTER_LOOP_GAD, (ULONG)&OptSwitches.CutAfterLoop, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_CUT_ZERO_TAILS_GAD, (ULONG)&OptSwitches.CutZeroTail, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_CUT_PATTERNS_GAD, (ULONG)&OptSwitches.CutPatts, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_REMAP_INSTRUMENTS_GAD, (ULONG)&OptSwitches.RemapInstr, TAG_DONE,
+ ENDGROUP_KIND,
+ BUTTON_KIND, (ULONG)OptPerformClicked, MSG_OPTIMIZE_GAD, TAG_DONE,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG OptimizationWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)OptimizationArgs,
+ XMWIN_GCount, Optimization_CNT,
+ XMWIN_Title, MSG_OPTIMIZATION_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, CHECKBOXIDCMP | BUTTONIDCMP | IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW,
+ XMWIN_HelpNode, (LONG)"Optimization",
+ TAG_DONE
+};
+
+
+
+GLOBALCALL void UpdateOptSwitches (void)
+{
+ struct WinUserData *wud = WDescr[WID_OPTIMIZATION].Wud;
+
+ if (wud && wud->Win)
+ SetGadgets (wud,
+ GD_RemPatts, OptSwitches.RemPatts,
+ GD_RemDupPatts, OptSwitches.RemDupPatts,
+ GD_RemInstr, OptSwitches.RemInstr,
+ GD_RemDupInstr, OptSwitches.RemDupInstr,
+ GD_CutAfterLoop, OptSwitches.CutAfterLoop,
+ GD_CutZeroTail, OptSwitches.CutZeroTail,
+ GD_CutPatterns, OptSwitches.CutPatts,
+ GD_RemapInstr, OptSwitches.RemapInstr,
+ -1);
+}
+
+
+
+/************************/
+/* Optimization Gadgets */
+/************************/
+
+static void OptPerformClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+ ULONG oldsize, newsize;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ oldsize = CalcSongSize (si);
+
+ xmProcessSong (si, NULL,
+ XMSNG_Optimize, XMOF_DEFAULT,
+ TAG_DONE);
+
+ UpdateSongInfo();
+
+ newsize = CalcSongSize (si);
+
+ ReleaseSemaphore (&si->Lock);
+
+ xmDisplayMessage (XMDMF_NOTE | XMDMF_USECATALOG, (STRPTR)MSG_SAVED_X_BYTES,
+ oldsize - newsize, ((oldsize - newsize) * 100) / oldsize );
+ }
+
+ MyCloseWindow (wud);
+}
--- /dev/null
+/*
+** PattPrefsWin.c
+**
+** Copyright (C) 1995,96 Bernardo Innocenti
+**
+** Pattern preferences panel and pattern size panel handling functions.
+*/
+
+#include <intuition/intuition.h>
+#include <libraries/gadtools.h>
+#include <libraries/patteditclass.h>
+#include <libraries/asl.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/utility.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "CustomClasses.h"
+#include "Gui.h"
+
+
+static LONG PattPrefsPreOpen (struct WinUserData *wud);
+static void PattPrefsPostClose (void);
+static void ReopenPatternWindow (void);
+
+static void AdvanceTracksClicked (struct WinUserData *wud);
+static void AdvanceLinesClicked (struct WinUserData *wud);
+static void MaxUndoLevelsClicked (struct WinUserData *wud);
+static void MaxUndoMemClicked (struct WinUserData *wud);
+static void ClipUnitClicked (struct WinUserData *wud);
+static void VScrollerPlaceClicked (struct WinUserData *wud);
+static void HScrollerPlaceClicked (struct WinUserData *wud);
+static void WrapVertClicked (struct WinUserData *wud);
+static void WrapHorizClicked (struct WinUserData *wud);
+static void HexLineNumbersClicked (struct WinUserData *wud);
+static void BlankZeroClicked (struct WinUserData *wud);
+static void DoRulerClicked (struct WinUserData *wud);
+static void DoTinyLinesClicked (struct WinUserData *wud);
+static void GetEditorFontClicked (struct WinUserData *wud);
+static void BGPenClicked (struct WinUserData *wud);
+static void TextPenClicked (struct WinUserData *wud);
+static void LinesPenClicked (struct WinUserData *wud);
+static void TinyLinesPenClicked (struct WinUserData *wud);
+static void PattPrefsOkClicked (struct WinUserData *wud);
+static void PattPrefsCancelClicked (struct WinUserData *wud);
+
+static void PattSizePostClose (void);
+
+static void PattSizeLinesClicked (struct WinUserData *wud);
+static void PattSizeTracksClicked (struct WinUserData *wud);
+static void PattSizeDoubleClicked (struct WinUserData *wud);
+static void PattSizeHalveClicked (struct WinUserData *wud);
+static void PattSizeOkClicked (struct WinUserData *wud);
+static void PattSizeCancelClicked (struct WinUserData *wud);
+
+
+
+enum {
+ GD_PattPrefsGroup0,
+ GD_PattPrefsGroup1,
+ GD_PattPrefsGroup2,
+ GD_AdvanceTracks,
+ GD_AdvanceLines,
+ GD_MaxUndoLevels,
+ GD_MaxUndoMem,
+ GD_ClipUnit,
+ GD_VertScroller,
+ GD_HorizScroller,
+ GD_PattPrefsGroup3,
+ GD_WrapVert,
+ GD_WrapHoriz,
+ GD_HexLineNumbers,
+ GD_BlankZero,
+ GD_DoRuler,
+ GD_DoTinyLines,
+ GD_Backdrop,
+ GD_PattPrefsGroup4,
+ GD_PattPrefsGroup5,
+ GD_EditorFont,
+ GD_GetEditorFont,
+ GD_PattPrefsGroup6,
+ GD_PattPrefsGroup7,
+ GD_BGPen,
+ GD_TextPen,
+ GD_PattPrefsGroup8,
+ GD_LinesPen,
+ GD_TinyLinesPen,
+ GD_PattPrefsGroup9,
+ GD_PattPrefsOk,
+ GD_PattPrefsCancel,
+
+ PattPrefs_CNT
+};
+
+
+
+enum {
+ GD_PattSizeGroup0,
+ GD_PattSizeGroup1,
+ GD_PattSizeGroup2,
+ GD_PattSizeLines,
+ GD_PattSizeTracks,
+ GD_PattSizeGroup3,
+ GD_PattSizeDouble,
+ GD_PattSizeGroup4,
+ GD_PattSizeHalve,
+ GD_PattSizeGroup5,
+ GD_PattSizeOk,
+ GD_PattSizeCancel,
+
+ PattSize_CNT
+};
+
+
+
+static struct PattSwitches OldPattSwitches;
+static struct Pattern *OldPatt = NULL;
+static struct SongInfo *OldSong = NULL;
+static ULONG OldPattNum = 0;
+static UBYTE *ColorTable;
+static LONG BGPenTags[] = {
+ GTPA_Color, 0,
+ GTPA_ColorTable, NULL,
+ GTPA_NumColors, 2,
+ TAG_DONE
+};
+static LONG TextPenTags[] = {
+ GTPA_Color, 0,
+ GTPA_NumColors, 2,
+ GTPA_ColorOffset, 1,
+ TAG_DONE };
+
+
+
+static STRPTR ScrollerPosLabels[] =
+{
+ (STRPTR)MSG_OFF_GAD,
+ (STRPTR)MSG_RIGHT_GAD,
+ (STRPTR)MSG_LEFT_GAD,
+ (STRPTR)NULL
+};
+
+
+
+static LONG PattPrefsArgs[] =
+{
+ HGROUP_KIND, BBFT_RIDGE,
+ VGROUP_KIND, 0,
+ INTEGER_KIND, (LONG)AdvanceTracksClicked, MSG_ADVANCE_TRACKS_GAD, 3, TAG_DONE,
+ INTEGER_KIND, (LONG)AdvanceLinesClicked, MSG_ADVANCE_LINES_GAD, 5, TAG_DONE,
+ INTEGER_KIND, (LONG)MaxUndoLevelsClicked, MSG_MAX_UNDO_LEVELS_GAD, 6, TAG_DONE,
+ INTEGER_KIND, (LONG)MaxUndoMemClicked, MSG_MAX_UNDO_MEM_GAD, 10, TAG_DONE,
+ INTEGER_KIND, (LONG)ClipUnitClicked, MSG_CLIPBOARD_UNIT_GAD, 3, TAG_DONE,
+ CYCLE_KIND, (LONG)VScrollerPlaceClicked,MSG_SCROLLER_POS_GAD, (LONG)ScrollerPosLabels, TAG_DONE,
+ CHECKBOX_KIND, (LONG)HScrollerPlaceClicked,MSG_HORIZ_SCROLLER_GAD, NULL, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ CHECKBOX_KIND, (LONG)WrapVertClicked, MSG_VERT_WRAP_GAD, NULL, TAG_DONE,
+ CHECKBOX_KIND, (LONG)WrapHorizClicked, MSG_HORIZ_WRAP_GAD, NULL, TAG_DONE,
+ CHECKBOX_KIND, (LONG)HexLineNumbersClicked, MSG_HEX_LINE_NUMBERS_GAD, NULL, TAG_DONE,
+ CHECKBOX_KIND, (LONG)BlankZeroClicked, MSG_BLANK_ZERO_GAD, NULL, TAG_DONE,
+ CHECKBOX_KIND, (LONG)DoRulerClicked, MSG_DO_RULER_GAD, NULL, TAG_DONE,
+ CHECKBOX_KIND, (LONG)DoTinyLinesClicked, MSG_DO_TINY_LINES_GAD, NULL, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_BACKDROP_GAD, NULL, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ VGROUP_KIND, BBFT_RIDGE,
+ HGROUP_KIND, 0,
+ TEXT_KIND, MSG_EDITOR_FONT_GAD, 0, GTTX_Border, TRUE, TAG_DONE,
+ IMAGEBUTTON_KIND, (ULONG)GetEditorFontClicked, IM_PICK, TAG_DONE,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ VGROUP_KIND, 0,
+ PALETTE_KIND, (LONG)BGPenClicked, MSG_BACKGROUND_PEN_GAD, TAG_MORE, (LONG)BGPenTags, TAG_DONE,
+ PALETTE_KIND, (LONG)TextPenClicked, MSG_TEXT_PEN_GAD, TAG_MORE, (LONG)TextPenTags, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ PALETTE_KIND, (LONG)LinesPenClicked, MSG_LINES_PEN_GAD, TAG_MORE, (LONG)TextPenTags, TAG_DONE,
+ PALETTE_KIND, (LONG)TinyLinesPenClicked, MSG_TINY_LINES_PEN_GAD, TAG_MORE, (LONG)TextPenTags, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ BUTTON_KIND, (LONG)PattPrefsOkClicked, MSG_UNDERSCORE_OK_GAD, TAG_DONE,
+ BUTTON_KIND, (LONG)PattPrefsCancelClicked, MSG_UNDERSCORE_CANCEL_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+static LONG PattSizeArgs[] =
+{
+ HGROUP_KIND, BBFT_RIDGE,
+ VGROUP_KIND, 0,
+ INTEGER_KIND, (LONG)PattSizeLinesClicked, MSG_LINES_GAD, 5, TAG_DONE,
+ INTEGER_KIND, (LONG)PattSizeTracksClicked, MSG_TRACKS_GAD, 3, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (LONG)PattSizeDoubleClicked, MSG_DOUBLE_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (LONG)PattSizeHalveClicked, MSG_HALVE_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ BUTTON_KIND, (LONG)PattSizeOkClicked, MSG_UNDERSCORE_OK_GAD, TAG_DONE,
+ BUTTON_KIND, (LONG)PattSizeCancelClicked, MSG_UNDERSCORE_CANCEL_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG PattPrefsWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)PattPrefsArgs,
+ XMWIN_GCount, PattPrefs_CNT,
+ XMWIN_Title, MSG_PATTPREFS_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, BUTTONIDCMP|INTEGERIDCMP|PALETTEIDCMP|CHECKBOXIDCMP|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
+ XMWIN_PreOpenFunc, (LONG)PattPrefsPreOpen,
+ XMWIN_PostOpenFunc, (LONG)UpdatePattPrefs,
+ XMWIN_PostCloseFunc,(LONG)PattPrefsPostClose,
+ XMWIN_HelpNode, (LONG)"PattPrefs",
+ TAG_DONE
+};
+
+
+
+XDEF LONG PattSizeWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)PattSizeArgs,
+ XMWIN_GCount, PattSize_CNT,
+ XMWIN_Title, MSG_PATTSIZE_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, BUTTONIDCMP|INTEGERIDCMP|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
+ XMWIN_PostOpenFunc, (LONG)UpdatePattSize,
+ XMWIN_PostCloseFunc,(LONG)PattSizePostClose,
+ XMWIN_HelpNode, (LONG)"PattSize",
+ TAG_DONE
+};
+
+
+
+static LONG PattPrefsPreOpen (struct WinUserData *wud)
+{
+ ULONG i, j, numcols;
+
+ numcols = 1 << Scr->RastPort.BitMap->Depth;
+
+ if (!ColorTable)
+ {
+ if (!(ColorTable = AllocVecPooled (Pool, numcols)))
+ return 1;
+
+ /* Fill ColorTable */
+ for (i = 0, j = 0; i < numcols; i++)
+ if (!(i & PattSwitches.TextPen))
+ ColorTable[j++] = i;
+
+ /* Clear remaining colors */
+ for (i = j ; i < numcols; i++)
+ ColorTable[i] = 0;
+
+ BGPenTags[1] = PattSwitches.LinesPen;
+ BGPenTags[3] = (ULONG) ColorTable;
+ BGPenTags[5] = j;
+ TextPenTags[1] = PattSwitches.TextPen;
+ TextPenTags[3] = numcols - 1;
+
+ memcpy (&OldPattSwitches, &PattSwitches, sizeof (PattSwitches));
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static void PattPrefsPostClose (void)
+{
+ FreeVecPooled (Pool, ColorTable); ColorTable = NULL;
+}
+
+
+static void ReopenPatternWindow (void)
+
+/* Closes all editor windows and reopens one */
+{
+ if (WDescr[WID_PATTERN].Wud)
+ {
+ if (WDescr[WID_PATTERN].Wud->Win)
+ {
+ /* Close all pattern editors... */
+ while (WDescr[WID_PATTERN].UseCnt > 1)
+ MyCloseWindow (WDescr[WID_PATTERN].Wud);
+
+ /* And close last window too... */
+ MyCloseWindow (WDescr[WID_PATTERN].Wud);
+
+ /* Now ensure the LayoutInfo for this window gets flushed... */
+ DeleteLayoutInfo (WDescr[WID_PATTERN].Wud);
+
+ /* And reopen one pattern editor to show changes! */
+ NewWindow (WID_PATTERN);
+ }
+ else DeleteLayoutInfo (WDescr[WID_PATTERN].Wud);
+ }
+}
+
+
+
+void UpdatePattPrefs (void)
+{
+ /* This variable is declared static because gadtools text
+ * gadgets do not buffer their texts and require them to be
+ * accessible whenever a refresh is needed :-(.
+ */
+ static UBYTE editorfont[40];
+
+ struct WinUserData *wud = WDescr[WID_PATTPREFS].Wud;
+
+ if (wud && wud->Win)
+ {
+ SPrintf (editorfont, "%s/%ld", EditorAttr.ta_Name, EditorAttr.ta_YSize);
+
+ SetGadgets (wud,
+ GD_AdvanceTracks, PattSwitches.AdvanceTracks,
+ GD_AdvanceLines, PattSwitches.AdvanceLines,
+ GD_MaxUndoLevels, PattSwitches.MaxUndoLevels,
+ GD_MaxUndoMem, PattSwitches.MaxUndoMem,
+ GD_VertScroller, PattSwitches.VScrollerPlace,
+ GD_HorizScroller, PattSwitches.HScrollerPlace,
+ GD_WrapHoriz, PattSwitches.Flags & PEF_HWRAP,
+ GD_WrapVert, PattSwitches.Flags & PEF_VWRAP,
+ GD_HexLineNumbers, PattSwitches.Flags & PEF_HEXMODE,
+ GD_BlankZero, PattSwitches.Flags & PEF_BLANKZERO,
+ GD_DoRuler, PattSwitches.Flags & PEF_DOCURSORRULER,
+ GD_DoTinyLines, PattSwitches.Flags & PEF_DOTINYLINES,
+ GD_EditorFont, editorfont,
+ GD_BGPen, PattSwitches.BGPen,
+ GD_TextPen, PattSwitches.TextPen,
+ GD_LinesPen, PattSwitches.LinesPen,
+ GD_TinyLinesPen, PattSwitches.TinyLinesPen,
+ -1);
+ }
+}
+
+
+
+static void PattSizePostClose (void)
+{
+ xmRemPattern (OldSong, -1, (ULONG)OldPatt);
+ OldPatt = NULL;
+}
+
+
+
+GLOBALCALL void UpdatePattSize (void)
+{
+ struct WinUserData *wud = WDescr[WID_PATTSIZE].Wud;
+
+ if (wud && wud->Win)
+ {
+ struct SongInfo *si;
+ struct Pattern *patt;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ patt = si->Patt[si->CurrentPatt];
+
+ /* Free previous backup copy, if any */
+
+ if (OldPatt) xmRemPattern (OldSong, -1, (ULONG)OldPatt);
+
+ /* Make a local backup copy of the current pattern (it may fail) */
+
+ OldSong = si;
+ OldPattNum = si->CurrentPatt;
+ OldPatt = CopyPattern (si, si->Patt[si->CurrentPatt], -1);
+
+ /* Update gadgets */
+ SetGadgets (wud,
+ GD_PattSizeLines, patt->Lines,
+ GD_PattSizeTracks, patt->Tracks,
+ -1);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+ }
+}
+
+
+
+static void AdvanceTracksClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+ PattSwitches.AdvanceTracks = (WORD) GetNumber (wud->Gadgets[GD_AdvanceTracks]);
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_AdvanceCurs, (PattSwitches.AdvanceTracks << 16) |
+ ((UWORD)PattSwitches.AdvanceLines),
+ TAG_DONE);
+}
+
+
+
+static void AdvanceLinesClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.AdvanceLines = (WORD) GetNumber(wud->Gadgets[GD_AdvanceLines]);
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_AdvanceCurs, (PattSwitches.AdvanceTracks << 16) |
+ ((UWORD)PattSwitches.AdvanceLines),
+ TAG_DONE);
+}
+
+
+
+static void MaxUndoLevelsClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.MaxUndoLevels = (WORD) GetNumber (wud->Gadgets[GD_MaxUndoLevels]);
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_MaxUndoLevels, PattSwitches.MaxUndoLevels,
+ TAG_DONE);
+}
+
+
+
+static void MaxUndoMemClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.MaxUndoMem = (WORD) GetNumber (wud->Gadgets[GD_MaxUndoMem]);
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_MaxUndoMem, PattSwitches.MaxUndoMem,
+ TAG_DONE);
+}
+
+
+
+static void VScrollerPlaceClicked (struct WinUserData *wud)
+{
+ PattSwitches.VScrollerPlace = IntuiMsg.Code;
+ ReopenPatternWindow ();
+}
+
+
+static void HScrollerPlaceClicked (struct WinUserData *wud)
+{
+ PattSwitches.HScrollerPlace ^= SCROLLERPLACE_BOTTOM;
+ ReopenPatternWindow ();
+}
+
+
+static void WrapVertClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.Flags ^= PEF_VWRAP;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_Flags, PattSwitches.Flags,
+ TAG_DONE);
+}
+
+
+
+static void WrapHorizClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.Flags ^= PEF_HWRAP;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_Flags, PattSwitches.Flags,
+ TAG_DONE);
+}
+
+
+
+static void HexLineNumbersClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.Flags ^= PEF_HEXMODE;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_Flags, PattSwitches.Flags,
+ TAG_DONE);
+}
+
+
+
+static void BlankZeroClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.Flags ^= PEF_BLANKZERO;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_Flags, PattSwitches.Flags,
+ TAG_DONE);
+}
+
+
+static void DoRulerClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.Flags ^= PEF_DOCURSORRULER;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_Flags, PattSwitches.Flags,
+ TAG_DONE);
+}
+
+
+
+static void DoTinyLinesClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.Flags ^= PEF_DOTINYLINES;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_Flags, PattSwitches.Flags,
+ TAG_DONE);
+}
+
+
+
+static void ClipUnitClicked (struct WinUserData *wud)
+{
+ PattSwitches.ClipboardUnit = (UBYTE) GetNumber (wud->Gadgets[GD_ClipUnit]);
+}
+
+
+
+static void GetEditorFontClicked (struct WinUserData *wud)
+{
+ FontRequest (&EditorAttr, FOF_FIXEDWIDTHONLY);
+ ReopenPatternWindow ();
+ UpdatePattPrefs();
+}
+
+
+
+static void BGPenClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.BGPen = (UWORD) IntuiMsg.Code;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_BGPen, PattSwitches.BGPen,
+ TAG_DONE);
+}
+
+
+
+static void TextPenClicked (struct WinUserData *wud)
+{
+ ULONG i, j, numcols = 1 << Scr->RastPort.BitMap->Depth;
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.TextPen = (UWORD) IntuiMsg.Code;
+
+ /* Recalculate Lines colors */
+ for (i = 0, j = 0; i < numcols; i++)
+ if (!(i & PattSwitches.TextPen))
+ ColorTable[j++] = i;
+
+ /* Clear remaining colors */
+ for (i = j ; i < numcols; i++)
+ ColorTable[i] = 0;
+
+ if (PattSwitches.LinesPen & PattSwitches.TextPen)
+ PattSwitches.LinesPen = ColorTable[(j > 1) ? 1 : 0];
+
+ if (PattSwitches.TinyLinesPen & PattSwitches.TextPen)
+ PattSwitches.TinyLinesPen = ColorTable[(j > 1) ? 1 : 0];
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_LinesPen], wud->Win, NULL,
+ GTPA_Color, PattSwitches.LinesPen,
+ GTPA_ColorTable, ColorTable,
+ GTPA_NumColors, j,
+ TAG_DONE);
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_TinyLinesPen], wud->Win, NULL,
+ GTPA_Color, PattSwitches.TinyLinesPen,
+ GTPA_ColorTable, ColorTable,
+ GTPA_NumColors, j,
+ TAG_DONE);
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_TextPen, PattSwitches.TextPen,
+ PEA_LinesPen, PattSwitches.LinesPen,
+ PEA_TinyLinesPen, PattSwitches.TinyLinesPen,
+ TAG_DONE);
+}
+
+
+
+static void LinesPenClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.LinesPen = (UWORD) IntuiMsg.Code;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_LinesPen, PattSwitches.LinesPen,
+ TAG_DONE);
+}
+
+
+
+static void TinyLinesPenClicked (struct WinUserData *wud)
+{
+ struct WinUserData *patternwud = WDescr[WID_PATTERN].Wud;
+
+ PattSwitches.TinyLinesPen = (UWORD) IntuiMsg.Code;
+
+ if (patternwud && patternwud->Win)
+ SetGadgetAttrs (patternwud->Gadgets[0], patternwud->Win, NULL,
+ PEA_TinyLinesPen, PattSwitches.TinyLinesPen,
+ TAG_DONE);
+}
+
+
+
+static void PattPrefsOkClicked (struct WinUserData *wud)
+{
+ MyCloseWindow (wud);
+}
+
+
+
+static void PattPrefsCancelClicked (struct WinUserData *wud)
+{
+ MyCloseWindow (wud);
+}
+
+
+
+static void PattSizeLinesClicked (struct WinUserData *wud)
+{
+}
+
+
+
+static void PattSizeTracksClicked (struct WinUserData *wud)
+{
+}
+
+
+
+static void PattSizeDoubleClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ struct Pattern *patt = si->Patt[si->CurrentPatt];
+ struct Pattern *newpatt;
+ UWORD i, j;
+
+ if (patt->Lines > MAXPATTLINES/2)
+ ShowMessage (MSG_PATT_TOO_LONG);
+
+ if (newpatt = xmAddPattern (si,
+ PATTA_Lines, patt->Lines << 1,
+ PATTA_Tracks, patt->Tracks,
+ PATTA_Name, patt->Name,
+ PATTA_Num, -1,
+ TAG_DONE))
+ {
+ for (i = 0; i < patt->Tracks; i++)
+ for (j = 0; j < patt->Lines; j++)
+ memcpy (&newpatt->Notes[i][j*2], &patt->Notes[i][j], sizeof (struct Note));
+
+ xmAddPattern (si,
+ PATTA_Num, si->CurrentPatt,
+ PATTA_Pattern, newpatt,
+ PATTA_Replace, TRUE,
+ TAG_DONE);
+
+ UpdatePattern();
+ }
+ else LastErr = ERROR_NO_FREE_STORE;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PattSizeHalveClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ struct Pattern *patt = si->Patt[si->CurrentPatt];
+ struct Pattern *newpatt;
+ UWORD i, j;
+
+
+ if (newpatt = xmAddPattern (si,
+ PATTA_Lines, patt->Lines >> 1,
+ PATTA_Tracks, patt->Tracks,
+ PATTA_Name, patt->Name,
+ PATTA_Num, -1,
+ TAG_DONE))
+ {
+ for (i = 0; i < newpatt->Tracks; i++)
+ for (j = 0; j < newpatt->Lines; j++)
+ memcpy (&newpatt->Notes[i][j], &patt->Notes[i][j*2], sizeof (struct Note));
+
+ xmAddPattern (si,
+ PATTA_Num, si->CurrentPatt,
+ PATTA_Pattern, newpatt,
+ PATTA_Replace, TRUE,
+ TAG_DONE);
+
+ UpdatePattern();
+ }
+ else LastErr = ERROR_NO_FREE_STORE;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PattSizeOkClicked (struct WinUserData *wud)
+{
+ MyCloseWindow (wud);
+}
+
+
+
+static void PattSizeCancelClicked (struct WinUserData *wud)
+{
+ if (OldPatt)
+ {
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (OldSong == si)
+ {
+ /* Restore original pattern */
+ CopyPattern (si, OldPatt, OldPattNum);
+
+ /* Free backup copy */
+ xmRemPattern (OldSong, -1, (ULONG)OldPatt);
+ OldPatt = NULL;
+ }
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdatePattern();
+
+ MyCloseWindow (wud);
+}
--- /dev/null
+/*
+** PatternWin.c
+**
+** Copyright (C) 1994,95,96 Bernardo Innocenti
+**
+** Parts of the code have been inspired by ScrollerWindow 0.3 demo
+** Copyright © 1994 Christoph Feck, TowerSystems.
+**
+** Pattern editor handling functions.
+**
+**
+** Diagram of object interconnections:
+**
+**
+** ScrollButtonClass objects
+** +----------+ +---------+ +----------+ +----------+
+** | UpButton | | DnButton| | SxButton | | DxButton |
+** +----------+ +---------+ +----------+ +----------+
+** | GA_ID = | GA_ID = | GA_ID = | GA_ID =
+** | PATTA_Up | PATTA_Down | PATTA_Left | PATTA_Right
+** | | | |
+** | +--------+ | |
+** | | +------------------+ |
+** | | | +----------------------------+
+** | | | | propgclass object icclass object
+** | | | | +----------------+ +-----------------+
+** | | | | +--| HSlider |<--------------------| EditorToHSlider |
+** | | | | | +----------------+ PATTA_LeftTrack = +-----------------+
+** | | | | | PGA_Top = PGA_Top ^
+** | | | | | PATTA_TopLine PATTA_DisplayTracks = |
+** | | | | | PGA_Visible |
+** | | | | | +-----------------------------+
+** V V V V V |
+** +---------------+ ************
+** | PattEditGad |----------->* Model *----------------> IDCMP_IDCMPUPDATE
+** +---------------+ ************ PATTA_CursLine to PatternWin
+** ^ | PATTA_CursTrack
+** | propgclass object |
+** | PGA_Top = PATTA_TopLine V icclass object
+** +----------------+ +-----------------+
+** | VSlider |<------| EditorToVSlider | PATTA_TopLine = PGA_Top
+** +----------------+ +-----------------+ PATTA_DisplayLines = PGA_Visible
+*/
+
+
+#include <intuition/intuition.h>
+#include <intuition/icclass.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <graphics/gfxbase.h>
+#include <libraries/gadtools.h>
+#include <libraries/iffparse.h>
+#include <libraries/patteditclass.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+#include <proto/layers.h>
+#include <proto/utility.h>
+#include <proto/dos.h>
+#include <proto/iffparse.h>
+#include <proto/diskfont.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+#include "CustomClasses.h"
+
+
+/* Gadget IDs */
+enum {
+ GD_PattEdit,
+ GD_UpButton,
+ GD_DnButton,
+ GD_VSlider,
+ GD_SxButton,
+ GD_DxButton,
+ GD_HSlider,
+
+ Pattern_CNT
+};
+
+
+/* Local function prototypes */
+
+static struct Gadget *CreatePattEdit (void);
+static struct Gadget *LayoutPatternWin (struct WinUserData *wud);
+
+static void PatternPostOpen (struct WinUserData *wud);
+static void PatternWindowCleanup(struct WinUserData *wud);
+
+static void RenderPatternWindow (struct WinUserData *wud);
+static void HandlePatternIDCMP (struct WinUserData *wud);
+static void PatternLoad (STRPTR name, ULONG num, ULONG count);
+static LONG PatternSave (STRPTR name, struct Pattern *patt);
+
+static void PatternMiOpen (void);
+static void PatternMiSave (void);
+static void PatternMiSaveAs (void);
+static void PatternMiSize (void);
+static void PatternMiMark (struct WinUserData *wud);
+static void PatternMiCut (struct WinUserData *wud);
+static void PatternMiCopy (struct WinUserData *wud);
+static void PatternMiPaste (struct WinUserData *wud);
+static void PatternMiErase (struct WinUserData *wud);
+static void PatternMiUndo (struct WinUserData *wud);
+static void PatternMiRedo (struct WinUserData *wud);
+static void PatternMiSettings (void);
+
+
+
+/* Local data */
+
+static struct Library *PattEditBase = NULL;
+static struct TextFont *EditorFont = NULL;
+static Object *Model = NULL,
+ *EditorToVSlider = NULL,
+ *EditorToHSlider = NULL;
+
+static UBYTE WindowTitle[128];
+static UBYTE TitleInfo[16];
+
+
+
+XDEF struct PattSwitches PattSwitches =
+{
+ 0, 1, /* AdvanceTracks, AdvanceLines */
+ 32, 8192, /* MaxUndoLevels, MaxUndoMem */
+ 0, /* Flags */
+ SCROLLERPLACE_RIGHT, /* VScrollerPlace */
+ SCROLLERPLACE_BOTTOM, /* HScrollerPlace */
+ 0, 0, /* ClipboardUnit, Pad0 */
+ 10, 0, /* TrackChars, Backdrop */
+ 0, 1, 2, 2 /* BGPen, TextPen, LinesPen, TinyLinesPen */
+};
+
+
+static LONG MapVSlider2Editor[] =
+{
+ PGA_Top, PEA_TopLine,
+ TAG_DONE
+};
+
+static LONG MapHSlider2Editor[] =
+{
+ PGA_Top, PEA_LeftTrack,
+ TAG_DONE
+};
+
+static LONG MapEditorToVSlider[] =
+{
+ PEA_TopLine, PGA_Top,
+ PEA_DisplayLines, PGA_Visible,
+ TAG_DONE
+};
+
+static LONG MapEditorToHSlider[] =
+{
+ PEA_LeftTrack, PGA_Top,
+ PEA_DisplayTracks, PGA_Visible,
+ TAG_DONE
+};
+
+static LONG MapUp2Editor[] =
+{
+ GA_ID, PEA_Up,
+ TAG_DONE
+};
+
+static LONG MapDn2Editor[] =
+{
+ GA_ID, PEA_Down,
+ TAG_DONE
+};
+
+static LONG MapSx2Editor[] =
+{
+ GA_ID, PEA_Left,
+ TAG_DONE
+};
+
+static LONG MapDx2Editor[] =
+{
+ GA_ID, PEA_Right,
+ TAG_DONE
+};
+
+
+
+static struct NewMenu PatternNewMenu[] = {
+ NM_TITLE, (STRPTR)MSG_PATTERNS_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_OPEN_MEN, (STRPTR)"O", 0, 0L, (APTR)PatternMiOpen,
+ NM_ITEM, (STRPTR)MSG_SAVE_MEN, (STRPTR)"S", 0, 0L, (APTR)PatternMiSave,
+ NM_ITEM, (STRPTR)MSG_SAVE_AS_MEN, (STRPTR)"A", 0, 0L, (APTR)PatternMiSaveAs,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_SIZE_MEN, NULL, 0, 0L, (APTR)PatternMiSize,
+ NM_TITLE, (STRPTR)MSG_EDIT_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_MARK_MEN, (STRPTR)"B", 0, 0L, (APTR)PatternMiMark,
+ NM_ITEM, (STRPTR)MSG_CUT_MEN, (STRPTR)"X", 0, 0L, (APTR)PatternMiCut,
+ NM_ITEM, (STRPTR)MSG_COPY_MEN, (STRPTR)"C", 0, 0L, (APTR)PatternMiCopy,
+ NM_ITEM, (STRPTR)MSG_PASTE_MEN, (STRPTR)"V", 0, 0L, (APTR)PatternMiPaste,
+ NM_ITEM, (STRPTR)MSG_ERASE_MEN, (STRPTR)"E", 0, 0L, (APTR)PatternMiErase,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_UNDO_MEN, (STRPTR)"U", 0, 0L, (APTR)PatternMiUndo,
+ NM_ITEM, (STRPTR)MSG_REDO_MEN, (STRPTR)"R", 0, 0L, (APTR)PatternMiRedo,
+ NM_TITLE, (STRPTR)MSG_SETTINGS_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_EDITOR_SETTINGS_MEN, (STRPTR)"P", 0, 0L, (APTR)PatternMiSettings,
+ NM_END, NULL, NULL, 0, 0L, NULL };
+
+
+
+XDEF LONG PatternWinTags[] =
+{
+ TAG_IGNORE, TRUE, /* WA_SizeB(Left|Right) */
+#ifdef OS30_ONLY
+ WA_BackFill, LAYERS_NOBACKFILL,
+#endif /* OS30_ONLY */
+ XMWIN_NewMenu, (LONG)PatternNewMenu,
+ XMWIN_LayoutFunc, (LONG)LayoutPatternWin,
+ XMWIN_GCount, Pattern_CNT,
+ XMWIN_Title, MSG_PATTERN_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET | WFLG_ACTIVATE|
+ WFLG_SIZEGADGET | WFLG_SIMPLE_REFRESH | WFLG_NOCAREREFRESH,
+ XMWIN_IDCMPFlags, IDCMP_MENUPICK|IDCMP_CLOSEWINDOW|IDCMP_ACTIVEWINDOW|IDCMP_INACTIVEWINDOW|IDCMP_NEWSIZE|IDCMP_IDCMPUPDATE|IDCMP_INTUITICKS,
+ XMWIN_IDCMPFunc, (LONG)HandlePatternIDCMP,
+ XMWIN_PostOpenFunc, (LONG)PatternPostOpen,
+ XMWIN_LayoutCleanup,(LONG)PatternWindowCleanup,
+ XMWIN_HelpNode, (LONG)"Pattern",
+ TAG_DONE
+};
+
+
+
+static struct Gadget *CreatePattEdit (void)
+{
+ /* We do not initialize PEA_Pattern right now because
+ * it is done later by UpdatePattern().
+ */
+ return ((struct Gadget *)NewObject (NULL, PATTEDITCLASS,
+ GA_ID, GD_PattEdit,
+ GA_Left, ((PattSwitches.VScrollerPlace == SCROLLERPLACE_LEFT) ? SizeWidth : OffX),
+ GA_Top, OffY,
+ GA_RelWidth, - SizeWidth - ((PattSwitches.VScrollerPlace == SCROLLERPLACE_LEFT) ? Scr->WBorRight : OffX),
+ GA_RelHeight, - OffY - (((PattSwitches.HScrollerPlace == SCROLLERPLACE_BOTTOM) ||
+ (PattSwitches.VScrollerPlace != SCROLLERPLACE_RIGHT)) ? SizeHeight : Scr->WBorBottom),
+ PEA_TextFont, EditorFont ? EditorFont : TopazFont,
+ PEA_AdvanceCurs, (PattSwitches.AdvanceTracks << 16) | PattSwitches.AdvanceLines,
+ PEA_MaxUndoLevels, PattSwitches.MaxUndoLevels,
+ PEA_MaxUndoMem, PattSwitches.MaxUndoMem,
+ PEA_Flags, PattSwitches.Flags,
+ PEA_TrackChars, PattSwitches.TrackChars,
+ PEA_BGPen, PattSwitches.BGPen,
+ PEA_TextPen, PattSwitches.TextPen,
+ PEA_LinesPen, PattSwitches.LinesPen,
+ PEA_TinyLinesPen, PattSwitches.TinyLinesPen,
+ TAG_DONE));
+}
+
+
+
+static struct Gadget *LayoutPatternWin (struct WinUserData *wud)
+{
+ struct Gadget *pattedit;
+
+ if (!PattEditBase)
+ if (!(PattEditBase = MyOpenLibrary (PATTEDITNAME, PATTEDITVERS)))
+ return NULL;
+
+ {
+ ULONG numcols = 1 << Scr->RastPort.BitMap->Depth; /**/
+
+ if (PattSwitches.TextPen >= numcols)
+ PattSwitches.TextPen = 1;
+
+ if (PattSwitches.LinesPen >= numcols)
+ PattSwitches.LinesPen = 2;
+
+ if (PattSwitches.TinyLinesPen >= numcols)
+ PattSwitches.TinyLinesPen = 2;
+ }
+
+ if (!(EditorFont = OpenFont (&EditorAttr)))
+ if (DiskfontBase)
+ EditorFont = OpenDiskFont (&EditorAttr);
+
+ if (!EditorFont)
+ {
+ CantOpenLib (EditorAttr.ta_Name, 0);
+ EditorFont = OpenFont (&TopazAttr);
+ }
+
+
+ PatternWinTags[0] = (PattSwitches.VScrollerPlace == SCROLLERPLACE_RIGHT) ?
+ WA_SizeBRight : WA_SizeBBottom;
+
+ if (PattSwitches.Backdrop)
+ {
+ wud->Flags = WFLG_BORDERLESS | WFLG_BACKDROP | WFLG_SMART_REFRESH | WFLG_NOCAREREFRESH;
+ wud->WindowSize.Left = wud->WindowSize.Top = 0;
+ wud->WindowSize.Width = Scr->Width;
+ wud->WindowSize.Height = Scr->Height;
+ }
+ else
+ {
+ wud->WindowSize.Width = max (wud->WindowSize.Width,
+ EditorFont->tf_XSize * (MINTRACKCHARS + 4));
+ wud->WindowSize.Height = max (wud->WindowSize.Height,
+ EditorFont->tf_YSize * 2);
+ }
+
+ if (!(pattedit = wud->Gadgets[GD_PattEdit] = CreatePattEdit()))
+ return NULL;
+
+ if (PattSwitches.VScrollerPlace)
+ {
+ if (!(wud->Gadgets[GD_UpButton] = CreateUpButton (GD_UpButton, pattedit, MapUp2Editor, PattSwitches.VScrollerPlace)))
+ return NULL;
+ if (!(wud->Gadgets[GD_DnButton] = CreateDnButton (GD_DnButton, pattedit, MapDn2Editor, PattSwitches.VScrollerPlace)))
+ return NULL;
+ if (!(wud->Gadgets[GD_VSlider] = CreateVSlider (GD_VSlider, pattedit, MapVSlider2Editor,
+ wud->Gadgets[GD_UpButton]->Height + wud->Gadgets[GD_DnButton]->Height,
+ PattSwitches.VScrollerPlace)))
+ return NULL;
+ }
+
+ if (PattSwitches.HScrollerPlace)
+ {
+ if (!(wud->Gadgets[GD_SxButton] = CreateSxButton (GD_SxButton, pattedit, MapSx2Editor)))
+ return NULL;
+ if (!(wud->Gadgets[GD_DxButton] = CreateDxButton (GD_DxButton, pattedit, MapDx2Editor)))
+ return NULL;
+ if (!(wud->Gadgets[GD_HSlider] = CreateHSlider (GD_HSlider, pattedit, MapHSlider2Editor,
+ wud->Gadgets[GD_DxButton]->Width + wud->Gadgets[GD_SxButton]->Width)))
+ return NULL;
+ }
+
+
+ /* Link gadgets together */
+
+ if (PattSwitches.VScrollerPlace)
+ {
+ wud->Gadgets[GD_PattEdit]->NextGadget = wud->Gadgets[GD_UpButton];
+ wud->Gadgets[GD_UpButton]->NextGadget = wud->Gadgets[GD_DnButton];
+ wud->Gadgets[GD_DnButton]->NextGadget = wud->Gadgets[GD_VSlider];
+ if (PattSwitches.HScrollerPlace)
+ wud->Gadgets[GD_VSlider]->NextGadget = wud->Gadgets[GD_SxButton];
+ }
+ if (PattSwitches.HScrollerPlace)
+ {
+ if (!PattSwitches.VScrollerPlace)
+ wud->Gadgets[GD_PattEdit]->NextGadget = wud->Gadgets[GD_SxButton];
+
+ wud->Gadgets[GD_SxButton]->NextGadget = wud->Gadgets[GD_DxButton];
+ wud->Gadgets[GD_DxButton]->NextGadget = wud->Gadgets[GD_HSlider];
+ }
+
+
+ if (PattSwitches.VScrollerPlace || PattSwitches.HScrollerPlace)
+ {
+ /* Create the Model */
+ if (Model = NewObject (NULL, MODELCLASS,
+ ICA_TARGET, ICTARGET_IDCMP,
+ TAG_DONE))
+ {
+ /* Connect Editor to Model */
+ SetAttrs (wud->Gadgets[GD_PattEdit],
+ ICA_TARGET, Model,
+ TAG_DONE);
+
+ if (PattSwitches.VScrollerPlace)
+ {
+ if (EditorToVSlider = NewObject (NULL, ICCLASS,
+ ICA_TARGET, wud->Gadgets[GD_VSlider],
+ ICA_MAP, MapEditorToVSlider,
+ TAG_DONE))
+ /* Connect Model to VSlider */
+ if (!DoMethod (Model, OM_ADDMEMBER, EditorToVSlider))
+ DisposeObject (EditorToVSlider); EditorToVSlider = NULL;
+ }
+
+ if (PattSwitches.HScrollerPlace)
+ {
+ if (EditorToHSlider = NewObject (NULL, ICCLASS,
+ ICA_TARGET, wud->Gadgets[GD_HSlider],
+ ICA_MAP, MapEditorToHSlider,
+ TAG_DONE))
+ /* Connect Model to HSlider */
+ if (!DoMethod (Model, OM_ADDMEMBER, EditorToHSlider))
+ DisposeObject (EditorToHSlider); EditorToHSlider = NULL;
+ }
+ }
+ }
+ else
+ /* Connect Editor to application */
+ SetAttrs (wud->Gadgets[GD_PattEdit],
+ ICA_TARGET, ICTARGET_IDCMP,
+ TAG_DONE);
+
+ return wud->Gadgets[GD_PattEdit];
+
+ /* NOTE: The model will also dispose its members... */
+/*
+ DisposeObject (Model); Model = NULL;
+ EditorToVSlider = NULL; EditorToHSlider = NULL;
+ return NULL;
+*/
+}
+
+
+
+
+#ifndef OS30_ONLY
+
+static ULONG __asm BFHookFunc (void)
+
+/* No-op backfilling hook. Since we are going to redraw the whole window
+ * anyway, we can disable backfilling. This avoids ugly flashing while
+ * resizing or revealing the window.
+ *
+ * This function hasn't the __saveds attribute because it makes no
+ * references to external data.
+ */
+{
+ return 1; /* Do nothing */
+}
+
+static struct Hook BFHook =
+{
+ NULL, NULL,
+ BFHookFunc,
+};
+#endif /* !OS30_ONLY */
+
+
+
+static void PatternPostOpen (struct WinUserData *wud)
+{
+ /* Limit window flashing by providing a no-op hook for window
+ * backfilling.
+ */
+#ifndef OS30_ONLY
+ InstallLayerHook (wud->Win->WLayer, (LayersBase->lib_Version >= 39) ?
+ ((struct Hook *)LAYERS_NOBACKFILL) : &BFHook);
+#endif /* !OS30_ONLY */
+
+ UpdatePattern();
+
+ /* Allow window resizing. Minimum size is chosen so that at least
+ * two lines are visible.
+ */
+ WindowLimits (wud->Win,
+ wud->Win->BorderLeft + wud->Win->BorderRight + EditorFont->tf_XSize * (10 + 4),
+ wud->Win->BorderTop + wud->Win->BorderBottom + EditorFont->tf_YSize * 2,
+ -1, -1);
+}
+
+
+
+static void PatternWindowCleanup (struct WinUserData *wud)
+{
+ /* NOTE: The model will also dispose its members. */
+ DisposeObject (Model); Model = NULL;
+ EditorToVSlider = NULL;
+ EditorToHSlider = NULL;
+
+ if (wud->Gadgets)
+ {
+ DisposeObject (wud->Gadgets[GD_VSlider]);
+ wud->Gadgets[GD_VSlider] = NULL;
+ DisposeObject (wud->Gadgets[GD_HSlider]);
+ wud->Gadgets[GD_HSlider] = NULL;
+
+ if (wud->Gadgets[GD_UpButton])
+ {
+ DisposeObject (wud->Gadgets[GD_UpButton]->GadgetRender);
+ DisposeObject (wud->Gadgets[GD_UpButton]);
+ wud->Gadgets[GD_UpButton] = NULL;
+ }
+
+ if (wud->Gadgets[GD_DnButton])
+ {
+ DisposeObject (wud->Gadgets[GD_DnButton]->GadgetRender);
+ DisposeObject (wud->Gadgets[GD_DnButton]);
+ wud->Gadgets[GD_DnButton] = NULL;
+ }
+
+ if (wud->Gadgets[GD_SxButton])
+ {
+ DisposeObject (wud->Gadgets[GD_SxButton]->GadgetRender);
+ DisposeObject (wud->Gadgets[GD_SxButton]);
+ wud->Gadgets[GD_SxButton] = NULL;
+ }
+
+ if (wud->Gadgets[GD_DxButton])
+ {
+ DisposeObject (wud->Gadgets[GD_DxButton]->GadgetRender);
+ DisposeObject (wud->Gadgets[GD_DxButton]);
+ wud->Gadgets[GD_DxButton] = NULL;
+ }
+
+ DisposeObject (wud->Gadgets[GD_PattEdit]);
+ wud->Gadgets[GD_PattEdit] = NULL;
+ }
+
+ if (EditorFont)
+ {
+ CloseFont (EditorFont);
+ EditorFont = NULL;
+ }
+
+ if (PattEditBase)
+ {
+ CloseLibrary (PattEditBase);
+ PattEditBase = NULL;
+ }
+}
+
+
+
+static void HandlePatternIDCMP (struct WinUserData *wud)
+{
+ switch (IntuiMsg.Class)
+ {
+ case IDCMP_IDCMPUPDATE:
+ {
+ struct TagItem *ti, *tstate = IntuiMsg.IAddress;
+ ULONG line = ((UWORD)~0), track = ((UWORD)~0);
+
+ while (ti = NextTagItem (&tstate))
+ switch (ti->ti_Tag)
+ {
+ case PEA_CursLine:
+ line = ti->ti_Data;
+ break;
+
+ case PEA_CursTrack:
+ track = ti->ti_Data;
+ break;
+
+ case PEA_ChangeNote:
+ /* TODO: Increment changes counter of song */
+ break;
+
+ default:
+ break;
+ }
+
+ if (track != ((UWORD)~0))
+ {
+ SPrintf (TitleInfo, " %lu, %lu", track, line);
+ RenderPatternWindow (wud);
+ }
+
+ break;
+ }
+
+ case IDCMP_ACTIVEWINDOW:
+ case IDCMP_INACTIVEWINDOW:
+ case IDCMP_NEWSIZE:
+ RenderPatternWindow (wud);
+ break;
+
+ case IDCMP_INTUITICKS:
+ if (!(wud->Gadgets[GD_PattEdit]->Flags & GFLG_SELECTED))
+ ActivateGadget (wud->Gadgets[GD_PattEdit], wud->Win, NULL);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+static void RenderPatternWindow (struct WinUserData *wud)
+
+/* Prints <TitleInfo> on the right hand of the window title bar */
+{
+ UWORD tlen, twidth;
+ struct RastPort *rp;
+ struct TextFont *oldfont;
+
+ tlen = strlen (TitleInfo); /* Couldn't get it from SPrintf()!! */
+
+ rp = wud->Win->RPort;
+ oldfont = rp->Font;
+ SetFont (rp, DrawInfo->dri_Font);
+
+ twidth = TextLength (rp, TitleInfo, tlen);
+
+ if (wud->Win->Flags & WFLG_WINDOWACTIVE)
+ {
+#ifndef OS30_ONLY
+ if (GfxBase->LibNode.lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ SetABPenDrMd (rp, DrawInfo->dri_Pens[FILLTEXTPEN],
+ DrawInfo->dri_Pens[FILLPEN], JAM2);
+#ifndef OS30_ONLY
+ else
+ {
+ SetAPen (rp, DrawInfo->dri_Pens[FILLTEXTPEN]);
+ SetBPen (rp, DrawInfo->dri_Pens[FILLPEN]);
+ SetDrMd (rp, JAM2);
+ }
+#endif /* !OS30_ONLY */
+ }
+ else
+ {
+#ifndef OS30_ONLY
+ if (GfxBase->LibNode.lib_Version >= 39)
+#endif /* !OS30_ONLY */
+ SetABPenDrMd (rp, DrawInfo->dri_Pens[TEXTPEN],
+ DrawInfo->dri_Pens[BACKGROUNDPEN], JAM2);
+#ifndef OS30_ONLY
+ else
+ {
+ SetAPen (rp, DrawInfo->dri_Pens[TEXTPEN]);
+ SetBPen (rp, DrawInfo->dri_Pens[BACKGROUNDPEN]);
+ SetDrMd (rp, JAM2);
+ }
+#endif /* !OS30_ONLY */
+ }
+
+ Move (rp, wud->Win->Width - twidth - 60, rp->TxBaseline + 1);
+ Text (rp, TitleInfo, tlen);
+
+ SetFont (rp, oldfont);
+}
+
+
+
+GLOBALCALL void UpdatePattern (void)
+{
+ struct WinUserData *wud = WDescr[WID_PATTERN].Wud;
+
+ if (wud && wud->Win)
+ {
+ struct SongInfo *si;
+ struct Pattern *patt;
+ ULONG currentinst;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ patt = si->Patt[si->CurrentPatt];
+ currentinst = si->CurrentInst;
+
+ SPrintf (WindowTitle, "%s/%03ld: %s (%ld/%ld)",
+ si->Title,
+ si->CurrentPatt,
+ patt->Name ? patt->Name : (UBYTE *)"", patt->Tracks, patt->Lines);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+ else
+ {
+ patt = NULL;
+ currentinst = 0;
+ strcpy (WindowTitle, "Pattern Editor");
+ }
+
+ SetWindowTitles (wud->Win, WindowTitle, NULL);
+
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_Pattern, patt,
+ PEA_CurrentInst, currentinst,
+ TAG_DONE);
+
+ SetGadgetAttrs (wud->Gadgets[GD_VSlider], wud->Win, NULL,
+ PGA_Total, patt ? patt->Lines : 0,
+ TAG_DONE);
+
+ SetGadgetAttrs (wud->Gadgets[GD_HSlider], wud->Win, NULL,
+ PGA_Total, patt ? patt->Tracks : 0,
+ TAG_DONE);
+ }
+
+ UpdatePattSize();
+}
+
+
+
+GLOBALCALL void UpdateEditorInst (void)
+{
+ struct WinUserData *wud = WDescr[WID_PATTERN].Wud;
+
+ if (wud && wud->Win)
+ {
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_CurrentInst, si->CurrentInst,
+ TAG_DONE);
+ ReleaseSemaphore (&si->Lock);
+ }
+ }
+}
+
+
+
+
+static void PatternLoad (STRPTR name, ULONG num, ULONG count)
+
+/* Load a pattern from an IFF PATT file. This function is called only
+ * by the file requester handler.
+ */
+{
+ struct SongInfo *si;
+ struct IFFHandle *iff;
+ LONG err;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ LockWindows();
+
+ if (iff = AllocIFF())
+ {
+ InitIFFasDOS (iff);
+ if (iff->iff_Stream = (ULONG) Open (name, MODE_OLDFILE))
+ {
+ if (!(err = OpenIFF (iff, IFFF_READ)))
+ {
+ LoadPattern (si, si->CurrentPatt + num, iff);
+ CloseIFF (iff);
+ }
+
+ Close (iff->iff_Stream);
+ }
+ else err = IoErr();
+
+ FreeIFF (iff);
+ }
+ else err = ERROR_NO_FREE_STORE;
+
+ ReleaseSemaphore (&si->Lock);
+
+ UpdatePatternList(); // This will also update the Pattern Editor.
+
+ UnlockWindows();
+
+ LastErr = err;
+ }
+}
+
+
+
+static LONG PatternSave (STRPTR name, struct Pattern *patt)
+
+/* Store a pattern to an IFF PATT file. */
+{
+ struct IFFHandle *iff;
+ LONG err;
+
+
+ if (iff = AllocIFF())
+ {
+ if (name)
+ {
+ InitIFFasDOS (iff);
+ iff->iff_Stream = (ULONG) Open (name, MODE_NEWFILE);
+ }
+ else /* Clipboard */
+ {
+ InitIFFasClip (iff);
+ iff->iff_Stream = (ULONG) OpenClipboard (PattSwitches.ClipboardUnit);
+ }
+
+ if (iff->iff_Stream)
+ {
+
+ if (!(err = OpenIFF (iff, IFFF_WRITE)))
+ {
+ SavePattern (iff, patt);
+ CloseIFF (iff);
+ }
+
+ if (name)
+ Close (iff->iff_Stream);
+ else
+ CloseClipboard ((struct ClipboardHandle *)iff->iff_Stream);
+ }
+ else err = IoErr();
+
+ FreeIFF (iff);
+ }
+ else err = ERROR_NO_FREE_STORE;
+
+ return err;
+}
+
+
+
+/*****************/
+/* Pattern Menus */
+/*****************/
+
+static void PatternMiOpen (void)
+{
+ StartFileRequest (FREQ_LOADPATT, PatternLoad);
+}
+
+
+
+static void PatternMiSave (void)
+{
+ struct SongInfo *si;
+ struct Pattern *patt;
+
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ LockWindows();
+ patt = si->Patt[si->CurrentPatt];
+ LastErr = PatternSave (patt->Name, patt);
+ UnlockWindows();
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PatternMiSaveAs (void)
+{
+ struct SongInfo *si;
+ struct Pattern *patt;
+ UBYTE name[PATHNAME_MAX];
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ LockWindows();
+
+ patt = si->Patt[si->CurrentPatt];
+
+ strncpy (name, patt->Name, PATHNAME_MAX-1);
+ name[PATHNAME_MAX-1] = '\0';
+
+ if (FileRequest (FREQ_SAVEINST, name))
+ LastErr = PatternSave (name, patt);
+
+ UnlockWindows();
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PatternMiSize (void)
+{
+ NewWindow (WID_PATTSIZE);
+}
+
+
+
+static void PatternMiMark (struct WinUserData *wud)
+{
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_MarkRegion, -1,
+ TAG_DONE);
+}
+
+
+
+static void PatternMiCut (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+ struct Pattern *patt, *cpatt;
+ struct Rectangle markregion;
+ UWORD i;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ patt = si->Patt[si->CurrentPatt];
+
+ if (GetAttr (PEA_MarkRegion, wud->Gadgets[GD_PattEdit], (ULONG *)&markregion))
+ {
+ if ((markregion.MaxX == 0) && (markregion.MaxY == 0))
+ return; /* Not in mark mode */
+
+ if (cpatt = xmAddPattern (si, -1,
+ PATTA_Tracks, markregion.MaxX - markregion.MinX + 1,
+ PATTA_Lines, 0, /* Do not allocate anything */
+ PATTA_Name, patt->Name,
+ TAG_DONE))
+ {
+ cpatt->Lines = markregion.MaxY - markregion.MinY + 1;
+
+ for (i = 0; i < cpatt->Tracks; i++)
+ cpatt->Notes[i] = patt->Notes[i + markregion.MinX] + markregion.MinY;
+
+ /* Snap marked region to Clipboard */
+ PatternSave (NULL, cpatt);
+
+ /* And then clear it */
+ for (i = 0; i < cpatt->Tracks; i++)
+ memset (cpatt->Notes[i], 0, sizeof (struct Note) * cpatt->Lines);
+
+ /* Free dummy pattern */
+ cpatt->Lines = 0;
+ for (i = 0; i < cpatt->Tracks; i++)
+ cpatt->Notes[i] = NULL;
+ xmRemPattern (si, -1, (ULONG)cpatt);
+
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_Pattern, patt, /* Refresh display */
+ PEA_MarkRegion, NULL, /* Stop marking */
+ TAG_DONE);
+ }
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PatternMiCopy (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+ struct Pattern *patt, *cpatt;
+ struct Rectangle markregion;
+ UWORD i;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ patt = si->Patt[si->CurrentPatt];
+
+ if (GetAttr (PEA_MarkRegion, wud->Gadgets[GD_PattEdit], (ULONG *)&markregion))
+ {
+ if ((markregion.MaxX == 0) && (markregion.MaxY == 0))
+ return; /* Not in mark mode */
+
+ if (cpatt = xmAddPattern (si, -1,
+ PATTA_Tracks, markregion.MaxX - markregion.MinX + 1,
+ PATTA_Lines, 0, /* Do not allocate anything */
+ PATTA_Name, patt->Name,
+ TAG_DONE))
+ {
+ cpatt->Lines = markregion.MaxY - markregion.MinY + 1;
+
+ for (i = 0; i < cpatt->Tracks; i++)
+ cpatt->Notes[i] = patt->Notes[i + markregion.MinX] + markregion.MinY;
+
+ PatternSave (NULL, cpatt);
+
+ /* Free dummy pattern */
+ cpatt->Lines = 0;
+ for (i = 0; i < cpatt->Tracks; i++)
+ cpatt->Notes[i] = NULL;
+ xmRemPattern (si, -1, (ULONG)cpatt);
+
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_MarkRegion, NULL, /* Stop marking */
+ TAG_DONE);
+ }
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PatternMiPaste (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+ struct IFFHandle *iff;
+ struct Pattern *patt, *cpatt;
+ ULONG track, line;
+ UWORD i;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ patt = si->Patt[si->CurrentPatt];
+
+
+ /* Get pattern in Clip */
+
+ if (iff = AllocIFF())
+ {
+ InitIFFasClip (iff);
+ if (iff->iff_Stream = (ULONG) OpenClipboard (PattSwitches.ClipboardUnit))
+ {
+ if (!OpenIFF (iff, IFFF_READ))
+ {
+ if (cpatt = LoadPattern (si, -1, iff))
+ {
+ if (GetAttr (PEA_CursTrack, wud->Gadgets[GD_PattEdit], &track) &&
+ GetAttr (PEA_CursLine, wud->Gadgets[GD_PattEdit], &line))
+ {
+ /* Paste it into the pattern */
+
+ for (i = 0; i < min (cpatt->Tracks, patt->Tracks - track); i++)
+ CopyMem (cpatt->Notes[i], patt->Notes[i + track] + line,
+ sizeof (struct Note) * min (patt->Lines - line, cpatt->Lines));
+
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_Pattern, patt, /* Refresh display */
+ PEA_CursTrack, min (track + cpatt->Tracks, patt->Tracks) - 1,
+ PEA_CursLine, min (line + cpatt->Lines, patt->Lines) - 1,
+ TAG_DONE);
+ }
+
+ xmRemPattern (si, -1, (ULONG)cpatt);
+ }
+ CloseIFF (iff);
+ }
+
+ CloseClipboard ((struct ClipboardHandle *)iff->iff_Stream);
+ }
+
+ FreeIFF (iff);
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PatternMiErase (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+ struct Pattern *patt;
+ struct Rectangle markregion;
+ UWORD i;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ patt = si->Patt[si->CurrentPatt];
+
+ if (GetAttr (PEA_MarkRegion, wud->Gadgets[GD_PattEdit], (ULONG *)&markregion))
+ {
+ if ((markregion.MaxX == 0) && (markregion.MaxY == 0))
+ return; /* Not in mark mode */
+
+ /* Clear marked region */
+ for (i = markregion.MinX; i <= markregion.MaxX; i++)
+ memset (patt->Notes[i] + markregion.MinY, 0, sizeof (struct Note) *
+ (markregion.MaxY - markregion.MinY + 1));
+
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_Pattern, patt, /* Refresh display */
+ PEA_MarkRegion, NULL, /* Stop marking */
+ TAG_DONE);
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PatternMiUndo (struct WinUserData *wud)
+{
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_UndoChange, 1,
+ TAG_DONE);
+}
+
+
+
+static void PatternMiRedo (struct WinUserData *wud)
+{
+ SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
+ PEA_UndoChange, -1,
+ TAG_DONE);
+}
+
+
+
+static void PatternMiSettings (void)
+{
+ NewWindow (WID_PATTPREFS);
+}
--- /dev/null
+/*
+** PlayWin.c
+**
+** Copyright (C) 1995,96 Bernardo Innocenti
+**
+** Play window handling functions.
+*/
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <libraries/gadtools.h>
+#include <dos/dostags.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/utility.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+#include "Player.h"
+#include "CustomClasses.h"
+
+
+
+/*****************************/
+/* Local function prototypes */
+/*****************************/
+
+static void PlayRewClicked (void);
+static void PlayPlayClicked (void);
+static void PlayFwdClicked (void);
+static void PlayStopClicked (void);
+static void PlayVolClicked (void);
+static void PlayRstClicked (void);
+
+
+
+enum {
+ GD_PlayGroup0,
+ GD_PlayGroup1,
+ GD_PlayGroup2,
+ GD_PlayGroup3,
+ GD_PlayRew,
+ GD_PlayPlay,
+ GD_PlayFwd,
+ GD_PlayStop,
+ GD_PlayVol,
+ GD_PlayGroup4,
+ GD_PlayPos,
+ GD_PlayGroup5,
+ GD_PlayTime,
+ GD_PlayTimeReset,
+
+ Play_CNT
+};
+
+
+
+static struct Process *PlayerProc = NULL;
+static struct MsgPort *PlayerPort = NULL;
+
+
+
+static LONG PlayArgs[] =
+{
+ HGROUP_KIND, 0,
+ VGROUP_KIND, 0,
+ HGROUP_KIND, 0,
+ IMAGEBUTTON_KIND, (LONG)PlayRewClicked, IM_REW, TAG_DONE,
+ IMAGEBUTTON_KIND, (LONG)PlayPlayClicked, IM_PLAY, TAG_DONE,
+ IMAGEBUTTON_KIND, (LONG)PlayFwdClicked, IM_FWD, TAG_DONE,
+ IMAGEBUTTON_KIND, (LONG)PlayStopClicked, IM_STOP, TAG_DONE,
+ ENDGROUP_KIND,
+ SLIDER_KIND, (LONG)PlayVolClicked, MSG_VOL_GAD, 0, 64, (LONG)"%lu", 3, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ TEXT_KIND, MSG_POS_GAD, 7, GTTX_Border, TRUE, TAG_DONE,
+ HGROUP_KIND, 0,
+ TEXT_KIND, MSG_TIME_GAD, 7, GTTX_Border, TRUE, TAG_DONE,
+ BUTTON_KIND, (LONG)PlayRstClicked, MSG_RST_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG PlayWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)PlayArgs,
+ XMWIN_GCount, Play_CNT,
+ XMWIN_Title, MSG_PLAY_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|BUTTONIDCMP|SLIDERIDCMP,
+ XMWIN_PostOpenFunc, (LONG)SetupPlayer,
+// XMWIN_PostOpenFunc, (LONG)UpdatePlay,
+ XMWIN_PostCloseFunc,(LONG)CleanupPlayer,
+ XMWIN_HelpNode, (LONG)"Play",
+ TAG_DONE
+};
+
+
+
+/*************************/
+/* Play window functions */
+/*************************/
+
+
+/*
+static void UpdatePlay (void)
+{
+
+}
+*/
+
+
+GLOBALCALL LONG SetupPlayer (void)
+{
+ LONG err;
+ BPTR PlayerSegList;
+
+ if (PlayerProc) return RETURN_OK;
+
+ if (PlayerPort = CreateMsgPort())
+ {
+ if (PlayerSegList = NewLoadSeg ("PROGDIR:Players/32Channels.player", NULL))
+ {
+ if (PlayerProc = CreateNewProcTags (
+ NP_Seglist, PlayerSegList,
+ NP_FreeSeglist, TRUE,
+ NP_Name, "XModule Player",
+ NP_Priority, 25,
+ NP_WindowPtr, NULL,
+ NP_Input, NULL,
+ NP_Output, NULL,
+ NP_Error, NULL,
+ NP_CopyVars, FALSE,
+ TAG_DONE))
+ {
+ struct PlayerCmd dummy;
+
+ dummy.pcmd_Message.mn_ReplyPort = PlayerPort;
+ dummy.pcmd_ID = PCMD_SETUP;
+
+ PutMsg (&PlayerProc->pr_MsgPort, (struct Message *)&dummy);
+
+ WaitPort (PlayerPort);
+ GetMsg (PlayerPort);
+
+ if (!dummy.pcmd_Err)
+ return RETURN_OK;
+
+ /* Error cleanup */
+
+ ShowRequest (MSG_PLAYER_INIT_ERR, 0, dummy.pcmd_Err);
+ CleanupPlayer();
+ return dummy.pcmd_Err;
+ }
+ else
+ err = ERROR_NO_FREE_STORE;
+
+ UnLoadSeg (PlayerSegList);
+ }
+ else
+ CantOpenLib ("PROGDIR:Players/32Channels.player", 0);
+
+ DeleteMsgPort (PlayerPort); PlayerPort = NULL;
+ }
+ else
+ err = ERROR_NO_FREE_STORE;
+
+ return err;
+}
+
+
+
+void CleanupPlayer (void)
+{
+ if (PlayerProc)
+ {
+ /* Signal player process to give up. dos.library will
+ * automatically UnLoadSeg() its code.
+ */
+ Signal ((struct Task *)PlayerProc, SIGBREAKF_CTRL_C);
+ PlayerProc = NULL;
+
+ DeleteMsgPort (PlayerPort); PlayerPort = NULL;
+ }
+}
+
+
+
+/****************/
+/* Play Gadgets */
+/****************/
+
+static void PlayPlayClicked (void)
+{
+ ShowRequestStr ("Enjoy the silence...\n\n"
+ "Sorry, XModule replay code is still under developement.\n"
+ "I'm adapting the DeliTracker 14bit-NotePlayer mixing engine\n"
+ "to XModule's internal module format, which will allow\n"
+ "upto 32 channels with 14bit stereo output at 56Khz max,\n"
+ "provided your machine is fast enough.",
+ "Incredible!", NULL);
+}
+
+
+
+static void PlayRewClicked (void)
+{
+/*
+ struct PlayerCmd cmd;
+
+ cmd.pcmd_Message.mn_ReplyPort = PlayerPort;
+ cmd.pcmd_ID = PCMD_INIT;
+ cmd.pcmd_Data = songinfo;
+
+ PutMsg (&PlayerProc->pr_MsgPort, (struct Message *)&cmd);
+
+ WaitPort (PlayerPort);
+ GetMsg (PlayerPort);
+
+ ShowString ("%ld", &cmd.pcmd_Err);
+*/
+}
+
+
+static void PlayFwdClicked (void)
+{
+/*
+ struct PlayerCmd cmd;
+
+ cmd.pcmd_Message.mn_ReplyPort = PlayerPort;
+ cmd.pcmd_ID = PCMD_PLAY;
+ cmd.pcmd_Data = songinfo;
+
+ PutMsg (&PlayerProc->pr_MsgPort, (struct Message *)&cmd);
+
+ WaitPort (PlayerPort);
+ GetMsg (PlayerPort);
+*/
+}
+
+
+static void PlayStopClicked (void)
+{
+}
+
+
+static void PlayVolClicked (void)
+{
+}
+
+
+static void PlayRstClicked (void)
+{
+}
--- /dev/null
+**
+** XModule replay routine 0.1
+**
+** Copyright (C) 1995,96 Bernardo Innocenti
+**
+**
+
+DEBUG_VERSION EQU 0 ; Debug Code On/Off
+USE_CALIBRATION EQU 0 ; Calibration On/Off
+STAND_ALONE EQU 0 ; Start without XModule
+
+MAXCHANNELS EQU 32
+
+ MACHINE 68020
+
+VersionTag MACRO
+ dc.b '$VER: XModule_Replay 0.1 (6.1.96) © 1995,96 by Bernardo Innocenti.',$a
+ endm
+
+
+ incdir Include:
+
+ include exec/types.i
+
+ include exec/io.i
+ include exec/macros.i
+ include exec/memory.i
+ include exec/tasks.i
+ include exec/execbase.i
+ include exec/funcdef.i
+ include hardware/intbits.i
+ include hardware/custom.i
+ include devices/audio.i
+ include dos/dos.i
+ include dos/dosextens.i
+ include graphics/gfxbase.i
+
+ include libraries/songclass.i
+
+ include Guru.i
+ include Player.i
+
+
+ xref _DATA_BAS_
+
+custom EQU $dff000
+
+
+ShowDebug MACRO
+ IFNE '\2',''
+ move.l \2,-(sp)
+ ENDC
+ lea .msg\@(pc),a0
+ move.l sp,a1 ; Arguments array
+ xref KPrintF
+ jsr KPrintF
+ IFNE '\2',''
+ lea 4(sp),sp ; Fix stack
+ ENDC
+ bra.s .cont
+.msg\@
+ dc.b \1,$a,0
+ even
+.cont
+ ENDM
+
+* An harmless Enforcer hit to help debugging operations :-)
+Enforcer MACRO
+ suba.l \1,\1
+ clr.l (\1)
+ ENDM
+
+
+
+***************************************************************************
+* Start
+***************************************************************************
+
+ SECTION XMPlay,code
+
+
+Start:
+ lea _DATA_BAS_+32766,a4
+ NEAR a4
+
+* Initialize SysBase
+ move.l 4.w,a6
+ move a6,_SysBase(a4)
+
+* D3 is the return code for the startup message
+ moveq #RETURN_OK,d3
+
+* Find this task's process structure
+ suba.l a1,a1
+ JSRLIB FindTask
+ move.l d0,MyTask(a4)
+ move.l d0,a2
+
+* Calculate ClockBase
+ move.l ex_EClockFrequency(a6),d0
+ move.l d0,d1
+ lsl.l #2,d1 ; #3 for panning support
+ add.l d1,d0
+ move.l d0,ClockBase ; Clock constant
+
+* Open graphics.library
+ lea GFXName(pc),a1
+ moveq.l #0,d0 ; Library version
+ JSRLIB OpenLibrary
+ move.l d0,_GFXBase(a4)
+ bne.s .gfx_ok
+
+ move.l #ERROR_INVALID_RESIDENT_LIBRARY,d3
+.gfx_ok
+
+ IFEQ STAND_ALONE
+
+* Wait for startup message
+ lea pr_MsgPort(a2),a2 ; a2 still contains our Process structure.
+ move.l a2,a0
+ JSRLIB WaitPort
+
+* Get startup message
+ move.l a2,a0
+ JSRLIB GetMsg
+ move.l d0,a1
+
+* Get parent task address
+ move.l MN_REPLYPORT(a1),a0
+ move.l MP_SIGTASK(a0),ParentTask(a4)
+
+* Reply startup message
+ move.l d3,pcmd_Err(a1) ; Put returncode
+ JSRLIB ReplyMsg
+
+ ENDC STAND_ALONE
+
+ tst.l d3 ; Error?
+ bne.s Exit
+
+* Go ahead to MainLoop
+
+
+***************************************************************************
+* Main loop
+***************************************************************************
+
+MainLoop:
+
+ IFEQ STAND_ALONE
+
+ moveq #0,d1
+ moveq #0,d0
+ move.b MP_SIGBIT(a2),d1
+ bset.l d1,d0
+ bset.l #SIGBREAKB_CTRL_C,d0
+ JSRLIB Wait
+ move.l d0,d2 ; Save signal bits
+
+* Check message port
+.checkmsg
+ move.l a2,a0
+ JSRLIB GetMsg
+ tst.l d0
+ beq.s .nomsg
+ move.l d0,a3
+
+ ELSE
+
+ moveq #0,d0
+
+ ENDC STAND_ALONE
+
+* Command Dispatcher
+ move.l pcmd_ID(a3),d0
+ cmp.l #PCMD_COUNT,d0 ; Check if command is supported
+ bge.s .unknown_msg
+
+* Switch (pcmd_ID)
+ lsl.l #2,d0
+ move.l a3,a0
+ move.l .jumptable(pc,d0),a1
+ jsr (a1)
+
+.reply_msg
+ IFEQ STAND_ALONE
+ move.l d0,pcmd_Err(a3) ; Set returncode
+ move.l a3,a1
+ JSRLIB ReplyMsg ; Return message to parent...
+
+ bra.s .checkmsg ; ...and check for more messages
+ ELSE
+ bra.s Exit
+ ENDC STAND_ALONE
+
+.unknown_msg
+ moveq #PERR_UNKNOWN_COMMAND,d0
+ bra.s .reply_msg
+
+.jumptable
+ dc.l CmdInit
+ dc.l CmdPlay
+
+.nomsg
+
+* Check for abort signal
+ btst.l #SIGBREAKB_CTRL_C,d2
+ beq.s MainLoop
+
+* Note: I'm falling down to Exit.
+
+
+
+***************************************************************************
+* Cleanup and Exit
+***************************************************************************
+
+Exit:
+
+* Close graphics.library
+
+ move.l _GFXBase(a4),d0
+ beq.s .noclose
+ move.l d0,a1
+ JSRLIB CloseLibrary
+.noclose
+
+
+* Exit point.
+* This rts will remove our process and call UnLoadSeg() on us.
+
+ rts
+
+ VersionTag
+
+GFXName dc.b 'graphics.library',0
+AudioDevName dc.b 'audio.device',0
+
+ EVEN
+
+
+
+***************************************************************************
+* LONG error = CmdInit (struct PlayerCmd *pcmd, struct ExecBase *SysBase)
+* D0 A0 A6
+***************************************************************************
+
+CmdInit
+ move.l a3,-(a7) ; Save a3
+ move.l pcmd_Data(a0),a3 ; get SongInfo
+
+* Allocate audio channels
+ moveq #0,d0
+ moveq #0,d1
+ lea AudioDevName(pc),a0
+ lea AllocIORequest(a4),a1
+ JSRLIB OpenDevice
+ tst.l d0
+ bne .AllocFail ; Error
+
+* Set audio interrupts
+ move.w AttnFlags(a6),d0
+ btst.l #AFB_68020,d0 ; >= 68020 CPU ?
+ bne.s .CPU68020 ; yes !
+
+.CPU68000
+ moveq #INTB_AUD0,d0
+ lea Audio0IRQ_000(a4),a1
+ JSRLIB SetIntVector ; set new Audio0-IRQ
+ move.l d0,_OldAudio0(a4)
+
+ moveq #INTB_AUD1,d0
+ lea Audio1IRQ_000(a4),a1
+ JSRLIB SetIntVector ; set new Audio1-IRQ
+ move.l d0,_OldAudio1(a4)
+ bra.s .CalcScale
+
+.CPU68020
+ moveq #INTB_AUD0,d0
+ lea Audio0IRQ_020(a4),a1
+ JSRLIB SetIntVector ; set new Audio0-IRQ
+ move.l d0,_OldAudio0(a4)
+
+ moveq #INTB_AUD1,d0
+ lea Audio1IRQ_020(a4),a1
+ JSRLIB SetIntVector ; set new Audio1-IRQ
+ move.l d0,_OldAudio1(a4)
+
+.CalcScale
+
+* Initialize ScaleFactor
+ move.w si_MaxTracks(a3),d1
+ move.w d1,ChannelNum(a4)
+ lsr.w #1,d1 ; !!!
+ moveq #32,d0 ; max. channels (16 for no panning!)
+ divu d1,d0
+ move.w d0,ScaleFactor ; set ScaleFactor
+
+* Initialize player. Warning: these calls will trash all registrers!
+ movem.l d2-d7/a2-a6,-(a7)
+ jsr InitSVolTable(pc)
+ jsr InitBoostTable(pc)
+ jsr InitTracksNChannels(pc)
+ bsr NewMixFreq ; MixPeriod berechnen
+ movem.l (sp)+,d2-d7/a2-a6
+
+* Audio hardware twiddling
+ lea custom,a0
+ move.w #$000f,dmacon(a0) ; Audio-DMA aus
+ move.w #$00ff,adkcon(a0)
+ move.w #$0780,intreq(a0) ; Audio-Req. löschen
+ move.w #$0780,intena(a0) ; Audio-Int. aus
+
+ moveq #RETURN_OK,d0
+ move.l (a7)+,a3
+ rts
+
+.AllocFail
+ moveq #PERR_NO_AUDIO,d0
+ move.l (a7)+,a3
+ rts
+
+
+
+***************************************************************************
+* LONG error = CmdPlay (struct PlayerCmd *pcmd, struct ExecBase *SysBase)
+* D0 A0 A6
+***************************************************************************
+
+CmdPlay
+ move.l pcmd_Data(a0),a0 ; get SongInfo
+ move.l a0,SongInfo
+ move.w si_MaxTracks(a0),mt_NumCh(a4) ; set channels
+
+ lea mt_ChanTemp,a0
+ moveq #MAXCHANNELS-1,d0
+ moveq #0,d1
+mt_clear
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ move.l d1,(a0)+
+ dbra d0,mt_clear
+
+ bsr mt_init2
+
+ moveq #RETURN_OK,d0
+ rts
+
+
+ FAR
+
+
+
+***************************************************************************
+* void SetTimer (timeval)
+* D0
+***************************************************************************
+
+SetTimer
+ RTS ; !!!
+
+
+***************************************************************************
+* void SetTimer (void)
+*
+***************************************************************************
+
+SongEnd
+ RTS ; !!!
+
+
+*-----------------------------------------------------------------------*
+;
+; Next Pattern
+
+NPatt
+; move.l dtg_StopInt(a5),a0
+; jsr (a0) !!!!!!!!!!!!!!!!!!!
+ clr.w mt_counter
+ clr.w mt_PBreakPos
+ clr.b mt_PosJumpFlag
+ clr.w mt_PatternPos
+ addq.w #1,mt_SongPos
+ move.w mt_MaxPos,d0
+ cmp.w mt_SongPos,d0
+ bne.s NEnd
+ clr.w mt_SongPos
+NEnd
+; move.l dtg_StartInt(a5),a0
+; jsr (a0) !!!!!!!!!!!!!!!!!!!
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Previous Pattern
+
+PPatt
+; move.l dtg_StopInt(a5),a0 !!!!!!!!!!!!!!!!!!!
+; jsr (a0)
+ clr.w mt_counter
+ clr.w mt_PBreakPos
+ clr.b mt_PosJumpFlag
+ clr.w mt_PatternPos
+ subq.w #1,mt_SongPos
+ bcc.s PEnd
+ move.w mt_MaxPos,d0
+ subq.w #1,d0
+ move.w d0,mt_SongPos
+PEnd
+; move.l dtg_StartInt(a5),a0 !!!!!!!!!!!!!!!!!!
+; jsr (a0)
+ rts
+
+
+
+*-----------------------------------------------------------------------*
+;
+; TakeTracker - Replay
+
+;********************************************
+;* ----- Protracker V1.1B Playroutine ----- *
+;* Lars "Zap" Hamre/Amiga Freelancers 1991 *
+;* Bekkeliveien 10, N-2010 STRØMMEN, Norway *
+;********************************************
+
+;---- Tempo ----
+
+SetTempo
+ move.l _SysBase,a4
+ move.l ex_EClockFrequency(a4),d1
+ add.l d1,d1
+ add.l d1,d1
+ add.l ex_EClockFrequency(a4),d1
+ lsr.l #1,d1
+ cmp.w #32,d0
+ bhs.s setemsk
+ moveq #32,d0
+setemsk divu d0,d1
+ move.l d1,d0
+ jsr SetTimer
+ rts
+
+;---- Playroutine ----
+
+ STRUCTURE PlayNote,0
+ UBYTE pn_Note
+ UBYTE pn_Inst
+ UBYTE pn_Cmd
+ UBYTE pn_Val
+ APTR pn_Start
+ ULONG pn_Length
+ LONG pn_LoopStart
+ LONG pn_Replen
+ ULONG pn_WaveStart
+ ULONG pn_RealLength
+ UWORD pn_Period
+ UWORD pn_WantedPeriod
+ UBYTE pn_FineTune
+ UBYTE pn_Volume
+ UBYTE pn_TonePortDirec
+ UBYTE pn_TonePortSpeed
+ UBYTE pn_VibratoCmd
+ UBYTE pn_VibratoPos
+ UBYTE pn_TremoloCmd
+ UBYTE pn_TremoloPos
+ UBYTE pn_WaveControl
+ UBYTE pn_GlissFunk
+ UBYTE pn_SampleOffset
+ UBYTE pn_PattPos
+ UBYTE pn_LoopCount
+ UBYTE pn_FunkOffset
+ LABEL pn_SIZEOF
+
+
+mt_init2
+
+ MOVE.L songinfo,a0
+ move.w si_Length(a0),mt_MaxPos
+
+ moveq #0,d0
+ MOVE.W si_GlobalTempo(a0),d0
+ BSR SetTempo
+
+ MOVE.W si_GlobalSpeed(a0),mt_speed
+ CLR.W mt_counter
+ CLR.W mt_SongPos
+ CLR.W mt_PBreakPos
+ CLR.B mt_PosJumpFlag
+ CLR.B mt_PBreakFlag
+ CLR.B mt_LowMask
+ CLR.B mt_PattDelTime
+ CLR.B mt_PattDelTime2
+ CLR.B mt_Reset
+ CLR.W mt_PatternPos
+
+ RTS
+
+mt_music
+ MOVEM.L D2-D7/A2-A6,-(SP)
+ TST.B mt_Reset
+ BEQ.S mt_mus
+ JSR SongEnd
+ SUBQ.B #1,mt_Reset
+ BEQ mt_exit
+ BSR mt_init2
+ CLR.B mt_Reset
+ BRA mt_exit
+mt_mus
+ ADDQ.W #1,mt_counter
+ MOVE.W mt_counter,d0
+ CMP.W mt_speed,d0
+ BLO.S mt_NoNewNote
+ CLR.W mt_counter
+ TST.B mt_PattDelTime2
+ BEQ mt_GetNewNote
+ BSR.S mt_NoNewAllChannels
+ BRA mt_dskip
+
+mt_NoNewNote
+ BSR.S mt_NoNewAllChannels
+ BRA mt_NoNewPosYet
+
+mt_NoNewAllChannels
+ move.l NoteStructure,a5
+ lea mt_ChanTemp,a6
+ move.w mt_NumCh,d5
+ subq.w #1,d5
+mt_NoNewLoop
+ bsr mt_CheckEfx
+ lea NoteChannel_SIZE(a5),a5
+ lea pn_SIZEOF(a6),a6
+ dbra d5,mt_NoNewLoop
+ RTS
+
+mt_GetNewNote
+ move.l songinfo,a0
+ move.l si_Patt(a0),a1
+ move.l si_Sequence(a0),a0
+ moveq #0,d0
+ move.w mt_SongPos,d0
+ add.w d0,d0
+ move.w (a0,d0.w),d0 ; Put current patt number in d0
+ mulu #pa_SIZEOF,d0
+ move.l pa_Notes(a0,d0),a0 ; Get pointer to tracks array
+
+ move.l NoteStructure,a5
+ lea mt_ChanTemp,a6
+ move.w mt_NumCh,d5
+ subq.w #1,d5
+
+mt_GetNewLoop
+ bsr mt_PlayVoice
+ lea NoteChannel_SIZE(a5),a5
+ lea pn_SIZEOF(a6),a6
+ dbra d5,mt_GetNewLoop
+ BRA mt_dskip
+
+mt_PlayVoice
+ CMP.W #NCHD_Ignore,nch_Stereo(a5)
+ BEQ mt_PerNop
+ TST.L (A6)
+ BNE.S mt_plvskip
+ BSR mt_PerNop
+mt_plvskip
+ move.l (a0)+,a4
+ moveq #0,d0
+ move.w d5,d0
+ add.l d0,d0
+ add.l d0,d0
+ move.l (a4,d0),a4 ; Pointer to channel
+ moveq #0,d0
+ move.w mt_PatternPos,d0
+ add.l d0,d0
+ add.l d0,d0
+ move.l (a4,d0),(a6) ; Current note in (a6)
+
+ moveq #0,d2
+ move.b note_Inst(a6),d2 ; Instrument number in d2
+ TST.W d2
+ BEQ mt_SetRegs
+ MOVEQ #0,d3
+
+* Retrieve Instrument structure
+ move.l songinfo,a1
+ lea si_Instr(a1),a1
+ mulu #in_SIZEOF,d2
+ lea (a1,d2),a1
+
+ move.l in_SampleData(a1),pn_Start(a6)
+ move.l in_Length(a1),pn_Length(a6)
+ move.l in_Length(a1),pn_RealLength(a6)
+ move.b in_FineTune+1(a1),pn_FineTune(a6)
+ move.b in_Volume(a1),pn_Volume(a6)
+ move.l in_Repeat(a1),d3 ; Get repeat
+ tst.l D3
+ beq.s mt_NoLoop
+ move.l pn_Start(a6),d2 ; Get start
+ add.l D3,D2 ; Add repeat
+ move.l D2,pn_LoopStart(a6)
+ move.l D2,pn_WaveStart(a6)
+ move.l in_Repeat(a1),d0 ; Get repeat
+ add.l in_Replen(a1),d0 ; Add replen
+ move.l d0,pn_Length(A6)
+ move.l in_Replen(a1),pn_Replen(a6) ; Save replen
+ MOVEQ #0,D0
+ MOVE.B pn_Volume(A6),D0
+ move.w d0,nch_Volume(a5)
+ bset.b #NCHB_Volume,nch_Changed(a5)
+ BRA.S mt_SetRegs
+
+mt_NoLoop
+ MOVE.L pn_Start(a6),d2
+ ADD.L D3,D2
+ MOVE.L D2,pn_LoopStart(A6)
+ MOVE.L D2,pn_WaveStart(A6)
+ MOVE.L in_Replen(a1),pn_Replen(a6) ; Save replen
+ MOVEQ #0,D0
+ MOVE.B pn_Volume(A6),D0
+ move.w d0,nch_Volume(a5)
+ bset.b #NCHB_Volume,nch_Changed(a5)
+
+mt_SetRegs
+ move.b pn_Note(a6),d0
+ beq mt_CheckMoreEfx2 ; If no note
+ move.w pn_Cmd(a6),D0
+ AND.W #$FFF0,D0
+ CMP.W #$0E50,D0 ; Check for E5
+ beq.s mt_DoSetFineTune
+ move.b pn_Cmd(A6),D0
+ cmp.b #EFF_TONEPORTAMENTO,d0 ; TonePortamento
+ beq.s mt_ChkTonePorta
+ cmp.b #EFF_TONEPVOLSLIDE,d0
+ beq.s mt_ChkTonePorta
+ cmp.b #EFF_SAMPLEOFFSET,d0 ; Sample Offset
+ BNE.S mt_SetPeriod
+ BSR mt_CheckMoreEfx
+ BRA.S mt_SetPeriod
+
+mt_DoSetFineTune
+ bsr mt_SetFineTune
+ bra.s mt_SetPeriod
+
+mt_ChkTonePorta
+ bsr mt_SetTonePorta
+ bra mt_CheckMoreEfx2
+
+mt_SetPeriod
+ MOVEM.L D0-D1/A0-A1,-(SP)
+ moveq #0,d1
+ move.b pn_Note(a6),d1 ; Find note period
+ sub.b #24,d1 ; Less octaves!!!
+ add.l d1,d1 ; Look up period table
+ move.l d1,d0
+ lea mt_PeriodTable,a1
+
+ MOVEQ #0,D1
+ move.b pn_FineTune(a6),d1
+ MULU #MAXNOTES*2,D1
+ ADD.L D1,A1
+ move.w (a1,d0.w),pn_Period(a6)
+ movem.l (sp)+,d0-d1/a0-a1
+
+ MOVE.W 2(A6),D0
+ AND.W #$0FF0,D0
+ CMP.W #$0ED0,D0 ; Notedelay
+ BEQ mt_CheckMoreEfx2
+
+ BTST #2,pn_WaveControl(A6)
+ BNE.S mt_vibnoc
+ CLR.B pn_VibratoPos(A6)
+mt_vibnoc
+ BTST #6,pn_WaveControl(A6)
+ BNE.S mt_trenoc
+ CLR.B pn_TremoloPos(A6)
+mt_trenoc
+ move.l pn_Start(a6),nch_SampleStart(a5)
+ move.l pn_Length(a6),d0
+ move.l d0,nch_SampleLength(a5)
+ bset.b #NCHB_Sample,nch_Changed(a5)
+ move.w pn_Period(A6),nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+mt_CheckMoreEfx2
+ move.l pn_LoopStart(A6),nch_RepeatStart(a5)
+ move.l pn_Replen(A6),d0
+ move.l d0,nch_RepeatLength(a5)
+ bset.b #NCHB_Repeat,nch_Changed(a5)
+
+ bra mt_CheckMoreEfx
+mt_dskip
+ MOVE.W mt_rsize,d0 ; !!!
+ ADD.W d0,mt_PatternPos
+ MOVE.B mt_PattDelTime,d0
+ BEQ.S mt_dskc
+ MOVE.B d0,mt_PattDelTime2
+ CLR.B mt_PattDelTime
+mt_dskc TST.B mt_PattDelTime2
+ BEQ.S mt_dska
+ SUBQ.B #1,mt_PattDelTime2
+ BEQ.S mt_dska
+ MOVE.W mt_rsize,D0
+ SUB.W D0,mt_PatternPos
+mt_dska TST.B mt_PBreakFlag
+ BEQ.S mt_nnpysk
+ SF mt_PBreakFlag
+ MOVEQ #0,D0
+ MOVE.W mt_PBreakPos,D0
+ CLR.W mt_PBreakPos
+ MULU mt_rsize,D0
+ MOVE.W D0,mt_PatternPos
+mt_nnpysk
+ MOVE.W mt_PatternPos,d0
+ CMP.W mt_psize,d0
+ BLO.S mt_NoNewPosYet
+mt_NextPosition
+ MOVE.W mt_PBreakPos,d0
+ MULU mt_rsize,d0
+ MOVE.W D0,mt_PatternPos
+ CLR.W mt_PBreakPos
+ CLR.B mt_PosJumpFlag
+ ADDQ.W #1,mt_SongPos
+ MOVE.W mt_SongPos,d1
+ CMP.W mt_MaxPos,d1
+ BLO.S mt_NoNewPosYet
+ MOVE.W mt_StartPos,mt_SongPos
+ MOVE.B #2,mt_Reset
+mt_NoNewPosYet
+ TST.B mt_PosJumpFlag
+ BNE.S mt_NextPosition
+ jsr NoteSignal
+mt_exit MOVEM.L (SP)+,D2-D7/A2-A6
+ RTS
+
+mt_CheckEfx
+ CMP.W #NCHD_Ignore,nch_Stereo(a5)
+ BEQ mt_Return
+ BSR mt_UpdateFunk
+ moveq #0,d0
+ move.b pn_Cmd(a6),d0
+ beq.s mt_PerNop
+ cmpi.b #EFF_COUNT,d0
+ bge.s mt_PerNop
+ move.l efftable(pc),a0
+ add.l d0,d0
+ add.l d0,d0
+ move.l (a0,d0),a0
+ jmp (a0)
+
+SetBack move.w pn_Period(a6),nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+mt_Return
+ RTS
+
+efftable
+ dc.l mt_PerNop ; EFF_NULL
+ dc.l mt_PortaUp ; EFF_PORTAMENTOUP
+ dc.l mt_PortaDown ; EFF_PORTAMENTODOWN
+ dc.l mt_TonePortamento ; EFF_TONEPORTAMENTO
+ dc.l mt_Vibrato ; EFF_VIBRATO
+ dc.l mt_TonePlusVolSlide ; EFF_TONEPVOLSLIDE
+ dc.l mt_VibratoPlusVolSlide ; EFF_VIBRATOVOLSLIDE
+ dc.l mt_Tremolo ; EFF_TREMOLO
+ dc.l mt_PerNop ; EFF_UNUSED
+ dc.l mt_PerNop ; EFF_SAMPLEOFFSET
+ dc.l mt_VolumeSlide ; EFF_VOLSLIDE
+ dc.l mt_PerNop ; EFF_POSJUMP
+ dc.l mt_PerNop ; EFF_SETVOLUME
+ dc.l mt_PerNop ; EFF_PATTERNBREAK
+ dc.l mt_E_Commands ; EFF_MISC
+ dc.l mt_PerNop ; EFF_SETSPEED
+ dc.l mt_PerNop ; EFF_SETTEMPO
+ dc.l mt_Arpeggio ; EFF_ARPEGGIO
+
+
+mt_PerNop
+ move.w pn_Period(A6),nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+ RTS
+
+mt_Arpeggio
+ MOVEQ #0,D0
+ MOVE.W mt_counter,D0
+ DIVS #3,D0
+ SWAP D0
+ CMP.W #0,D0
+ BEQ.S mt_Arpeggio2
+ CMP.W #2,D0
+ BEQ.S mt_Arpeggio1
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ LSR.B #4,D0
+ BRA.S mt_Arpeggio3
+
+mt_Arpeggio1
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #15,D0
+ BRA.S mt_Arpeggio3
+
+mt_Arpeggio2
+ MOVE.W pn_Period(A6),D2
+ BRA.S mt_Arpeggio4
+
+mt_Arpeggio3
+ ASL.W #1,D0
+ MOVEQ #0,D1
+ MOVE.B pn_FineTune(A6),D1
+ MULU #36*2,D1
+ LEA mt_PeriodTable,A0
+ ADD.L D1,A0
+ MOVEQ #0,D1
+ MOVE.W pn_Period(A6),D1
+ MOVEQ #35,D3 ; bugfix !!!
+mt_arploop
+ MOVE.W (A0,D0.W),D2
+ CMP.W (A0),D1
+ BHS.S mt_Arpeggio4
+ ADDQ.L #2,A0
+ DBRA D3,mt_arploop
+ RTS
+
+mt_Arpeggio4
+ move.w d2,nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+; MOVE.W D2,6(A5)
+ RTS
+
+mt_FinePortaUp
+ TST.W mt_counter
+ BNE mt_Return
+ MOVE.B #$0F,mt_LowMask
+mt_PortaUp
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B mt_LowMask,D0
+ MOVE.B #$FF,mt_LowMask
+ SUB.W D0,pn_Period(A6)
+ MOVE.W pn_Period(A6),D0
+ AND.W #$0FFF,D0
+ CMP.W #113,D0
+ BPL.S mt_PortaUskip
+ AND.W #$F000,pn_Period(A6)
+ OR.W #113,pn_Period(A6)
+mt_PortaUskip
+ MOVE.W pn_Period(A6),D0
+ AND.W #$0FFF,D0
+ move.w d0,nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+ RTS
+
+mt_FinePortaDown
+ TST.W mt_counter
+ BNE mt_Return
+ MOVE.B #$0F,mt_LowMask
+mt_PortaDown
+ CLR.W D0
+ MOVE.B pn_Val(A6),D0
+ AND.B mt_LowMask,D0
+ MOVE.B #$FF,mt_LowMask
+ ADD.W D0,pn_Period(A6)
+ MOVE.W pn_Period(A6),D0
+ AND.W #$0FFF,D0
+ CMP.W #856,D0
+ BMI.S mt_PortaDskip
+ AND.W #$F000,pn_Period(A6)
+ OR.W #856,pn_Period(A6)
+mt_PortaDskip
+ MOVE.W pn_Period(A6),D0
+ AND.W #$0FFF,D0
+ move.w d0,nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+ RTS
+
+mt_SetTonePorta
+ MOVE.L a0,-(sp)
+ MOVE.W (A6),D2
+ AND.W #$0FFF,D2
+ MOVEQ #0,D0
+ MOVE.B pn_FineTune(A6),D0
+ MULU #36*2,D0 ; bugfix !!!
+ LEA mt_PeriodTable,A0
+ ADD.L D0,A0
+ MOVEQ #0,D0
+mt_StpLoop
+ CMP.W (A0,D0.W),D2
+ BHS.S mt_StpFound
+ ADDQ.W #2,D0
+ CMP.W #36*2,D0 ; bugfix !!!
+ BLO.S mt_StpLoop
+ MOVEQ #35*2,D0
+mt_StpFound
+ MOVE.B pn_FineTune(A6),D2
+ AND.B #8,D2
+ BEQ.S mt_StpGoss
+ TST.W D0
+ BEQ.S mt_StpGoss
+ SUBQ.W #2,D0
+mt_StpGoss
+ MOVE.W (A0,D0.W),D2
+ MOVE.L (SP)+,A0
+ MOVE.W D2,pn_WantedPeriod(A6)
+ MOVE.W pn_Period(A6),D0
+ CLR.B pn_TonePortDirec(A6)
+ CMP.W D0,D2
+ BEQ.S mt_ClearTonePorta
+ BGE mt_Return
+ MOVE.B #1,pn_TonePortDirec(A6)
+ RTS
+
+mt_ClearTonePorta
+ CLR.W pn_WantedPeriod(A6)
+ RTS
+
+mt_TonePortamento
+ MOVE.B pn_Val(A6),D0
+ BEQ.S mt_TonePortNoChange
+ MOVE.B D0,pn_TonePortSpeed(A6)
+ CLR.B pn_Val(A6)
+mt_TonePortNoChange
+ TST.W pn_WantedPeriod(A6)
+ BEQ mt_Return
+ MOVEQ #0,D0
+ MOVE.B pn_TonePortSpeed(A6),D0
+ TST.B pn_TonePortDirec(A6)
+ BNE.S mt_TonePortaUp
+mt_TonePortaDown
+ ADD.W D0,pn_Period(A6)
+ MOVE.W pn_WantedPeriod(A6),D0
+ CMP.W pn_Period(A6),D0
+ BGT.S mt_TonePortaSetPer
+ MOVE.W pn_WantedPeriod(A6),pn_Period(A6)
+ CLR.W pn_WantedPeriod(A6)
+ BRA.S mt_TonePortaSetPer
+
+mt_TonePortaUp
+ SUB.W D0,pn_Period(A6)
+ MOVE.W pn_WantedPeriod(A6),D0
+ CMP.W pn_Period(A6),D0
+ BLT.S mt_TonePortaSetPer
+ MOVE.W pn_WantedPeriod(A6),pn_Period(A6)
+ CLR.W pn_WantedPeriod(A6)
+
+mt_TonePortaSetPer
+ MOVE.W pn_Period(A6),D2
+ MOVE.B pn_GlissFunk(A6),D0
+ AND.B #$0F,D0
+ BEQ.S mt_GlissSkip
+ MOVEQ #0,D0
+ MOVE.B pn_FineTune(A6),D0
+ MULU #36*2,D0
+ LEA mt_PeriodTable,A0
+ ADD.L D0,A0
+ MOVEQ #0,D0
+mt_GlissLoop
+ CMP.W (A0,D0.W),D2
+ BHS.S mt_GlissFound
+ ADDQ.W #2,D0
+ CMP.W #36*2,D0
+ BLO.S mt_GlissLoop
+ MOVEQ #35*2,D0
+mt_GlissFound
+ MOVE.W (A0,D0.W),D2
+mt_GlissSkip
+ move.w d2,nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+ RTS
+
+mt_Vibrato
+ MOVE.B pn_Val(A6),D0
+ BEQ.S mt_Vibrato2
+ MOVE.B pn_VibratoCmd(A6),D2
+ AND.B #$0F,D0
+ BEQ.S mt_vibskip
+ AND.B #$F0,D2
+ OR.B D0,D2
+mt_vibskip
+ MOVE.B pn_Val(A6),D0
+ AND.B #$F0,D0
+ BEQ.S mt_vibskip2
+ AND.B #$0F,D2
+ OR.B D0,D2
+mt_vibskip2
+ MOVE.B D2,pn_VibratoCmd(A6)
+mt_Vibrato2
+ MOVE.B pn_VibratoPos(A6),D0
+ LEA mt_VibratoTable,A4
+ LSR.W #2,D0
+ AND.W #$001F,D0
+ MOVEQ #0,D2
+ MOVE.B pn_WaveControl(A6),D2
+ AND.B #$03,D2
+ BEQ.S mt_vib_sine
+ LSL.B #3,D0
+ CMP.B #1,D2
+ BEQ.S mt_vib_rampdown
+ MOVE.B #255,D2
+ BRA.S mt_vib_set
+mt_vib_rampdown
+ TST.B pn_VibratoPos(A6)
+ BPL.S mt_vib_rampdown2
+ MOVE.B #255,D2
+ SUB.B D0,D2
+ BRA.S mt_vib_set
+mt_vib_rampdown2
+ MOVE.B D0,D2
+ BRA.S mt_vib_set
+mt_vib_sine
+ MOVE.B (A4,D0.W),D2
+mt_vib_set
+ MOVE.B pn_VibratoCmd(A6),D0
+ AND.W #15,D0
+ MULU D0,D2
+ LSR.W #7,D2
+ MOVE.W pn_Period(A6),D0
+ TST.B pn_VibratoPos(A6)
+ BMI.S mt_VibratoNeg
+ ADD.W D2,D0
+ BRA.S mt_Vibrato3
+mt_VibratoNeg
+ SUB.W D2,D0
+mt_Vibrato3
+ move.w d0,nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+ MOVE.B pn_VibratoCmd(A6),D0
+ LSR.W #2,D0
+ AND.W #$003C,D0
+ ADD.B D0,pn_VibratoPos(A6)
+ RTS
+
+mt_TonePlusVolSlide
+ BSR mt_TonePortNoChange
+ BRA mt_VolumeSlide
+
+mt_VibratoPlusVolSlide
+ BSR.S mt_Vibrato2
+ BRA mt_VolumeSlide
+
+mt_Tremolo
+ MOVE.B pn_Val(A6),D0
+ BEQ.S mt_Tremolo2
+ MOVE.B pn_TremoloCmd(A6),D2
+ AND.B #$0F,D0
+ BEQ.S mt_treskip
+ AND.B #$F0,D2
+ OR.B D0,D2
+mt_treskip
+ MOVE.B pn_Val(A6),D0
+ AND.B #$F0,D0
+ BEQ.S mt_treskip2
+ AND.B #$0F,D2
+ OR.B D0,D2
+mt_treskip2
+ MOVE.B D2,pn_TremoloCmd(A6)
+mt_Tremolo2
+ MOVE.B pn_TremoloPos(A6),D0
+ LEA mt_VibratoTable,A4
+ LSR.W #2,D0
+ AND.W #$001F,D0
+ MOVEQ #0,D2
+ MOVE.B pn_WaveControl(A6),D2
+ LSR.B #4,D2
+ AND.B #$03,D2
+ BEQ.S mt_tre_sine
+ LSL.B #3,D0
+ CMP.B #1,D2
+ BEQ.S mt_tre_rampdown
+ MOVE.B #255,D2
+ BRA.S mt_tre_set
+mt_tre_rampdown
+ TST.B pn_VibratoPos(A6)
+ BPL.S mt_tre_rampdown2
+ MOVE.B #255,D2
+ SUB.B D0,D2
+ BRA.S mt_tre_set
+mt_tre_rampdown2
+ MOVE.B D0,D2
+ BRA.S mt_tre_set
+mt_tre_sine
+ MOVE.B (A4,D0.W),D2
+mt_tre_set
+ MOVE.B pn_TremoloCmd(A6),D0
+ AND.W #15,D0
+ MULU D0,D2
+ LSR.W #6,D2
+ MOVEQ #0,D0
+ MOVE.B pn_Volume(A6),D0
+ TST.B pn_TremoloPos(A6)
+ BMI.S mt_TremoloNeg
+ ADD.W D2,D0
+ BRA.S mt_Tremolo3
+mt_TremoloNeg
+ SUB.W D2,D0
+mt_Tremolo3
+ BPL.S mt_TremoloSkip
+ CLR.W D0
+mt_TremoloSkip
+ CMP.W #$40,D0
+ BLS.S mt_TremoloOk
+ MOVE.W #$40,D0
+mt_TremoloOk
+ move.w d0,nch_Volume(a5)
+ bset.b #NCHB_Volume,nch_Changed(a5)
+ MOVE.B pn_TremoloCmd(A6),D0
+ LSR.W #2,D0
+ AND.W #$003C,D0
+ ADD.B D0,pn_TremoloPos(A6)
+ RTS
+
+mt_SampleOffset
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ BEQ.S mt_sononew
+ MOVE.B D0,pn_SampleOffset(A6)
+mt_sononew
+ MOVE.B pn_SampleOffset(A6),D0
+ LSL.L #7,D0
+ CMP.L pn_Length(A6),D0
+ BGE.S mt_sofskip
+ SUB.L D0,pn_Length(A6)
+ LSL.L #1,D0
+ ADD.L D0,pn_Start(A6)
+ RTS
+mt_sofskip
+ MOVE.L #$0001,pn_Length(A6)
+ RTS
+
+mt_VolumeSlide
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ LSR.B #4,D0
+ TST.B D0
+ BEQ.S mt_VolSlideDown
+mt_VolSlideUp
+ ADD.B D0,pn_Volume(A6)
+ CMP.B #$40,pn_Volume(A6)
+ BMI.S mt_vsuskip
+ MOVE.B #$40,pn_Volume(A6)
+mt_vsuskip
+ MOVE.B pn_Volume(A6),D0
+ move.w d0,nch_Volume(a5)
+ bset.b #NCHB_Volume,nch_Changed(a5)
+ RTS
+
+mt_VolSlideDown
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+mt_VolSlideDown2
+ SUB.B D0,pn_Volume(A6)
+ BPL.S mt_vsdskip
+ CLR.B pn_Volume(A6)
+mt_vsdskip
+ MOVE.B pn_Volume(A6),D0
+ move.w d0,nch_Volume(a5)
+ bset.b #NCHB_Volume,nch_Changed(a5)
+ RTS
+
+mt_PositionJump
+ MOVE.W mt_SongPos,D0
+ ADDQ.W #1,D0
+ CMP.W mt_MaxPos,D0
+ BNE.S mt_pj1
+ MOVE.B #1,mt_Reset
+mt_pj1 MOVE.B pn_Val(A6),D0
+ CMP.W mt_SongPos,D0
+ BNE.S mt_pj0
+ MOVE.B #1,mt_Reset
+ TST.W mt_PatternPos
+ BNE.S mt_pj0
+ MOVE.B #2,mt_Reset
+mt_pj0 SUBQ.B #1,D0
+ MOVE.W D0,mt_SongPos
+ BPL.S mt_pj2
+ MOVE.B #2,mt_Reset
+mt_pj2 CLR.W mt_PBreakPos
+ ST mt_PosJumpFlag
+ RTS
+
+mt_VolumeChange
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ CMP.B #$40,D0
+ BLS.S mt_VolumeOk
+ MOVEQ #$40,D0
+mt_VolumeOk
+ MOVE.B D0,pn_Volume(A6)
+ move.w d0,nch_Volume(a5)
+ bset.b #NCHB_Volume,nch_Changed(a5)
+ RTS
+
+mt_PatternBreak
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ MOVE.L D0,D2
+ LSR.W #4,D0
+ MULU #10,D0
+ AND.W #$0F,D2
+ ADD.W D2,D0
+ CMP.W #63,D0
+ BHI.S mt_pj2
+ MOVE.W D0,mt_PBreakPos
+ ST.B mt_PosJumpFlag
+ RTS
+
+mt_SetSpeed
+ MOVEQ #0,D0
+ MOVE.B 3(A6),D0
+ BNE.S mt_SetSpd
+ CMP.W #32,D0
+ BHS SetTempo
+mt_SetSpd
+ TST.W D0
+ BEQ.S mt_SetBack
+ CLR.W mt_counter
+ MOVE.W D0,mt_speed
+mt_SetBack
+ RTS
+
+mt_CheckMoreEfx
+ BSR mt_UpdateFunk
+ MOVE.B 2(A6),D0
+ AND.B #$0F,D0
+ CMP.B #$9,D0
+ BEQ mt_SampleOffset
+ CMP.B #$B,D0
+ BEQ mt_PositionJump
+ CMP.B #$D,D0
+ BEQ mt_PatternBreak
+ CMP.B #$E,D0
+ BEQ.S mt_E_Commands
+ CMP.B #$F,D0
+ BEQ.S mt_SetSpeed
+ CMP.B #$C,D0
+ BEQ mt_VolumeChange
+ BRA mt_PerNop
+
+mt_E_Commands
+ MOVE.B pn_Val(A6),D0 ; !!!
+ AND.B #$F0,D0
+ LSR.B #4,D0
+; BEQ.S mt_FilterOnOff
+ CMP.B #1,D0
+ BEQ mt_FinePortaUp
+ CMP.B #2,D0
+ BEQ mt_FinePortaDown
+ CMP.B #3,D0
+ BEQ.S mt_SetGlissControl
+ CMP.B #4,D0
+ BEQ.S mt_SetVibratoControl
+ CMP.B #5,D0
+ BEQ.S mt_SetFineTune
+ CMP.B #6,D0
+ BEQ.S mt_JumpLoop
+ CMP.B #7,D0
+ BEQ mt_SetTremoloControl
+ CMP.B #9,D0
+ BEQ mt_RetrigNote
+ CMP.B #$A,D0
+ BEQ mt_VolumeFineUp
+ CMP.B #$B,D0
+ BEQ mt_VolumeFineDown
+ CMP.B #$C,D0
+ BEQ mt_NoteCut
+ CMP.B #$D,D0
+ BEQ mt_NoteDelay
+ CMP.B #$E,D0
+ BEQ mt_PatternDelay
+ CMP.B #$F,D0
+ BEQ mt_FunkIt
+ RTS
+
+mt_FilterOnOff
+; MOVE.B pn_Val(A6),D0
+; AND.B #1,D0
+; ASL.B #1,D0
+; AND.B #$FD,$BFE001
+; OR.B D0,$BFE001
+; RTS
+
+mt_SetGlissControl
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ AND.B #$F0,pn_GlissFunk(A6)
+ OR.B D0,pn_GlissFunk(A6)
+ RTS
+
+mt_SetVibratoControl
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ AND.B #$F0,pn_WaveControl(A6)
+ OR.B D0,pn_WaveControl(A6)
+ RTS
+
+mt_SetFineTune
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ MOVE.B D0,pn_FineTune(A6)
+ RTS
+
+mt_JumpLoop
+ TST.W mt_counter
+ BNE mt_Return
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ BEQ.S mt_SetLoop
+ TST.B pn_LoopCount(A6)
+ BEQ.S mt_jumpcnt
+ SUBQ.B #1,pn_LoopCount(A6)
+ BEQ mt_Return
+mt_jmploop MOVE.W pn_PattPos(A6),mt_PBreakPos
+ ST mt_PBreakFlag
+ RTS
+
+mt_jumpcnt
+ MOVE.B D0,pn_LoopCount(A6)
+ BRA.S mt_jmploop
+
+mt_SetLoop
+ MOVE.W mt_PatternPos,D0
+ LSR.W #4,D0
+ MOVE.B D0,pn_PattPos(A6)
+ RTS
+
+mt_SetTremoloControl
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ LSL.B #4,D0
+ AND.B #$0F,pn_WaveControl(A6)
+ OR.B D0,pn_WaveControl(A6)
+ RTS
+
+mt_RetrigNote
+ MOVE.L D1,-(SP)
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ BEQ.S mt_rtnend
+ MOVEQ #0,D1
+ MOVE.W mt_counter,D1
+ BNE.S mt_rtnskp
+ MOVE.W (A6),D1
+ AND.W #$0FFF,D1
+ BNE.S mt_rtnend
+ MOVEQ #0,D1
+ MOVE.W mt_counter,D1
+mt_rtnskp
+ DIVU D0,D1
+ SWAP D1
+ TST.W D1
+ BNE.S mt_rtnend
+mt_DoRetrig
+ move.l pn_Start(a6),nch_SampleStart(a5)
+ move.l pn_Length(a6),d0
+ move.l d0,nch_SampleLength(a5)
+ bset.b #NCHB_Sample,nch_Changed(a5)
+ move.l pn_LoopStart(A6),nch_RepeatStart(a5)
+ move.l pn_Replen(A6),d0
+ move.l d0,nch_RepeatLength(a5)
+ bset.b #NCHB_Repeat,nch_Changed(a5)
+ move.w pn_Period(A6),nch_Frequency(a5)
+ bset.b #NCHB_Frequency,nch_Changed(a5)
+mt_rtnend
+ move.l (sp)+,d1
+ rts
+
+mt_VolumeFineUp
+ TST.W mt_counter
+ BNE mt_Return
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #$F,D0
+ BRA mt_VolSlideUp
+
+mt_VolumeFineDown
+ TST.W mt_counter
+ BNE mt_Return
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ BRA mt_VolSlideDown2
+
+mt_NoteCut
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ CMP.W mt_counter,D0
+ BNE mt_Return
+ CLR.B pn_Volume(A6)
+ MOVEQ #0,D0
+ move.w d0,nch_Volume(a5)
+ bset.b #NCHB_Volume,nch_Changed(a5)
+ RTS
+
+mt_NoteDelay
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ CMP.W mt_counter,D0
+ BNE mt_Return
+ MOVE.W (A6),D0
+ BEQ mt_Return
+ MOVE.L D1,-(SP)
+ BRA mt_DoRetrig
+
+mt_PatternDelay
+ TST.W mt_counter
+ BNE mt_Return
+ MOVEQ #0,D0
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ TST.B mt_PattDelTime2
+ BNE mt_Return
+ ADDQ.B #1,D0
+ MOVE.B D0,mt_PattDelTime
+ RTS
+
+mt_FunkIt
+ TST.W mt_counter
+ BNE mt_Return
+ MOVE.B pn_Val(A6),D0
+ AND.B #$0F,D0
+ LSL.B #4,D0
+ AND.B #$0F,pn_GlissFunk(A6)
+ OR.B D0,pn_GlissFunk(A6)
+ TST.B D0
+ BEQ mt_Return
+mt_UpdateFunk
+ MOVEM.L A0/D1,-(SP)
+ MOVEQ #0,D0
+ MOVE.B pn_GlissFunk(A6),D0
+ LSR.B #4,D0
+ BEQ.S mt_funkend
+ LEA mt_FunkTable,A0
+ MOVE.B (A0,D0.W),D0
+ ADD.B D0,pn_FunkOffset(A6)
+ BTST #7,pn_FunkOffset(A6)
+ BEQ.S mt_funkend
+ CLR.B pn_FunkOffset(A6)
+
+ MOVE.L pn_LoopStart(A6),D0
+ move.l pn_Replen(a6),d1
+ ADD.L D1,D0
+ ADD.L D1,D0
+ MOVE.L pn_WaveStart(A6),A0
+ ADDQ.L #1,A0
+ CMP.L D0,A0
+ BLO.S mt_funkok
+ MOVE.L pn_LoopStart(A6),A0
+mt_funkok
+ MOVE.L A0,pn_WaveStart(A6)
+ MOVEQ #-1,D0
+ SUB.B (A0),D0
+ MOVE.B D0,(A0)
+mt_funkend
+ MOVEM.L (SP)+,A0/D1
+ RTS
+
+
+mt_FunkTable dc.b 0,5,6,7,8,10,11,13,16,19,22,26,32,43,64,128
+
+mt_VibratoTable
+ dc.b 000,024,049,074,097,120,141,161
+ dc.b 180,197,212,224,235,244,250,253
+ dc.b 255,253,250,244,235,224,212,197
+ dc.b 180,161,141,120,097,074,049,024
+
+mt_PeriodTable
+; Tuning 0, Normal
+ dc.w 856,808,762,720,678,640,604,570,538,508,480,453
+ dc.w 428,404,381,360,339,320,302,285,269,254,240,226
+ dc.w 214,202,190,180,170,160,151,143,135,127,120,113
+; Tuning 1
+ dc.w 850,802,757,715,674,637,601,567,535,505,477,450
+ dc.w 425,401,379,357,337,318,300,284,268,253,239,225
+ dc.w 213,201,189,179,169,159,150,142,134,126,119,113
+; Tuning 2
+ dc.w 844,796,752,709,670,632,597,563,532,502,474,447
+ dc.w 422,398,376,355,335,316,298,282,266,251,237,224
+ dc.w 211,199,188,177,167,158,149,141,133,125,118,112
+; Tuning 3
+ dc.w 838,791,746,704,665,628,592,559,528,498,470,444
+ dc.w 419,395,373,352,332,314,296,280,264,249,235,222
+ dc.w 209,198,187,176,166,157,148,140,132,125,118,111
+; Tuning 4
+ dc.w 832,785,741,699,660,623,588,555,524,495,467,441
+ dc.w 416,392,370,350,330,312,294,278,262,247,233,220
+ dc.w 208,196,185,175,165,156,147,139,131,124,117,110
+; Tuning 5
+ dc.w 826,779,736,694,655,619,584,551,520,491,463,437
+ dc.w 413,390,368,347,328,309,292,276,260,245,232,219
+ dc.w 206,195,184,174,164,155,146,138,130,123,116,109
+; Tuning 6
+ dc.w 820,774,730,689,651,614,580,547,516,487,460,434
+ dc.w 410,387,365,345,325,307,290,274,258,244,230,217
+ dc.w 205,193,183,172,163,154,145,137,129,122,115,109
+; Tuning 7
+ dc.w 814,768,725,684,646,610,575,543,513,484,457,431
+ dc.w 407,384,363,342,323,305,288,272,256,242,228,216
+ dc.w 204,192,181,171,161,152,144,136,128,121,114,108
+; Tuning -8
+ dc.w 907,856,808,762,720,678,640,604,570,538,508,480
+ dc.w 453,428,404,381,360,339,320,302,285,269,254,240
+ dc.w 226,214,202,190,180,170,160,151,143,135,127,120
+; Tuning -7
+ dc.w 900,850,802,757,715,675,636,601,567,535,505,477
+ dc.w 450,425,401,379,357,337,318,300,284,268,253,238
+ dc.w 225,212,200,189,179,169,159,150,142,134,126,119
+; Tuning -6
+ dc.w 894,844,796,752,709,670,632,597,563,532,502,474
+ dc.w 447,422,398,376,355,335,316,298,282,266,251,237
+ dc.w 223,211,199,188,177,167,158,149,141,133,125,118
+; Tuning -5
+ dc.w 887,838,791,746,704,665,628,592,559,528,498,470
+ dc.w 444,419,395,373,352,332,314,296,280,264,249,235
+ dc.w 222,209,198,187,176,166,157,148,140,132,125,118
+; Tuning -4
+ dc.w 881,832,785,741,699,660,623,588,555,524,494,467
+ dc.w 441,416,392,370,350,330,312,294,278,262,247,233
+ dc.w 220,208,196,185,175,165,156,147,139,131,123,117
+; Tuning -3
+ dc.w 875,826,779,736,694,655,619,584,551,520,491,463
+ dc.w 437,413,390,368,347,328,309,292,276,260,245,232
+ dc.w 219,206,195,184,174,164,155,146,138,130,123,116
+; Tuning -2
+ dc.w 868,820,774,730,689,651,614,580,547,516,487,460
+ dc.w 434,410,387,365,345,325,307,290,274,258,244,230
+ dc.w 217,205,193,183,172,163,154,145,137,129,122,115
+; Tuning -1
+ dc.w 862,814,768,725,684,646,610,575,543,513,484,457
+ dc.w 431,407,384,363,342,323,305,288,272,256,242,228
+ dc.w 216,203,192,181,171,161,152,144,136,128,121,114
+
+;/* End of File */
+
+
+
+*-----------------------------------------------------------------------*
+;
+; 14Bit/32Voices-NotePlayer
+
+NoteSignal
+ movem.l a2-a4,-(sp)
+
+ move.l NoteInfo,d0 ; get NoteStructure
+ beq AO_End
+ move.l d0,a4
+
+ move.l nst_Channels(a4),d0 ; pointer to first channel in list
+AO_PlayLoop
+ move.l d0,a2 ; ^NoteChannel
+
+ cmpi.w #NCHD_Ignore,nch_Stereo(a2) ; skip this channel ?
+ beq AO_PlayNext ; yes !
+
+ move.l nch_NotePlayer(a2),d0 ; channel array
+ beq AO_PlayNext ; no array !
+ move.l d0,a3
+
+ move.l (a3),a0
+ tst.l (a0) ; Channel disabled ?
+ beq AO_PlayNext ; yes !
+
+ move.l 4(a3),a3 ; ^TrackStructure
+AO_Sample
+ btst.b #NCHB_Sample,nch_Changed(a2)
+ beq.s AO_Repeat ; no new sample !
+
+ move.l nch_SampleStart(a2),a0
+ move.l nch_SampleLength(a2),d0
+ add.l d0,a0
+ move.l a0,td_SPtr(a3) ; Pointer auf das Sampleende
+ neg.l d0
+ move.l d0,td_SLen(a3) ; negative Sample-Restbytes
+
+ clr.l td_LLen(a3) ; Kein LoopSample vorhanden
+
+ clr.w td_FreC(a3) ; -> Kein Überlauf bei erstem Samplebyte!
+AO_Repeat
+ btst.b #NCHB_Repeat,nch_Changed(a2)
+ beq.s AO_Frequency ; no new repeat !
+
+ move.l nch_RepeatStart(a2),a0
+ move.l nch_RepeatLength(a2),d0
+ moveq #2,d1
+ cmp.l d0,d1 ; Repeat-Length only 1 Word ?
+ bne.s AO_RepSamp ; No !
+ move.l a0,a1
+ move.b (a1)+,d1
+ cmp.b (a1)+,d1 ; Is it a NullSample ?
+ bne.s AO_RepSamp ; No !
+ moveq #0,d0
+AO_RepSamp
+ add.l d0,a0
+ move.l a0,td_LPtr(a3) ; LoopSample-Endadr.
+ neg.l d0
+ move.l d0,td_LLen(a3) ; negative LoopSample-Länge
+AO_Frequency
+ btst.b #NCHB_Frequency,nch_Changed(a2)
+ beq.s AO_Volume ; no new freq !
+
+ move.l nch_Frequency(a2),td_Fre(a3) ; Frequency for this channel
+AO_Volume
+ btst.b #NCHB_Volume,nch_Changed(a2)
+ beq.s AO_PlayNext ; no new vol !
+
+ moveq #0,d0
+ move.w nch_Volume(a2),d0
+ lsl.l #6,d0
+ divu nst_MaxVolume(a4),d0
+ move.w d0,td_Vol(a3) ; Volume for this channel
+AO_PlayNext
+ move.l nch_NextChannel(a2),d0
+ bne AO_PlayLoop ; next channel
+AO_End
+ movem.l (sp)+,a2-a4
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Set the master volume/balance
+
+;SetVolBal
+; move.l NoteInfo,d0 ; get NoteStructure
+; beq .End
+; move.l d0,a4
+; move.l delibase,a5 ; get DeliBase
+;
+; move.w dtg_SndVol(a5),d0
+; mulu dtg_SndLBal(a5),d0
+; lsr.w #6,d0
+; move.w d0,Volume07 ; left volume
+;
+; move.w dtg_SndVol(a5),d0
+; mulu dtg_SndRBal(a5),d0
+; lsr.w #6,d0
+; move.w d0,Volume8F ; right volume
+;.End
+; rts
+
+*-----------------------------------------------------------------------*
+;
+; Set the mixing frequency
+
+NewMixFreq:
+ move.l ClockBase(pc),d0
+ move.l PlayFreq,d1
+ divu d1,d0
+ move.w d0,MixPeriod
+ move.l ClockBase(pc),d1
+ divu d0,d1
+ move.w d1,MixFreq
+ rts
+
+
+*-----------------------------------------------------------------------*
+;
+; Shut down the NotePlayer
+
+NoteFinish
+ move.l NoteInfo,d0 ; get NoteStructure
+ beq .End
+ move.l d0,a4 ; copy Ptr
+
+ lea custom,a0
+ move.w #$000f,dmacon(a0) ; Audio-DMA aus
+ move.w #$00ff,adkcon(a0)
+ move.w #$0780,intreq(a0) ; Audio-Req. löschen
+ move.w #$0780,intena(a0) ; Audio-Int. aus
+
+ move.l nst_Channels(a4),d0 ; pointer to first channel in list
+.FreeChan
+ move.l d0,a0
+ clr.l nch_NotePlayer(a0) ; clear channel array pointer
+.FreeNext
+ move.l nch_NextChannel(a0),d0
+ bne.s .FreeChan ; next channel
+
+ moveq #INTB_AUD1,d0
+ move.l _OldAudio1,a1
+ JSRLIB SetIntVector ; set old Audio1-IRQ
+
+ moveq #INTB_AUD0,d0
+ move.l _OldAudio0,a1
+ JSRLIB SetIntVector ; set old Audio0-IRQ
+
+ lea AllocIORequest,a1 ; close audio.device
+ JSRLIB CloseDevice
+
+ clr.l NoteInfo ; shutdown complete
+.End
+ moveq #0,d0
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Boost-Value automatisch berechnen
+
+CalcBoostValue
+ tst.l BoostFlag ; Boost-Wert automatisch berechnen ?
+ beq.s .End ; nein, dann raus !
+
+ moveq #0,d0
+ move.w ChannelNum,d0
+ lsl.w #7,d0
+ add.w #128+256,d0
+ move.w d0,d1
+ lsr.w #1,d1
+ sub.w d1,d0
+ move.w #8*256,d1
+ cmp.w d0,d1
+ bcc.s .End
+ move.w d1,d0
+.End
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; 14Bit-Output-Tabelle erzeugen (14Bit Routine by Markus "maw" Weichselbaum)
+
+ IFEQ USE_CALIBRATION
+
+InitBoostTable
+ lea BitTable,a0 ; 14Bit-Tabelle erzeugen
+
+ move.w #32768-1,d0
+ move.w #-32768,d1
+ move.l BoostFactor,d2
+.Loop move.w d1,d3
+ muls d2,d3
+ asr.l #8,d3
+ move.l #-32768,d4
+ cmp.l d3,d4
+ bgt.s .Fail
+ move.l #32767,d4
+ cmp.l d3,d4
+ bgt.s .Okay
+.Fail move.w d4,d3
+.Okay move.w d3,d4 ; ist das 16 bit sample negativ?
+ bmi.s .IsNeg
+.IsPos lsr.w #8,d3 ; hi byte fertig machen
+ lsr.b #2,d4 ; vom lo nehmen wir nur die oberen 6 bits
+ bra.s .Write
+.IsNeg neg.w d3 ; erstmal auf positiv
+ lsr.w #8,d3 ; das hi byte fertig machen
+ neg.b d3 ; hi byte zurueck auf negativ
+ neg.w d4 ; nochmal auf positiv
+ lsr.b #2,d4 ; runter schieben
+ neg.w d4 ; jetzt noch zurueck auf negativ
+.Write move.b d3,(a0)+ ; hier haben wir das hi byte
+ move.b d4,(a0)+ ; hier haben wir das lo byte
+ addq.w #2,d1
+ dbra d0,.Loop
+ rts
+
+ ENDC
+
+*-----------------------------------------------------------------------*
+;
+; 14Bit-Output-Tabelle erzeugen (14Bit Routine by Christian Buchner)
+
+ IFNE USE_CALIBRATION
+
+TABLE_ENTRIES EQU 32768
+
+InitBoostTable movem.l a2-a3/d2-d7,-(sp)
+
+ lea CalibData+128,a2
+
+.negative move.l a2,a1 ; count the number of steps
+ moveq #128-1,d0 ; in the negative range
+ moveq #0,d5
+.count1 move.b -(a1),d1
+ ext.w d1
+ ext.l d1
+ add.l d1,d5
+ dbra d0,.count1 ; d5=number of steps
+
+ move.l #256*TABLE_ENTRIES/2,d6
+ divu BoostFactor+2,d6 ; divide by boost factor
+ divu d6,d5 ; calculate stepsize (d5) and
+ moveq #0,d7
+ swap d5
+ move.w d5,d7 ; fractional part (d7)
+ clr.w d5
+ swap d5
+ move.w d6,a3
+
+ lea BTable,a0 ; place at the middle of table
+ move.w #TABLE_ENTRIES/2-1,d0 ; number of negative values -1
+ moveq #-1,d1 ; HI value
+ moveq #-1,d2 ; LO value
+ moveq #0,d3 ; counter
+.fetchnext1 move.b (a2,d1.w),d4 ; add calibtable to counter
+ ext.w d4
+ add.w d4,d3
+ add.w d4,d2 ; maximize LO value
+.outerloop1 tst.w d3
+ bgt.s .positive1
+.negative1 subq.w #1,d1
+ cmp.w #$FF7F,d1
+ beq.s .fillup1
+ bra.s .fetchnext1
+.positive1 move.b d2,-(a0) ; store LO and HI value
+ move.b d1,-(a0)
+ sub.w d5,d2 ; subtract step size
+ sub.w d5,d3 ; decrement counter
+ sub.w d7,d6 ; sum up fractional part
+ bpl.s .repeat1
+ add.w a3,d6
+ subq.w #1,d2 ; decrement lo value
+ subq.w #1,d3 ; decrement counter
+.repeat1 dbra d0,.outerloop1
+ bra.s .positive
+
+.fillup1 move.w #$8000,-(a0) ; fill up the rest of the
+ dbra d0,.fillup1 ; table with negative minimum
+
+.positive move.l a2,a1 ; count the number of steps
+ moveq #128-1,d0 ; in the positive range
+ moveq #0,d5
+.count2 move.b (a1)+,d1
+ ext.w d1
+ ext.l d1
+ add.l d1,d5
+ dbra d0,.count2 ; d5=number of steps
+
+ move.l #256*TABLE_ENTRIES/2,d6
+ divu BoostFactor+2,d6 ; divide by boost factor
+ divu d6,d5 ; calculate stepsize (d5) and
+ moveq #0,d7
+ swap d5
+ move.w d5,d7 ; fractional part (d7)
+ clr.w d5
+ swap d5
+ move.w d6,a3
+
+ lea BTable,a0 ; place at the middle of table
+ move.w #TABLE_ENTRIES/2-1,d0 ; number of positive values -1
+ moveq #0,d1 ; HI value
+ moveq #0,d2 ; LO value
+ moveq #0,d3 ; counter
+.fetchnext2 move.b (a2,d1.w),d4 ; add calibtable to counter
+ ext.w d4
+ add.w d4,d3
+.outerloop2 tst.w d3
+ bgt.s .positive2
+.negative2 addq.w #1,d1 ; increment HI value
+ cmp.w #$80,d1
+ beq.s .fillup2
+ sub.w d4,d2 ; reset LO value
+ bra.s .fetchnext2
+.positive2 move.b d1,(a0)+ ; store HI and LO value
+ move.b d2,(a0)+
+ add.w d5,d2 ; add step size
+ sub.w d5,d3 ; decrement counter
+ sub.w d7,d6 ; sum up fractional part
+ bpl.s .repeat2
+ add.w a3,d6
+ addq.w #1,d2 ; increment LO value
+ subq.w #1,d3 ; decrement counter
+.repeat2 dbra d0,.outerloop2
+ bra.s .finished
+
+.fillup2 move.w #$7F7E,(a0)+ ; fill up the rest of the
+ dbra d0,.fillup2 ; table with positive maximum
+
+.finished movem.l (sp)+,a2-a3/d2-d7
+ rts
+
+ ENDC
+
+*-----------------------------------------------------------------------*
+;
+; Volume-Tabelle (signed) erzeugen
+
+InitSVolTable
+ lea VTable,a0 ; Volume-Tabelle erzeugen
+
+ moveq #65-1,d2
+ moveq #0,d4
+ move.w ScaleFactor,d5
+.Loop1 moveq #0,d0
+ move.w #256-1,d1
+.Loop2 move.w d0,d3
+ ext.w d3
+ muls d4,d3 ; Tabellennummer (0...64) * Samplewert
+ lsr.l #2,d3
+ andi.w #~1,d3
+ move.w d3,(a0)+
+ addq.w #1,d0
+ dbra d1,.Loop2
+ add.w d5,d4 ; > 1 = Oversampling
+ dbra d2,.Loop1
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Volume-Tabelle (unsigned) erzeugen
+
+InitUVolTable
+ lea VTable,a0 ; Volume-Tabelle erzeugen
+
+ moveq #65-1,d2
+ moveq #0,d4
+ move.w ScaleFactor,d5
+.Loop1 moveq #-128,d0
+ move.w #256-1,d1
+.Loop2 move.w d0,d3
+ ext.w d3
+ muls d4,d3 ; Tabellennummer (0...64) * Samplewert
+ lsr.l #2,d3
+ andi.w #~1,d3
+ move.w d3,(a0)+
+ addq.w #1,d0
+ dbra d1,.Loop2
+ add.w d5,d4 ; > 1 = Oversampling
+ dbra d2,.Loop1
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Alle 32 Software-Tracks initialisieren
+
+InitTracksNChannels
+ lea T00Data(pc),a0
+ lea QuietInstr(pc),a1
+ moveq #32-1,d0
+.Loop move.l (a1),td_SPtr(a0) ; Zum Abspielen vorbereiten
+ move.l 4(a1),td_SLen(a0)
+ move.l 8(a1),td_LPtr(a0)
+ move.l 12(a1),td_LLen(a0)
+ move.w #64,td_Vol(a0) ; Volle Lautstärke
+ move.l #8287,td_Fre(a0) ; C 0
+ clr.w td_FreC(a0)
+ lea td_SIZE(a0),a0
+ dbra d0,.Loop
+
+* Alle 4 Audiokanäle initialisieren:
+
+ lea custom+aud0,a0
+ moveq #4-1,d0
+.Init move.l #EmptyCBuf,ac_ptr(a0) ; Erstmal nichts spielen!
+ move.w #MBuf_SIZE/2,ac_len(a0)
+ move.w MixPeriod(pc),ac_per(a0)
+ move.w #0,ac_vol(a0)
+ adda.w #ac_SIZEOF,a0
+ dbra d0,.Init
+
+ clr.w C03BufOff ; Erste Songdaten kommen in Buffer 0
+ clr.w C12BufOff
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Init Sound
+
+InitSnd
+ move.l NoteInfo,d0 ; get NoteStructure
+ beq.s .End
+ move.l d0,a4
+
+ bsr InitTracksNChannels
+
+ IFNE DEBUG_VERSION
+ move.l #InitSndTxt,-(sp)
+ lea InformationFmt(pc),a0
+ bsr WriteInfoTxt
+ addq.w #4,sp
+ ENDC
+.End
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Remove Sound
+
+EndSnd
+ move.l NoteInfo,d0 ; get NoteStructure
+ beq.s .End
+ move.l d0,a4
+
+ bsr InitTracksNChannels
+
+ IFNE DEBUG_VERSION
+ move.l #EndSndTxt,-(sp)
+ lea InformationFmt(pc),a0
+ bsr WriteInfoTxt
+ addq.w #4,sp
+ ENDC
+.End
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Start playing
+
+StartSnd
+ lea custom,a0
+ move.w #$800f,dmacon(a0) ; Audio-DMA an
+ move.w #$00ff,adkcon(a0)
+ move.w #$0780,intreq(a0) ; Audio-Req. löschen
+ move.w #$8180,intena(a0) ; Audio-Int. an
+ moveq #64,d0
+ move.w d0,aud0+ac_vol(a0)
+ move.w d0,aud1+ac_vol(a0)
+ moveq #1,d0
+ move.w d0,aud2+ac_vol(a0)
+ move.w d0,aud3+ac_vol(a0)
+.End
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Stop playing
+
+StopSnd
+ lea custom,a0
+ move.w #$000f,dmacon(a0) ; Audio-DMA aus
+ move.w #$00ff,adkcon(a0)
+ move.w #$0780,intreq(a0) ; Audio-Req. löschen
+ move.w #$0780,intena(a0) ; Audio-Int. aus
+.End
+ rts
+
+*-----------------------------------------------------------------------*
+;
+; Audio-Interrupts für 68000 und 68010
+
+* Tracks 00-0F zusammenmischen:
+
+Int0_000:
+
+* Achtung! Soeben begann das Abspielen des beim
+* letzten Interrupt gefüllten Channel-Buffers!
+
+ move.w MixPeriod(pc),d0
+ lea Softy0IRQ_000,a1
+ cmpi.b #NT_SOFTINT,LN_TYPE(a1)
+ bne.s .Period
+ addi.w #32,d0 ; step down
+ move.w d0,MixPeriod
+ move.l ClockBase(pc),d1
+ divu d0,d1
+ move.w d1,MixFreq
+.Period
+ move.w d0,aud0+ac_per(a0)
+ move.w d0,aud3+ac_per(a0)
+ move.w d0,aud1+ac_per(a0)
+ move.w d0,aud2+ac_per(a0)
+
+ JSRLIB Cause
+
+ move.w #INTF_AUD0,custom+intreq ; AudioInt-Request löschen
+ rts
+
+Soft0_000:
+ movem.l d2-d7/a2-a6,-(sp)
+
+* Das rechenaufwendige Mixen passiert im Softint
+
+ lea custom,a0
+
+; move.w #$f00,color(a0)
+
+ lea T00Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T01Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T02Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T03Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T04Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T05Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T06Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T07Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T08Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T09Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T0AData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T0BData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T0CData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T0DData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T0EData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+ lea T0FData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_000
+
+; move.w #$00f,color(a0)
+
+ lea C0Buf,a2 ; Mix-Buffer konvertieren & löschen
+ adda.w C03BufOff(pc),a2
+ move.l a2,aud0+ac_ptr(a0)
+ lea C3Buf,a3
+ adda.w C03BufOff(pc),a3
+ move.l a3,aud3+ac_ptr(a0)
+ lea MBuffer,a4
+ lea BTable,a5
+ move.w #MBuf_SIZE/2-1,d0
+ moveq #0,d1
+.Convert
+ movem.w (a4),d2-d3
+ move.l d1,(a4)+
+ move.b 0(a5,d2.w),(a2)+ ; hier haben wir das hi byte
+ move.b 0(a5,d3.w),(a2)+
+ move.b 1(a5,d2.w),(a3)+ ; hier haben wir das lo byte
+ move.b 1(a5,d3.w),(a3)+
+ dbra d0,.Convert
+
+ eor.w #MBuf_SIZE,C03BufOff ; Buffer flippen
+
+; move.w #$000,color(a0)
+
+ movem.l (sp)+,d2-d7/a2-a6
+ rts
+
+
+* Tracks 10-1F zusammenmischen:
+
+Int1_000:
+
+* Achtung! Soeben begann das Abspielen des beim
+* letzten Interrupt gefüllten Channel-Buffers!
+
+ move.w MixPeriod(pc),d0
+ lea Softy1IRQ_000,a1
+ cmpi.b #NT_SOFTINT,LN_TYPE(a1)
+ bne.s .Period
+ addi.w #32,d0 ; step down
+ move.w d0,MixPeriod
+ move.l ClockBase(pc),d1
+ divu d0,d1
+ move.w d1,MixFreq
+.Period
+ move.w d0,aud0+ac_per(a0)
+ move.w d0,aud3+ac_per(a0)
+ move.w d0,aud1+ac_per(a0)
+ move.w d0,aud2+ac_per(a0)
+
+ JSRLIB Cause
+
+ move.w #INTF_AUD1,custom+intreq ; AudioInt-Request löschen
+ rts
+
+Soft1_000:
+ movem.l d2-d7/a2-a6,-(sp)
+
+* Das rechenaufwendige Mixen passiert im Softint
+
+ lea custom,a0
+
+; move.w #$0f0,color(a0)
+
+ lea T10Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T11Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T12Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T13Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T14Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T15Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T16Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T17Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T18Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T19Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T1AData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T1BData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T1CData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T1DData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T1EData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+ lea T1FData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_000
+
+; move.w #$00f,color(a0)
+
+ lea C1Buf,a2 ; Mix-Buffer konvertieren & löschen
+ adda.w C12BufOff(pc),a2
+ move.l a2,aud1+ac_ptr(a0)
+ lea C2Buf,a3
+ adda.w C12BufOff(pc),a3
+ move.l a3,aud2+ac_ptr(a0)
+ lea MBuffer,a4
+ lea BTable,a5
+ move.w #MBuf_SIZE/2-1,d0
+ moveq #0,d1
+.Convert
+ movem.w (a4),d2-d3
+ move.l d1,(a4)+
+ move.b 0(a5,d2.w),(a2)+ ; hier haben wir das hi byte
+ move.b 0(a5,d3.w),(a2)+
+ move.b 1(a5,d2.w),(a3)+ ; hier haben wir das lo byte
+ move.b 1(a5,d3.w),(a3)+
+ dbra d0,.Convert
+
+ eor.w #MBuf_SIZE,C12BufOff ; Buffer flippen
+
+; move.w #$000,color(a0)
+
+ movem.l (sp)+,d2-d7/a2-a6
+ rts
+
+
+*
+* Ein Sample zum Play-Buffer dazumixen
+*
+* D2 = Gesamt-Volume des Channels
+* A2 = ^TrackData des zu mixenden Tracks
+*
+
+MixChannel_000:
+ move.l td_SPtr(a2),a3 ; Pointer auf das Sampleende
+ move.l td_SLen(a2),d3 ; negative Restbytes des Samples
+ bmi.s .MxCalc ; noch nicht am Sampleende angekommen!
+ move.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl .MxEnd ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+.MxCalc mulu.w td_Vol(a2),d2 ; Volume des Samples
+ lsr.w #6,d2
+ add.w d2,d2
+ lsl.w #8,d2
+ lea VTable,a4 ; ^Volume-Table
+ adda.l d2,a4
+ move.l td_Fre(a2),d5 ; individuelle Sample-Frequency
+ divu MixFreq(pc),d5 ; globale Mix-Frequency
+ moveq #0,d4
+ move.w d5,d4 ; Period-Vorkommastellen
+ clr.w d5
+ divu MixFreq(pc),d5 ; Period-Nachkommastellen
+ moveq #0,d6
+ move.w td_FreC(a2),d6 ; Period-Nachkomma-Zähler
+ move.w d4,d7
+ mulu #MBuf_SIZE,d7
+ move.w d5,d0
+ mulu #MBuf_SIZE,d0
+ add.l d6,d0
+ clr.w d0
+ swap d0
+ add.l d0,d7
+ add.l d3,d7 ; Restbytes nach dem Mixen
+
+*
+* Hauptschleife - Registerbelegung:
+* D0.W = Temp.Register für die Samplebytes
+* D1.W = Temp.Register für die Samplewords
+* D2.W = Zähler für die MixLoop
+* D3.L = Restbytes vom Sample
+* D4.L = Vorkomma Faktor
+* D5.W = Nachkomma Faktor
+* D6.W = Nachkomma Zähler
+* D7 = Restbytes nach dem Mixen
+* A0 =
+* A1 =
+* A2 = Zeiger auf TrackData des Tracks
+* A3 = Zeiger auf das Ende des Samples
+* A4 = Zeiger auf Volume-Tabelle des Samples
+* A5 = Zeiger auf den Mix-Buffer
+* A6 =
+*
+
+.MxSamp lea MBuffer,a5 ; Pointer auf den Mix-Buffer
+ move.w #MBuf_SIZE/2-1,d2 ; Zähler, bis MixBuffer voll
+
+ tst.l d7 ; wird das Sampleende beim Mixen erreicht ?
+ bmi .MxTst2 ; Nein, dann schnelle Mix-Routine benutzen!
+
+.MxTst1 tst.w d4 ; Period-Vorkommastellen Null?
+ bne.s .MxLop3 ; Nein, dann normale Mix-Routine benutzen!
+ moveq #1,d4
+
+ adda.l d3,a3
+ moveq #0,d0
+ move.b (a3)+,d0 ; erstes Samplebyte holen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+.MxLop0 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop1
+ add.l d4,d3
+ bmi.s .MxGet1 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+ adda.l d3,a3
+.MxGet1 moveq #0,d0
+ move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+.MxLop1 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop2
+ add.l d4,d3
+ bmi.s .MxGet2 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+ adda.l d3,a3
+.MxGet2 moveq #0,d0
+ move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+.MxLop2 dbra d2,.MxLop0 ; bis MixBuffer voll
+ add.l d3,d4
+ suba.l d4,a3
+ bra.s .MxPerC
+
+.MxLop3 moveq #0,d0
+ move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ bmi.s .MxLop4 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+.MxLop4 moveq #0,d0
+ move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ bmi.s .MxLop5 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+.MxLop5 dbra d2,.MxLop3 ; bis MixBuffer voll
+
+.MxPerC move.w d6,td_FreC(a2) ; Neuer Stand des Runtersample-Zählers
+.MxSPtr move.l a3,td_SPtr(a2) ; Endadresse des Samples sichern
+.MxSLen move.l d3,td_SLen(a2) ; Neue Position im Sample erreicht
+
+.MxEnd rts
+
+.MxTst2 tst.w d4 ; Period-Vorkommastellen Null?
+ bne.s .MxLop9 ; Nein, dann normale Mix-Routine benutzen!
+ moveq #1,d4
+
+ adda.l d3,a3
+ move.l d7,d3
+ moveq #0,d0
+ move.b (a3)+,d0 ; erstes Samplebyte holen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+.MxLop6 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop7
+ moveq #0,d0
+ move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+.MxLop7 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop8
+ moveq #0,d0
+ move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+.MxLop8 dbra d2,.MxLop6 ; bis MixBuffer voll
+ add.l d3,d4
+ suba.l d4,a3
+ bra.s .MxPerC
+
+.MxLop9 moveq #0,d0
+ move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ moveq #0,d0
+ move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ add.w d0,d0
+ move.w 0(a4,d0.w),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ dbra d2,.MxLop9 ; bis MixBuffer voll
+ bra.s .MxPerC
+
+*-----------------------------------------------------------------------*
+;
+; Audio-Interrupts für 68020 und höher
+
+* Tracks 00-0F zusammenmischen:
+
+Int0_020:
+
+* Achtung! Soeben begann das Abspielen des beim
+* letzten Interrupt gefüllten Channel-Buffers!
+
+ move.w MixPeriod(pc),d0
+ lea Softy0IRQ_020,a1
+ cmpi.b #NT_SOFTINT,LN_TYPE(a1)
+ bne.s .Period
+ addi.w #32,d0 ; step down
+ move.w d0,MixPeriod
+ move.l ClockBase(pc),d1
+ divu d0,d1
+ move.w d1,MixFreq
+.Period
+ move.w d0,aud0+ac_per(a0)
+ move.w d0,aud3+ac_per(a0)
+ move.w d0,aud1+ac_per(a0)
+ move.w d0,aud2+ac_per(a0)
+
+ JSRLIB Cause
+
+ move.w #INTF_AUD0,custom+intreq ; AudioInt-Request löschen
+ rts
+
+Soft0_020:
+ movem.l d2-d7/a2-a6,-(sp)
+
+* Das rechenaufwendige Mixen passiert im Softint
+
+ lea custom,a0
+
+; move.w #$f00,color(a0)
+
+ lea T00Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T01Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T02Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T03Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T04Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T05Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T06Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T07Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T08Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T09Data(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T0AData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T0BData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T0CData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T0DData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T0EData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+ lea T0FData(pc),a2
+ move.w Volume07(pc),d2
+ bsr MixChannel_020
+
+; move.w #$00f,color(a0)
+
+ lea C0Buf,a2 ; Mix-Buffer konvertieren & löschen
+ adda.w C03BufOff(pc),a2
+ move.l a2,aud0+ac_ptr(a0)
+ lea C3Buf,a3
+ adda.w C03BufOff(pc),a3
+ move.l a3,aud3+ac_ptr(a0)
+ lea MBuffer,a4
+ lea BTable,a5
+ move.w #MBuf_SIZE/2-1,d0
+ moveq #0,d1
+.Convert
+ movem.w (a4),d2-d3
+ move.l d1,(a4)+
+ move.b 0(a5,d2.w),(a2)+ ; hier haben wir das hi byte
+ move.b 0(a5,d3.w),(a2)+
+ move.b 1(a5,d2.w),(a3)+ ; hier haben wir das lo byte
+ move.b 1(a5,d3.w),(a3)+
+ dbra d0,.Convert
+
+ eor.w #MBuf_SIZE,C03BufOff ; Buffer flippen
+
+; move.w #$000,color(a0)
+
+ movem.l (sp)+,d2-d7/a2-a6
+ rts
+
+
+* Tracks 10-1F zusammenmischen:
+
+Int1_020:
+
+* Achtung! Soeben begann das Abspielen des beim
+* letzten Interrupt gefüllten Channel-Buffers!
+
+ move.w MixPeriod(pc),d0
+ lea Softy1IRQ_020,a1
+ cmpi.b #NT_SOFTINT,LN_TYPE(a1)
+ bne.s .Period
+ addi.w #32,d0 ; step down
+ move.w d0,MixPeriod
+ move.l ClockBase(pc),d1
+ divu d0,d1
+ move.w d1,MixFreq
+.Period
+ move.w d0,aud0+ac_per(a0)
+ move.w d0,aud3+ac_per(a0)
+ move.w d0,aud1+ac_per(a0)
+ move.w d0,aud2+ac_per(a0)
+
+ JSRLIB Cause
+
+ move.w #INTF_AUD1,custom+intreq ; AudioInt-Request löschen
+ rts
+
+Soft1_020:
+ movem.l d2-d7/a2-a6,-(sp)
+
+* Das rechenaufwendige Mixen passiert im Softint
+
+ lea custom,a0
+
+; move.w #$0f0,color(a0)
+
+ lea T10Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T11Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T12Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T13Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T14Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T15Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T16Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T17Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T18Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T19Data(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T1AData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T1BData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T1CData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T1DData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T1EData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+ lea T1FData(pc),a2
+ move.w Volume8F(pc),d2
+ bsr MixChannel_020
+
+; move.w #$00f,color(a0)
+
+ lea C1Buf,a2 ; Mix-Buffer konvertieren & löschen
+ adda.w C12BufOff(pc),a2
+ move.l a2,aud1+ac_ptr(a0)
+ lea C2Buf,a3
+ adda.w C12BufOff(pc),a3
+ move.l a3,aud2+ac_ptr(a0)
+ lea MBuffer,a4
+ lea BTable,a5
+ move.w #MBuf_SIZE/2-1,d0
+ moveq #0,d1
+.Convert
+ movem.w (a4),d2-d3
+ move.l d1,(a4)+
+ move.b 0(a5,d2.w),(a2)+ ; hier haben wir das hi byte
+ move.b 0(a5,d3.w),(a2)+
+ move.b 1(a5,d2.w),(a3)+ ; hier haben wir das lo byte
+ move.b 1(a5,d3.w),(a3)+
+ dbra d0,.Convert
+
+ eor.w #MBuf_SIZE,C12BufOff ; Buffer flippen
+
+; move.w #$000,color(a0)
+
+ movem.l (sp)+,d2-d7/a2-a6
+ rts
+
+
+*
+* Ein Sample zum Play-Buffer dazumixen
+*
+* D2 = Gesamt-Volume des Channels
+* A2 = ^TrackData des zu mixenden Tracks
+*
+
+MixChannel_020:
+ move.l td_SPtr(a2),a3 ; Pointer auf das Sampleende
+ move.l td_SLen(a2),d3 ; negative Restbytes des Samples
+ bmi.s .MxCalc ; noch nicht am Sampleende angekommen!
+ move.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl .MxEnd ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+.MxCalc mulu.w td_Vol(a2),d2 ; Volume des Samples
+ lsr.w #6,d2
+ add.w d2,d2
+ lsl.w #8,d2
+ lea VTable,a4 ; ^Volume-Table
+ adda.l d2,a4
+ move.l td_Fre(a2),d5 ; individuelle Sample-Frequency
+ divu MixFreq(pc),d5 ; globale Mix-Frequency
+ moveq #0,d4
+ move.w d5,d4 ; Period-Vorkommastellen
+ clr.w d5
+ divu MixFreq(pc),d5 ; Period-Nachkommastellen
+ moveq #0,d6
+ move.w td_FreC(a2),d6 ; Period-Nachkomma-Zähler
+ move.w d4,d7
+ mulu #MBuf_SIZE,d7
+ move.w d5,d0
+ mulu #MBuf_SIZE,d0
+ add.l d6,d0
+ clr.w d0
+ swap d0
+ add.l d0,d7
+ add.l d3,d7 ; Restbytes nach dem Mixen
+
+*
+* Hauptschleife - Registerbelegung:
+* D0.W = Temp.Register für die Samplebytes
+* D1.W = Temp.Register für die Samplewords
+* D2.W = Zähler für die MixLoop
+* D3.L = Restbytes vom Sample
+* D4.L = Vorkomma Faktor
+* D5.W = Nachkomma Faktor
+* D6.W = Nachkomma Zähler
+* D7 = Restbytes nach dem Mixen
+* A0 =
+* A1 =
+* A2 = Zeiger auf TrackData des Tracks
+* A3 = Zeiger auf das Ende des Samples
+* A4 = Zeiger auf Volume-Tabelle des Samples
+* A5 = Zeiger auf den Mix-Buffer
+* A6 =
+*
+
+.MxSamp lea MBuffer,a5 ; Pointer auf den Mix-Buffer
+ move.w #MBuf_SIZE/2-1,d2 ; Zähler, bis MixBuffer voll
+
+ tst.l d7 ; wird das Sampleende beim Mixen erreicht ?
+ bmi .MxTst2 ; Nein, dann schnelle Mix-Routine benutzen!
+
+.MxTst1 moveq #0,d0
+ tst.w d4 ; Period-Vorkommastellen Null?
+ bne.s .MxLop3 ; Nein, dann normale Mix-Routine benutzen!
+ moveq #1,d4
+
+ adda.l d3,a3
+ move.b (a3)+,d0 ; erstes Samplebyte holen
+ move.w 0(a4,d0.w*2),d1
+.MxLop0 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop1
+ add.l d4,d3
+ bmi.s .MxGet1 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+ adda.l d3,a3
+.MxGet1 move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+.MxLop1 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop2
+ add.l d4,d3
+ bmi.s .MxGet2 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+ adda.l d3,a3
+.MxGet2 move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+.MxLop2 dbra d2,.MxLop0 ; bis MixBuffer voll
+ add.l d3,d4
+ suba.l d4,a3
+ bra.s .MxPerC
+
+.MxLop3 move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ bmi.s .MxLop4 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+.MxLop4 move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ bmi.s .MxLop5 ; noch nicht am Sampleende angekommen!
+ add.l td_LLen(a2),d3 ; ist ein Loop-Sample vorhanden ?
+ bpl.s .MxPerC ; Kein Loopsample da, also raus!
+ move.l td_LPtr(a2),a3 ; Pointer auf das Loopende
+.MxLop5 dbra d2,.MxLop3 ; bis MixBuffer voll
+
+.MxPerC move.w d6,td_FreC(a2) ; Neuer Stand des Runtersample-Zählers
+.MxSPtr move.l a3,td_SPtr(a2) ; Endadresse des Samples sichern
+.MxSLen move.l d3,td_SLen(a2) ; Neue Position im Sample erreicht
+
+.MxEnd rts
+
+.MxTst2 moveq #0,d0
+ tst.w d4 ; Period-Vorkommastellen Null?
+ bne.s .MxLop9 ; Nein, dann normale Mix-Routine benutzen!
+ moveq #1,d4
+
+ adda.l d3,a3
+ move.l d7,d3
+ move.b (a3)+,d0 ; erstes Samplebyte holen
+ move.w 0(a4,d0.w*2),d1
+.MxLop6 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop7
+ move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+.MxLop7 add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ bcc.s .MxLop8
+ move.b (a3)+,d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+.MxLop8 dbra d2,.MxLop6 ; bis MixBuffer voll
+ add.l d3,d4
+ suba.l d4,a3
+ bra.s .MxPerC
+
+.MxLop9 move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ move.b 0(a3,d3.l),d0 ; ein Sample zum Buffer dazumixen
+ move.w 0(a4,d0.w*2),d1
+ add.w d1,(a5)+
+ add.w d5,d6 ; Sample fortschalten
+ addx.l d4,d3
+ dbra d2,.MxLop9 ; bis MixBuffer voll
+ bra.s .MxPerC
+
+*-----------------------------------------------------------------------*
+;
+; Daten für die Audio-Interrupt Routine
+
+ CNOP 0,4
+
+ClockBase: dc.l 0 ;Clock-Konstante
+MixPeriod: dc.w 256 ;Gesamt-Period für alle Kanäle
+MixFreq: dc.w 8287 ;Gesamt-Frequenz für alle Kanäle
+
+* Variablen der Audio-Interrupt-Routine:
+
+Volume07: dc.w 64 ;Channel-Volumes für links und rechts
+Volume8F: dc.w 64
+
+C03BufOff: dc.w 0 ;Buffer-Flip-Offset (0 oder MBuf_SIZE)
+C12BufOff: dc.w 0
+
+
+* 32 Track-Data-Strukturen:
+
+ rsreset
+td_SPtr rs.l 1 ; Zeiger auf das Sampleende
+td_SLen rs.l 1 ; negative Sample-Restbytes
+td_LPtr rs.l 1 ; Endadr. des Loopteils
+td_LLen rs.l 1 ; negative Länge des Loopteils
+td_Vol rs.w 1 ; Volume-Wert 0...64
+td_Fre rs.l 1 ; Frequenz-Wert
+td_FreC rs.w 1 ; Frequenz-Nachkomma-Zähler
+td_SIZE rs.l 0
+
+
+T00Data: ds.b td_SIZE
+T01Data: ds.b td_SIZE
+T02Data: ds.b td_SIZE
+T03Data: ds.b td_SIZE
+T04Data: ds.b td_SIZE
+T05Data: ds.b td_SIZE
+T06Data: ds.b td_SIZE
+T07Data: ds.b td_SIZE
+T08Data: ds.b td_SIZE
+T09Data: ds.b td_SIZE
+T0AData: ds.b td_SIZE
+T0BData: ds.b td_SIZE
+T0CData: ds.b td_SIZE
+T0DData: ds.b td_SIZE
+T0EData: ds.b td_SIZE
+T0FData: ds.b td_SIZE
+T10Data: ds.b td_SIZE
+T11Data: ds.b td_SIZE
+T12Data: ds.b td_SIZE
+T13Data: ds.b td_SIZE
+T14Data: ds.b td_SIZE
+T15Data: ds.b td_SIZE
+T16Data: ds.b td_SIZE
+T17Data: ds.b td_SIZE
+T18Data: ds.b td_SIZE
+T19Data: ds.b td_SIZE
+T1AData: ds.b td_SIZE
+T1BData: ds.b td_SIZE
+T1CData: ds.b td_SIZE
+T1DData: ds.b td_SIZE
+T1EData: ds.b td_SIZE
+T1FData: ds.b td_SIZE
+
+
+* Stummes Instrument:
+
+QuietInstr:
+ dc.l QuietInstr ; Endadresse
+ dc.l 0 ; Restlänge
+ dc.l QuietInstr ; Ende LoopSample
+ dc.l 0 ; Länge LoopSample
+
+
+
+*-----------------------------------------------------------------------*
+
+;
+;
+ SECTION GenieTabBuff,BSS
+;
+;
+
+* Umrechnungs-Tabelle für versch. Lautstärken:
+* (wird durch NoteInit initialisiert)
+
+VTable: ds.w 256*65
+
+
+*-----------------------------------------------------------------------*
+
+;
+;
+ SECTION GenieBitBuff,BSS
+;
+;
+
+* Umrechnungs-Tabelle für 14Bit Ausgabe:
+* (wird beim Start initialisiert)
+
+BitTable:
+ ds.b 256*128
+BTable:
+ ds.b 256*128
+
+
+*-----------------------------------------------------------------------*
+
+;
+;
+ SECTION GenieMixBuff,BSS
+;
+;
+
+* Mix-Buffer:
+
+MBuf_SIZE equ 200
+
+MBuffer:
+ ds.w MBuf_SIZE
+MBufferEnd:
+
+
+
+*-----------------------------------------------------------------------*
+;
+; NoteChannels - From TakeTracker player
+
+;
+;
+ SECTION NoteChan,BSS
+;
+;
+
+NoteChannels ds.b NoteChannel_SIZE*32
+
+
+
+*-----------------------------------------------------------------------*
+
+;
+;
+ SECTION GenieSampBuff,BSS_C
+;
+;
+
+* Channel-Buffer:
+
+CBuffers:
+C0Buf: ds.b MBuf_SIZE
+ ds.b MBuf_SIZE ;Für Double-Buffering!
+C1Buf: ds.b MBuf_SIZE
+ ds.b MBuf_SIZE ;Für Double-Buffering!
+C2Buf: ds.b MBuf_SIZE
+ ds.b MBuf_SIZE ;Für Double-Buffering!
+C3Buf: ds.b MBuf_SIZE
+ ds.b MBuf_SIZE ;Für Double-Buffering!
+CBuffersEnd:
+
+EmptyCBuf:
+ ds.b MBuf_SIZE
+
+
+*-----------------------------------------------------------------------*
+
+ section __MERGED,data
+
+
+**********************************************************************
+* Player Data
+**********************************************************************
+
+MyTask dc.l 0
+ParentTask dc.l 0
+
+; Library bases
+
+_SysBase dc.l 0
+_GFXBase dc.l 0
+
+retval dc.l 0
+
+_OldAudio0: dc.l 0 ; Old audio interrupt vectors
+_OldAudio1: dc.l 0
+
+NoteInfo: dc.l 0
+
+PlayFreq dc.l 14433
+ScaleFactor dc.w 0
+ChannelNum dc.w 0
+BoostFlag dc.l 1 ; Automatic Volume Boost Calculation
+BoostFactor dc.l 0
+
+
+
+**********************************************************************
+* From TakeTracker player
+**********************************************************************
+
+delibase dc.l 0
+
+songinfo dc.l 0
+mt_size dc.l 0
+
+mt_NumCh dc.w 0
+mt_rsize dc.w 0
+mt_psize dc.w 0
+
+NotePlay dc.l NoteStructure
+
+NoteStructure dc.l NoteChannels ; NoteChannel Array
+ dc.l NSTF_Period!NSTF_Signed!NSTF_8Bit ; 8-Bit signed sample data
+ dc.l 28867 ; max. frequency
+ dc.w 64 ; max. volume
+ dcb.b 18,0
+
+mt_ChanTemp ds.b pn_SIZEOF * MAXCHANNELS
+
+mt_speed dc.w 0
+mt_counter dc.w 0
+mt_SongPos dc.w 0
+mt_StartPos dc.w 0
+mt_MaxPos dc.w 0
+mt_PBreakPos dc.w 0
+mt_PatternPos dc.w 0
+mt_PosJumpFlag dc.b 0
+mt_PBreakFlag dc.b 0
+mt_LowMask dc.b 0
+mt_PattDelTime dc.b 0
+mt_PattDelTime2 dc.b 0
+mt_Reset dc.b 0
+
+
+**********************************************************************
+* AudioInterrupts
+**********************************************************************
+
+Audio0IRQ_000
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Int0_000 ; Code
+
+Audio1IRQ_000
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Int1_000 ; Code
+
+Audio0IRQ_020
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Int0_020 ; Code
+
+Audio1IRQ_020
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Int1_020 ; Code
+
+
+**********************************************************************
+* SoftInterrupts
+**********************************************************************
+
+Softy0IRQ_000
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Soft0_000 ; Code
+
+Softy1IRQ_000
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Soft1_000 ; Code
+
+
+Softy0IRQ_020
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Soft0_020 ; Code
+
+Softy1IRQ_020
+ dc.l 0 ; Succ
+ dc.l 0 ; Pred
+ dc.b NT_INTERRUPT ; Type
+ dc.b 0 ; Pri
+ dc.l audiointname ; Name
+ dc.l 0 ; Data
+ dc.l Soft1_020 ; Code
+
+
+**********************************************************************
+* AudioRequest
+**********************************************************************
+
+AllocIORequest:
+ dc.l 0 ; LN_SUCC
+ dc.l 0 ; LN_PRED
+ dc.b 0 ; LN_TYPE
+ dc.b ADALLOC_MAXPREC ; LN_PRI
+ dc.l 0 ; LN_NAME
+
+ dc.l 0 ; message reply port
+ dc.w 0 ; message len in bytes
+
+ dc.l 0 ; device node pointer
+ dc.l 0 ; unit (driver private)
+ dc.w 0 ; device command
+ dc.b 0 ; special flags
+ dc.b 0 ; error or warning code
+ dc.w 0 ; ioa_AllocKey
+ dc.l ChannelMap ; ioa_Data
+ dc.l 1 ; ioa_Length
+ dc.w 0 ; ioa_Period
+ dc.w 0 ; ioa_Volume
+ dc.w 0 ; ioa_Cycles
+ dc.l 0 ; LN_SUCC
+ dc.l 0 ; LN_PRED
+ dc.b 0 ; LN_TYPE
+ dc.b 0 ; LN_PRI
+ dc.l 0 ; LN_NAME
+ dc.l 0 ; message reply port
+ dc.w 0 ; message len in bytes
+
+
+ChannelMap dc.b $0f ; Alle Kanäle belegen
+
+audiointname dc.b 'XModule Player',0
+ even
+
+ END
--- /dev/null
+/* Player.h
+**
+** Copyright (C) 1993,94,95 Bernardo Innocenti
+**
+** Use 4 chars wide TABs to read this source
+**
+** Definitions for the player interface.
+*/
+
+struct PlayerCmd
+{
+ struct Message pcmd_Message;
+ ULONG pcmd_ID;
+ APTR pcmd_Data;
+ LONG pcmd_Err;
+};
+
+
+enum
+{
+ PCMD_SETUP = -1,
+ PCMD_INIT,
+ PCMD_PLAY,
+};
--- /dev/null
+ IFND XMREPLAY_I
+XMREPLAY_I SET 1
+**
+** XModule replay routine 1.0
+**
+** Copyright (C) 1995 Bernardo Innocenti
+**
+** Assembler structure definitions for player
+**
+
+ IFND EXEC_TYPES_I
+ include exec/types.i
+ ENDC
+
+
+ STRUCTURE PlayerCmd,MN_SIZE
+ ULONG pcmd_ID
+ APTR pcmd_Data
+ LONG pcmd_Err
+ LABEL PlayerCmd_size
+
+
+ ENUM -1
+ EITEM PCMD_SETUP
+ EITEM PCMD_PLAY
+ EITEM PCMD_INIT
+ EITEM PCMD_COUNT
+
+ ENUM 0
+ EITEM PERR_OK
+ EITEM PERR_UNKNOWN_COMMAND
+ EITEM PERR_NO_AUDIO
+
+
+
+ STRUCTURE NoteStruct,0
+ APTR nst_Channels ; pointer to a list of notechannels
+ ULONG nst_Flags ; misc flags (see below)
+ ULONG nst_MaxFrequency ; max. frequency of this player (28,867 Hz in DMA mode)
+ UWORD nst_MaxVolume ; max. volume of this player (in most cases 64)
+ STRUCT nst_Reserved,18 ; reserved for future use (must be 0 for now)
+ LABEL NoteStruct_SIZE
+
+
+ BITDEF NST,Dummy,0 ; only a dummy-NoteStruct (no NotePlayer needed)
+ BITDEF NST,Period,1 ; Amiga period supplied instead of frequency
+ BITDEF NST,ExtPeriod,2 ; Extended period (period*4) supplied instead of frequency
+ BITDEF NST,NTSCTiming,3 ; Period/ExtPeriod supplied in NTSC instead of PAL
+ BITDEF NST,EvenLength,4 ; Samplelength supplied as WORD instead of LONG
+ BITDEF NST,AllRepeats,5 ; play Repeats even if no One-Shot part was played yet
+
+ BITDEF NST,Reverse,8 ; little endian byte ordering
+ BITDEF NST,Signed,9 ; sample data is signed linear (2's complement)
+ BITDEF NST,Unsigned,10 ; -"- unsigned linear
+ BITDEF NST,Ulaw,11 ; -"- U-law (logarithmic)
+ BITDEF NST,Alaw,12 ; -"- A-law (logarithmic)
+ BITDEF NST,Float,13 ; -"- IEEE floats
+
+ BITDEF NST,7Bit,16 ; sample data is in 7-bit format
+ BITDEF NST,8Bit,17 ; -"- bytes
+ BITDEF NST,16Bit,18 ; -"- 16-bit words
+ BITDEF NST,24Bit,19 ; -"- 24-bit data
+ BITDEF NST,32Bit,20 ; -"- longwords
+ BITDEF NST,64Bit,21 ; -"- quadwords
+
+NSTD_TypeMask EQU NSTF_Reverse!NSTF_Signed!NSTF_Unsigned!NSTF_Ulaw!NSTF_Alaw!NSTF_Float
+NSTD_SizeMask EQU NSTF_7Bit!NSTF_8Bit!NSTF_16Bit!NSTF_24Bit!NSTF_32Bit!NSTF_64Bit
+
+
+ STRUCTURE NoteChannel,0
+ APTR nch_NextChannel ; next channel in the list (NULL if last)
+ ULONG nch_NotePlayer ; for use by the noteplayer (the deliplayer must ignore this)
+ WORD nch_Reserved0 ; reserved for future use (must be 0 for now)
+ UBYTE nch_Private ; just what it says
+ UBYTE nch_Changed ; what has changed since last call
+ WORD nch_StereoPos ; set this field when the InitNote function is called
+ WORD nch_Stereo ; describes "where" this channel is supposed to play
+ APTR nch_SampleStart ; ^sampledata
+ ULONG nch_SampleLength ; size of sample
+ APTR nch_RepeatStart ; ^repeat part of sample
+ ULONG nch_RepeatLength ; size of repeat part
+ ULONG nch_Frequency ; frequency (or period) of sample
+ UWORD nch_Volume ; volume of sample
+ STRUCT nch_Reserved1,26 ; reserved for future use (must be 0 for now)
+ LABEL NoteChannel_SIZE
+
+
+ BITDEF NCH,Stereo,0 ; pan position of channel has changed (???)
+ BITDEF NCH,Sample,1 ; one-shot part of sample has changed
+ BITDEF NCH,Repeat,2 ; repeat part of sample has changed
+ BITDEF NCH,Frequency,3 ; frequency has changed
+ BITDEF NCH,Volume,4 ; volume (or pan position ???) has changed
+ BITDEF NCH,Trigger,5 ; trigger sample
+
+ BITDEF NCH,Loop,16 ; sample has looped at least once (set by DeliTracker)
+
+NCHD_Ignore EQU -32768 ; ignore this notechannel
+NCHD_Balanced EQU 0 ; play balanced on both sides
+NCHD_FarLeft EQU -32767 ; play only on left speaker
+NCHD_FarRight EQU 32767 ; play only on right speaker
+
+
+ ENDC ; !XMREPLAY_I
--- /dev/null
+/*
+** Prefs.c
+**
+** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+**
+** Save and load preferences.
+*/
+
+
+#include <exec/memory.h>
+#include <prefs/prefhdr.h>
+#include <libraries/iffparse.h>
+#include <libraries/asl.h>
+#include <libraries/reqtools.h>
+#include <libraries/xmodule.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/iffparse.h>
+#include <proto/intuition.h>
+#include <proto/asl.h>
+#include <proto/reqtools.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+#define PRHD_VERS 6 /* Current version of XModule's Preferences file */
+
+#define ID_XMPR MAKE_ID('X','M','P','R') /* XModule PRefs */
+#define ID_XMFN MAKE_ID('X','M','F','N') /* XModule FoNts */
+#define ID_XMSC MAKE_ID('X','M','S','C') /* XModule SCreen */
+#define ID_XMWN MAKE_ID('X','M','W','N') /* XModule WiNdow */
+#define ID_XMFR MAKE_ID('X','M','F','R') /* XModule File Requester */
+
+/*
+ * XModule Preferences format:
+ *
+ * FORM PREF Standard preferences file format
+ * PRHD Standard preferences chunk
+ * XMPR XModulePRefs
+ * XMFN XModuleFoNts
+ * XMSC XModuleSCreen
+ * XMWN XModuleWiNdow (appears several times, once per window)
+ * XMFR XModuleFileRequester (appears several times, once per requester)
+ * EOF
+ */
+
+struct XMPrefs
+{
+ struct GuiSwitches GuiSwitches;
+ struct SaveSwitches SaveSwitches;
+ struct ClearSwitches ClearSwitches;
+ struct OptSwitches OptSwitches;
+ struct PattSwitches PattSwitches;
+ UBYTE SaverName[32];
+};
+
+
+struct XMWindow
+{
+ LONG WindowID;
+ BOOL WindowOpen,
+ WindowZoomed;
+ struct IBox WindowSize,
+ WindowZoom;
+};
+
+struct XMFonts
+{
+ /* The TextAttr->ta_Name field does not point
+ * to the font name!
+ */
+ struct TextAttr ScreenAttr,
+ WindowAttr,
+ ListAttr,
+ EditorAttr;
+ UBYTE ScreenFontName[32],
+ WindowFontName[32],
+ ListFontName[32],
+ EditorFontName[32];
+};
+
+
+
+struct XMFRPrefs
+{
+ struct IBox FReqSize;
+ UBYTE Dir[PATHNAME_MAX],
+ Pattern[PATHNAME_MAX];
+};
+
+
+
+/* Local function prototypes */
+
+static LONG SaveXMWN (struct IFFHandle *iff, struct WinUserData *wud);
+static LONG LoadXMWN (struct IFFHandle *iff);
+static LONG SaveXMFR (struct IFFHandle *iff, struct XMFileReq *xmfr);
+static LONG LoadXMFR (struct IFFHandle *iff, struct XMFileReq *xmfr);
+
+
+
+GLOBALCALL LONG LoadPrefs (CONST_STRPTR filename)
+{
+ struct IFFHandle *iff;
+ struct ContextNode *cn;
+ LONG err;
+ UWORD frcount = 0;
+ BOOL update_screen = FALSE;
+
+ static LONG stopchunks[] =
+ {
+ ID_PREF, ID_PRHD,
+ ID_PREF, ID_XMPR,
+ ID_PREF, ID_XMFN,
+ ID_PREF, ID_XMSC,
+ ID_PREF, ID_XMWN,
+ ID_PREF, ID_XMFR
+ };
+
+
+ if (!(iff = AllocIFF()))
+ return ERROR_NO_FREE_STORE;
+
+ if (!(iff->iff_Stream = (ULONG) Open (filename, MODE_OLDFILE)))
+ {
+ err = IoErr();
+ goto error1;
+ }
+
+ InitIFFasDOS (iff);
+
+ if (err = OpenIFF (iff, IFFF_READ))
+ goto error2;
+
+ if (err = StopChunks (iff, stopchunks, 6))
+ goto error3;
+
+ while (1)
+ {
+ if (err = ParseIFF (iff, IFFPARSE_SCAN))
+ {
+ if (err == IFFERR_EOC) continue;
+ else
+ {
+ if (err == IFFERR_EOF) err = 0;
+ break; /* Free resources & exit */
+ }
+ }
+
+ if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_PREF))
+ {
+ switch (cn->cn_ID)
+ {
+ case ID_PRHD:
+ {
+ struct PrefHeader prhd;
+
+ if ((err = ReadChunkBytes (iff, &prhd, sizeof (prhd))) !=
+ sizeof (prhd)) goto error3;
+
+ if (prhd.ph_Version != PRHD_VERS)
+ {
+ ShowRequestArgs (MSG_BAD_PREFS_VERSION, 0, NULL);
+ goto error3;
+ }
+
+ break;
+ }
+
+ case ID_XMPR:
+ {
+ struct XMPrefs xmpr;
+
+ if ((err = ReadChunkBytes (iff, &xmpr, sizeof (xmpr))) !=
+ sizeof (xmpr)) goto error3;
+
+ memcpy (&GuiSwitches, &xmpr.GuiSwitches, sizeof (GuiSwitches));
+ memcpy (&SaveSwitches, &xmpr.SaveSwitches, sizeof (SaveSwitches));
+ memcpy (&ClearSwitches, &xmpr.ClearSwitches, sizeof (ClearSwitches));
+ memcpy (&OptSwitches, &xmpr.OptSwitches, sizeof (OptSwitches));
+ memcpy (&PattSwitches, &xmpr.PattSwitches, sizeof (PattSwitches));
+
+ {
+ struct XMHook *saver;
+
+ /* FIXME: move this code in a private library
+ * function such as SetDefaultSaver()
+ */
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+ if (saver = (struct XMHook *)FindName (
+ (struct List *)&XModuleBase->xm_Savers, xmpr.SaverName))
+ XModuleBase->xm_DefaultSaver = saver;
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ }
+
+ UpdateGuiSwitches ();
+ UpdateInstrSwitches ();
+ UpdateSaveSwitches ();
+ UpdateClearSwitches ();
+ UpdateOptSwitches ();
+ UpdateSampleMenu ();
+ UpdatePattPrefs ();
+
+ break;
+ }
+
+ case ID_XMFN:
+ {
+ struct XMFonts xmfn;
+
+ if ((err = ReadChunkBytes (iff, &xmfn, sizeof (xmfn))) !=
+ sizeof (xmfn)) goto error3;
+
+ xmfn.ScreenAttr.ta_Name = xmfn.ScreenFontName;
+ xmfn.WindowAttr.ta_Name = xmfn.WindowFontName;
+ xmfn.ListAttr.ta_Name = xmfn.ListFontName;
+ xmfn.EditorAttr.ta_Name = xmfn.EditorFontName;
+
+ if (CmpTextAttr (&xmfn.ScreenAttr, &ScreenAttr) ||
+ CmpTextAttr (&xmfn.WindowAttr, &WindowAttr) ||
+ CmpTextAttr (&xmfn.ListAttr, &ListAttr) ||
+ CmpTextAttr (&xmfn.EditorAttr, &EditorAttr))
+ {
+ CopyTextAttrPooled (Pool, &xmfn.ScreenAttr, &ScreenAttr);
+ CopyTextAttrPooled (Pool, &xmfn.WindowAttr, &WindowAttr);
+ CopyTextAttrPooled (Pool, &xmfn.ListAttr, &ListAttr);
+ CopyTextAttrPooled (Pool, &xmfn.EditorAttr, &EditorAttr);
+ update_screen = TRUE;
+ }
+
+ break;
+ }
+
+ case ID_XMSC:
+ {
+ struct ScrInfo newscrinfo;
+
+ if ((err = ReadChunkBytes (iff, &newscrinfo, sizeof (newscrinfo))) !=
+ sizeof (newscrinfo)) goto error3;
+
+ if (memcmp (&ScrInfo, &newscrinfo, sizeof (ScrInfo)))
+ {
+ if (Scr)
+ {
+ CloseDownScreen();
+ update_screen = TRUE;
+ }
+
+ memcpy (&ScrInfo, &newscrinfo, sizeof (ScrInfo));
+ }
+
+ break;
+ }
+
+ case ID_XMWN:
+ if (err = LoadXMWN (iff))
+ goto error3;
+ break;
+
+ case ID_XMFR:
+ if (frcount < FREQ_COUNT)
+ {
+ if (err = LoadXMFR (iff, &FileReqs[frcount]))
+ goto error3;
+ frcount++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+error3:
+ CloseIFF (iff);
+error2:
+ Close (iff->iff_Stream);
+error1:
+ FreeIFF (iff);
+
+ if (Scr)
+ {
+ if (update_screen)
+ err = SetupScreen();
+ else ReopenWindows();
+ }
+
+ return err;
+}
+
+
+
+GLOBALCALL LONG SavePrefs (CONST_STRPTR filename)
+{
+ struct IFFHandle *iff;
+ LONG err;
+
+ if (!(iff = AllocIFF()))
+ return ERROR_NO_FREE_STORE;
+
+ if (!(iff->iff_Stream = (ULONG) Open (filename, MODE_NEWFILE)))
+ {
+ err = IoErr();
+ goto error1;
+ }
+
+ InitIFFasDOS (iff);
+
+ if (err = OpenIFF (iff, IFFF_WRITE))
+ goto error2;
+
+
+ /* Write PREF */
+
+ if (err = PushChunk (iff, ID_PREF, ID_FORM, IFFSIZE_UNKNOWN))
+ goto error3;
+
+ /* Store PRHD chunk */
+ {
+ struct PrefHeader prhd = {PRHD_VERS, 0, 0};
+
+ if (err = PushChunk (iff, 0, ID_PRHD, sizeof (struct PrefHeader)))
+ goto error3;
+
+ if ((err = WriteChunkBytes (iff, &prhd, sizeof (struct PrefHeader))) !=
+ sizeof (struct PrefHeader))
+ goto error3;
+
+ if (err = PopChunk (iff))
+ goto error3;
+ }
+
+ /* Store XMPR Chunk */
+ {
+ struct XMPrefs xmpr;
+
+ memcpy (&xmpr.GuiSwitches, &GuiSwitches, sizeof (GuiSwitches));
+ memcpy (&xmpr.SaveSwitches, &SaveSwitches, sizeof (SaveSwitches));
+ memcpy (&xmpr.ClearSwitches, &ClearSwitches, sizeof (ClearSwitches));
+ memcpy (&xmpr.OptSwitches, &OptSwitches, sizeof (OptSwitches));
+ memcpy (&xmpr.PattSwitches, &PattSwitches, sizeof (PattSwitches));
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+ if (XModuleBase->xm_DefaultSaver)
+ strncpy (xmpr.SaverName, XModuleBase->xm_DefaultSaver->xmh_Link.ln_Name, 32);
+ else
+ xmpr.SaverName[0] = '\0';
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+
+ if (err = PushChunk (iff, 0, ID_XMPR, sizeof (xmpr)))
+ goto error3;
+
+ if ((err = WriteChunkBytes (iff, &xmpr, sizeof (xmpr))) !=
+ sizeof (xmpr))
+ goto error3;
+
+ if (err = PopChunk (iff))
+ goto error3;
+ }
+
+
+ /* Store XMFN chunk */
+ {
+ struct XMFonts xmfn;
+
+ if (err = PushChunk (iff, 0, ID_XMFN, sizeof (xmfn)))
+ goto error3;
+
+ xmfn.ScreenAttr = ScreenAttr;
+ xmfn.WindowAttr = WindowAttr;
+ xmfn.ListAttr = ListAttr;
+ xmfn.EditorAttr = EditorAttr;
+
+ if (ScreenAttr.ta_Name)
+ strncpy (xmfn.ScreenFontName, ScreenAttr.ta_Name, 32);
+ if (WindowAttr.ta_Name)
+ strncpy (xmfn.WindowFontName, WindowAttr.ta_Name, 32);
+ if (ListAttr.ta_Name)
+ strncpy (xmfn.ListFontName, ListAttr.ta_Name, 32);
+ if (EditorAttr.ta_Name)
+ strncpy (xmfn.EditorFontName, EditorAttr.ta_Name, 32);
+
+ if ((err = WriteChunkBytes (iff, &xmfn, sizeof (xmfn))) !=
+ sizeof (xmfn))
+ goto error3;
+
+ if (err = PopChunk (iff))
+ goto error3;
+ }
+
+ /* Store XMSC Chunk */
+ {
+ if (err = PushChunk (iff, 0, ID_XMSC, sizeof (ScrInfo)))
+ goto error3;
+
+ if ((err = WriteChunkBytes (iff, &ScrInfo, sizeof (ScrInfo))) !=
+ sizeof (ScrInfo))
+ goto error3;
+
+ if (err = PopChunk (iff))
+ goto error3;
+ }
+
+
+ /* Store XMWN Chunks */
+ {
+ struct WinUserData *wud;
+
+ for (wud = (struct WinUserData *) WindowList.lh_Head;
+ wud->Link.mln_Succ;
+ wud = (struct WinUserData *)wud->Link.mln_Succ)
+ {
+ if (err = SaveXMWN (iff, wud))
+ goto error3;
+ }
+ }
+
+
+ /* Store XMFR Chunks */
+ {
+ ULONG i;
+
+ for (i = 0; i < FREQ_COUNT; i++)
+ SaveXMFR (iff, &FileReqs[i]);
+ }
+
+ err = PopChunk (iff); /* Pop PREF */
+
+error3:
+ CloseIFF (iff);
+
+error2:
+ Close (iff->iff_Stream);
+
+error1:
+ FreeIFF (iff);
+
+ return err;
+}
+
+
+
+static LONG LoadXMWN (struct IFFHandle *iff)
+{
+ LONG err;
+ struct XMWindow xmwn;
+ struct WinUserData *wud;
+ struct Window *win;
+
+ if ((err = ReadChunkBytes (iff, &xmwn, sizeof (xmwn))) != sizeof (xmwn))
+ return err;
+
+ if (xmwn.WindowID >= WID_COUNT) return RETURN_OK;
+
+ if (!(wud = WDescr[xmwn.WindowID].Wud))
+ if (!(wud = CreateWUD (xmwn.WindowID)))
+ return ERROR_NO_FREE_STORE;
+
+ win = wud->Win;
+
+ if (!win)
+ wud->WUDFlags |= xmwn.WindowOpen ? WUDF_REOPENME : 0; /* Open this window later */
+ else if (!xmwn.WindowOpen)
+ MyCloseWindow (wud); /* Close this window now */
+
+
+ memcpy (&wud->WindowSize, &xmwn.WindowSize, sizeof (struct IBox));
+ memcpy (&wud->WindowZoom, &xmwn.WindowZoom, sizeof (struct IBox));
+
+// wud->WindowSize.Left = xmwn.WindowSize.Left;
+// wud->WindowSize.Top = xmwn.WindowSize.Top;
+// wud->WindowZoom.Left = xmwn.WindowSize.Left;
+// wud->WindowZoom.Top = xmwn.WindowZoom.Top;
+
+// if (wud->Flags & WFLG_SIZEGADGET)
+// {
+// wud->WindowSize.Width = xmwn.WindowSize.Width;
+// wud->WindowSize.Height = xmwn.WindowSize.Height;
+// wud->WindowZoom.Left = xmwn.WindowSize.Width;
+// wud->WindowZoom.Height = xmwn.WindowZoom.Height;
+// }
+
+ if (win)
+ {
+ if (xmwn.WindowZoomed)
+ {
+ if (!(win->Flags & WFLG_ZOOMED)) ZipWindow (win);
+ ChangeWindowBox (win, wud->WindowZoom.Left, wud->WindowZoom.Top,
+ win->Width, win->Height);
+ }
+ else
+ {
+ if (win->Flags & WFLG_ZOOMED) ZipWindow (win);
+ ChangeWindowBox (win, wud->WindowSize.Left, wud->WindowSize.Top,
+ win->Width, win->Height);
+ }
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static LONG SaveXMWN (struct IFFHandle *iff, struct WinUserData *wud)
+{
+ LONG err;
+ struct XMWindow xmwn;
+ struct Window *win = wud->Win;
+
+ if (err = PushChunk (iff, 0, ID_XMWN, sizeof (xmwn)))
+ return err;
+
+ xmwn.WindowID = wud->WindowID;
+
+ memcpy (&xmwn.WindowSize, &wud->WindowSize, sizeof (struct IBox));
+ memcpy (&xmwn.WindowZoom, &wud->WindowZoom, sizeof (struct IBox));
+
+ if (win)
+ {
+ if (win->Flags == WFLG_ZOOMED)
+ {
+ memcpy (&xmwn.WindowZoom, &win->LeftEdge, sizeof (struct IBox));
+ xmwn.WindowZoom.Width -= win->BorderLeft + win->BorderRight;
+ xmwn.WindowZoom.Height -= win->BorderTop + win->BorderBottom;
+ }
+ else
+ {
+ memcpy (&xmwn.WindowSize, &win->LeftEdge, sizeof (struct IBox));
+ xmwn.WindowSize.Width -= win->BorderLeft + win->BorderRight;
+ xmwn.WindowSize.Height -= win->BorderTop + win->BorderBottom;
+ }
+ }
+
+ xmwn.WindowOpen = win ? TRUE : FALSE;
+ xmwn.WindowZoomed = win ? (win->Flags & WFLG_ZOOMED) : FALSE;
+
+ if ((err = WriteChunkBytes (iff, &xmwn, sizeof (xmwn))) != sizeof (xmwn))
+ return err;
+
+ if (err = PopChunk (iff))
+ return err;
+
+ return RETURN_OK;
+}
+
+
+
+static LONG LoadXMFR (struct IFFHandle *iff, struct XMFileReq *xmfr)
+{
+ LONG err;
+ struct XMFRPrefs xmfrp;
+
+ if (!ReqToolsBase && !AslBase)
+ SetupRequesters();
+
+ if (!xmfr->FReq) return RETURN_FAIL;
+
+ if ((err = ReadChunkBytes (iff, &xmfrp, sizeof (xmfrp))) != sizeof (xmfrp))
+ return err;
+
+ if (ReqToolsBase)
+ {
+ rtChangeReqAttr (xmfr->FReq,
+ RT_TopOffset, xmfrp.FReqSize.Top,
+ RT_LeftOffset, xmfrp.FReqSize.Left,
+ RTFI_Height, xmfrp.FReqSize.Height,
+ /* Width not available in ReqTools */
+ RTFI_Dir, xmfrp.Dir,
+ RTFI_MatchPat, xmfrp.Pattern,
+ TAG_DONE);
+ }
+ else if (AslBase)
+ {
+ struct FileRequester *fr;
+
+ if (!(fr = AllocAslRequestTags (ASL_FileRequest,
+ (xmfr->Title == -1) ? TAG_IGNORE : ASLFR_TitleText, (xmfr->Title == -1) ? NULL : STR(xmfr->Title),
+ ASLFR_Flags1, xmfr->Flags | FRF_PRIVATEIDCMP,
+ ASLFR_Flags2, FRF_REJECTICONS,
+ ASLFR_InitialLeftEdge, xmfrp.FReqSize.Left,
+ ASLFR_InitialTopEdge, xmfrp.FReqSize.Top,
+ ASLFR_InitialWidth, xmfrp.FReqSize.Width,
+ ASLFR_InitialHeight, xmfrp.FReqSize.Height,
+ ASLFR_InitialDrawer, xmfrp.Dir,
+ ASLFR_InitialPattern, xmfrp.Pattern,
+ TAG_DONE)))
+ return RETURN_FAIL;
+
+ FreeAslRequest (xmfr->FReq);
+ xmfr->FReq = fr;
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static LONG SaveXMFR (struct IFFHandle *iff, struct XMFileReq *xmfr)
+{
+ LONG err;
+ struct XMFRPrefs xmfrp = { 0 };
+
+ if (err = PushChunk (iff, 0, ID_XMFR, sizeof (xmfrp)))
+ return err;
+
+ if (xmfr->FReq)
+ {
+ if (AslBase)
+ {
+ struct FileRequester *fr = (struct FileRequester *)xmfr->FReq;
+
+ xmfrp.FReqSize.Left = fr->fr_LeftEdge;
+ xmfrp.FReqSize.Top = fr->fr_TopEdge;
+ xmfrp.FReqSize.Width = fr->fr_Width;
+ xmfrp.FReqSize.Height = fr->fr_Height;
+ strncpy (xmfrp.Dir, fr->fr_Drawer, PATHNAME_MAX);
+ strncpy (xmfrp.Pattern, fr->fr_Pattern, PATHNAME_MAX);
+ }
+ else if (ReqToolsBase)
+ {
+ struct rtFileRequester *fr = (struct rtFileRequester *)xmfr->FReq;
+
+ xmfrp.FReqSize.Left = fr->LeftOffset;
+ xmfrp.FReqSize.Top = fr->TopOffset;
+ xmfrp.FReqSize.Width = 0; /* Width not available in ReqTools */
+ xmfrp.FReqSize.Height = fr->ReqHeight;
+ strncpy (xmfrp.Dir, fr->Dir, PATHNAME_MAX);
+ strncpy (xmfrp.Pattern, fr->MatchPat, PATHNAME_MAX);
+ }
+ }
+
+ if ((err = WriteChunkBytes (iff, &xmfrp, sizeof (xmfrp))) != sizeof (xmfrp))
+ return err;
+
+ if (err = PopChunk (iff)) /* Pop XMFR */
+ return err;
+
+ return RETURN_OK;
+}
--- /dev/null
+/*
+** PrefsWin.c
+**
+** Copyright (C) 1994,95,96,97 by Bernardo Innocenti
+**
+** Preferences panel handling routines.
+*/
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+#include <libraries/asl.h>
+#include <graphics/displayinfo.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+#include <proto/gadtools.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+#include "CustomClasses.h"
+
+
+
+/* Gadgets IDs */
+
+enum {
+ GD_PrefsGroup0,
+ GD_PrefsGroup1,
+ GD_PrefsGroup2,
+ GD_PubScreen,
+ GD_GetPubScreen,
+ GD_PrefsGroup3,
+ GD_DisplayMode,
+ GD_GetDisplayMode,
+ GD_PrefsGroup4,
+ GD_WindowFont,
+ GD_GetWindowFont,
+ GD_PrefsGroup5,
+ GD_ListViewFont,
+ GD_GetListViewFont,
+ GD_PrefsGroup6,
+ GD_PrefsGroup7,
+ GD_Requesters,
+ GD_Refresh,
+ GD_PrefsGroup8,
+ GD_UseDataTypes,
+ GD_AppIcon,
+ GD_PrefsGroup9,
+ GD_LogLevel,
+ GD_PrefsGroup10,
+ GD_LogToFile,
+ GD_LogFile,
+ GD_PrefsGroup11,
+ GD_PrefsGroup12,
+ GD_AutosaveTime,
+ GD_AskAutosave,
+ GD_PrefsGroup13,
+ GD_DoBackups,
+ GD_BackupVersions,
+ GD_BackupTemplate,
+ GD_PrefsGroup14,
+ GD_PrefsUse,
+ GD_PrefsCancel,
+
+ Prefs_CNT
+};
+
+
+
+/* Local functions prototypes */
+
+static void PrefsPostOpen (struct WinUserData *wud);
+static void PrefsPostClose (void);
+
+static void PubScreenClicked (struct WinUserData *wud);
+static void GetPubScreenClicked (struct WinUserData *wud);
+static void GetDisplayModeClicked (struct WinUserData *wud);
+static void GetWindowFontClicked (struct WinUserData *wud);
+static void GetListViewFontClicked (struct WinUserData *wud);
+static void RequestersClicked (struct WinUserData *wud);
+static void RefreshClicked (struct WinUserData *wud);
+static void LogLevelClicked (struct WinUserData *wud);
+static void LogFileClicked (struct WinUserData *wud);
+static void BackupTemplateClicked (struct WinUserData *wud);
+static void BackupVersionsClicked (struct WinUserData *wud);
+static void AutosaveTimeClicked (struct WinUserData *wud);
+static void PrefsUseClicked (struct WinUserData *wud);
+static void PrefsCancelClicked (struct WinUserData *wud);
+
+
+
+/* Local data */
+
+static struct ScrInfo NewScrInfo;
+static struct GuiSwitches NewGuiSwitches;
+static struct TextAttr NewWindowAttr;
+static struct TextAttr NewListAttr;
+
+
+
+static UBYTE *RequestersLabels[] = {
+ (UBYTE *)MSG_ASL_GAD,
+ (UBYTE *)MSG_REQTOOLS_GAD,
+ NULL
+};
+
+
+static UBYTE *RefreshLabels[] = {
+ (UBYTE *)MSG_SIMPLE_GAD,
+ (UBYTE *)MSG_SMART_GAD,
+ NULL
+};
+
+
+
+static ULONG PrefsArgs[] =
+{
+ VGROUP_KIND, BBFT_RIDGE,
+ HGROUP_KIND, 0,
+ STRING_KIND, (ULONG)PubScreenClicked, MSG_PUBLIC_SCREEN_GAD, 32, TAG_DONE,
+ IMAGEBUTTON_KIND, (ULONG)GetPubScreenClicked, IM_PICK, TAG_DONE,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ TEXT_KIND, MSG_DISPLAY_MODE_GAD, 0, GTTX_Border, TRUE, TAG_DONE,
+ IMAGEBUTTON_KIND, (ULONG)GetDisplayModeClicked, IM_PICK, TAG_DONE,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ TEXT_KIND, MSG_WINDOW_FONT_GAD, 0, GTTX_Border, TRUE, TAG_DONE,
+ IMAGEBUTTON_KIND, (ULONG)GetWindowFontClicked, IM_PICK, TAG_DONE,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ TEXT_KIND, MSG_LISTVIEW_FONT_GAD, 0, GTTX_Border, TRUE, TAG_DONE,
+ IMAGEBUTTON_KIND, (ULONG)GetListViewFontClicked, IM_PICK, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ HGROUP_KIND, BBFT_RIDGE,
+ VGROUP_KIND, 0,
+ CYCLE_KIND, (ULONG)RequestersClicked, MSG_REQUESTERS_GAD, (ULONG)RequestersLabels, TAG_DONE,
+ CYCLE_KIND, (ULONG)RefreshClicked, MSG_REFRESH_GAD, (ULONG)RefreshLabels, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ CHECKBOX_KIND, NULL, MSG_USE_DATATYPES_GAD, (ULONG)&NewGuiSwitches.UseDataTypes, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_APPICON_GAD, (ULONG)&NewGuiSwitches.ShowAppIcon, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ VGROUP_KIND, BBFT_RIDGE,
+ SLIDER_KIND, (ULONG)LogLevelClicked, MSG_LOG_LEVEL_GAD, 0, 8, (ULONG)"%lu", 2, GA_Immediate, TRUE, TAG_DONE,
+ HGROUP_KIND, 0,
+ CHECKBOX_KIND, NULL, MSG_LOG_TO_FILE_GAD, (ULONG)&NewGuiSwitches.LogToFile, TAG_DONE,
+ STRING_KIND, (ULONG)LogFileClicked, 0, 128, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ VGROUP_KIND, BBFT_RIDGE,
+ HGROUP_KIND, 0,
+ CHECKBOX_KIND, NULL, MSG_ASK_AUTOSAVE_GAD, (ULONG)&NewGuiSwitches.AskAutosave, TAG_DONE,
+ SLIDER_KIND, (ULONG)AutosaveTimeClicked, MSG_AUTOSAVE_TIME_GAD, 0, 99, (ULONG)"%lu", 3, TAG_DONE,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ CHECKBOX_KIND, NULL, MSG_DO_BACKUPS_GAD, (ULONG)&NewGuiSwitches.DoBackups, TAG_DONE,
+ SLIDER_KIND, (ULONG)BackupVersionsClicked, MSG_BACKUP_VERSIONS_GAD, 1, 99, (ULONG)"%lu", 3, TAG_DONE,
+ ENDGROUP_KIND,
+ STRING_KIND, (ULONG)BackupTemplateClicked, MSG_BACKUP_TEMPLATE_GAD, 64, TAG_DONE,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)PrefsUseClicked, MSG_UNDERSCORE_USE_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)PrefsCancelClicked, MSG_UNDERSCORE_CANCEL_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG PrefsWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)PrefsArgs,
+ XMWIN_GCount, Prefs_CNT,
+ XMWIN_Title, MSG_PREFS_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, TEXTIDCMP|STRINGIDCMP|CHECKBOXIDCMP|BUTTONIDCMP|CYCLEIDCMP|SLIDERIDCMP|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
+ XMWIN_PostOpenFunc, (LONG)PrefsPostOpen,
+ XMWIN_PostCloseFunc,(LONG)PrefsPostClose,
+ XMWIN_HelpNode, (LONG)"Prefs",
+ TAG_DONE
+};
+
+
+
+static void PrefsPostOpen (struct WinUserData *wud)
+{
+ memcpy (&NewScrInfo, &ScrInfo, sizeof (struct ScrInfo));
+ memcpy (&NewGuiSwitches, &GuiSwitches, sizeof (struct GuiSwitches));
+ CopyTextAttrPooled (Pool, &WindowAttr, &NewWindowAttr);
+ CopyTextAttrPooled (Pool, &ListAttr, &NewListAttr);
+
+ UpdatePrefsWindow();
+}
+
+
+
+static void PrefsPostClose (void)
+{
+ FreeVecPooled (Pool, NewWindowAttr.ta_Name); NewWindowAttr.ta_Name = NULL;
+ FreeVecPooled (Pool, NewListAttr.ta_Name); NewListAttr.ta_Name = NULL;
+}
+
+
+
+/********************/
+/* Prefs Functions */
+/********************/
+
+GLOBALCALL void UpdatePrefsWindow()
+{
+ /* These variables are declared static because gadtools text
+ * gadgets do not buffer their texts and require them to be
+ * accessible whenever a refresh is needed :-(.
+ */
+ static UBYTE windowfont[40], listfont[40];
+ static struct NameInfo nameinfo;
+
+ struct WinUserData *wud = WDescr[WID_PREFS].Wud;
+
+
+ if (wud && wud->Win)
+ {
+ if (NewScrInfo.DisplayID)
+ GetDisplayInfoData (NULL, (void *)&nameinfo, sizeof (nameinfo), DTAG_NAME, NewScrInfo.DisplayID);
+ else
+ strcpy (nameinfo.Name, STR(MSG_CLONE_DEF_SCREEN));
+
+ SPrintf (windowfont, "%s/%ld", NewWindowAttr.ta_Name, NewWindowAttr.ta_YSize);
+ SPrintf (listfont, "%s/%ld", NewListAttr.ta_Name, NewListAttr.ta_YSize);
+
+ SetGadgets (wud,
+ GD_Requesters, NewGuiSwitches.UseReqTools,
+ GD_UseDataTypes, NewGuiSwitches.UseDataTypes,
+ GD_Refresh, NewGuiSwitches.SmartRefresh,
+ GD_AppIcon, NewGuiSwitches.ShowAppIcon,
+ GD_DisplayMode, nameinfo.Name,
+ GD_PubScreen, NewScrInfo.PubScreenName,
+ GD_WindowFont, windowfont,
+ GD_ListViewFont, listfont,
+ GD_LogLevel, NewGuiSwitches.LogLevel,
+ GD_LogToFile, NewGuiSwitches.LogToFile,
+ GD_LogFile, NewGuiSwitches.LogFile,
+ GD_AskAutosave, NewGuiSwitches.AskAutosave,
+ GD_AutosaveTime, NewGuiSwitches.AutosaveTime,
+ GD_DoBackups, NewGuiSwitches.DoBackups,
+ GD_BackupTemplate, NewGuiSwitches.BackupTemplate,
+ GD_BackupVersions, NewGuiSwitches.BackupVersions,
+ -1);
+ }
+}
+
+
+
+/******************/
+/* Prefs Gadgets */
+/******************/
+
+static void PubScreenClicked (struct WinUserData *wud)
+{
+ strcpy (NewScrInfo.PubScreenName, GetString (wud->Gadgets[GD_PubScreen]));
+}
+
+
+
+static void GetPubScreenClicked (struct WinUserData *wud)
+{
+ /**/
+}
+
+
+
+static void GetDisplayModeClicked (struct WinUserData *wud)
+{
+ ScrModeRequest (&NewScrInfo);
+ UpdatePrefsWindow();
+}
+
+static void GetWindowFontClicked (struct WinUserData *wud)
+{
+ FontRequest (&NewWindowAttr, 0);
+ UpdatePrefsWindow();
+}
+
+static void GetListViewFontClicked (struct WinUserData *wud)
+{
+ FontRequest (&NewListAttr, 0);
+ UpdatePrefsWindow();
+}
+
+
+
+static void RequestersClicked (struct WinUserData *wud)
+{
+ NewGuiSwitches.UseReqTools ^= 1;
+}
+
+
+
+static void RefreshClicked (struct WinUserData *wud)
+{
+ NewGuiSwitches.SmartRefresh ^= 1;
+}
+
+
+
+static void LogLevelClicked (struct WinUserData *wud)
+{
+ NewGuiSwitches.LogLevel = IntuiMsg.Code;
+}
+
+
+
+static void LogFileClicked (struct WinUserData *wud)
+{
+ strcpy (NewGuiSwitches.LogFile, GetString (wud->Gadgets[GD_LogFile]));
+}
+
+
+
+static void AutosaveTimeClicked (struct WinUserData *wud)
+{
+ NewGuiSwitches.AutosaveTime = IntuiMsg.Code;
+}
+
+
+
+static void BackupTemplateClicked (struct WinUserData *wud)
+{
+ strcpy (NewGuiSwitches.BackupTemplate, GetString (wud->Gadgets[GD_BackupTemplate]));
+}
+
+
+
+static void BackupVersionsClicked (struct WinUserData *wud)
+{
+ NewGuiSwitches.BackupVersions = IntuiMsg.Code;
+}
+
+
+
+static void PrefsUseClicked (struct WinUserData *wud)
+{
+ BOOL change_screen = FALSE,
+ change_reqs = FALSE;
+
+ if (memcmp (&ScrInfo, &NewScrInfo, sizeof (struct ScrInfo)))
+ {
+ change_screen = TRUE;
+ memcpy (&ScrInfo, &NewScrInfo, sizeof (struct ScrInfo));
+ }
+
+ if (memcmp (&GuiSwitches, &NewGuiSwitches, sizeof (struct GuiSwitches)))
+ {
+ if (GuiSwitches.UseReqTools != NewGuiSwitches.UseReqTools)
+ change_reqs = TRUE;
+ if (GuiSwitches.SmartRefresh != NewGuiSwitches.SmartRefresh)
+ change_screen = TRUE;
+
+ memcpy (&GuiSwitches, &NewGuiSwitches, sizeof (struct GuiSwitches));
+ }
+
+ if (CmpTextAttr (&NewWindowAttr, &WindowAttr))
+ {
+ CopyTextAttrPooled (Pool, &NewWindowAttr, &WindowAttr);
+ change_screen = TRUE;
+ }
+
+ if (CmpTextAttr (&NewListAttr, &ListAttr))
+ {
+ CopyTextAttrPooled (Pool, &NewListAttr, &ListAttr);
+ change_screen = TRUE;
+ }
+
+ MyCloseWindow (wud);
+
+ if (change_reqs)
+ SetupRequesters();
+
+ if (GuiSwitches.ShowAppIcon)
+ CreateAppIcon (ToolBoxDropIcon);
+ else
+ DeleteAppIcon ();
+
+ if (change_screen)
+ {
+ CloseDownScreen();
+ if (SetupScreen())
+ {
+ /* For some reason we have lost the screen: exit immediatly! */
+ Quit = TRUE;
+ ShowRequesters = FALSE;
+ }
+ }
+}
+
+
+
+static void PrefsCancelClicked (struct WinUserData *wud)
+{
+ MyCloseWindow (wud);
+}
--- /dev/null
+/*
+** ProgressWin.c
+**
+** Copyright (C) 1993,94,95,96 Bernardo Innocenti
+**
+** Parts of this code are:
+**
+** Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
+** All Rights Reserved
+**
+** Report status information for an operation in progress.
+*/
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+#include <libraries/asl.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/graphics.h>
+#include <proto/layers.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Gadgets IDs */
+enum
+{
+ GD_ProgressGroup0,
+ GD_Action,
+ GD_Percent,
+ GD_ProgressAbort,
+
+ Progress_CNT
+};
+
+enum
+{
+ GD_LogGroup0,
+ GD_LogList,
+
+ Log_CNT
+};
+
+
+/* Local functions prototypes */
+
+static void ShowStats (struct Gadget *Gadget, LONG Value, LONG Max);
+static void ProgressAbortClicked (void);
+static void LogPostClose (void);
+static LONG ProgressPreOpen (struct WinUserData *wud);
+
+
+
+static UWORD LogLines = 0;
+static UWORD ProgressOpenCount = 0;
+struct List LogList;
+static struct IntuiText ProgressIT = { 0 };
+static BOOL ProgressAborting = FALSE;
+
+
+
+static LONG ProgressArgs[] =
+{
+ TEXT_KIND, NULL, 20, GTTX_Clipped, TRUE, TAG_DONE,
+ TEXT_KIND, NULL, 20, GTTX_Border, TRUE, TAG_DONE,
+ BUTTON_KIND, (LONG)ProgressAbortClicked, MSG_UNDERSCORE_ABORT_GAD, TAG_DONE,
+ ENDGROUP_KIND
+};
+
+
+
+static LONG LogArgs[] =
+{
+ LISTVIEW_KIND, NULL, NULL, (LONG)&LogList, GTLV_ReadOnly, TRUE, TAG_DONE,
+ ENDGROUP_KIND
+};
+
+
+
+
+LONG ProgressWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)ProgressArgs,
+ XMWIN_GCount, Progress_CNT,
+ XMWIN_Title, MSG_PROGRESS_TITLE,
+ XMWIN_IDCMPFlags, BUTTONIDCMP|TEXTIDCMP|IDCMP_REFRESHWINDOW,
+ XMWIN_PostCloseFunc, (LONG)LogPostClose,
+ TAG_DONE
+};
+
+
+
+LONG LogWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)LogArgs,
+ XMWIN_GCount, Log_CNT,
+ XMWIN_Title, MSG_LOG_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, LISTVIEWIDCMP|IDCMP_REFRESHWINDOW|IDCMP_CLOSEWINDOW,
+ XMWIN_PreOpenFunc, (LONG)ProgressPreOpen,
+ TAG_DONE
+};
+
+
+
+GLOBALCALL void OpenProgressWindow (void)
+{
+ ProgressOpenCount++;
+ if (ProgressOpenCount > 1) return;
+
+ LockWindows();
+
+ NewWindow (WID_PROGRESS);
+}
+
+
+
+GLOBALCALL void CloseProgressWindow (void)
+{
+ ProgressOpenCount--;
+ if (ProgressOpenCount) return;
+
+ UnlockWindows ();
+
+ MyCloseWindow (WDescr[WID_PROGRESS].Wud);
+}
+
+
+
+static LONG ProgressPreOpen (struct WinUserData *wud)
+{
+ ProgressIT.FrontPen = DrawInfo->dri_Pens[FILLTEXTPEN] ? DrawInfo->dri_Pens[FILLTEXTPEN] : DrawInfo->dri_Pens[FILLPEN];
+
+ ProgressIT.DrawMode = (DrawInfo->dri_Pens[FILLPEN] == DrawInfo->dri_Pens[FILLTEXTPEN] || !DrawInfo->dri_Pens[FILLTEXTPEN]) ?
+ (JAM1 | COMPLEMENT) : (JAM1);
+
+ ProgressIT.ITextFont = wud->Attr;
+
+ return RETURN_OK;
+}
+
+
+
+static void LogPostClose (void)
+{
+ /* Free ListView nodes */
+ while (!IsListEmpty(&LogList))
+ RemListViewNode (LogList.lh_Head);
+ LogLines = 0;
+}
+
+
+
+GLOBALCALL void DisplayAction (ULONG msg)
+
+/* Tell user what is happening in the Progress window. */
+{
+ DisplayActionStr (STR(msg));
+}
+
+
+
+GLOBALCALL void DisplayActionStr (CONST_STRPTR str)
+
+/* Tell user what is happening in the Progress window. */
+{
+ struct WinUserData *wud;
+
+ if ((wud = WDescr[WID_PROGRESS].Wud) && wud->Win)
+ GT_SetGadgetAttrs (wud->Gadgets[GD_Action], wud->Win, NULL,
+ GTTX_Text, str,
+ TAG_DONE);
+}
+
+
+
+/* Tell user how are things going. Also check for abort */
+GLOBALCALL LONG DisplayProgress (LONG Num, LONG Max)
+{
+ struct WinUserData *wud;
+
+ if ((wud = WDescr[WID_PROGRESS].Wud) && wud->Win)
+ {
+ /* Check for CTRL-C Break */
+ if (SetSignal (0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
+ return ERROR_BREAK;
+
+
+ /* Check abort and resizing */
+ HandleIDCMP();
+
+ if (ProgressAborting)
+ {
+ ProgressAborting = FALSE;
+ return ERROR_BREAK;
+ }
+
+ if (wud->Win)
+ {
+ struct Layer_Info *layerinfo;
+ UBYTE buf[48];
+
+ /* Attempt to lock the LayerInfo associated with the
+ * screen where the Progress window resides in.
+ *
+ * We do this to prevent blocking the operation in progress
+ * when someone else (e.g. Intuition) is keeping the lock.
+ *
+ * Note that there is no AttemptLockLayerInfo() function
+ * and LockLayerInfo() will wait if the layer is already
+ * locked, which is exactly what we are trying to avoid.
+ *
+ * Our workaround is checking the LockLayersCount before
+ * locking the LayerInfo.
+ *
+ * TODO: Experiment with AttemptLockLayerRom().
+ */
+
+ /* Perhaps using Scr->LayerInfo would be the same */
+ layerinfo = wud->Win->RPort->Layer->LayerInfo;
+
+ Forbid();
+ if (!layerinfo->LockLayersCount)
+ {
+ struct Gadget *g;
+
+ LockLayerInfo (layerinfo);
+ Permit();
+
+ g = wud->Gadgets[GD_Percent];
+
+ /* Update Stats */
+ ShowStats (g, Num, Max);
+
+ /* Display progress string */
+
+ SPrintf (buf, STR(MSG_PERCENT_DONE), Num, Max, (Num * 100) / Max);
+
+ ProgressIT.IText = buf;
+
+ PrintIText (wud->Win->RPort, &ProgressIT,
+ g->LeftEdge + 2 + (g->Width - 4 - IntuiTextLength (&ProgressIT)) / 2,
+ g->TopEdge + 1 + (g->Height - 2 - wud->Win->RPort->TxHeight) / 2);
+
+ UnlockLayerInfo (layerinfo);
+ }
+ else Permit();
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+static void ShowStats (struct Gadget *Gadget, LONG Value, LONG Max)
+
+/* Show the percentage bars. */
+{
+ struct RastPort *RPort = WDescr[WID_PROGRESS].Wud->Win->RPort;
+ LONG MaxWidth = Gadget->Width - 4,
+ Width;
+
+
+ if (Max < 1) Max = 0;
+ if (Value > Max) Value = Max;
+
+
+ if((Width = (MaxWidth * Value) / Max) > 0)
+ {
+ if(Width != MaxWidth)
+ {
+ SetAPen (RPort,0);
+ RectFill (RPort, Gadget->LeftEdge + 2 + Width - 1, Gadget->TopEdge + 1,
+ Gadget->LeftEdge + Gadget->Width - 3, Gadget->TopEdge + Gadget->Height - 2);
+ }
+
+ SetAPen (RPort, DrawInfo->dri_Pens[FILLPEN]);
+ RectFill (RPort,Gadget->LeftEdge + 2,Gadget->TopEdge + 1,
+ Gadget->LeftEdge + Width + 1, Gadget->TopEdge + Gadget->Height - 2);
+ }
+ else
+ {
+ SetAPen (RPort, 0);
+ RectFill (RPort, Gadget->LeftEdge + 2, Gadget->TopEdge + 1,
+ Gadget->LeftEdge + Gadget->Width - 3, Gadget->TopEdge + Gadget->Height - 2);
+ }
+}
+
+
+
+
+GLOBALCALL void ShowMessage (ULONG msg, ...)
+
+/* Localized interface to ShowString(). */
+{
+ ShowString (STR(msg), (LONG *)(&msg+1));
+}
+
+
+GLOBALCALL void ShowString (CONST_STRPTR s, LONG *args)
+
+/* Formats a string and shows it to the user in the Log Window.
+ * If the Log Window can't be opened, this function will fall
+ * to ShowRequest() or to Printf().
+ */
+{
+ struct WinUserData *wud;
+
+ if (!IntuitionBase)
+ {
+ if (StdOut) VPrintf ((STRPTR)s, args);
+ return;
+ }
+
+ wud = WDescr[WID_LOG].Wud;
+
+ if (!wud || !wud->Win)
+ {
+ NewWindow (WID_LOG);
+ wud = WDescr[WID_LOG].Wud;
+ }
+
+ if (wud && wud->Win)
+ {
+ GT_SetGadgetAttrs (wud->Gadgets[GD_LogList], wud->Win, NULL,
+ GTLV_Labels, ~0,
+ TAG_DONE);
+
+ if (LogLines > 30)
+ {
+ RemListViewNode (LogList.lh_Head);
+ LogLines--;
+ }
+
+ if (AddListViewNodeA (&LogList, s, args))
+ LogLines++;
+ else
+ DisplayBeep(Scr);
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_LogList], wud->Win, NULL,
+ GTLV_Labels, &LogList,
+ GTLV_Top, 30,
+ TAG_DONE);
+ }
+ else
+ ShowRequestStr (s, NULL, args);
+}
+
+
+
+static void ProgressAbortClicked (void)
+{
+ ProgressAborting = TRUE;
+}
--- /dev/null
+/*
+** Requesters.c
+**
+** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+**
+** Handle asyncronous file requester
+*/
+
+#include <exec/memory.h>
+#include <libraries/asl.h>
+#include <libraries/reqtools.h>
+#include <dos/dostags.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/graphics.h>
+#include <proto/intuition.h>
+#include <proto/asl.h>
+#include <proto/reqtools.h>
+
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Local function prototypes */
+static LONG CheckOverwrite (CONST_STRPTR path);
+
+
+
+/* This structure is used for passing parameters to
+ * the requester process and to get the result back.
+ */
+struct FileReqMsg
+{
+ struct Message ExecMsg;
+ struct XMFileReq *XMFReq; /* XMFileReq structure to use */
+
+ /* User call back function which does something with the file name.
+ * If the user picked multiple files, the user function will be called
+ * once on each selection. The user function can tell which entry is
+ * being processed examining the <num> and <count> arguments.
+ */
+ void (*Func)(STRPTR file, ULONG num, ULONG count);
+ APTR Result; /* Replied by FileRequester process */
+ UBYTE PathName[PATHNAME_MAX]; /* Replied by FileRequester process */
+};
+
+
+
+XDEF struct XMFileReq FileReqs[FREQ_COUNT] =
+{
+ { NULL, MSG_SELECT_MODULES, FRF_DOMULTISELECT | FRF_DOPATTERNS },
+ { NULL, MSG_SAVE_MODULE, FRF_DOSAVEMODE | FRF_DOPATTERNS },
+ { NULL, MSG_SELECT_INSTRUMENTS, FRF_DOMULTISELECT | FRF_DOPATTERNS },
+ { NULL, MSG_SAVE_INSTRUMENT, FRF_DOSAVEMODE | FRF_DOPATTERNS },
+ { NULL, MSG_SELECT_PATTERN, FRF_DOPATTERNS },
+ { NULL, MSG_SAVE_PATTERN, FRF_DOSAVEMODE | FRF_DOPATTERNS },
+ { NULL, 0, FRF_DOPATTERNS },
+ { NULL, 0, FRF_DOSAVEMODE | FRF_DOPATTERNS },
+};
+
+
+static APTR FontReq = NULL;
+static struct Process *FileReqTask = NULL;
+static struct MsgPort *FileReqPort = NULL;
+XDEF ULONG FileReqSig;
+
+
+
+
+/* This function is a process that puts out
+ * an asyncronous ASL FileRequester.
+ */
+HOOKCALL static void AslRequestProc (void)
+{
+ struct MsgPort *prMsgPort = &(((struct Process *)FindTask (NULL))->pr_MsgPort);
+ struct FileReqMsg *frmsg;
+ struct XMFileReq *xmfr;
+
+ /* Wait startup packet */
+ WaitPort (prMsgPort);
+ frmsg = (struct FileReqMsg *)GetMsg (prMsgPort);
+ xmfr = frmsg->XMFReq;
+ frmsg->Result = NULL;
+
+ if (AslRequestTags (xmfr->FReq,
+ ASLFR_Window, ThisTask->pr_WindowPtr,
+ TAG_DONE))
+ {
+ /* Build file path */
+ strncpy (frmsg->PathName,
+ ((struct FileRequester *)xmfr->FReq)->fr_Drawer, PATHNAME_MAX);
+ AddPart (frmsg->PathName,
+ ((struct FileRequester *)xmfr->FReq)->fr_File, PATHNAME_MAX);
+ frmsg->Result = frmsg->PathName;
+ }
+
+ /* Signal that we are done.
+ * We forbid here to avoid beeing unloaded until exit.
+ */
+ Forbid();
+ ReplyMsg ((struct Message *)frmsg);
+}
+
+
+
+/* This function is a process that puts out
+ * an asyncronous ReqTools FileRequester.
+ */
+HOOKCALL static void RtRequestProc (void)
+{
+ struct MsgPort *prMsgPort = &(((struct Process *)FindTask(NULL))->pr_MsgPort);
+ struct FileReqMsg *frmsg;
+ struct XMFileReq *xmfr;
+ ULONG flags = 0;
+ UBYTE filename[PATHNAME_MAX];
+
+ filename[0] = 0;
+
+ /* Wait startup packet */
+ WaitPort (prMsgPort);
+ frmsg = (struct FileReqMsg *)GetMsg (prMsgPort);
+ xmfr = frmsg->XMFReq;
+
+ if (xmfr->Flags & FRF_DOSAVEMODE) flags |= FREQF_SAVE;
+ if (xmfr->Flags & FRF_DOPATTERNS) flags |= FREQF_PATGAD;
+ if (xmfr->Flags & FRF_DOMULTISELECT) flags |= FREQF_MULTISELECT;
+
+ if (!(frmsg->Result = rtFileRequest (xmfr->FReq, filename, STR(xmfr->Title),
+ RT_ShareIDCMP, FALSE,
+ RT_Window, ThisTask->pr_WindowPtr,
+ RTFI_Flags, flags,
+ TAG_DONE)))
+ goto error;
+
+ /* Build file path */
+ strncpy (frmsg->PathName, ((struct rtFileRequester *)xmfr->FReq)->Dir,
+ PATHNAME_MAX);
+ AddPart (frmsg->PathName, filename, PATHNAME_MAX);
+
+ if (!(xmfr->Flags & FRF_DOMULTISELECT)) frmsg->Result = frmsg->PathName;
+
+error:
+ /* Signal that we are done.
+ * We forbid here to avoid beeing unloaded until exit.
+ */
+ Forbid();
+ ReplyMsg ((struct Message *)frmsg);
+}
+
+
+
+GLOBALCALL LONG StartFileRequest (ULONG freq, void (*func)(STRPTR file, ULONG num, ULONG count))
+
+/* Spawns a process that opens a FileRequester.
+ * Do not touch freq until the child process returns!
+ *
+ * INPUTS
+ * freq - Pointer to an ASL or ReqTools FileRequester
+ * func - Pointer to a function to call when FileRequester returns
+ *
+ * RETURN
+ *
+ * err - 0 if OK, IOErr()-style error otherwise.
+ */
+{
+ struct FileReqMsg *frmsg;
+
+ /* Do not spawn more than one file requester process at a time */
+ if (FileReqTask)
+ {
+ LastErr = ~0;
+ return RETURN_FAIL;
+ }
+
+ if (!(frmsg = AllocMem (sizeof (struct FileReqMsg), MEMF_PUBLIC)))
+ return ERROR_NO_FREE_STORE;
+
+ if (!(FileReqTask = CreateNewProcTags (
+ NP_Entry, (ReqToolsBase ? RtRequestProc : AslRequestProc),
+ NP_Name, PRGNAME " FileRequester",
+ NP_CopyVars, FALSE,
+ TAG_DONE)))
+ {
+ FreeMem (frmsg, sizeof (struct FileReqMsg));
+ return ERROR_NO_FREE_STORE;
+ }
+
+ /* Now setup & send FReqMsg */
+ frmsg->ExecMsg.mn_ReplyPort = FileReqPort;
+ frmsg->XMFReq = &FileReqs[freq];
+ frmsg->Func = func;
+ PutMsg (&(FileReqTask->pr_MsgPort), (struct Message *)frmsg);
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL void HandleFileRequest (void)
+{
+ struct FileReqMsg *frmsg;
+ struct XMFileReq *xmfr;
+
+ if (!(frmsg = (struct FileReqMsg *)GetMsg (FileReqPort)))
+ return;
+
+ xmfr = frmsg->XMFReq;
+
+ FileReqTask = NULL; /* The FileRequest Process is now gone. */
+
+ if (frmsg->Result)
+ {
+ if (xmfr->Flags & FRF_DOMULTISELECT)
+ {
+ if (AslBase)
+ {
+ struct FileRequester *fr = xmfr->FReq;
+ UBYTE Name[PATHNAME_MAX];
+ LONG i;
+
+ for (i = 0; i < fr->fr_NumArgs; i++)
+ {
+ if (NameFromLock (fr->fr_ArgList[i].wa_Lock, Name, PATHNAME_MAX))
+ if (AddPart (Name, fr->fr_ArgList[i].wa_Name, PATHNAME_MAX))
+ frmsg->Func (Name, i, fr->fr_NumArgs);
+ }
+ }
+ else if (ReqToolsBase)
+ {
+ struct rtFileList *fl;
+ UBYTE Name[PATHNAME_MAX];
+ ULONG num_entries = 0, i = 0;
+
+ /* Calculate number of entries in FileList */
+ fl = (struct rtFileList *)frmsg->Result;
+ while (fl)
+ {
+ fl = fl->Next;
+ num_entries++;
+ }
+
+ fl = (struct rtFileList *)frmsg->Result;
+ while (fl)
+ {
+ strncpy (Name, ((struct rtFileRequester *)xmfr->FReq)->Dir, PATHNAME_MAX);
+ AddPart (Name, fl->Name, PATHNAME_MAX);
+ frmsg->Func (Name, i++, num_entries);
+ fl = fl->Next;
+ }
+
+ rtFreeFileList (frmsg->Result); /* Free multiselect buffer */
+ }
+ }
+ else
+ /* Call user hook */
+ frmsg->Func (frmsg->Result, 0, 1);
+ }
+
+ FreeMem (frmsg, sizeof (struct FileReqMsg)); /* Free FileReqMsg */
+}
+
+
+
+GLOBALCALL STRPTR FileRequest (ULONG freq, STRPTR file)
+
+/* Puts up a simple FileRequester to ask the user for a filename.
+ * If the requester is in save mode, will optionally check for
+ * overwrite and ask the user to confirm.
+ */
+{
+ STRPTR retval = NULL;
+ struct XMFileReq *xmfr = &FileReqs[freq];
+ BOOL again;
+
+ if (!xmfr->FReq) return NULL;
+
+ LockWindows();
+
+ do
+ {
+ again = FALSE;
+
+ if (AslBase)
+ {
+ if (AslRequestTags (xmfr->FReq,
+ ASLFR_Window, ThisTask->pr_WindowPtr,
+ ASLFR_InitialFile, FilePart (file),
+ TAG_DONE))
+ {
+ /* Build file path */
+ strncpy (file, ((struct FileRequester *)xmfr->FReq)->fr_Drawer,
+ PATHNAME_MAX);
+ AddPart (file, ((struct FileRequester *)xmfr->FReq)->fr_File,
+ PATHNAME_MAX);
+
+ retval = file;
+ }
+ }
+ else if (ReqToolsBase)
+ {
+ UBYTE filename[PATHNAME_MAX];
+ ULONG flags = 0;
+
+
+ strncpy (filename, FilePart (file), PATHNAME_MAX);
+
+ if (xmfr->Flags & FRF_DOSAVEMODE) flags |= FREQF_SAVE;
+ if (xmfr->Flags & FRF_DOPATTERNS) flags |= FREQF_PATGAD;
+ if (xmfr->Flags & FRF_DOMULTISELECT) flags |= FREQF_MULTISELECT;
+
+ if (rtFileRequest (xmfr->FReq, filename, (xmfr->Title == -1) ? NULL : STR(xmfr->Title),
+ RT_ShareIDCMP, TRUE,
+ RTFI_Flags, flags,
+ TAG_DONE))
+ {
+ /* Build file path */
+ strncpy (file, ((struct rtFileRequester *)(xmfr->FReq))->Dir, PATHNAME_MAX);
+ AddPart (file, filename, PATHNAME_MAX);
+
+ retval = file;
+ }
+ }
+
+
+ if (retval && (xmfr->Flags & FRF_DOSAVEMODE))
+ {
+ switch (CheckOverwrite (retval))
+ {
+ case 1: /* Yes */
+ break;
+
+ case 2: /* Choose Another */
+ again = TRUE;
+ break;
+
+ default: /* No */
+ retval = NULL;
+ break;
+ }
+ }
+
+ } while (again);
+
+ UnlockWindows();
+
+ return retval;
+}
+
+
+
+static LONG CheckOverwrite (CONST_STRPTR path)
+
+/* Checks if the given file already exists and
+ * kindly asks the user if he knows what he's doing.
+ *
+ * RETURN
+ * 0 - Abort
+ * 1 - Continue
+ * 2 - Open the requester again
+ */
+{
+ if (GuiSwitches.AskOverwrite)
+ {
+ BPTR fh;
+
+ if (fh = Open (path, MODE_OLDFILE))
+ {
+ Close (fh);
+
+ return ShowRequest (MSG_FILE_EXISTS, MSG_OVERWRITE, FilePart (path));
+ }
+ }
+
+ return 1;
+}
+
+
+
+GLOBALCALL LONG ShowRequestStr (CONST_STRPTR text, CONST_STRPTR gtext, APTR args)
+
+/* Simple N-way requester function. Passing NULL as <gtext>
+ * will put a single "Ok" button. Will fall to a Printf()
+ * when IntuitionBase is NULL.
+ */
+{
+ LONG ret;
+
+
+ if (!gtext) gtext = STR(MSG_OK);
+
+ if (!ShowRequesters) return 1;
+
+ LockWindows();
+
+ if (ReqToolsBase)
+ {
+ /* This silly thing gets around a nasty SAS/C 6.56 bug which
+ * causes a CXERR (and generates wrong code) when making a
+ * libcall with a parameter in A4.
+ */
+ volatile APTR rtargs = args;
+
+ static LONG tags[] =
+ {
+ RTEZ_ReqTitle, 0,
+ RT_ShareIDCMP, FALSE,
+ TAG_DONE
+ };
+
+ tags[1] = (LONG)STR(MSG_XMODULE_REQUEST);
+
+ ret = rtEZRequestA (text, gtext, NULL, rtargs, (struct TagItem *)tags);
+ }
+ else if (IntuitionBase)
+ {
+ static struct EasyStruct es =
+ {
+ sizeof (struct EasyStruct),
+ 0,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ es.es_Title = STR(MSG_XMODULE_REQUEST);
+ es.es_TextFormat = (STRPTR)text;
+ es.es_GadgetFormat = (STRPTR)gtext;
+ ret = EasyRequestArgs (ThisTask->pr_WindowPtr, &es, NULL, args);
+ }
+ else
+ {
+ VPrintf (text, args);
+ FPutC (StdOut, (LONG)'\n');
+ ret = 1;
+ }
+
+ UnlockWindows();
+
+ return ret;
+}
+
+
+
+GLOBALCALL LONG ShowRequestArgs (ULONG msg, ULONG gadgets, APTR args)
+
+/* Localized interface to ShowRequestStr(). The <msg> and
+ * <gadgets> arguments are any MSG_#? from Locale.h.
+ */
+{
+ return ShowRequestStr (STR(msg), STR(gadgets), args);
+}
+
+
+
+GLOBALCALL LONG ShowRequest (ULONG msg, ULONG gadgets, ...)
+
+/* Localized, Variable arguments stub for ShowRequestStr()
+ * The <msg> and <gadgets> arguments are any MSG_#? from Locale.h.
+ */
+{
+ return ShowRequestStr (STR(msg), STR(gadgets), (APTR)(((LONG *)(&gadgets))+1));
+}
+
+
+
+GLOBALCALL LONG ScrModeRequest (struct ScrInfo *scrinfo)
+
+/* Let user choose a new screen mode and store mode attributes in the ScrInfo
+ * structure.
+ *
+ * Returns: TRUE for success, FALSE for failure.
+ */
+{
+ BOOL ChangeScreen = FALSE;
+
+ if (AslBase && AslBase->lib_Version >= 38) /* ASL */
+ {
+ struct ScreenModeRequester *ScrModeReq;
+ struct List customsmlist;
+ struct DisplayMode clonemode;
+
+
+ /* Setup custom screen mode for Workbench cloning */
+
+ memset (&clonemode, 0, sizeof (clonemode));
+
+ clonemode.dm_Node.ln_Name = STR(MSG_CLONE_WB);
+ clonemode.dm_DimensionInfo.Header.StructID = DTAG_DIMS;
+ clonemode.dm_DimensionInfo.Header.DisplayID = 0xFFFFFFFF;
+ clonemode.dm_DimensionInfo.Header.SkipID = TAG_SKIP;
+ clonemode.dm_DimensionInfo.Header.Length = (sizeof (struct DimensionInfo) - sizeof (struct QueryHeader)) / 2;
+ clonemode.dm_PropertyFlags = DIPF_IS_WB;
+
+ NEWLIST (&customsmlist);
+ ADDHEAD (&customsmlist, (struct Node *)&clonemode);
+
+
+ if (ScrModeReq = AllocAslRequest (ASL_ScreenModeRequest, NULL))
+ {
+ LockWindows();
+
+ if (AslRequestTags (ScrModeReq,
+ ASLSM_Window, ThisTask->pr_WindowPtr,
+ ASLSM_DoWidth, TRUE,
+ ASLSM_DoHeight, TRUE,
+ ASLSM_DoDepth, TRUE,
+ ASLSM_DoOverscanType, TRUE,
+ ASLSM_DoAutoScroll, TRUE,
+ ASLSM_InitialDisplayID, GetVPModeID (&Scr->ViewPort),
+ ASLSM_InitialDisplayWidth, Scr->Width,
+ ASLSM_InitialDisplayHeight, Scr->Height,
+ ASLSM_InitialDisplayDepth, DrawInfo->dri_Depth,
+ ASLSM_InitialAutoScroll, Scr->Flags & AUTOSCROLL,
+ ASLSM_InitialOverscanType, scrinfo->OverscanType,
+ ASLSM_MinWidth, 640,
+ ASLSM_MinHeight, 200,
+ ASLSM_CustomSMList, &customsmlist,
+ TAG_DONE))
+ {
+ if (ScrModeReq->sm_DisplayID == 0xFFFFFFFF)
+ scrinfo->DisplayID = 0; /* Picked special clone WB mode */
+ else
+ {
+ scrinfo->DisplayID = ScrModeReq->sm_DisplayID;
+ scrinfo->Width = ScrModeReq->sm_DisplayWidth;
+ scrinfo->Height = ScrModeReq->sm_DisplayHeight;
+ scrinfo->Depth = ScrModeReq->sm_DisplayDepth;
+ scrinfo->OverscanType = ScrModeReq->sm_OverscanType;
+ scrinfo->AutoScroll = ScrModeReq->sm_AutoScroll;
+ }
+ ChangeScreen = TRUE;
+ }
+
+ FreeAslRequest (ScrModeReq);
+ UnlockWindows();
+ }
+ }
+ else /* ReqTools */
+ {
+ struct rtScreenModeRequester *ScrModeReq;
+ BOOL CloseReqTools = FALSE;
+
+ if (!ReqToolsBase)
+ {
+ if (!(ReqToolsBase = (struct ReqToolsBase *)
+ OpenLibrary ("reqtools.library", 38)))
+ {
+ CantOpenLib ("reqtools.library", 38);
+ return FALSE;
+ }
+ CloseReqTools = TRUE;
+ }
+
+ if (ScrModeReq = rtAllocRequestA (RT_SCREENMODEREQ, NULL))
+ {
+ LockWindows();
+
+ if (rtScreenModeRequest (ScrModeReq, NULL,
+ RTSC_Flags, SCREQF_OVERSCANGAD | SCREQF_AUTOSCROLLGAD |
+ SCREQF_SIZEGADS | SCREQF_DEPTHGAD | SCREQF_GUIMODES,
+ RT_ShareIDCMP, TRUE,
+ TAG_DONE))
+ {
+ scrinfo->DisplayID = ScrModeReq->DisplayID;
+ scrinfo->Width = ScrModeReq->DisplayWidth;
+ scrinfo->Height = ScrModeReq->DisplayHeight;
+ scrinfo->Depth = ScrModeReq->DisplayDepth;
+ scrinfo->OverscanType = ScrModeReq->OverscanType;
+ scrinfo->AutoScroll = ScrModeReq->AutoScroll;
+ ChangeScreen = TRUE;
+ }
+
+ rtFreeRequest (ScrModeReq);
+ UnlockWindows();
+ }
+
+ if (CloseReqTools)
+ {
+ CloseLibrary ((struct Library *)ReqToolsBase);
+ ReqToolsBase = NULL;
+ }
+ }
+
+ return ChangeScreen;
+}
+
+
+
+GLOBALCALL LONG FontRequest (struct TextAttr *ta, ULONG flags)
+
+/* Requests a font to the user and copies the selected font to the
+ * passed TextAttr structure. The ta_Name field is allocated with
+ * AllocVec() and the font name is copied to it.
+ *
+ * Returns: FALSE for failure, anything else for success.
+ */
+{
+ struct TextAttr *result = NULL;
+
+ LockWindows();
+
+ if (AslBase)
+ {
+ if (AslRequestTags (FontReq,
+ ASLFO_Window, ThisTask->pr_WindowPtr,
+ ASLFO_Flags, FOF_DOSTYLE | flags,
+ TAG_DONE))
+ result = &((struct FontRequester *)FontReq)->fo_Attr;
+ }
+ else if (ReqToolsBase)
+ {
+ if (rtFontRequest (FontReq, NULL,
+ RT_ShareIDCMP, TRUE,
+ RTFO_Flags, FREQF_SCALE | FREQF_STYLE | ((flags & FOF_FIXEDWIDTHONLY) ? FREQF_FIXEDWIDTH : 0),
+ TAG_DONE))
+ result = &((struct rtFontRequester *)FontReq)->Attr;
+
+ }
+
+ if (result) CopyTextAttrPooled (Pool, result, ta);
+
+ UnlockWindows();
+
+ return result != NULL;
+}
+
+
+
+GLOBALCALL void FreeFReq (void)
+{
+ ULONG i;
+
+ /* Terminate async requester */
+ if (FileReqTask)
+ {
+ while (!(SetSignal (0L, FileReqSig) & FileReqSig))
+ ShowRequest (MSG_CLOSE_FILEREQUESTER, MSG_CONTINUE, NULL);
+
+ FileReqTask = NULL;
+ }
+
+ if (FileReqPort)
+ {
+ struct FileReqMsg *frmsg = (struct FileReqMsg *) GetMsg (FileReqPort);
+
+ if (frmsg)
+ {
+ if ((frmsg->XMFReq->Flags & FRF_DOMULTISELECT) && ReqToolsBase)
+ rtFreeFileList (frmsg->Result);
+ FreeMem (frmsg, sizeof (struct FileReqMsg));
+ }
+
+ DeleteMsgPort (FileReqPort); FileReqPort = NULL;
+ Signals &= ~FileReqSig;
+ }
+
+ if (AslBase)
+ {
+ for (i = 0; i < FREQ_COUNT; i++)
+ {
+ FreeAslRequest (FileReqs[i].FReq);
+ FileReqs[i].FReq = NULL;
+ }
+
+ FreeAslRequest (FontReq); FontReq = NULL;
+ CloseLibrary (AslBase); AslBase = NULL;
+ }
+
+ if (ReqToolsBase)
+ {
+ for (i = 0; i < FREQ_COUNT; i++)
+ {
+ rtFreeRequest (FileReqs[i].FReq);
+ FileReqs[i].FReq = NULL;
+ }
+
+ rtFreeRequest (FontReq); FontReq = NULL;
+ CloseLibrary ((struct Library *)ReqToolsBase); ReqToolsBase = NULL;
+ }
+}
+
+
+
+GLOBALCALL LONG SetupAsl (void)
+{
+ ULONG i;
+ struct XMFileReq *xmfr;
+
+ if (!AslBase)
+ {
+ if (!(AslBase = OpenLibrary ("asl.library", 37)))
+ {
+ CantOpenLib ("asl.library", 37);
+ return RETURN_FAIL;
+ }
+ }
+
+ for (i = 0; i < FREQ_COUNT; i++)
+ {
+ xmfr = &FileReqs[i];
+
+ if (!(xmfr->FReq = AllocAslRequestTags (ASL_FileRequest,
+ (xmfr->Title == -1) ? TAG_IGNORE : ASLFR_TitleText, (xmfr->Title == -1) ? NULL : STR(xmfr->Title),
+ ASLFR_Flags1, xmfr->Flags | FRF_PRIVATEIDCMP,
+ ASLFR_Flags2, FRF_REJECTICONS,
+ TAG_DONE)))
+ return RETURN_FAIL;
+
+ }
+
+ if (!(FontReq = AllocAslRequestTags (ASL_FontRequest,
+ TAG_DONE)))
+ return RETURN_FAIL;
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL LONG SetupReqTools (void)
+{
+ ULONG i;
+
+ if (!(ReqToolsBase = (struct ReqToolsBase *)OpenLibrary ("reqtools.library", 38)))
+ {
+ CantOpenLib ("reqtools.library", 38);
+ return RETURN_FAIL;
+ }
+
+ for (i = 0; i < FREQ_COUNT; i++)
+ if (!(FileReqs[i].FReq = rtAllocRequestA (RT_FILEREQ, NULL)))
+ return RETURN_FAIL;
+
+ if (!(FontReq = rtAllocRequestA (RT_FONTREQ, NULL)))
+ return RETURN_FAIL;
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL LONG SetupRequesters (void)
+{
+ FreeFReq();
+
+ if (!FileReqPort) /* Create FileRequester reply port */
+ {
+ if (!(FileReqPort = CreateMsgPort ())) return ERROR_NO_FREE_STORE;
+ FileReqSig = 1 << FileReqPort->mp_SigBit;
+ Signals |= FileReqSig;
+ }
+
+ if (GuiSwitches.UseReqTools)
+ {
+ if (SetupReqTools())
+ {
+ GuiSwitches.UseReqTools = FALSE;
+ return SetupAsl();
+ }
+ }
+ else if (SetupAsl())
+ {
+ GuiSwitches.UseReqTools = TRUE;
+ return SetupReqTools();
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+** $Id:$
+**
+** Copyright (C) 1993,94,95,96,98,99 by Bernardo Innocenti
+**
+** Requesters support functions
+*/
+
+GLOBALCALL STRPTR FileRequest (ULONG freq, STRPTR file);
+GLOBALCALL LONG StartFileRequest (ULONG freq, void (*func)(STRPTR file, ULONG num, ULONG count));
+GLOBALCALL void HandleFileRequest (void);
+GLOBALCALL LONG ShowRequestStr (CONST_STRPTR text, CONST_STRPTR gtext, APTR args);
+GLOBALCALL LONG ShowRequestArgs (ULONG msg, ULONG gadgets, APTR args);
+GLOBALCALL LONG ShowRequest (ULONG msg, ULONG gadgets, ...);
+GLOBALCALL void FreeFReq (void);
+GLOBALCALL LONG SetupRequesters (void);
+GLOBALCALL LONG ScrModeRequest (struct ScrInfo *scrinfo);
+GLOBALCALL LONG FontRequest (struct TextAttr *ta, ULONG flags);
--- /dev/null
+/*
+** Rexx.c
+**
+** Copyright (C) 1994,95,96,97 by Bernardo Innocenti
+**
+** ARexx interface handling routines
+*/
+
+#include <exec/memory.h>
+#include <rexx/rxslib.h>
+#include <rexx/storage.h>
+#include <rexx/errors.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+#include <proto/rexxsyslib.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+struct RexxCmd
+{
+ const STRPTR Cmd;
+ const STRPTR Template;
+ LONG (*Func)(struct RexxMsg *, LONG *);
+ LONG Pad; /* Makes each entry 16 bytes long for faster array access */
+};
+
+
+/* Local function prototypes */
+
+static LONG ExecRexxCmd (struct RexxMsg *msg, struct RexxCmd *cmd, const UBYTE *rexxargs);
+
+static LONG RexxActivate (struct RexxMsg *msg, LONG *args);
+static LONG RexxClear (struct RexxMsg *msg, LONG *args);
+static LONG RexxClose (struct RexxMsg *msg, LONG *args);
+static LONG RexxColumn (struct RexxMsg *msg, LONG *args);
+static LONG RexxCopy (struct RexxMsg *msg, LONG *args);
+static LONG RexxCursor (struct RexxMsg *msg, LONG *args);
+static LONG RexxCut (struct RexxMsg *msg, LONG *args);
+static LONG RexxDeactivate (struct RexxMsg *msg, LONG *args);
+static LONG RexxErase (struct RexxMsg *msg, LONG *args);
+static LONG RexxGotoBookmark (struct RexxMsg *msg, LONG *args);
+static LONG RexxHelp (struct RexxMsg *msg, LONG *args);
+static LONG RexxIconify (struct RexxMsg *msg, LONG *args);
+static LONG RexxLine (struct RexxMsg *msg, LONG *args);
+static LONG RexxLockGui (struct RexxMsg *msg, LONG *args);
+static LONG RexxNew (struct RexxMsg *msg, LONG *args);
+static LONG RexxOpen (struct RexxMsg *msg, LONG *args);
+static LONG RexxOptimize (struct RexxMsg *msg, LONG *args);
+static LONG RexxPaste (struct RexxMsg *msg, LONG *args);
+static LONG RexxPrint (struct RexxMsg *msg, LONG *args);
+static LONG RexxQuit (struct RexxMsg *msg, LONG *args);
+static LONG RexxRequestFile (struct RexxMsg *msg, LONG *args);
+static LONG RexxRequestResponse (struct RexxMsg *msg, LONG *args);
+static LONG RexxRequestNotify (struct RexxMsg *msg, LONG *args);
+static LONG RexxSave (struct RexxMsg *msg, LONG *args);
+static LONG RexxSaveInstrument (struct RexxMsg *msg, LONG *args);
+static LONG RexxScreenToBack (struct RexxMsg *msg, LONG *args);
+static LONG RexxScreenToFront (struct RexxMsg *msg, LONG *args);
+static LONG RexxSelectInstrument(struct RexxMsg *msg, LONG *args);
+static LONG RexxSetBookmark (struct RexxMsg *msg, LONG *args);
+static LONG RexxShowMessage (struct RexxMsg *msg, LONG *args);
+static LONG RexxUnLockGui (struct RexxMsg *msg, LONG *args);
+static LONG RexxVersion (struct RexxMsg *msg, LONG *args);
+
+
+static struct Library *RexxSysBase = NULL;
+
+XDEF struct MsgPort *PubPort = NULL;
+XDEF ULONG PubPortSig = 0L;
+XDEF UBYTE PubPortName[16]; /* ARexx host name */
+
+
+static struct RexxCmd RexxCmds[] =
+{
+ { "ACTIVATE", NULL, RexxActivate },
+ { "CLEAR", "FORCE/S", RexxClear },
+ { "CLOSE", "FORCE/S", RexxClose },
+ { "COLUMN", "/N/A", RexxColumn },
+ { "COPY", NULL, RexxCopy },
+ { "CURSOR", "UP/S,DOWN/S,LEFT/S,RIGHT/S", RexxCursor },
+ { "CUT", NULL, RexxCut },
+ { "DEACTIVATE", NULL, RexxDeactivate },
+ { "ERASE", "FORCE/S", RexxErase },
+ { "GOTOBOOKMARK", NULL, RexxGotoBookmark },
+ { "HELP", "COMMAND,PROMPT/S", RexxHelp },
+ { "ICONIFY", NULL, RexxIconify },
+ { "LINE", "/N/A", RexxLine },
+ { "LOCKGUI", NULL, RexxLockGui },
+ { "NEW", "PORTNAME/K", RexxNew },
+ { "OPEN", "FILENAME,FORCE/S", RexxOpen },
+ { "OPTIMIZE", NULL, RexxOptimize },
+ { "PASTE", NULL, RexxPaste },
+ { "PRINT", "PROMPT/S", RexxPrint },
+ { "QUIT", "FORCE/S", RexxQuit },
+ { "REQUESTFILE", "TITLE/K,PATH/K,FILE/K,PATTERN/K", RexxRequestFile },
+ { "REQUESTRESPONSE","TITLE/K,PROMPT/K", RexxRequestResponse },
+ { "REQUESTNOTIFY", "PROMPT/K", RexxRequestNotify },
+ { "SAVE", "NAME,FORMAT", RexxSave },
+ { "SAVEINSTRUMENT", "NAME", RexxSaveInstrument },
+ { "SCREENTOBACK", NULL, RexxScreenToBack },
+ { "SCREENTOFRONT", NULL, RexxScreenToFront },
+ { "SELECTINSTRUMENT","/N/A", RexxSelectInstrument },
+ { "SETBOOKMARK", NULL, RexxSetBookmark },
+ { "SHOWMESSAGE", "MSG/A", RexxShowMessage },
+ { "UNLOCKGUI", NULL, RexxUnLockGui },
+ { "VERSION", NULL, RexxVersion }
+};
+
+#define COMMAND_CNT (sizeof (RexxCmds) / sizeof (struct RexxCmd))
+
+
+GLOBALCALL void HandleRexxMsg (void)
+
+/* Arexx command handler */
+{
+ struct RexxMsg *msg;
+ LONG compare, high, low, mid, skip, cmdlen;
+ STRPTR arg0;
+ UBYTE cmd[32];
+
+ while (msg = (struct RexxMsg *) GetMsg (PubPort))
+ {
+ if (IsRexxMsg (msg))
+ {
+ msg->rm_Result1 = RETURN_FAIL;
+
+ /* Find command name length */
+ arg0 = ARG0(msg);
+ cmdlen = 0;
+
+ while (*arg0 && (*arg0 != ' ') && (*arg0 != '\t')
+ && (*arg0 != '\n') && (cmdlen < 31))
+ {
+ cmd[cmdlen] = *arg0 & ~(1 << 5); /* Fast toupper() */
+ cmdlen++;
+ arg0++;
+ }
+ cmd[cmdlen] = '\0';
+
+ skip = 0;
+ low = 0;
+ high = COMMAND_CNT - 1;
+
+ /* Perform an optimized binary serch to find the ARexx
+ * command in the ARexx commands array.
+ */
+ do
+ {
+ /* Search optimization. Skip first characters until they
+ * are different.
+ * We must test low != high because otherwise this
+ * loop would go past the end of the string, as the
+ * two strings we compare are identical).
+ */
+ if (low != high)
+ while ((RexxCmds[low].Cmd[skip] == RexxCmds[high].Cmd[skip])
+ && (RexxCmds[low].Cmd[skip] == cmd[skip]))
+ skip++;
+
+ mid = (low + high) >> 1;
+ compare = strcmp (RexxCmds[mid].Cmd + skip, cmd + skip);
+
+ if (compare > 0)
+ high = mid - 1;
+ else if (compare < 0)
+ low = mid + 1;
+ else
+ {
+ msg->rm_Result1 = ExecRexxCmd (msg, &RexxCmds[mid],
+ ARG0(msg) + cmdlen);
+ break; /* Exit from loop */
+ }
+ }
+ while (low <= high);
+ }
+
+ ReplyMsg ((struct Message *)msg);
+ }
+}
+
+
+
+static LONG ExecRexxCmd (struct RexxMsg *msg, struct RexxCmd *cmd, const UBYTE *rexxargs)
+{
+ struct RDArgs *rdargs;
+ UBYTE *argbuf;
+ ULONG arglen = strlen (rexxargs) + 1; /* Space for newline */
+ LONG rc = RC_ERROR;
+ LONG argarray[6] = { 0L }; /* Max 6 arguments allowed! */
+
+ ShowRequesters = FALSE;
+
+ if (!cmd->Template)
+ rc = (cmd->Func)(msg, NULL); /* Call command directly */
+ else if (argbuf = AllocVec (arglen, MEMF_ANY))
+ {
+ /* Copy arguments to temporary buffer.
+ * ReadArgs() also requires a newline.
+ */
+ strcpy (argbuf, rexxargs);
+ argbuf[arglen-1] = '\n';
+
+ if (rdargs = AllocDosObject (DOS_RDARGS, NULL))
+ {
+ rdargs->RDA_Source.CS_Buffer = argbuf;
+ rdargs->RDA_Source.CS_Length = arglen;
+ rdargs->RDA_Flags |= RDAF_NOPROMPT;
+
+ if (ReadArgs ((volatile STRPTR)cmd->Template, argarray, rdargs))
+ {
+ /* Call Command server */
+ rc = (cmd->Func)(msg, argarray);
+
+ FreeArgs (rdargs);
+ }
+ FreeDosObject (DOS_RDARGS, rdargs);
+ }
+ FreeVec (argbuf);
+ }
+
+ ShowRequesters = TRUE;
+
+ return rc;
+}
+
+
+
+GLOBALCALL LONG CreateRexxPort (void)
+
+/* Setup public port for ARexx host */
+{
+ ULONG i = 0;
+
+ if (!PubPortName[0]) return RETURN_FAIL;
+
+ if (!(RexxSysBase = OpenLibrary (RXSNAME, 36L)))
+ return RETURN_FAIL;
+
+ if (!(PubPort = CreateMsgPort ())) return ERROR_NO_FREE_STORE;
+
+ PubPortSig = 1 << PubPort->mp_SigBit;
+ Signals |= PubPortSig;
+
+ Forbid();
+ while (FindPort (PubPortName))
+ SPrintf (PubPortName, "XMODULE.%ld", ++i);
+
+ PubPort->mp_Node.ln_Name = PubPortName;
+ PubPort->mp_Node.ln_Pri = 1;
+ AddPort (PubPort);
+ Permit();
+
+ return RETURN_OK;
+}
+
+
+GLOBALCALL void DeleteRexxPort (void)
+{
+ if (PubPort)
+ {
+ RemPort (PubPort);
+ KillMsgPort (PubPort); PubPort = NULL;
+ }
+
+ if (RexxSysBase)
+ { CloseLibrary (RexxSysBase); RexxSysBase = NULL; }
+}
+
+/************************/
+/* Rexx Command servers */
+/************************/
+
+static LONG RexxActivate (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+static LONG RexxClear (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+static LONG RexxClose (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+static LONG RexxColumn (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+static LONG RexxCopy (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+static LONG RexxCursor (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+static LONG RexxCut (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+static LONG RexxDeactivate (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxErase (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxGotoBookmark (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxHelp (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxIconify (struct RexxMsg *msg, LONG *args)
+{
+ Iconify();
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxLine (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxLockGui (struct RexxMsg *msg, LONG *args)
+{
+ LockWindows();
+
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxNew (struct RexxMsg *msg, LONG *args)
+{
+ struct SongInfo *si;
+
+ if (si = xmCreateSong (
+ SNGA_ReadyToUse, TRUE,
+ XMSNG_AddToList, -1,
+ XMSNG_Active, TRUE,
+ TAG_DONE))
+ {
+ ReleaseSemaphore (&si->Lock);
+ return RETURN_OK;
+ }
+
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxOpen (struct RexxMsg *msg, LONG *args)
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ if (args[0])
+ {
+ if (si = xmLoadModule ((STRPTR)args[0],
+ XMSNG_OldSong, NULL,
+ XMSNG_AddToList, TRUE,
+ XMSNG_Active, TRUE,
+ TAG_DONE))
+ ReleaseSemaphore (&si->Lock);
+ }
+ else
+ {
+ UBYTE filename[PATHNAME_MAX];
+
+ filename[0] = '\0';
+
+ if (FileRequest (FREQ_LOADMOD, filename))
+ {
+ if (si = xmLoadModule (filename,
+ XMSNG_OldSong, NULL,
+ XMSNG_AddToList, TRUE,
+ XMSNG_Active, TRUE,
+ TAG_DONE))
+ ReleaseSemaphore (&si->Lock);
+ }
+ }
+
+ UnlockWindows();
+
+ return LastErr;
+}
+
+
+
+static LONG RexxOptimize (struct RexxMsg *msg, LONG *args)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ xmProcessSong (si, NULL,
+ XMSNG_Optimize, XMOF_DEFAULT,
+ TAG_DONE);
+
+ UpdateSongInfo();
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxPaste (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxPrint (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxQuit (struct RexxMsg *msg, LONG *args)
+{
+ Quit = 1;
+ if (args[0]) GuiSwitches.AskExit = FALSE;
+
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxRequestFile (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxRequestResponse (struct RexxMsg *msg, LONG *args)
+{
+ return ShowRequestStr ((STRPTR)args[0], (STRPTR)args[1], NULL);
+}
+
+
+
+static LONG RexxRequestNotify (struct RexxMsg *msg, LONG *args)
+{
+ ShowRequestStr ((STRPTR)args[0], NULL, NULL);
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxSave (struct RexxMsg *msg, LONG *args)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ if (args[0])
+ SetAttrs (si,
+ SNGA_Path, (STRPTR)args[0],
+ TAG_DONE);
+
+ if (args[1])
+ {
+ struct XMHook *saver;
+
+ /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
+
+ /* Try to get the shared semaphore */
+ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
+ /* Check if we can get the exclusive version */
+ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
+ /* Oh well, wait for the shared lock */
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+
+ if (saver = (struct XMHook *)FindName ((struct List *)&XModuleBase->xm_Loaders, (STRPTR)args[1]))
+ LastErr = xmSaveModuleA (si, si->Path, saver, NULL);
+ else
+ LastErr = ERROR_OBJECT_NOT_FOUND;
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ }
+ else
+ LastErr = xmSaveModuleA (si, si->Path, NULL, NULL);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ return LastErr;
+}
+
+
+
+static LONG RexxSaveInstrument (struct RexxMsg *msg, LONG *args)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ struct Instrument *instr;
+
+ if (instr = si->Instr[si->CurrentInst])
+ {
+ if (args[0])
+ LastErr = SaveInstrument (instr, (STRPTR)args[0]);
+ else
+ LastErr = SaveInstrument (instr, instr->Name);
+ }
+ else LastErr = ERROR_OBJECT_NOT_FOUND;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ return LastErr;
+}
+
+
+
+static LONG RexxSelectInstrument (struct RexxMsg *msg, LONG *args)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ if (*((ULONG *)args[0]) >= MAXINSTRUMENTS)
+ return RETURN_FAIL;
+
+ si->CurrentInst = *((ULONG *)args[0]);
+
+ ReleaseSemaphore (&si->Lock);
+
+ UpdateInstrInfo();
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxScreenToBack (struct RexxMsg *msg, LONG *args)
+{
+ if (Scr) ScreenToBack (Scr);
+
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxScreenToFront (struct RexxMsg *msg, LONG *args)
+{
+ if (Scr)
+ {
+ ScreenToFront (Scr);
+ if (ThisTask->pr_WindowPtr) ActivateWindow (ThisTask->pr_WindowPtr);
+ }
+
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxSetBookmark (struct RexxMsg *msg, LONG *args)
+{
+ return RETURN_FAIL;
+}
+
+
+
+static LONG RexxShowMessage (struct RexxMsg *msg, LONG *args)
+{
+ ShowString ((STRPTR)args[0], NULL);
+
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxUnLockGui (struct RexxMsg *msg, LONG *args)
+{
+ UnlockWindows();
+ return RETURN_OK;
+}
+
+
+
+static LONG RexxVersion (struct RexxMsg *msg, LONG *args)
+{
+ UBYTE RexxVer[8];
+
+ SPrintf (RexxVer, "%ld.%ld", VERSION, REVISION);
+
+// SetRexxVar ((struct Message *)msg, "RESULT", RexxVer, strlen (RexxVer));
+ msg->rm_Result2=(LONG)CreateArgstring (RexxVer, (LONG)strlen(RexxVer));
+
+ return RETURN_OK;
+}
--- /dev/null
+******************************************************************************
+*
+* $VER: RomTag.asm (1.21.97)
+*
+* Copyright (C) 1995,96,97 by Bernardo Innocenti
+*
+******************************************************************************
+*
+* This source is based on the RomTag.asm source I've found somewhere :-)
+*
+*
+ INCLUDE "exec/types.i"
+ INCLUDE "exec/macros.i"
+ INCLUDE "exec/libraries.i"
+ INCLUDE "exec/lists.i"
+ INCLUDE "exec/alerts.i"
+ INCLUDE "exec/initializers.i"
+ INCLUDE "exec/resident.i"
+ INCLUDE "exec/execbase.i"
+ INCLUDE "libraries/dos.i"
+
+; BOOPSI class libraries should use this structure as the base for their
+; library data. This allows developers to obtain the class pointer for
+; performing object-less inquiries.
+
+ STRUCTURE ClassBase,0
+ STRUCT cl_Lib,LIB_SIZE ; Embedded library
+ UWORD cl_Pad ; Align the structure
+ APTR cl_Class ; Class pointer
+ APTR cl_SegList ; SegList pointer
+ LABEL ClassLibrary_SIZEOF
+
+;---------------------------------------------------------------------------
+
+ XDEF _LibFuncTable
+ XDEF _LibDataTable
+
+ XREF _LibName
+ XREF _LibId
+ XREF __UserLibInit
+ XREF __UserLibCleanup
+
+ XREF __GetEngine
+
+ IFD XMHOOK
+ XREF __SetupXMHook
+ ENDC
+
+;---------------------------------------------------------------------------
+
+ SECTION Code
+
+; First executable location, must return an error to the caller
+
+ moveq #-1,d0
+ rts
+
+;---------------------------------------------------------------------------
+
+_ROMTAG
+ DC.W RTC_MATCHWORD ; UWORD RT_MATCHWORD
+ DC.L _ROMTAG ; APTR RT_MATCHTAG
+ DC.L _ENDCODE ; APTR RT_ENDSKIP
+ DC.B RTF_AUTOINIT ; UBYTE RT_FLAGS
+ DC.B LIBVERSION ; UBYTE RT_VERSION
+ DC.B NT_LIBRARY ; UBYTE RT_TYPE
+ DC.B 0 ; BYTE RT_PRI <--- WARNING: Using negative values here will cause trouble!
+ DC.L _LibName ; APTR RT_NAME
+ DC.L _LibId ; APTR RT_IDSTRING
+ DC.L _LibInitTable ; APTR RT_INIT
+
+
+* The RomTag specified that we were RTF_AUTOINIT. This means that rt_Init
+* points to the table below. (Without RTF_AUTOINIT it would point to a
+* routine to run.)
+*
+* Our library base is a standard struct Library, followed by a WORD
+* padding, a pointer to the boopsi Class structure of the external
+* boopsi class and a pointer to our SegList. The SegList pointer
+* will be returned by LibExpunge() in order to have our code UnloadSeg()'ed
+* The Class pointer should be initialized by UserLibInit().
+
+_LibInitTable
+ dc.l ClassLibrary_SIZEOF
+ dc.l _LibFuncTable
+ dc.l _LibDataTable
+ dc.l _LibInit
+
+
+
+* Table of functions included in this library; the first 4 are the same
+* for any library and for internal Exec use only.
+
+V_DEF MACRO
+ dc.w \1 + (* - _LibFuncTable)
+ ENDM
+
+_LibFuncTable
+* dc.w -1 ; It's weird... The cool way didn't work for me 8-(
+* V_DEF _LibOpen
+* V_DEF _LibClose
+* V_DEF _LibExpunge
+* V_DEF _LibExtFunc
+* V_DEF __GetEngine
+* IFD XMHOOK
+* V_DEF __SetupXMHook
+* ENDC
+* dc.w -1
+
+ dc.l _LibOpen
+ dc.l _LibClose
+ dc.l _LibExpunge
+ dc.l _LibExtFunc
+ dc.l __GetEngine
+ IFD XMHOOK
+ dc.l __SetupXMHook
+ ENDC
+ dc.l -1
+
+
+_LibDataTable
+ INITBYTE LN_TYPE,NT_LIBRARY
+ INITLONG LN_NAME,_LibName
+ INITBYTE LN_PRI,-5
+ INITBYTE LIB_FLAGS,(LIBF_SUMUSED!LIBF_CHANGED)
+ INITWORD LIB_VERSION,LIBVERSION
+ INITWORD LIB_REVISION,LIBREVISION
+ INITLONG LIB_IDSTRING,_LibId
+ dc.w 0
+
+
+ CNOP 0,4
+
+
+* The following function will be called at startup time.
+*
+* Inputs:
+* LibPtr (d0) - Pointer to the library base, initialized due to the
+* specifications in DataTable
+* SegList (a0) - BPTR to the segment list
+* _SysBase (a6) - The usual ExecBase pointer
+*
+* Result:
+* LibPtr, if all was okay and the library may be linked into the
+* system library list. NULL otherwise.
+*
+_LibInit:
+ move.l d0,a1
+ move.l a0,cl_SegList(a1) ; Save SegList
+
+; Check CPU for 68020 or better
+ IFD _MC68020_
+ move.w AttnFlags(a6),d1
+ btst.w #AFB_68020,d1
+ beq.s fail$
+ ENDC
+
+ move.l a6,-(sp) ; Save SysBase
+ move.l d0,a6 ; Put our base in a6
+ jsr __UserLibInit ; Call user init
+ move.l a6,a1 ; save our base to a1
+ move.l (sp)+,a6 ; Retrieve SysBase
+ tst.l d0
+ beq.s fail$
+ rts
+
+fail$
+ bsr FreeBase ; Free library base
+ moveq #0,d0
+ rts
+
+
+* The following functions are called from exec.library/OpenLibrary(),
+* exec.library/CloseLibrary() and exec.library/ExpungeLibrary(),
+* respectively. Exec passes our library base pointer in A6.
+*
+* Task switching will be turned off while these functions are being
+* executed, so they must be as short as possible. As the data inside
+* the library base is protected with Forbid(), these functions must
+* not make calls which would explicitly or implicitly turn on multitasking.
+* This includes opening other disk based libraries. The problem may be
+* overcame by protecting the library base with a SignalSemaphore.
+*
+
+
+* This function is called from exec.library/OpenLibrary().
+*
+* Inputs:
+* LibPtr (a6) - Pointer to the library base
+* Version (d0) - The suggested version number
+*
+* Result:
+* LibPtr, if successful, NULL otherwise
+*
+
+_LibOpen:
+ addq.w #1,LIB_OPENCNT(a6)
+ bclr.b #LIBB_DELEXP,LIB_FLAGS(a6) ; Prevent delayed expunge
+ move.l a6,d0
+ rts
+
+
+
+* This function is called from exec/CloseLibrary().
+*
+* Inputs:
+* LibPtr (A6) - pointer to the library base as returned from OpenLibrary().
+*
+* Result:
+* Segment list of the library (see arguments of _LibInit), if there
+* was a delayed expunge and the library is no longer open, NULL
+* otherwise.
+*
+_LibClose:
+ subq.w #1,LIB_OPENCNT(a6)
+ tst.w LIB_OPENCNT(a6)
+ bne.s .NoExpunge
+ btst.b #LIBB_DELEXP,LIB_FLAGS(a6)
+ beq.s .NoExpunge
+
+ bra.s _LibExpunge
+
+.NoExpunge
+ moveq.l #0,d0
+ rts
+
+
+
+* This function is called from exec.library/RemoveLibrary().
+*
+* Inputs:
+* LibPtr (A6) - pointer to the library base.
+*
+* Result:
+* Segment list of the library (see arguments of _LibInit()),
+* if the library isn't opened currently, NULL otherwise.
+*
+
+_LibExpunge:
+
+ ; Flag library base for delayed expunge
+ bset.b #LIBB_DELEXP,LIB_FLAGS(a6)
+ tst.w LIB_OPENCNT(a6) ; Only expunge if OpenCnt == 0
+ bne.s .DoNotExpunge
+
+.NotOpen
+
+ jsr __UserLibCleanup ; Call user cleanup code
+ tst.l d0
+ beq.s .DoNotExpunge
+
+ move.l cl_SegList(a6),-(sp) ; Save SegList pointer
+
+ move.l a6,a1
+ REMOVE ; Remove us from Exec library list.
+
+
+; Free the library base
+
+ move.l a6,a1 ; LibBase
+ move.l a6,-(sp) ; Save A6
+ move.l 4.w,a6 ; Load SysBase
+ bsr FreeBase ; Free our library base
+ move.l (sp)+,a6 ; Restore A6
+
+ move.l (sp)+,d0 ; Return our SegList
+ rts
+
+.DoNotExpunge
+
+; NOTE: I'm falling in _LibExtFunc from here!
+
+
+
+* Dummy function to return 0
+
+_LibExtFunc:
+ moveq #0,d0
+ rts
+
+
+* Frees our library base
+*
+* Inputs:
+* LibBase (a1) - Pointer to Library structure.
+* SysBase (a6) - Pointer to SysBase
+*
+FreeBase:
+ moveq.l #0,d0
+ move.l a1,a0
+ move.w LIB_NEGSIZE(a0),d0
+ suba.l d0,a1 ; Get pointer to real start of library base
+ add.w LIB_POSSIZE(a0),d0 ; Total library size (LIB_POSSIZE + LIB_NEGSIZE)
+ JSRLIB FreeMem
+ rts
+
+;-----------------------------------------------------------------------
+
+_ENDCODE
+
+ END
--- /dev/null
+CODE=FAR
+PARAMETERS=REGISTERS
+NOSTACKCHECK
+STRINGMERGE
+ERRORREXX
+NOMULTIPLEINCLUDES
+SMALLCODE
+SMALLDATA
+NOVERSION
+ABSFUNCPOINTER
+UTILITYLIBRARY
+NOICONS
+MEMORYSIZE=HUGE
+ONERROR=CONTINUE
+OPTIMIZERTIME
+MULTIPLECHARACTERCONSTANTS
+DEFINE NO_REQTOOLS_OBSOLETE
+DEFINE ASL_V38_NAMES_ONLY
+DEFINE INTUI_V36_NAMES_ONLY
+DEFINE IFFPARSE_V37_NAMES_ONLY
+DEFINE USE_BUILTIN_MATH
+DEFINE __USE_SYSBASE
+IGNORE=193
+IGNORE=306
+PUBSCREEN=Workbench
--- /dev/null
+/*
+** SampleWin.c
+**
+** Copyright (C) 1994,95,96 Bernardo Innocenti
+**
+** Sample editor handling functions.
+*/
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+#include <graphics/rpattr.h>
+#include <graphics/gfxmacros.h>
+
+#include <clib/exec_protos.h>
+#include <clib/intuition_protos.h>
+#include <clib/graphics_protos.h>
+#include <clib/gadtools_protos.h>
+#include <clib/xmodule_protos.h>
+
+#include <pragmas/exec_sysbase_pragmas.h>
+#include <pragmas/intuition_pragmas.h>
+#include <pragmas/gadtools_pragmas.h>
+#include <pragmas/graphics_pragmas.h>
+#include <pragmas/xmodule_pragmas.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Gadgets IDs */
+
+enum {
+ GD_SampScroll,
+ GD_SampZoomIn,
+ GD_SampRangeAll,
+ GD_SampShowRange,
+ GD_SampZoomOut,
+ GD_SampClearRange,
+ GD_SampShowAll,
+ GD_SampSize,
+ GD_SampPlayR,
+ GD_SampPlayD,
+ GD_SampPlayL,
+ GD_SampPlayA,
+ GD_DisplayStart,
+ GD_DisplayEnd,
+ GD_DisplayLen,
+ GD_RangeStart,
+ GD_RangeEnd,
+ GD_RangeLen,
+ GD_RepStart,
+ GD_RepEnd,
+ GD_RepLen,
+ GD_SampBox,
+
+ Sample_CNT
+};
+
+
+/* Sample rendering modes */
+
+enum {
+ SAMP_PIXEL,
+ SAMP_LINE,
+ SAMP_FILLED
+};
+
+
+/* Local functions prototypes */
+
+static void SampleRender (struct WinUserData *wud);
+static LONG SampBoxSetup (struct Gadget *g);
+static void HandleSampleIDCMP (void);
+static void DrawRange (void);
+static void UpdateRange (WORD newend);
+static void UpdateRangeInfo (void);
+
+static void DrawPixelGraph (struct RastPort *rp, BYTE *samp,
+ UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step);
+static void DrawLineGraph (struct RastPort *rp, BYTE *samp,
+ UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step);
+static void DrawFilledGraph (struct RastPort *rp, BYTE *samp,
+ UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step);
+
+static void SampScrollClicked (void);
+static void SampZoomInClicked (void);
+static void SampRangeAllClicked (void);
+static void SampShowRangeClicked (void);
+static void SampZoomOutClicked (void);
+static void SampClearRangeClicked (void);
+static void SampShowAllClicked (void);
+static void SampPlayRClicked (void);
+static void SampPlayDClicked (void);
+static void SampPlayLClicked (void);
+static void SampPlayAClicked (void);
+static void DisplayStartClicked (void);
+static void DisplayEndClicked (void);
+static void DisplayLenClicked (void);
+static void RangeStartClicked (void);
+static void RangeEndClicked (void);
+static void RangeLenClicked (void);
+static void RepStartClicked (void);
+static void RepLenClicked (void);
+static void RepEndClicked (void);
+static void SampBoxClicked (void);
+
+static void SampleMiCut (void);
+static void SampleMiCopy (void);
+static void SampleMiPaste (void);
+static void SampleMiPoints (void);
+static void SampleMiLines (void);
+static void SampleMiFilled (void);
+
+
+static struct IBox SampBox; /* Sample Box Coordinates */
+static WORD RangeStartX, RangeEndX, RangePole;
+static WORD LoopPole1, LoopPole2;
+static LONG DisplayStart, DisplayEnd;
+
+
+
+static struct Gadget *SampleGadgets[Sample_CNT];
+
+static struct NewMenu SampleNewMenu[] = {
+ NM_TITLE, (STRPTR)MSG_EDIT_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_CUT_MEN, (STRPTR)"X", 0, 0L, (APTR)SampleMiCut,
+ NM_ITEM, (STRPTR)MSG_COPY_MEN, (STRPTR)"C", 0, 0L, (APTR)SampleMiCopy,
+ NM_ITEM, (STRPTR)MSG_PASTE_MEN, (STRPTR)"V", 0, 0L, (APTR)SampleMiPaste,
+ NM_TITLE, (STRPTR)MSG_RENDER_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_POINTS_MEN, NULL, CHECKIT, 6L, (APTR)SampleMiPoints,
+ NM_ITEM, (STRPTR)MSG_LINES_MEN, NULL, CHECKIT|CHECKED, 5L, (APTR)SampleMiLines,
+ NM_ITEM, (STRPTR)MSG_FILLED_MEN, NULL, CHECKIT, 3L, (APTR)SampleMiFilled,
+ NM_END, NULL, NULL, 0, 0L, NULL
+};
+
+static UWORD SampleGTypes[] = {
+ SCROLLER_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ NUMBER_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ BUTTON_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ INTEGER_KIND,
+ GENERIC_KIND
+};
+
+struct NewGadget SampleNGad[] = {
+ 4, 154, 623, 8, NULL, NULL, GD_SampScroll, 0, NULL, (APTR)SampScrollClicked,
+ 4, 1, 105, 12, (UBYTE *)"Zoom _In", NULL, GD_SampZoomIn, PLACETEXT_IN, NULL, (APTR)SampZoomInClicked,
+ 112, 1, 105, 12, (UBYTE *)"Range _All", NULL, GD_SampRangeAll, PLACETEXT_IN, NULL, (APTR)SampRangeAllClicked,
+ 112, 27, 105, 12, (UBYTE *)"Show Range", NULL, GD_SampShowRange, PLACETEXT_IN, NULL, (APTR)SampShowRangeClicked,
+ 4, 14, 105, 12, (UBYTE *)"Zoom _Out", NULL, GD_SampZoomOut, PLACETEXT_IN, NULL, (APTR)SampZoomOutClicked,
+ 112, 14, 105, 12, (UBYTE *)"_Clear Range", NULL, GD_SampClearRange, PLACETEXT_IN, NULL, (APTR)SampClearRangeClicked,
+ 4, 27, 105, 12, (UBYTE *)"Show All", NULL, GD_SampShowAll, PLACETEXT_IN, NULL, (APTR)SampShowAllClicked,
+ 112, 40, 105, 12, (UBYTE *)"Sample Size", NULL, GD_SampSize, PLACETEXT_LEFT, NULL, NULL,
+ 220, 27, 105, 12, (UBYTE *)"Play Range", NULL, GD_SampPlayR, PLACETEXT_IN, NULL, (APTR)SampPlayRClicked,
+ 220, 1, 105, 12, (UBYTE *)"Play Display", NULL, GD_SampPlayD, PLACETEXT_IN, NULL, (APTR)SampPlayDClicked,
+ 220, 40, 105, 12, (UBYTE *)"Loop Play", NULL, GD_SampPlayL, PLACETEXT_IN, NULL, (APTR)SampPlayLClicked,
+ 220, 14, 105, 12, (UBYTE *)"Play All", NULL, GD_SampPlayA, PLACETEXT_IN, NULL, (APTR)SampPlayAClicked,
+ 414, 11, 69, 13, (UBYTE *)"_Display", NULL, GD_DisplayStart, PLACETEXT_LEFT, NULL, (APTR)DisplayStartClicked,
+ 486, 11, 69, 13, NULL, NULL, GD_DisplayEnd, 0, NULL, (APTR)DisplayEndClicked,
+ 558, 11, 69, 13, NULL, NULL, GD_DisplayLen, 0, NULL, (APTR)DisplayLenClicked,
+ 414, 25, 69, 13, (UBYTE *)"_Range", NULL, GD_RangeStart, PLACETEXT_LEFT, NULL, (APTR)RangeStartClicked,
+ 486, 25, 69, 13, NULL, NULL, GD_RangeEnd, 0, NULL, (APTR)RangeEndClicked,
+ 558, 25, 69, 13, NULL, NULL, GD_RangeLen, 0, NULL, (APTR)RangeLenClicked,
+ 414, 39, 69, 13, (UBYTE *)"R_epeat", NULL, GD_RepStart, PLACETEXT_LEFT, NULL, (APTR)RepStartClicked,
+ 486, 39, 69, 13, NULL, NULL, GD_RepEnd, 0, NULL, (APTR)RepEndClicked,
+ 558, 39, 69, 13, NULL, NULL, GD_RepLen, 0, NULL, (APTR)RepLenClicked,
+ 0, 0, 0, 0, NULL, NULL, GD_SampBox, 0, NULL, (APTR)SampBoxClicked
+};
+
+static static ULONG SampleGTags[] = {
+ PGA_Freedom, LORIENT_HORIZ, GA_RelVerify, TRUE, TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ GTNM_Border, TRUE, TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ GTIN_MaxChars, 6, TAG_DONE,
+ XMGAD_SetupFunc, (ULONG)SampBoxSetup, TAG_DONE
+};
+
+
+static struct IntuiText SampleIText[] = {
+ 1, 0, JAM1,445, 5, NULL, (UBYTE *)"Start", NULL,
+ 1, 0, JAM1,516, 5, NULL, (UBYTE *)"End", NULL,
+ 1, 0, JAM1,589, 5, NULL, (UBYTE *)"Length", NULL };
+
+#define Sample_TNUM 3
+
+
+
+struct WinUserData SampleWUD =
+{
+ { NULL, NULL },
+ NULL,
+ SampleGadgets,
+ NULL,
+ NULL,
+ NULL,
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ 0,
+
+ CloseSampleWindow,
+ NULL,
+ HandleSampleIDCMP,
+
+ SampleNewMenu,
+ NULL,
+ Sample_CNT,
+ WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_ACTIVATE,
+ SCROLLERIDCMP|ARROWIDCMP|BUTTONIDCMP|INTEGERIDCMP|IDCMP_MENUPICK|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|IDCMP_MOUSEMOVE,
+ (STRPTR)MSG_SAMPLE_TITLE,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+
+static void SampleRender (struct WinUserData *wud)
+{
+/* DrawBevelBox (wud->Win->RPort, SampBox.Left - 2, SampBox.Top - 1,
+ SampBox.Width + 4, SampBox.Height + 2,
+ GT_VisualInfo, VisualInfo,
+ GTBB_Recessed, TRUE,
+ TAG_DONE );
+
+ RenderWindowTexts (wud, SampleIText, Sample_TNUM);
+*/
+ UpdateSampGraph();
+ UpdateSampInfo();
+}
+
+
+
+GLOBALCALL LONG OpenSampleWindow (void)
+{
+ struct Window *win;
+ struct SongInfo *si;
+
+ DisplayStart = RangeStartX = RangeEndX = 0;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ DisplayEnd = si->Instr[si->CurrentInst]->Length - 1;
+ ReleaseSemaphore (&si->Lock);
+ }
+ else
+ DisplayEnd = 0;
+
+ if (DisplayEnd < 0) DisplayEnd = 0;
+ LoopPole1 = LoopPole2 = 0;
+
+ win = MyOpenWindow (&SampleWUD);
+
+ UpdateSampleMenu();
+
+ return (!win);
+}
+
+
+
+GLOBALCALL void CloseSampleWindow (void)
+{
+ MyCloseWindow (SampleWUD.Win);
+}
+
+
+
+static LONG SampBoxSetup (struct Gadget *g)
+{
+ /**/
+ SampBox.Left = OffX; // + ComputeX (&SampleWUD, 4) + 2;
+ SampBox.Top = OffY; // + ComputeY (&SampleWUD, 53) + 1;
+ SampBox.Width = 500;//ComputeX (&SampleWUD, 622) - 4;
+ SampBox.Height = 300;//ComputeY (&SampleWUD, 100) - 2;
+
+ g->LeftEdge = SampBox.Left;
+ g->TopEdge = SampBox.Top;
+ g->Width = SampBox.Width;
+ g->Height = SampBox.Height;
+
+ g->Flags |= GFLG_GADGHNONE;
+ g->Activation |= GACT_IMMEDIATE | GACT_FOLLOWMOUSE | GACT_RELVERIFY;
+ g->GadgetType |= GTYP_BOOLGADGET; /* Preserve GadTools special flags */
+
+ return RETURN_OK;
+}
+
+
+
+static void HandleSampleIDCMP (void)
+{
+ WORD mousex; //, mousey;
+
+ if (IntuiMsg.Class != IDCMP_MOUSEMOVE)
+ return;
+
+ mousex = IntuiMsg.MouseX - SampBox.Left;
+// mousey = IntuiMsg.MouseY - SampBox.Top;
+
+ /* Clip mouse position */
+ if (mousex < 0) mousex = 0;
+// if (mousey < 0) mousey = 0;
+ if (mousex >= SampBox.Width) mousex = SampBox.Width - 1;
+// if (mousey >= SampBox.Height) mousey = SampBox.Height - 1;
+
+ if (mousex == RangeEndX) /* Do not redraw until mouse position has changed */
+ return;
+
+ UpdateRange (mousex);
+}
+
+
+/********************/
+/* Sample Functions */
+/********************/
+
+static void DrawPixelGraph (struct RastPort *rp, BYTE *samp,
+ UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step)
+{
+ ULONG i;
+ UWORD x;
+
+ for (i = 0, x = xmin ; x < xmax ; i += step, x++)
+ WritePixel (rp, x, (samp[i]*height) >> 8 + ycoord);
+}
+
+
+
+static void DrawLineGraph (struct RastPort *rp, BYTE *samp,
+ UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step)
+{
+ ULONG i;
+ UWORD x;
+
+ Move (rp, xmin, ycoord);
+
+ for (i = 0, x = xmin ; x < xmax ; i += step, x++)
+ Draw (rp, x, (samp[i]*height)/256 + ycoord);
+}
+
+
+
+static void DrawFilledGraph (struct RastPort *rp, BYTE *samp,
+ UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step)
+{
+ UWORD x;
+ ULONG i;
+
+ for (i = 0, x = xmin ; x < xmax ; i += step, x++)
+ {
+ Move (rp, x, ycoord);
+ Draw (rp, x, (samp[i]*height)/256 + ycoord);
+ }
+}
+
+
+
+GLOBALCALL void UpdateSample (void)
+
+/* You call this function when the selected instrument has changed. */
+{
+ struct SongInfo *si;
+
+ if (SampleWUD.Win)
+ {
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ DisplayStart = RangeStartX = RangeEndX = DisplayEnd = 0;
+
+ if (si->CurrentInst)
+ DisplayEnd = si->Instr[si->CurrentInst]->Length - 1;
+
+ if (DisplayEnd < 0) DisplayEnd = 0;
+
+ UpdateSampGraph();
+ UpdateSampInfo();
+
+ ReleaseSemaphore (&si->Lock);
+ }
+ }
+}
+
+
+
+GLOBALCALL void UpdateSampleMenu (void)
+{
+ if (SampleWUD.Win)
+ {
+ struct Menu *menu = SampleWUD.Win->MenuStrip;
+ struct MenuItem *item = menu->NextMenu->FirstItem;
+
+ ClearMenuStrip (SampleWUD.Win);
+
+
+ /* Clear checkmarks */
+
+ item->Flags &= ~CHECKED;
+ item->NextItem->Flags &= ~CHECKED;
+ item->NextItem->NextItem->Flags &= ~CHECKED;
+
+
+ /* Set checkmark */
+
+ switch (GuiSwitches.SampDrawMode)
+ {
+ case SAMP_PIXEL:
+ item->Flags |= CHECKED;
+ break;
+
+ case SAMP_LINE:
+ item->NextItem->Flags |= CHECKED;
+ break;
+
+ case SAMP_FILLED:
+ item->NextItem->NextItem->Flags |= CHECKED;
+ break;
+ }
+
+ ResetMenuStrip (SampleWUD.Win, menu);
+ }
+}
+
+
+
+GLOBALCALL void UpdateSampInfo (void)
+{
+ struct SongInfo *si;
+
+ if (!SampleWUD.Win) return;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ struct Instrument *instr = si->Instr[si->CurrentInst];
+ LONG repend;
+
+ repend = instr->Repeat + instr->Replen - 1;
+ if (repend < 0) repend = 0;
+
+ SetGadgets (&SampleWUD,
+ GD_RepStart, instr->Repeat,
+ GD_RepEnd, repend,
+ GD_RepLen, instr->Replen,
+ GD_SampSize, instr->Length,
+ GD_DisplayStart, DisplayStart,
+ GD_DisplayEnd, DisplayEnd,
+ GD_DisplayLen, DisplayEnd ? (DisplayEnd - DisplayStart + 1) : 0,
+ -1);
+
+ /* Loop markers */
+ {
+ struct RastPort *rp = SampleWUD.Win->RPort;
+
+ SetDrMd (rp, COMPLEMENT);
+ SetDrPt (rp, 0xFF00);
+
+ /* Delete previous loop */
+ if (LoopPole1)
+ {
+ Move (rp, LoopPole1, SampBox.Top);
+ Draw (rp, LoopPole1, SampBox.Top + SampBox.Height);
+ LoopPole1 = 0;
+ }
+ if (LoopPole2)
+ {
+ Move (rp, LoopPole2, SampBox.Top);
+ Draw (rp, LoopPole2, SampBox.Top + SampBox.Height);
+ LoopPole2 = 0;
+ }
+
+ if (instr->Replen) /* Draw new loop */
+ {
+ if (DisplayStart <= instr->Repeat && instr->Repeat <= DisplayEnd)
+ {
+ LoopPole1 = SampBox.Left + ((SampBox.Width * (instr->Repeat - DisplayStart)) / (DisplayEnd - DisplayStart + 1));
+
+ Move (rp, LoopPole1, SampBox.Top);
+ Draw (rp, LoopPole1, SampBox.Top + SampBox.Height - 1);
+ }
+
+ if (DisplayStart <= repend && repend <= DisplayEnd+1)
+ {
+ LoopPole2 = SampBox.Left + ((SampBox.Width * (repend - DisplayStart)) / (DisplayEnd - DisplayStart + 1));
+
+ Move (rp, LoopPole2, SampBox.Top);
+ Draw (rp, LoopPole2, SampBox.Top + SampBox.Height - 1);
+ }
+ }
+
+ SetDrPt (rp, 0xFFFF);
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void UpdateRangeInfo (void)
+{
+ WORD rs = DisplayStart + RangeStartX,
+ re = DisplayStart + RangeEndX;
+
+ if (!SampleWUD.Win) return;
+
+ SetGadgets (&SampleWUD,
+ GD_RangeStart, rs,
+ GD_RangeEnd, re,
+ GD_RangeLen, abs(re - rs),
+ -1);
+}
+
+
+
+GLOBALCALL void UpdateSampGraph (void)
+{
+ struct SongInfo *si;
+ struct RastPort *rp;
+ BYTE *samp;
+ ULONG maxpen;
+ UWORD step, xmin, xmax, height, ycoord;
+
+ if (!SampleWUD.Win) return;
+
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ rp = SampleWUD.Win->RPort;
+ samp = si->Instr[si->CurrentInst]->Sample;
+
+ xmin = SampBox.Left;
+ xmax = xmin + SampBox.Width;
+ height = SampBox.Height;
+ ycoord = SampBox.Top + height/2;
+ step = si->Instr[si->CurrentInst]->Length / SampBox.Width;
+
+ /* This helps with samples smaller than the graphic x size */
+ if (step == 0) step = 1;
+
+ /* Clear instrument rectangle */
+
+ SetDrMd (rp, JAM1);
+ SetAPen (rp, DrawInfo->dri_Pens[BACKGROUNDPEN]);
+ RectFill (rp, SampBox.Left, SampBox.Top,
+ SampBox.Left + SampBox.Width - 1, SampBox.Top + SampBox.Height - 1);
+ LoopPole1 = LoopPole2 = 0;
+
+
+ if (GfxBase->lib_Version >= 39) /* Optimized drawing with V39 */
+ {
+ GetRPAttrs (rp,
+ RPTAG_MaxPen, &maxpen,
+ TAG_DONE);
+ SetMaxPen (rp, max(DrawInfo->dri_Pens[TEXTPEN], DrawInfo->dri_Pens[FILLPEN]));
+ }
+
+ /* Draw mid line */
+ SetAPen (rp, DrawInfo->dri_Pens[TEXTPEN]);
+ Move (rp, xmin, ycoord);
+ Draw (rp, xmax-1, ycoord);
+
+ /* Draw sample graphic */
+
+ if (samp)
+ {
+ switch (GuiSwitches.SampDrawMode)
+ {
+ case SAMP_PIXEL:
+ DrawPixelGraph (rp, samp, xmin, xmax, ycoord, height, step);
+ break;
+
+ case SAMP_LINE:
+ DrawLineGraph (rp, samp, xmin, xmax, ycoord, height, step);
+ break;
+
+ case SAMP_FILLED:
+ DrawFilledGraph (rp, samp, xmin, xmax, ycoord, height, step);
+ break;
+ }
+ }
+
+ DrawRange(); /* Redraw range */
+
+ /* Restore MaxPen if appropriate */
+ if (GfxBase->lib_Version >= 39) SetMaxPen (rp, maxpen);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void DrawRange (void)
+{
+ ULONG maxpen;
+ WORD xmin, xmax;
+ struct RastPort *rp = SampleWUD.Win->RPort;
+
+ if (RangeStartX > RangeEndX)
+ {
+ xmin = RangeEndX;
+ xmax = RangeStartX;
+ }
+ else
+ {
+ xmin = RangeStartX;
+ xmax = RangeEndX;
+ }
+
+ /* Optimized drawing for V39 */
+
+ if (GfxBase->lib_Version >= 39)
+ {
+ GetRPAttrs (rp,
+ RPTAG_MaxPen, &maxpen,
+ TAG_DONE);
+ SetMaxPen (rp, max(DrawInfo->dri_Pens[TEXTPEN], DrawInfo->dri_Pens[FILLPEN]));
+ }
+
+ SetDrMd (rp, COMPLEMENT);
+ RectFill (rp, SampBox.Left + xmin, SampBox.Top,
+ SampBox.Left + xmax, SampBox.Top + SampBox.Height - 1);
+
+ /* Restore MaxPen if appropriate */
+ if (GfxBase->lib_Version >= 39) SetMaxPen (rp, maxpen);
+}
+
+
+static void UpdateRange (WORD newend)
+
+/* Optimized range offset drawing */
+{
+ WORD pole = RangeStartX; /* The fixed end of the range */
+
+ RangeStartX = RangeEndX;
+ RangeEndX = newend;
+
+ if (RangeEndX < pole && RangeStartX <= pole) /* Range _left_ of pole */
+ {
+ if (RangeStartX > RangeEndX) RangeStartX--; /* Grow range */
+ else if (RangeStartX < RangeEndX) RangeEndX--; /* Reduce range */
+ DrawRange(); /* Draw/clear offset area */
+
+ }
+ else if (RangeEndX > pole && RangeStartX >= pole) /* Range _right_ of pole */
+ {
+ if (RangeStartX < RangeEndX) RangeStartX++; /* Grow range */
+ else if (RangeStartX > RangeEndX) RangeEndX++; /* Reduce range */
+ DrawRange(); /* Draw/clear offset area */
+ }
+ else /* Mouse has crossed the pole: it must be redrawn */
+ {
+ DrawRange();
+ RangeStartX = RangeEndX = pole;
+ DrawRange();
+ }
+
+ RangeStartX = pole;
+ RangeEndX = newend;
+ UpdateRangeInfo();
+}
+
+
+
+/******************/
+/* Sample Gadgets */
+/******************/
+
+static void SampScrollClicked (void)
+{
+}
+
+
+
+static void SampZoomInClicked (void)
+{
+}
+
+
+
+static void SampRangeAllClicked (void)
+{
+ DrawRange(); /* Delete previous range */
+ RangeStartX = 0;
+ RangeEndX = SampBox.Width;
+ DrawRange();
+ UpdateRangeInfo();
+}
+
+
+
+static void SampShowRangeClicked (void)
+{
+}
+
+
+
+static void SampZoomOutClicked (void)
+{
+}
+
+
+
+static void SampClearRangeClicked (void)
+{
+ if (RangeStartX == RangeEndX) return;
+
+ DrawRange();
+ RangeStartX = RangeEndX = 0;
+ DrawRange();
+ UpdateRangeInfo();
+}
+
+
+
+static void SampShowAllClicked (void)
+{
+ struct SongInfo *si;
+
+ DisplayStart = 0;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ DisplayEnd = si->Instr[si->CurrentInst]->Length - 1;
+ ReleaseSemaphore (&si->Lock);
+ }
+ else
+ DisplayEnd = 0;
+ UpdateSampGraph();
+ UpdateSampInfo();
+}
+
+
+
+static void SampPlayRClicked (void)
+{
+}
+
+static void SampPlayDClicked (void)
+{
+}
+
+static void SampPlayLClicked (void)
+{
+}
+
+static void SampPlayAClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ struct Instrument *instr = si->Instr[si->CurrentInst];
+
+ PlaySample (instr->Sample, instr->Length, instr->Volume, 0x1AC); /**/
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+static void DisplayStartClicked (void)
+{
+}
+
+static void DisplayEndClicked (void)
+{
+}
+
+static void DisplayLenClicked (void)
+{
+}
+
+static void RangeStartClicked (void)
+{
+}
+
+static void RangeEndClicked (void)
+{
+}
+
+static void RangeLenClicked (void)
+{
+}
+
+
+
+static void RepStartClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ struct Instrument *instr = si->Instr[si->CurrentInst];
+
+ instr->Repeat = GetNumber (SampleGadgets[GD_RepStart]);
+
+ if (instr->Repeat & 1) instr->Repeat--;
+
+ if (instr->Repeat >= instr->Length)
+ instr->Repeat = instr->Length - 2;
+
+ if (((LONG)instr->Repeat) < 0)
+ instr->Repeat = 0;
+
+ if (instr->Repeat + instr->Replen > instr->Length - 2)
+ instr->Replen = instr->Length - instr->Repeat;
+
+ if (((LONG)instr->Replen) < 0)
+ instr->Replen = 0;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSampInfo();
+}
+
+
+
+static void RepLenClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ struct Instrument *instr = si->Instr[si->CurrentInst];
+
+ instr->Replen = GetNumber (SampleGadgets[GD_RepLen]);
+
+ if (instr->Replen & 1) instr->Replen++;
+
+ if (instr->Replen + instr->Repeat >= instr->Length)
+ instr->Replen = instr->Length - instr->Repeat;
+
+ if (((LONG)instr->Replen) < 0)
+ instr->Replen = 0;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSampInfo();
+}
+
+
+
+static void RepEndClicked (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ struct Instrument *instr = si->Instr[si->CurrentInst];
+
+ instr->Replen = GetNumber (SampleGadgets[GD_RepEnd]) - instr->Repeat;
+
+ if (instr->Replen & 1) instr->Replen++;
+
+ if (instr->Replen + instr->Repeat >= instr->Length)
+ instr->Replen = instr->Length - instr->Repeat;
+
+ if (((LONG)instr->Replen) < 0) instr->Replen = 0;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSampInfo();
+}
+
+
+
+static void SampBoxClicked (void)
+{
+ if (IntuiMsg.Class == IDCMP_GADGETDOWN)
+ {
+ DrawRange(); /* Clear old range */
+ RangePole = RangeStartX = RangeEndX = IntuiMsg.MouseX - SampBox.Left;
+ DrawRange(); /* Draw pole */
+ UpdateRangeInfo();
+
+ //SetPointer (SampleWUD.Win, BlockPointer, 16, 16, -8, -7);
+ }
+ else if (IntuiMsg.Class == IDCMP_GADGETUP)
+ ; //ClearPointer (SampleWUD.Win);
+}
+
+
+/****************/
+/* Sample Menus */
+/****************/
+
+static void SampleMiCut (void)
+{
+ /* routine when (sub)item "Cut" is selected. */
+}
+
+static void SampleMiCopy (void)
+{
+ /* routine when (sub)item "Copy" is selected. */
+}
+
+static void SampleMiPaste (void)
+{
+ /* routine when (sub)item "Paste" is selected. */
+}
+
+static void SampleMiPoints (void)
+{
+ GuiSwitches.SampDrawMode = SAMP_PIXEL;
+ UpdateSampGraph();
+}
+
+static void SampleMiLines (void)
+{
+ GuiSwitches.SampDrawMode = SAMP_LINE;
+ UpdateSampGraph();
+}
+
+static void SampleMiFilled (void)
+{
+ GuiSwitches.SampDrawMode = SAMP_FILLED;
+ UpdateSampGraph();
+}
--- /dev/null
+/*
+** SaversWin.c
+**
+** Copyright (C) 1994,95,96 Bernardo Innocenti
+**
+** Savers panel handling functions.
+*/
+
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/*!*/
+/* Replaced by format properties stored inside hooks
+UBYTE FormatProperties[] =
+{
+ XMSF_NO_INSTRUMENTS|XMSF_NO_NAMES, // MED
+ XMSF_NO_INSTRUMENTS|XMSF_NO_NAMES, // OctaMED
+ ALLOWS_NO_INST|ALLOWS_NO_NAMES // MIDI File
+};
+
+ "MED",
+ "OctaMED",
+ "ScreamTracker",
+ "MIDI File",
+*/
+
+
+
+
+/* Gadgets IDs */
+enum {
+ GD_SaversGroup0,
+ GD_SaversGroup1,
+ GD_SaversList,
+// GD_SaversGroup2,
+ GD_SaversGroup3,
+ GD_SaveSequence,
+ GD_SaveInstruments,
+ GD_SavePatterns,
+ GD_SaveNames,
+ GD_SaveIcons,
+// GD_SaversGroup4,
+// GD_PackMode,
+// GD_PackOptions,
+ GD_SaversGroup5,
+ GD_FormatDescription,
+ GD_FormatAuthor,
+ GD_SaversGroup6,
+ GD_SaversGroup7,
+ GD_MaxLength,
+ GD_MaxTracks,
+ GD_MaxInstruments,
+ GD_SaversGroup8,
+ GD_MaxPatterns,
+ GD_MaxPattLen,
+ GD_MaxSampleLen,
+ GD_SaversGroup9,
+ GD_FormatUse,
+ GD_FormatCancel,
+ Savers_CNT
+};
+
+
+
+
+/*****************************/
+/* Local function prototypes */
+/*****************************/
+
+static void SaversPostOpen (void);
+static void UpdateSaverInfo (void);
+
+static void SaversListClicked (struct WinUserData *wud);
+/* static void PackModeClicked (struct WinUserData *wud);
+static void PackOptionsClicked (struct WinUserData *wud); */
+static void FormatUseClicked (struct WinUserData *wud);
+static void FormatCancelClicked (struct WinUserData *wud);
+
+
+
+XDEF struct SaveSwitches SaveSwitches = {1, 1, 1, 0, 1};
+XDEF struct SaveSwitches SaveSwitchesBackup;
+
+
+
+/*
+static STRPTR PackModeLabels[] =
+{
+ (STRPTR)MSG_NONE_GAD,
+ (STRPTR)MSG_XPK_GAD,
+ (STRPTR)MSG_LHA_GAD,
+ (STRPTR)0
+};
+*/
+
+
+static ULONG SaversArgs[] =
+{
+ HGROUP_KIND, 0,
+ LISTVIEW_KIND, (ULONG)SaversListClicked, 0, NULL, TAG_DONE,
+// VGROUP_KIND, 0,
+ VGROUP_KIND, BBFT_RIDGE,
+ CHECKBOX_KIND, NULL, MSG_SF_SEQUENCE_GAD, (ULONG)&SaveSwitches.SaveSeq, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_SF_INSTRUMENTS_GAD, (ULONG)&SaveSwitches.SaveInstr, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_SF_PATTERNS_GAD, (ULONG)&SaveSwitches.SavePatt, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_SF_NAMES_GAD, (ULONG)&SaveSwitches.SaveNames, TAG_DONE,
+ CHECKBOX_KIND, NULL, MSG_ADD_ICON_GAD, (ULONG)&SaveSwitches.SaveIcons, TAG_DONE,
+ ENDGROUP_KIND,
+// VGROUP_KIND, BBFT_RIDGE,
+// CYCLE_KIND, (ULONG)PackModeClicked, MSG_MODE_GAD, (ULONG)PackModeLabels, TAG_DONE,
+// BUTTON_KIND, (ULONG)PackOptionsClicked, MSG_OPTIONS_DOTS_GAD, TAG_DONE,
+// ENDGROUP_KIND,
+// ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ VGROUP_KIND, BBFT_RIDGE,
+ TEXT_KIND, MSG_DESCRIPTION, 25, GTTX_Border, TRUE, TAG_DONE,
+ TEXT_KIND, MSG_AUTHOR, 15, GTTX_Border, TRUE, TAG_DONE,
+ HGROUP_KIND, 0,
+ VGROUP_KIND, 0,
+ NUMBER_KIND, MSG_MAXLENGTH, 3, TAG_DONE,
+ NUMBER_KIND, MSG_MAXTRACKS, 3, TAG_DONE,
+ NUMBER_KIND, MSG_MAXINSTRUMENTS, 3, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ NUMBER_KIND, MSG_MAXPATTERNS, 5, TAG_DONE,
+ NUMBER_KIND, MSG_MAXPATTLEN, 5, TAG_DONE,
+ NUMBER_KIND, MSG_MAXSAMPLELEN, 9, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ HGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)FormatUseClicked, MSG_UNDERSCORE_USE_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)FormatCancelClicked, MSG_UNDERSCORE_CANCEL_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG SaversWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)SaversArgs,
+ XMWIN_GCount, Savers_CNT,
+ XMWIN_Title, MSG_SAVERS_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, CHECKBOXIDCMP|LISTVIEWIDCMP|BUTTONIDCMP|CYCLEIDCMP|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
+ XMWIN_PostOpenFunc, (LONG)SaversPostOpen,
+ XMWIN_HelpNode, (LONG)"Savers",
+ TAG_DONE
+};
+
+
+
+static void SaversPostOpen (void)
+{
+ UpdateSaveSwitches();
+
+ /* Backup SaveSwitches for "Cancel" option */
+ memcpy (&SaveSwitchesBackup, &SaveSwitches, sizeof (SaveSwitchesBackup));
+}
+
+
+
+GLOBALCALL void UpdateSaveSwitches (void)
+{
+ struct WinUserData *wud = WDescr[WID_SAVERS].Wud;
+ struct XMHook *saver;
+
+ if (wud && wud->Win)
+ {
+ LONG savernum;
+
+#ifdef OS30_ONLY
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#else
+ /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
+
+ /* Try to get the shared semaphore */
+ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
+ /* Check if we can get the exclusive version */
+ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
+ /* Oh well, wait for the shared lock */
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#endif
+
+ if (saver = XModuleBase->xm_DefaultSaver)
+ {
+ struct XMHook *tmp = (struct XMHook *)XModuleBase->xm_Savers.mlh_Head;
+ savernum = 0;
+
+ while (tmp != saver)
+ {
+ tmp = (struct XMHook *)tmp->xmh_Link.ln_Succ;
+ savernum++;
+ }
+ }
+ else savernum = ~0;
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SaversList], wud->Win, NULL,
+ GTLV_Labels, &XModuleBase->xm_Savers,
+ GTLV_Selected, savernum,
+ GTLV_MakeVisible, savernum,
+ TAG_DONE);
+
+ UpdateSaverInfo ();
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ }
+}
+
+
+
+static void UpdateSaverInfo (void)
+{
+ struct XMHook *saver = XModuleBase->xm_DefaultSaver;
+ struct WinUserData *wud = WDescr[WID_SAVERS].Wud;
+
+ if (wud && wud->Win)
+ {
+ SetGadgets (wud,
+ GD_SaveSequence, SaveSwitches.SaveSeq,
+ GD_SaveInstruments, SaveSwitches.SaveInstr,
+ GD_SavePatterns, SaveSwitches.SavePatt,
+ GD_SaveIcons, SaveSwitches.SaveIcons,
+ GD_SaveNames, SaveSwitches.SaveNames,
+ GD_FormatDescription, saver ? saver->xmh_Descr : NULL,
+ GD_FormatAuthor, saver ? saver->xmh_Author : NULL,
+ GD_MaxLength, saver ? saver->xmh_MaxLength : 0,
+ GD_MaxTracks, saver ? saver->xmh_MaxTracks : 0,
+ GD_MaxInstruments, saver ? saver->xmh_MaxInstruments : 0,
+ GD_MaxPatterns, saver ? saver->xmh_MaxPatterns : 0,
+ GD_MaxPattLen, saver ? saver->xmh_MaxPattLen : 0,
+ GD_MaxSampleLen, saver ? saver->xmh_MaxSampleLen : 0,
+ -1);
+
+ if (saver)
+ {
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SaveSequence], wud->Win, NULL,
+ GA_Disabled, !(saver->xmh_Flags & XMHF_EXCLUDE_SEQUENCE),
+ TAG_DONE);
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SavePatterns], wud->Win, NULL,
+ GA_Disabled, !(saver->xmh_Flags & XMHF_EXCLUDE_PATTERNS),
+ TAG_DONE);
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SaveInstruments], wud->Win, NULL,
+ GA_Disabled, !(saver->xmh_Flags & XMHF_EXCLUDE_INSTRUMENTS),
+ TAG_DONE);
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SaveNames], wud->Win, NULL,
+ GA_Disabled, !(saver->xmh_Flags & XMHF_EXCLUDE_NAMES),
+ TAG_DONE);
+ }
+ }
+}
+
+
+
+/******************/
+/* Savers Gadgets */
+/******************/
+
+static void SaversListClicked (struct WinUserData *wud)
+{
+ WORD i;
+ struct XMHook *tmp;
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+
+ tmp = (struct XMHook *)XModuleBase->xm_Savers.mlh_Head;
+
+ for (i = IntuiMsg.Code; (i > 0) && tmp ; i--)
+ tmp = (struct XMHook *)tmp->xmh_Link.ln_Succ;
+
+ if (tmp) XModuleBase->xm_DefaultSaver = tmp;
+
+ UpdateSaverInfo();
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+}
+
+
+
+/*
+static void PackModeClicked (struct WinUserData *wud)
+{
+
+}
+
+
+
+static void PackOptionsClicked (struct WinUserData *wud)
+{
+
+}
+*/
+
+
+
+static void FormatUseClicked (struct WinUserData *wud)
+{
+ MyCloseWindow (wud);
+}
+
+
+
+static void FormatCancelClicked (struct WinUserData *wud)
+{
+ /* Restore old SaveSwitches */
+ memcpy (&SaveSwitches, &SaveSwitchesBackup, sizeof (SaveSwitchesBackup));
+
+ MyCloseWindow (wud);
+}
--- /dev/null
+/*
+** SequenceWin.c
+**
+** Copyright (C) 1993,94,95,96 Bernardo Innocenti
+**
+** Sequence editor handling functions.
+*/
+
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/xmodule.h>
+
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Gadget IDs */
+
+enum
+{
+ GD_SequenceGroup0,
+ GD_SequenceGroup1,
+ GD_SequenceGroup2,
+ GD_PattList,
+ GD_PattName,
+ GD_SequenceGroup3,
+ GD_PattAdd,
+ GD_PattDel,
+ GD_PattUp,
+ GD_PattDown,
+ GD_SequenceGroup4,
+ GD_SeqList,
+ GD_SequenceGroup5,
+ GD_SeqInsert,
+ GD_SeqDel,
+ GD_SeqUp,
+ GD_SeqDown,
+
+ Sequence_CNT
+};
+
+
+
+/* Local function prototypes */
+
+static void SequencePostClose (void);
+
+static void SeqListClicked (struct WinUserData *wud);
+static void PattAddClicked (struct WinUserData *wud);
+static void PattDelClicked (struct WinUserData *wud);
+static void PattUpClicked (struct WinUserData *wud);
+static void PattDownClicked (struct WinUserData *wud);
+static void PattNameClicked (struct WinUserData *wud);
+static void PattListClicked (struct WinUserData *wud);
+static void SeqDelClicked (struct WinUserData *wud);
+static void SeqUpClicked (struct WinUserData *wud);
+static void SeqDownClicked (struct WinUserData *wud);
+static void SeqInsertClicked (struct WinUserData *wud);
+
+
+struct Gadget *SequenceGadgets[Sequence_CNT];
+struct List SequenceList;
+struct List PatternsList;
+static ULONG SequenceSecs = 0, SequenceMicros = 0;
+static ULONG PatternSecs = 0, PatternMicros = 0;
+
+
+
+static ULONG SequenceArgs[] =
+{
+ HGROUP_KIND, BBFT_RIDGE,
+ VGROUP_KIND, 0,
+ LISTVIEW_KIND, (ULONG)PattListClicked, MSG_PATTERNS_GAD, (ULONG)&PatternsList, TAG_DONE,
+ STRING_KIND, (ULONG)PattNameClicked, MSG_UNDERSCORE_NAME_GAD, 128, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)PattAddClicked, MSG_UNDERSCORE_ADD_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)PattDelClicked, MSG_DEL_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)PattUpClicked, MSG_UP_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)PattDownClicked, MSG_DOWN_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ HGROUP_KIND, BBFT_RIDGE,
+ LISTVIEW_KIND, (ULONG)SeqListClicked, MSG_SEQUENCE_GAD, (ULONG)&SequenceList, TAG_DONE,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)SeqInsertClicked, MSG_UNDERSCORE_INS_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)SeqDelClicked, MSG_DEL_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)SeqUpClicked, MSG_UNDERSCORE_UP_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)SeqDownClicked, MSG_UNDERSCORE_DOWN_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG SequenceWinTags[] =
+{
+ XMWIN_LayoutArgs, (LONG)SequenceArgs,
+ XMWIN_GCount, Sequence_CNT,
+ XMWIN_Title, MSG_SEQUENCE_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, BUTTONIDCMP|LISTVIEWIDCMP|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
+ XMWIN_PostOpenFunc, (LONG)UpdatePatternList,
+ XMWIN_PostCloseFunc,(LONG)SequencePostClose,
+ XMWIN_HelpNode, (LONG)"Sequence",
+ TAG_DONE
+};
+
+
+
+static void SequencePostClose (void)
+{
+ while (!IsListEmpty (&SequenceList))
+ RemListViewNode (SequenceList.lh_Head);
+
+ while (!IsListEmpty (&PatternsList))
+ RemListViewNode (PatternsList.lh_Head);
+}
+
+
+
+/**********************/
+/* Sequence Functions */
+/**********************/
+
+GLOBALCALL void UpdateSequenceList (void)
+{
+ struct WinUserData *wud = WDescr[WID_SEQUENCE].Wud;
+ struct SongInfo *si;
+ ULONG i;
+ UWORD pos;
+
+ if (wud && wud->Win)
+ {
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SeqList], wud->Win, NULL,
+ GTLV_Labels, ~0,
+ TAG_DONE);
+
+ while (!IsListEmpty (&SequenceList))
+ RemListViewNode (SequenceList.lh_Head);
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ for (i = 0 ; i < si->Length; i++)
+ {
+ pos = si->Sequence[i];
+ AddListViewNode (&SequenceList, "%03lu - %03ld %s", i, pos,
+ (pos >= si->NumPatterns) ? (STRPTR)"--none--" : si->Patt[pos]->Name);
+ }
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SeqList], wud->Win, NULL,
+ GTLV_Labels, &SequenceList,
+ GTLV_Selected, si->CurrentPos,
+ GTLV_MakeVisible, si->CurrentPos,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+ }
+}
+
+
+
+GLOBALCALL void UpdatePatternList (void)
+{
+ struct WinUserData *wud = WDescr[WID_SEQUENCE].Wud;
+ struct SongInfo *si;
+ struct Pattern *patt;
+ ULONG i;
+
+ if (wud && wud->Win)
+ {
+ GT_SetGadgetAttrs (wud->Gadgets[GD_PattList], wud->Win, NULL,
+ GTLV_Labels, ~0,
+ TAG_DONE);
+
+ while (!IsListEmpty (&PatternsList))
+ RemListViewNode (PatternsList.lh_Head);
+
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ for (i = 0 ; i < si->NumPatterns; i++)
+ {
+ if (patt = si->Patt[i])
+ {
+ AddListViewNode (&PatternsList, "%03lu (%lu,%lu) %s",
+ i, patt->Tracks, patt->Lines,
+ patt->Name ? patt->Name : (STRPTR) STR(MSG_UNNAMED));
+ }
+ else AddListViewNode (&PatternsList, STR(MSG_EMPTY));
+ }
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_PattName], wud->Win, NULL,
+ GTST_String, si->NumPatterns ? si->Patt[si->CurrentPatt]->Name : NULL,
+ TAG_DONE);
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_PattList], wud->Win, NULL,
+ GTLV_Labels, &PatternsList,
+ GTLV_Selected, si->CurrentPatt,
+ GTLV_MakeVisible, si->CurrentPatt,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSequenceList();
+ }
+
+ UpdatePattern();
+}
+
+
+
+/********************/
+/* Sequence Gadgets */
+/********************/
+
+static void SeqListClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (IntuiMsg.Code == si->CurrentPos)
+ {
+ /* Check Double Click */
+ if (DoubleClick (SequenceSecs, SequenceMicros, IntuiMsg.Seconds, IntuiMsg.Micros))
+ {
+ si->Sequence[si->CurrentPos] = si->CurrentPatt;
+ UpdateSequenceList();
+ }
+ }
+
+ si->CurrentPos = IntuiMsg.Code;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ SequenceSecs = IntuiMsg.Seconds;
+ SequenceMicros = IntuiMsg.Micros;
+}
+
+
+
+static void PattAddClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (si->NumPatterns >= MAXPATTERNS)
+ DisplayBeep (Scr);
+ else
+ {
+ xmAddPatternA (si, NULL);
+ UpdateSongInfo(); /* this also calls UpdatePatternList() */
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PattDelClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (si->NumPatterns <= 1)
+ DisplayBeep (Scr);
+ else
+ xmRemPattern (si, si->CurrentPatt, 0);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateSongInfo();
+}
+
+
+
+static void PattUpClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (si->CurrentPatt == 0)
+ DisplayBeep (Scr);
+ else
+ {
+ struct Pattern *tmp_patt;
+
+ /* Swap pattern with its predecessor */
+ tmp_patt = si->Patt[si->CurrentPatt];
+ si->Patt[si->CurrentPatt] = si->Patt[si->CurrentPatt - 1];
+ si->Patt[si->CurrentPatt - 1] = tmp_patt;
+
+ SetAttrs (si,
+ SNGA_CurrentPatt, si->CurrentPatt - 1,
+ TAG_DONE);
+
+ UpdatePatternList();
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PattDownClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (si->CurrentPatt >= si->NumPatterns - 1)
+ DisplayBeep (Scr);
+ else
+ {
+ struct Pattern *tmp_patt;
+
+ /* Swap pattern with its successor */
+ tmp_patt = si->Patt[si->CurrentPatt];
+ si->Patt[si->CurrentPatt] = si->Patt[si->CurrentPatt + 1];
+ si->Patt[si->CurrentPatt + 1] = tmp_patt;
+
+ SetAttrs (si,
+ SNGA_CurrentPatt, si->CurrentPatt + 1,
+ TAG_DONE);
+
+ UpdatePatternList();
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PattNameClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ xmSetPattern (si, si->CurrentPatt,
+ PATTA_Name, GetString (wud->Gadgets[GD_PattName]),
+ TAG_DONE);
+
+ UpdatePatternList();
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void PattListClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ if (IntuiMsg.Code == si->CurrentPatt)
+ {
+ /* Check Double Click */
+ if (DoubleClick (PatternSecs, PatternMicros, IntuiMsg.Seconds, IntuiMsg.Micros))
+ NewWindow(WID_PATTERN);
+ }
+ else
+ {
+ si->CurrentPatt = IntuiMsg.Code;
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_PattName], wud->Win, NULL,
+ GTST_String, si->Patt[si->CurrentPatt]->Name,
+ TAG_DONE);
+
+ UpdatePattern();
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ PatternSecs = IntuiMsg.Seconds;
+ PatternMicros = IntuiMsg.Micros;
+}
+
+
+
+static void SeqDelClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ ULONG i;
+
+ /* You can't have an empty sequence */
+ if (si->Length == 1)
+ DisplayBeep (Scr);
+ else
+ {
+ /* Shift positions back */
+ for (i = si->CurrentPos ; i < si->Length; i++)
+ si->Sequence[i] = si->Sequence[i+1];
+
+ xmSetSongLen (si, si->Length - 1);
+
+ UpdateSongInfo(); /* Will call also UpdateSequenceList() */
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void SeqUpClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ UWORD temp;
+
+ if (si->CurrentPos < 1)
+ DisplayBeep (Scr);
+ else
+ {
+ temp = si->Sequence[si->CurrentPos];
+ si->Sequence[si->CurrentPos] = si->Sequence[si->CurrentPos - 1];
+ si->Sequence[si->CurrentPos - 1] = temp;
+
+ si->CurrentPos--;
+ UpdateSequenceList();
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void SeqDownClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ UWORD temp;
+
+ if (si->CurrentPos >= si->Length - 1)
+ DisplayBeep (Scr);
+ else
+ {
+ temp = si->Sequence[si->CurrentPos];
+ si->Sequence[si->CurrentPos] = si->Sequence[si->CurrentPos + 1];
+ si->Sequence[si->CurrentPos + 1] = temp;
+
+ si->CurrentPos++;
+ UpdateSequenceList();
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void SeqInsertClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ if (!(xmSetSongLen (si, si->Length + 1)))
+ DisplayBeep (Scr);
+ else
+ {
+ ULONG i;
+
+ for (i = si->Length - 1; i > si->CurrentPos; i--)
+ si->Sequence[i] = si->Sequence[i-1];
+
+ si->Sequence[si->CurrentPos] = si->CurrentPatt;
+ }
+
+ ReleaseSemaphore (&si->Lock);
+
+ UpdateSongInfo(); /* This will also update the sequence list */
+ }
+}
--- /dev/null
+/*
+** SongClass.c
+**
+** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+**
+** Song class dispatcher and handling functions.
+*/
+
+/****** songclass/--background-- *******************************************
+*
+* NAME
+* songclass -- XModule 'boopsi'-oriented song implementation.
+*
+* DESCRIPTION
+* The song class is an object oriented way to handle a song. The song
+* class handles all data storing mechanisms for you and adds a layer
+* of abstraction between the song internal data structures and the
+* application. The advantage is that the internal structures can be
+* changed while keeping compatibility with existing software.
+*
+* Another great advantage of being a 'boopsi' class is that the song
+* can notify other boopsi objects whenever its attributes change.
+* This simplifies the task of keeping the user interface updated each
+* time the user (or an ARexx macro, or whatever) changes something.
+*
+* For speed reasons, the song class does also allow 'white box'
+* istance access. This means that you can also directly access
+* the internal data structures of the song, without using standard
+* boopsi methods. You are ONLY allowed to READ public fields, but not
+* to write any of them. The main reason to forbid direct writing is
+* that the song class must send notifications to its targets, but it
+* does also allow the song implementation to change in future version
+* without breaking existing applications.
+*
+* When you create a new istance of the song class, the object handle
+* you get is actually a SongInfo structure. This is only possible
+* because the song class is a subclass of the rootclass, whose istance
+* is placed at a negative offset in the object handle.
+* Future song class implementations could require to be subclasses
+* of other classes, such as the gadget class or even the datatypes
+* class. This problem will be probably got around by keeping the
+* root class as the real superclass of the song and creating an
+* istance of the other superclass which will be passed all the
+* methods which are not recognized by the song its-self. Call this
+* boopsi polymorphism, if you like to :-)
+*
+* QUOTATION
+* Don't be a tuna head.
+*
+****************************************************************************
+*/
+
+
+#include <exec/memory.h>
+#include <intuition/classes.h>
+#include <intuition/classusr.h>
+#include <libraries/xmodule.h>
+
+#include <clib/alib_protos.h>
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+#include <proto/icon.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Local function prototypes */
+
+static ULONG HOOKCALL SongDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct SongInfo *si),
+ REG(a1, Msg msg));
+
+INLINE ULONG SongNewMethod (Class *cl, struct SongInfo *si, struct opSet *opset);
+INLINE ULONG SongDisposeMethod (Class *cl, struct SongInfo *si, Msg msg);
+INLINE ULONG SongSetMethod (Class *cl, struct SongInfo *si, struct opSet *opset);
+
+static BOOL GuessAuthor (struct SongInfo *si);
+static UWORD *SetSongLen (struct SongInfo *si, ULONG len);
+static struct Pattern *AddPattern (struct SongInfo *si, struct TagItem *tags);
+static LONG SetPattern (struct SongInfo *si, ULONG patnum, struct TagItem *tags);
+static void RemPattern (struct SongInfo *si, ULONG patnum, ULONG newpatt);
+static struct Instrument *AddInstrument (struct SongInfo *si, ULONG num, struct TagItem *tags);
+static LONG SetInstrument (struct SongInfo *si, ULONG num, struct TagItem *tags);
+static void RemInstrument (struct SongInfo *si, ULONG num);
+static void SwapInstruments (struct SongInfo *si, LONG n, LONG m);
+
+
+
+
+GLOBALCALL Class *InitSongClass (void)
+{
+ Class *class;
+
+ if (class = MakeClass (NULL, ROOTCLASS, NULL, sizeof (struct SongInfo), 0))
+ class->cl_Dispatcher.h_Entry = (ULONG (*)()) SongDispatcher;
+
+ return class;
+}
+
+
+
+GLOBALCALL void FreeSongClass (Class *cl)
+{
+ while (!(FreeClass (cl)))
+ ShowRequest (MSG_CLOSE_ALL_SONGS, MSG_CONTINUE, NULL);
+}
+
+
+
+static ULONG HOOKCALL SongDispatcher (
+ REG(a0, Class *cl),
+ REG(a2, struct SongInfo *si),
+ REG(a1, Msg msg))
+{
+ ULONG result = 0;
+
+ /* Warning: the song class can only be a subclass of the rootclass because
+ * its istance must be at offset 0 in the object handle.
+ */
+
+ switch (msg->MethodID)
+ {
+ case OM_NEW:
+ result = SongNewMethod (cl, si, (struct opSet *)msg);
+ break;
+
+ case OM_DISPOSE:
+ result = SongDisposeMethod (cl, si, msg);
+ break;
+
+ case OM_SET:
+ case OM_UPDATE:
+ result = SongSetMethod (cl, si, (struct opSet *)msg);
+ break;
+
+ case OM_GET:
+ break;
+
+ case OM_NOTIFY:
+ break;
+
+ case SNGM_ADDPATTERN:
+ result = (ULONG) AddPattern (si, ((struct opSet *)msg)->ops_AttrList);
+ break;
+
+ case SNGM_SETPATTERN:
+ result = (ULONG) SetPattern (si, ((struct spSetPattern *)msg)->spsp_PattNum,
+ ((struct spSetPattern *)msg)->spsp_AttrList);
+
+ case SNGM_REMPATTERN:
+ RemPattern (si,
+ ((struct spRemPattern *)msg)->sprp_PattNum,
+ ((struct spRemPattern *)msg)->sprp_NewPatt);
+ break;
+
+ case SNGM_ADDINSTRUMENT:
+ result = (ULONG) AddInstrument (si,
+ ((struct spAddInstrument *)msg)->spsi_InstrNum,
+ ((struct spAddInstrument *)msg)->spsi_AttrList);
+ break;
+
+ case SNGM_SETINSTRUMENT:
+ result = (ULONG) SetInstrument (si,
+ ((struct spSetInstrument *)msg)->spsi_InstrNum,
+ ((struct spSetInstrument *)msg)->spsi_AttrList);
+ break;
+
+ case SNGM_REMINSTRUMENT:
+ RemInstrument (si,
+ ((struct spRemInstrument *)msg)->spri_Num);
+ break;
+
+ case SNGM_SWAPINSTRUMENTS:
+ SwapInstruments (si,
+ ((struct spSwapInstruments *)msg)->spsi_Num1,
+ ((struct spSwapInstruments *)msg)->spsi_Num2);
+ break;
+
+ default:
+
+ /* Unsupported method: let our superclass take a look at it. */
+ result = DoSuperMethodA (cl, (Object *)si, msg);
+ break;
+ }
+
+ return result;
+}
+
+
+
+INLINE ULONG SongNewMethod (Class *cl, struct SongInfo *si, struct opSet *opset)
+
+/* OM_NEW */
+{
+ if (si = (struct SongInfo *)DoSuperMethodA (cl, (Object *)si, (Msg)opset))
+ {
+ ULONG dummy;
+
+ /* Clear object instance */
+ memset (si, 0, sizeof (struct SongInfo));
+
+ /* Initialize song instance to defaults values */
+ si->Pool = XModuleBase->xm_Pool;
+ si->DefNumTracks = DEF_NUMTRACKS;
+ si->DefPattLen = DEF_PATTLEN;
+ si->GlobalSpeed = DEF_SONGSPEED;
+ si->GlobalTempo = DEF_SONGTEMPO;
+ si->Instr = si->InstrumentsTable;
+ DupStringPooled (si->Pool, STR(MSG_AUTHOR_UNKNOWN), &si->Author);
+ DupStringPooled (si->Pool, STR(MSG_SONG_UNTITLED), &si->Title);
+ si->Link.ln_Name = si->Title;
+ CurrentTime (&si->CreationDate, &dummy);
+ InitSemaphore (&si->Lock);
+
+
+ if (opset->ops_AttrList)
+ {
+ /* Set generic attributes */
+
+ SongSetMethod (cl, si, opset);
+ si->Changes = 0;
+
+
+ /* Set initialization-only attributes */
+
+ if (GetTagData (SNGA_ReadyToUse, FALSE, opset->ops_AttrList))
+ {
+ if (!DoMethod ((Object *)si, SNGM_ADDPATTERN, NULL))
+ {
+ DisposeObject (si);
+ return NULL;
+ }
+
+ /* Add one position to the new song */
+ SetAttrs (si, SNGA_Length, 1, TAG_DONE);
+
+ if (!si->Sequence)
+ {
+ DisposeObject (si);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return (ULONG)si;
+}
+
+
+
+INLINE ULONG SongDisposeMethod (Class *cl, struct SongInfo *si, Msg msg)
+
+/* OM_DISPOSE */
+{
+ LONG i;
+
+ /* All this stuff could be removed as all allocations are made
+ * inside a memory pool.
+ */
+
+ /* Remove song sequence */
+ SetAttrs (si, SNGA_Length, 0, TAG_DONE);
+
+ /* Free patterns */
+ for (i = si->NumPatterns - 1; i >= 0 ; i--)
+ DoMethod ((Object *)si, SNGM_REMPATTERN, i, 0);
+
+ /* Free instruments */
+ for (i = 1 ; i <= si->LastInstrument ; i++)
+ DoMethod ((Object *)si, SNGM_REMINSTRUMENT, i);
+
+ FreeVecPooled (si->Pool, si->Title);
+ FreeVecPooled (si->Pool, si->Author);
+ FreeVecPooled (si->Pool, si->Description);
+ FreeVecPooled (si->Pool, si->Path);
+
+ /* And let our superclass free the istance */
+ return DoSuperMethodA (cl, (Object *)si, msg);
+}
+
+
+
+INLINE ULONG SongSetMethod (Class *cl, struct SongInfo *si, struct opSet *opset)
+
+/* OM_SET */
+{
+ struct TagItem *ti, *tstate = opset->ops_AttrList;
+ BOOL do_supermethod = FALSE;
+ ULONG oldchanges = si->Changes;
+
+
+ while (ti = NextTagItem(&tstate))
+ {
+ switch (ti->ti_Tag)
+ {
+ case SNGA_CurrentPatt:
+ si->CurrentPatt = ti->ti_Data;
+ break;
+
+ case SNGA_CurrentPos:
+ si->CurrentPos = ti->ti_Data;
+ break;
+
+ case SNGA_CurrentInst:
+ si->CurrentInst = ti->ti_Data;
+ break;
+
+ case SNGA_CurrentLine:
+ si->CurrentLine = ti->ti_Data;
+ break;
+
+ case SNGA_CurrentTrack:
+ si->CurrentTrack = ti->ti_Data;
+ break;
+
+ case SNGA_Length:
+ SetSongLen (si, ti->ti_Data);
+ break;
+
+ case SNGA_Title:
+ if (DupStringPooled (si->Pool, (STRPTR)ti->ti_Data, &si->Title))
+ {
+ if (si->Title)
+ si->Link.ln_Name = si->Title;
+ else
+ si->Link.ln_Name = STR(MSG_UNNAMED);
+ si->Changes++;
+ }
+ break;
+
+ case SNGA_Author:
+ if (ti->ti_Data == -1)
+ {
+ if (GuessAuthor (si))
+ si->Changes++;
+ }
+ else if (DupStringPooled (si->Pool, (STRPTR)ti->ti_Data, &si->Author))
+ si->Changes++;
+ break;
+
+ case SNGA_Description:
+ if (DupStringPooled (si->Pool, (STRPTR)ti->ti_Data, &si->Description))
+ si->Changes++;
+ break;
+
+ case SNGA_Path:
+ if (DupStringPooled (si->Pool, (STRPTR)ti->ti_Data, &si->Path))
+ si->Changes++;
+ break;
+
+ case SNGA_Changes:
+ if (ti->ti_Data == -1)
+ si->Changes++;
+ else
+ si->Changes = ti->ti_Data;
+ break;
+
+ case SNGA_TotalChanges:
+ si->TotalChanges = ti->ti_Data;
+ break;
+
+ case SNGA_CreationDate:
+ si->CreationDate = ti->ti_Data;
+ break;
+
+ case SNGA_LastChanged:
+ si->LastChanged = ti->ti_Data;
+ break;
+
+ case SNGA_DefaultTracks:
+ si->DefNumTracks = ti->ti_Data;
+ break;
+
+ case SNGA_DefaultPattLen:
+ si->DefPattLen = ti->ti_Data;
+ break;
+
+ case SNGA_GlobalSpeed:
+ {
+ LONG speed = ti->ti_Data;
+
+ if (speed < 1) speed = 1;
+ if (speed > 31) speed = 31;
+
+ if (si->GlobalSpeed != speed)
+ {
+ si->GlobalSpeed = speed;
+ si->Changes++;
+ }
+ break;
+ }
+
+ case SNGA_GlobalTempo:
+ {
+ LONG tempo = ti->ti_Data;
+
+ if (tempo > 255) tempo = 255;
+ if (tempo < 32) tempo = 32;
+
+ if (si->GlobalTempo != tempo)
+ {
+ si->GlobalTempo = tempo;
+ si->Changes++;
+ }
+ break;
+ }
+
+ case SNGA_RestartPos:
+ {
+ LONG restart = ti->ti_Data;
+
+ if (restart < 0) restart = 0;
+ if (restart >= si->Length) restart = si->Length - 1;
+
+ if (si->RestartPos != restart)
+ {
+ si->RestartPos = restart;
+ si->Changes++;
+ }
+ break;
+ }
+
+ default:
+ do_supermethod = TRUE;
+ break;
+ }
+ } /* End while (NextTagItem()) */
+
+ if (do_supermethod)
+ return (DoSuperMethodA (cl, (Object *)si, (Msg)opset));
+ else
+ /* Returns the number of attributes which have changed */
+ return si->Changes - oldchanges;
+}
+
+
+
+static UWORD *SetSongLen (struct SongInfo *si, ULONG len)
+{
+ ULONG len_quantized;
+
+ if (len == si->Length)
+ return si->Sequence;
+
+ si->Changes++;
+
+ if (len == 0)
+ {
+ /* Deallocate sequence */
+
+ FreeVecPooled (si->Pool, si->Sequence);
+ si->Sequence = NULL;
+ si->Length = 0;
+ return NULL;
+ }
+
+ /* Check for too many song positions */
+
+ if (len > MAXPOSITIONS)
+ return NULL;
+
+
+ len_quantized = (len + SEQUENCE_QUANTUM - 1) & ~(SEQUENCE_QUANTUM - 1);
+
+
+ if (!si->Sequence)
+ {
+ /* Create a new sequence table */
+
+ if (si->Sequence = AllocVecPooled (si->Pool,
+ len_quantized * sizeof (UWORD)))
+ {
+ si->Length = len;
+ memset (si->Sequence, 0, len * sizeof (UWORD)); /* Clear sequence table */
+ }
+ }
+ else if (si->Length > len_quantized)
+ {
+ UWORD *newseq;
+
+ /* Shrink sequence table */
+
+ si->Length = len;
+
+ if (newseq = AllocVecPooled (si->Pool, len_quantized * sizeof (UWORD)))
+ {
+ CopyMem (si->Sequence, newseq, len * sizeof (UWORD));
+ FreeVecPooled (si->Pool, si->Sequence);
+ si->Sequence = newseq;
+ }
+ /* If the previous allocation failed we ignore it and continue
+ * without shrinking the sequence table.
+ */
+ }
+ else if (si->Length <= len_quantized - SEQUENCE_QUANTUM)
+ {
+ UWORD *newseq;
+
+ /* Expand the sequence table */
+
+ if (!(newseq = AllocVecPooled (si->Pool, len_quantized * sizeof (UWORD))))
+ return NULL;
+
+ /* Now replace the the old sequence with the new and delete the old one */
+
+ CopyMem (si->Sequence, newseq, si->Length * sizeof (UWORD));
+ FreeVecPooled (si->Pool, si->Sequence);
+
+ /* Clear the new sequence table entries */
+ memset (newseq + si->Length, 0, (len - si->Length) * sizeof (UWORD));
+ si->Length = len;
+ si->Sequence = newseq;
+ }
+ else
+ {
+ /* No reallocation */
+
+ if (si->Length > len)
+ /* Clear the new sequence table entries */
+ memset (si->Sequence + si->Length, 0, (si->Length - len) * sizeof (UWORD));
+
+ si->Length = len;
+ }
+
+ if (si->CurrentPos >= si->Length)
+ si->CurrentPos = si->Length - 1;
+
+ return si->Sequence;
+}
+
+
+
+static struct Pattern *AddPattern (struct SongInfo *si, struct TagItem *tags)
+
+/* SNGM_ADDPATTERN
+ *
+ * Allocates a pattern structure and adds it to the the passed song.
+ * The tracks are also allocated and attached to the pattern structure.
+ *
+ * PATTA_Num - When this attribute is passed, the pattern will be inserted
+ * before the existing pattern <patnum>. Patterns >= <patnum> will be moved
+ * ahead one slot. The position table is updated inserting references to
+ * the new pattern immediately before each occurence of patnum, so that the
+ * two patterns are allways played together.
+ * If patnum is -1, the pattern will be allocated but NOT inserted in the song.
+ *
+ * PATTA_Replace - If TRUE, the existing pattern <patnum> will be replaced by
+ * the new one. The other pattern will be freed after the new one is
+ * allocated.
+ *
+ * PATTA_Pattern - specifies an already allocated pattern structure to be
+ * used instead of allocating a new one. Adding patterns allocated
+ * by another istance of the song class is illegal.
+ *
+ * PATTA_Lines - Number of lines in each track. If lines is 0, track data
+ * is not allocated.
+ *
+ * RETURNS
+ * Pointer to the newly allocated pattern structure, NULL for failure.
+ */
+{
+ struct Pattern *patt;
+ ULONG numpatt_quantized;
+ ULONG i;
+ ULONG tracks, lines, patnum, replace;
+
+ tracks = GetTagData (PATTA_Tracks, si->DefNumTracks, tags);
+ lines = GetTagData (PATTA_Lines, si->DefPattLen, tags);
+ patnum = GetTagData (PATTA_Num, si->NumPatterns, tags);
+ replace = GetTagData (PATTA_Replace, FALSE, tags);
+ patt = (struct Pattern *)GetTagData (PATTA_Pattern, NULL, tags);
+
+ if (!tracks || (si->NumPatterns == MAXPATTERNS))
+ return NULL;
+
+
+ /* Round up to an even number of quantums */
+ numpatt_quantized = (si->NumPatterns + PATTERNS_QUANTUM - 1) & ~(PATTERNS_QUANTUM - 1);
+
+ if (!si->Patt)
+ {
+ /* Create a new pattern table */
+
+ si->Patt = AllocVecPooled (si->Pool,
+ PATTERNS_QUANTUM * sizeof (struct Pattern *));
+ }
+ else if (si->NumPatterns + 1 >= numpatt_quantized)
+ {
+ struct Pattern **newpatt;
+
+ /* Expand patterns table */
+
+ if (newpatt = AllocVecPooled (si->Pool,
+ (numpatt_quantized + PATTERNS_QUANTUM) * sizeof (struct Pattern *)))
+ {
+ CopyMem (si->Patt, newpatt, si->NumPatterns * sizeof (struct Pattern *));
+ FreeVecPooled (si->Pool, si->Patt);
+ si->Patt = newpatt;
+ }
+ else return NULL;
+ }
+
+
+ /* Pattern allocation */
+
+ if (!patt)
+ {
+ if (patt = CAllocPooled (si->Pool, sizeof (struct Pattern) +
+ tracks * sizeof (struct Note *)))
+ {
+ patt->Tracks = tracks;
+ patt->Lines = lines;
+
+ if (lines)
+ for (i = 0 ; i < tracks ; i++)
+ {
+ if (!(patt->Notes[i] = (struct Note *)
+ CAllocPooled (si->Pool, sizeof (struct Note) * lines)))
+ {
+ ULONG j;
+
+ for (j = 0 ; j < tracks ; j++)
+ FreePooled (si->Pool, patt->Notes[i], lines * sizeof (struct Note));
+
+ FreePooled (si->Pool, patt, sizeof (struct Pattern) +
+ tracks * sizeof (struct Note *));
+ return NULL;
+ }
+ }
+
+ DupStringPooled (si->Pool, (STRPTR)GetTagData (PATTA_Name, NULL, tags), &patt->Name);
+ }
+ }
+
+ if (patt)
+ {
+ if (patnum != -1)
+ {
+ if (replace) /* Free old pattern */
+ {
+ ULONG j;
+
+ for (j = 0 ; j < si->Patt[patnum]->Tracks ; j++)
+ FreePooled (si->Pool, si->Patt[patnum]->Notes[j],
+ si->Patt[patnum]->Lines * sizeof (struct Note));
+
+ FreeVecPooled (si->Pool, si->Patt[patnum]->Name);
+ FreePooled (si->Pool, si->Patt[patnum], sizeof (struct Pattern) +
+ patt->Tracks * sizeof (struct Note *));
+ }
+
+ si->Patt[patnum] = patt;
+
+ if (!replace)
+ {
+ if (patnum < si->NumPatterns)
+ {
+ /* Shift subsequent patterns one position ahead */
+ for (i = si->NumPatterns ; i > patnum ; i--)
+ si->Patt[i] = si->Patt[i-1];
+ }
+
+ si->NumPatterns++;
+ }
+
+ si->Changes++;
+ }
+
+ if (tracks > si->MaxTracks)
+ si->MaxTracks = tracks;
+ }
+
+ return patt;
+}
+
+
+
+static LONG SetPattern (struct SongInfo *si, ULONG num, struct TagItem *tags)
+
+/* SNGM_SETPATTERN */
+{
+ struct TagItem *ti;
+ struct Pattern *patt;
+
+ if (patt = si->Patt[num])
+ if (ti = FindTagItem (PATTA_Name, tags))
+ {
+ DupStringPooled (si->Pool, (STRPTR)ti->ti_Data, &si->Patt[num]->Name);
+ si->Changes++;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+static void RemPattern (struct SongInfo *si, ULONG patnum, ULONG newpatt)
+
+/* SNGM_REMPATTERN
+ *
+ * Remove a pattern from a song. All patterns >= <patnum> will be moved
+ * back one slot. The position table is updated replacing each
+ * occurence of <patnum> with pattern <newpatt>. If <patnum> is -1,
+ * newpatt will be used as a pointer to a pattern structure which has been
+ * created by the song, but has not been added to the pattern table. This
+ * is useful for freeing patterns created with num = -1 in the
+ * SNGM_ADDPATTERN method.
+ */
+{
+ struct Pattern *patt;
+ ULONG i;
+
+ if (patnum == -1)
+ {
+ patt = (struct Pattern *)newpatt;
+ newpatt = - 1;
+ }
+ else
+ patt = si->Patt[patnum];
+
+ if (patt)
+ {
+ ULONG j;
+
+ if (patnum < si->NumPatterns)
+ si->Patt[patnum] = NULL;
+
+ for (j = 0 ; j < patt->Tracks ; j++)
+ FreePooled (si->Pool, patt->Notes[j], patt->Lines * sizeof (struct Note));
+
+ FreeVecPooled (si->Pool, patt->Name);
+ FreePooled (si->Pool, patt, sizeof (struct Pattern) +
+ patt->Tracks * sizeof (struct Note *));
+ }
+
+ if (patnum < si->NumPatterns)
+ {
+ /* Scroll subsequent patterns */
+ for (i = patnum; i < si->NumPatterns; i++)
+ si->Patt[i] = si->Patt[i+1];
+ }
+
+ if (si->Sequence)
+ {
+ /* Adjust position table */
+
+ for (i = 0 ; i < si->Length ; i++)
+ {
+ /* Substitute references to the old pattern in the position table */
+ if (si->Sequence[i] == patnum) si->Sequence[i] = newpatt;
+
+ /* Fix pattern numbers */
+ if (si->Sequence[i] > patnum) si->Sequence[i]--;
+ }
+ }
+
+ if (patnum != -1)
+ {
+ si->Changes++;
+ si->NumPatterns--;
+
+ if (si->NumPatterns)
+ {
+ if (si->CurrentPatt >= si->NumPatterns)
+ si->CurrentPatt = si->NumPatterns - 1;
+ }
+ else
+ {
+ FreeVecPooled (si->Pool, si->Patt);
+ si->Patt = NULL;
+ }
+ }
+}
+
+
+
+static struct Instrument *AddInstrument (struct SongInfo *si, ULONG num, struct TagItem *tags)
+
+/* SNGM_ADDINSTRUMENT
+ *
+ * Allocates an instrument structure and adds it to the the passed song.
+ * It will also free the old instrument if there is one already.
+ * If <num> is -1, it will load the instrument in the slot right after
+ * the last instrument. If <num> is 0, it will load the instrument in
+ * the first free slot.
+ *
+ * RETURNS
+ * Pointer to the newly allocated instrument structure, NULL for failure.
+ */
+{
+ struct Instrument *instr;
+
+ if (num == -1)
+ num = si->LastInstrument + 1;
+
+ if (num == 0)
+ {
+ for (num = 1; num <= si->LastInstrument; num++)
+ if (!si->Instr[num]) break;
+
+ if (num == MAXINSTRUMENTS) return NULL;
+ }
+
+
+ if (num > MAXINSTRUMENTS)
+ return NULL;
+
+ if (si->Instr[num])
+ RemInstrument (si, num);
+
+ if (!(instr = CAllocPooled (si->Pool, sizeof (struct Instrument))))
+ return NULL;
+
+ si->Instr[num] = instr;
+ if (num > si->LastInstrument)
+ si->LastInstrument = num;
+
+ SetInstrument (si, num, tags);
+
+ return instr;
+}
+
+
+
+static void SwapInstruments (struct SongInfo *si, LONG n, LONG m)
+
+/* SNGM_SWAPINSTRUMENTS
+ *
+ * Swaps two instruments and updates the LastInstrument field if required.
+ *
+ * RETURNS
+ * Pointer to the newly allocated instrument structure, NULL for failure.
+ */
+{
+ struct Instrument *tmp;
+
+ if (n == m) return;
+
+ tmp = si->Instr[n];
+ si->Instr[n] = si->Instr[m];
+ si->Instr[m] = tmp;
+
+ n = max (n,m);
+
+ if (n >= si->LastInstrument)
+ {
+ for ( ; n >= 0; n--)
+ if (si->Instr[n]) break;
+
+ DB (kprintf ("n = %ld\n", n));
+
+ si->LastInstrument = n;
+ }
+}
+
+
+
+static LONG SetInstrument (struct SongInfo *si, ULONG num, struct TagItem *tags)
+
+/* SNGM_SETINSTRUMENT */
+{
+ struct Instrument *instr;
+ struct TagItem *ti;
+
+ if ((num <= si->LastInstrument) && (instr = si->Instr[num]))
+ {
+ while (ti = NextTagItem (&tags))
+ switch (ti->ti_Tag)
+ {
+ case INSTRA_Type:
+ instr->Type = ti->ti_Data;
+ break;
+
+ case INSTRA_Name:
+ DupStringPooled (si->Pool, (STRPTR)ti->ti_Data, &instr->Name);
+ break;
+
+ case INSTRA_Volume:
+ instr->Volume = ti->ti_Data;
+ break;
+
+ case INSTRA_Sample:
+ instr->Sample = (BYTE *) ti->ti_Data;
+ break;
+
+ case INSTRA_Length:
+ instr->Length = ti->ti_Data;
+ break;
+
+ case INSTRA_Flags:
+ instr->Flags = ti->ti_Data;
+ break;
+
+ case INSTRA_LoopStart:
+ instr->Repeat = ti->ti_Data;
+ break;
+
+ case INSTRA_LoopLen:
+ instr->Replen = ti->ti_Data;
+ break;
+
+ case INSTRA_LoopEnd:
+ instr->Replen = ti->ti_Data - instr->Repeat - 2;
+ break;
+
+ case INSTRA_FineTune:
+ instr->FineTune = ti->ti_Data;
+ /* Note: I'm falling through here! */
+
+ default:
+ break;
+ }
+ /* End while (NextTagItem()) */
+
+ si->Changes++;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void RemInstrument (struct SongInfo *si, ULONG num)
+
+/* SNGM_REMINSTRUMENT */
+{
+ if ((num <= si->LastInstrument) && (si->Instr[num]))
+ {
+ FreeVec (si->Instr[num]->Sample);
+ FreeVecPooled (si->Pool, si->Instr[num]->Name);
+ FreePooled (si->Pool, si->Instr[num], sizeof (struct Instrument));
+ si->Instr[num] = NULL;
+ si->Changes++;
+ }
+
+ if (num == si->LastInstrument)
+ {
+ ULONG i;
+
+ for (i = num - 1; i > 0 ; i--)
+ if (si->Instr[i])
+ break;
+
+ si->LastInstrument = i;
+ }
+}
+
+
+
+#if 0 /* --- This part has been removed --- */
+
+static WORD ModType (BPTR fh)
+
+/* Guess source module type */
+{
+ UBYTE __aligned str[0x30];
+ WORD trackertype;
+
+ if (Read (fh, str, 0x30) != 0x30)
+ return -1;
+
+ /* Check XModule */
+ if (!(strncmp (str, "FORM", 4) || strncmp (str+8, "XMOD", 4)))
+ return FMT_XMODULE;
+
+ /* Check MED */
+ if (!(strncmp (str, "MMD", 3)))
+ return FMT_MED;
+
+ /* Check Oktalyzer */
+ if (!(strncmp (str, "OKTASONG", 8)))
+ return FMT_OKTALYZER;
+
+ if (!(strncmp (&str[0x2C], "SCRM", 4)))
+ return FMT_SCREAMTRACKER;
+
+ if (!(strncmp (str, "Extended module: ", 17)))
+ return FMT_FASTTRACKER2;
+
+ /* Check #?Tracker */
+ if ((trackertype = IsTracker (fh)) != FMT_UNKNOWN)
+ return trackertype;
+
+ switch (ShowRequestArgs (MSG_UNKNOWN_MOD_FORMAT, MSG_SOUND_PRO_CANCEL, NULL))
+ {
+ case 1:
+ return FMT_STRACKER;
+ break;
+
+ case 2:
+ return FMT_PTRACKER;
+ break;
+
+ default:
+ break;
+ }
+
+ return FMT_UNKNOWN;
+}
+#endif /* --- This part has been removed --- */
+
+
+
+#define tolower(c) ((c) | (1<<5))
+
+static BOOL GuessAuthor (struct SongInfo *si)
+
+/* Tries to find the author of the song by looking up the instrument
+ * names.
+ */
+{
+ UBYTE *name;
+ ULONG i, j;
+
+ if (!si->Instr) return FALSE;
+
+ for (i = 1; i <= si->LastInstrument; i++)
+ {
+ if (!si->Instr[i]) continue;
+
+ name = si->Instr[i]->Name;
+
+ /* Check for IntuiTracker-style embedded author name */
+ if (name[0] == '#')
+ {
+ for (j = 1; name[j] == ' '; j++); /* Skip extra blanks */
+
+ /* Skip "by " */
+ if ((tolower(name[j]) == 'b') && (tolower(name[j+1]) == 'y') && name[j+2] == ' ')
+ j += 3;
+
+ for (; name[j] == ' '; j++); /* Skip extra blanks */
+
+ if (name[j])
+ {
+ SetAttrs (si, SNGA_Author, &(name[j]), TAG_DONE);
+ return TRUE; /* Stop looking for author */
+ }
+ }
+
+ /* Now look for the occurence of "by ", "by:", "by\0" or "(c)". Ignore case. */
+ for (j = 0; name[j]; j++)
+ {
+ if (( (tolower(name[j]) == 'b') && (tolower(name[j+1]) == 'y') &&
+ ((name[j+2] == ' ') || (name[j+2] == ':') || (name[j+2] == '\0')) ) ||
+ ((name[j] == '(') && (tolower(name[j+1]) == 'c') && (name[j+2] == ')') ))
+ {
+ j+=3; /* Skip 'by ' */
+
+ /* Skip extra blanks/punctuations */
+ while (name[j] == ' ' || name[j] == ':' || name[j] == '.') j++;
+
+ if (name[j]) /* Check if the end is reached */
+ /* The name of the author comes (hopefully) right after 'by ' */
+ SetAttrs (si, SNGA_Author, &(name[j]), TAG_DONE);
+ else
+ /* The name of the author is stored in the next instrument */
+ if (i < si->LastInstrument - 1)
+ SetAttrs (si, SNGA_Author, si->Instr[i+1]->Name, TAG_DONE);
+
+ return TRUE; /* Stop loop */
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+GLOBALCALL ULONG CalcInstSize (struct SongInfo *si)
+
+/* Calculate total size of all instruments in a song. */
+{
+ ULONG i;
+ ULONG size = 0;
+
+ for (i = 1; i < si->LastInstrument; i++)
+ {
+ if (!si->Instr[i]) continue;
+ size += si->Instr[i]->Length + sizeof (struct Instrument);
+ }
+
+ return size;
+}
+
+
+
+GLOBALCALL ULONG CalcSongSize (struct SongInfo *si)
+
+/* Calculate total size of a song */
+{
+ ULONG i;
+ struct Pattern *patt;
+ ULONG size = sizeof (struct SongInfo) + CalcInstSize (si);
+
+ /* Calculate total patterns size */
+
+ for ( i = 0; i < si->NumPatterns; i++)
+ {
+ if (patt = si->Patt[i])
+ size += patt->Lines * patt->Tracks * sizeof (struct Note) + sizeof (struct Pattern);
+ }
+
+ return size;
+}
+
+
+
+GLOBALCALL ULONG CalcSongTime (struct SongInfo *si)
+
+/* Calculate song length in seconds
+ *
+ * One note lasts speed/50 seconds at 125bpm.
+ */
+{
+ ULONG i, j, k;
+ struct Pattern *patt;
+ struct Note *note;
+ ULONG speed = si->GlobalSpeed,
+// tempo = si->GlobalTempo,
+ ticks = speed * 20,
+ millisecs = 0;
+
+
+ for (i = 0; i < si->Length; i++)
+ {
+ patt = si->Patt[si->Sequence[i]];
+
+ for (j = 0; j < patt->Lines; j++)
+ for (k = 0; k < patt->Tracks; k++)
+ {
+ note = &patt->Notes[k][j];
+
+ switch (note->EffNum)
+ {
+ case EFF_POSJUMP:
+ if (note->EffVal > i)
+ {
+ i = note->EffVal;
+ j = patt->Lines;
+ k = patt->Tracks;
+ }
+ else return millisecs;
+ break;
+
+ case EFF_SETSPEED:
+ /* At speed 1, one line lasts one VBlank,
+ * that is 20 milliseconds.
+ */
+ ticks = note->EffVal * 20;
+ break;
+
+ case EFF_PATTERNBREAK:
+ j = patt->Lines;
+ k = patt->Tracks;
+ break;
+
+ /* case EFF_MISC: Loop */
+ }
+
+ millisecs += ticks;
+ }
+ }
+
+ return millisecs;
+}
--- /dev/null
+ IFND LIBRARIES_SONGLCASS_I
+LIBRARIES_SONGCLASS_I SET 1
+**
+** $VER: songclass.i 3.9 (7.1.96)
+** Copyright (C) 1995,96 Bernardo Innocenti
+**
+** Set TAB size to 8 chars to read this header file
+**
+** Assembler structure definitions for the song class
+**
+
+
+ IFND EXEC_TYPES_I
+ include exec/types.i
+ ENDC ; EXEC_TYPES_I
+
+ IFND EXEC_NODES_I
+ include exec/nodes.i
+ ENDC ; EXEC_NODES_I
+
+ IFND EXEC_SEMAPHORES_I
+ include exec/semaphores.i
+ ENDC ; EXEC_SEMAPHORES_I
+
+
+MAXOCTAVES EQU 6 ; Number of octaves currently supported
+MAXNOTES EQU (12*MAXOCTAVES+1) ; Entries in note conversion table
+MAXTRACKS EQU 255 ; Maximum number of tracks in a pattern
+MAXINSTRUMENTS EQU 255 ; Maximum number of instruments in a song
+MAXPATTLINES EQU 32767 ; Maximum number of lines in a pattern
+MAXPOSITIONS EQU 32767 ; Maximum number of song positions
+MAXPATTERNS EQU 32767 ; Maximum number of patterns
+
+
+
+**************************************************************************
+** Some definitions transcripted from "XModule.h"
+**************************************************************************
+
+ ENUM 0
+
+ EITEM EFF_NULL
+ EITEM EFF_PORTAMENTOUP
+ EITEM EFF_PORTAMENTODOWN
+ EITEM EFF_TONEPORTAMENTO
+ EITEM EFF_VIBRATO
+ EITEM EFF_TONEPVOLSLIDE
+ EITEM EFF_VIBRATOVOLSLIDE
+ EITEM EFF_TREMOLO
+ EITEM EFF_UNUSED
+ EITEM EFF_SAMPLEOFFSET
+ EITEM EFF_VOLSLIDE
+ EITEM EFF_POSJUMP
+ EITEM EFF_SETVOLUME
+ EITEM EFF_PATTERNBREAK
+ EITEM EFF_MISC
+ EITEM EFF_SETSPEED
+ EITEM EFF_SETTEMPO
+ EITEM EFF_ARPEGGIO
+
+ EITEM EFF_COUNT
+
+
+
+ STRUCTURE Note,0
+ UBYTE note_Note
+ UBYTE note_Inst
+ UBYTE note_Vol
+ UBYTE note_Pad
+ UBYTE note_EffNum
+ UBYTE note_EffVal
+ LABEL Note_SIZEOF
+
+
+ STRUCTURE Instrument,0
+ UWORD in_InstType ; Instrument type (See defs)
+ UWORD in_Volume ; Volume (max $40)
+ APTR in_Name ; Instrument Name
+ APTR in_SampleData ; Sampled data
+ ULONG in_Length ; Length of instr
+ ULONG in_Repeat ; Loop start (No loop = 0)
+ ULONG in_Replen ; Loop size (No loop = 1)
+ WORD in_FineTune ; Instrument FineTune (-8..+7)
+ UWORD in_Flags ; Unused
+ LABEL in_SIZEOF
+
+
+ STRUCTURE Pattern,0
+ UWORD pa_Tracks ; Support for variable number of tracks
+ UWORD pa_Lines ; Number of lines in pattern
+ APTR pa_Name ; Pattern Name
+ STRUCT pa_Reserved,8 ; Reserved for future enhancements
+ LABEL pa_Notes,0 ; Pointers to all tracks follow
+ LABEL pa_SIZEOF
+
+
+* Song class white box istance
+*
+* You can read from this structure directly for speed critical operations.
+* Modifying the data contained in the SongInfo structure is only allowed
+* by means of object methods.
+*
+* Before using a public song, please protect yourself against other tasks
+* modifying these fields. Always obtainin a (shared) lock on the
+* SignalSemaphore contained in the SongInfo structure before doing anything
+* with it. The methods provided by the song class will not handle access
+* arbitration for you.
+*
+ STRUCTURE SongInfo,LN_SIZE
+ STRUCT si_Lock,SS_SIZE
+ UWORD si_Length ; Number of positions in song
+ UWORD si_MaxTracks ; Number of tracks in song
+ UWORD si_NumPatterns ; Number of patterns in song
+ UWORD si_LastInstrument ; Unused
+ UWORD si_CurrentPatt
+ UWORD si_CurrentPos
+ UWORD si_CurrentInst
+ UWORD si_CurrentLine
+ UWORD si_CurrentTrack
+ UWORD si_DefNumTracks ; Default number of tracks in new patterns
+ UWORD si_DefPattLen ; Default number of lines in new patterns
+
+ UWORD si_GlobalSpeed ; Default song speed
+ UWORD si_GlobalTempo ; Default song tempo
+ UWORD si_RestartPos ; Position to restart from
+ STRUCT si_Reserved,8 ; Reserved for future enhancements
+
+* Note: all data beyond this point is longword aligned
+
+ ULONG si_Flags ; See definitions below
+ APTR si_Pool ; The memory pool where song data
+ ; must be allocated from.
+ APTR si_Sequence ; UWORD *: Pointer to song sequence
+ APTR si_Patt
+ APTR si_Instr
+ APTR si_ActiveTracks ; UWORD *: Active Tracks (-1 = disabled)
+ APTR si_Title ; Song name
+ APTR si_Author ; Author of song
+ APTR si_Path ; Original song path
+ APTR si_Description ; Verbose song description
+
+ ULONG si_Changes ; Number of changes made to this song
+ ULONG si_TotalChanges ; Total number of changes made so far
+ ULONG si_CreationDate ; Date of OM_NEW
+ ULONG si_LastChanged ; Date of last saving
+
+ LABEL si_SIZEOF
+
+
+
+ ENDC ; !LIBRARIES_SONGCLASS_I
--- /dev/null
+/*
+** SongInfoWin.c
+**
+** Copyright (C) 1993,94,95,96 Bernardo Innocenti
+**
+** Handle Song Information panel.
+*/
+
+#include <exec/nodes.h>
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/gadtools.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+/* Gadgets IDs */
+
+enum
+{
+ GD_SongInfoGroup0,
+ GD_SongInfoGroup1,
+ GD_SongList,
+ GD_SongInfoGroup2,
+ GD_NewSong,
+ GD_OpenSong,
+ GD_SaveSong,
+ GD_DelSong,
+ GD_SongInfoGroup3,
+ GD_SongName,
+ GD_AuthorName,
+ GD_SongInfoGroup4,
+ GD_SongInfoGroup5,
+ GD_Tempo,
+ GD_Speed,
+ GD_Restart,
+ GD_SongInfoGroup6,
+ GD_SongLength,
+ GD_Patterns,
+ GD_Tracks,
+ GD_SongInfoGroup7,
+ GD_TotalSize,
+ GD_InstSize,
+
+ SongInfo_CNT
+};
+
+
+
+
+/* Local function Prototypes */
+
+static void SongInfoPostOpen (void);
+
+static void SongListClicked (struct WinUserData *wud);
+static void SongNameClicked (struct WinUserData *wud);
+static void AuthorNameClicked (struct WinUserData *wud);
+static void TempoClicked (struct WinUserData *wud);
+static void SpeedClicked (struct WinUserData *wud);
+static void RestartClicked (struct WinUserData *wud);
+static void NewSongClicked (struct WinUserData *wud);
+static void OpenSongClicked (struct WinUserData *wud);
+static void DelSongClicked (struct WinUserData *wud);
+static void SaveSongClicked (struct WinUserData *wud);
+
+static void SongInfoMiMergeSongs (void);
+static void SongInfoMiJoinSongs (void);
+static void SongInfoMiClearSong (void);
+
+
+
+static struct NewMenu SongInfoNewMenu[] =
+{
+ NM_TITLE, (STRPTR)MSG_SONG_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_MERGE_SONGS_MEN, "M", 0, 0L, (APTR)SongInfoMiMergeSongs,
+ NM_ITEM, (STRPTR)MSG_JOIN_SONGS_MEN, "J", 0, 0L, (APTR)SongInfoMiJoinSongs,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_CLEAR_MEN, "K", 0, 0L, (APTR)SongInfoMiClearSong,
+ NM_END, NULL, NULL, 0, 0L, NULL
+};
+
+
+
+static ULONG SongInfoArgs[] =
+{
+ HGROUP_KIND, 0,
+ LISTVIEW_KIND, (ULONG)SongListClicked, 0, NULL, TAG_DONE,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)NewSongClicked, MSG_UNDERSCORE_NEW_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)OpenSongClicked, MSG_OPEN_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)SaveSongClicked, MSG_SAVE_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)DelSongClicked, MSG_DEL_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ VGROUP_KIND, BBFT_RIDGE,
+ STRING_KIND, (ULONG)SongNameClicked, MSG_SONG_NAME_GAD, 31, TAG_DONE,
+ STRING_KIND, (ULONG)AuthorNameClicked, MSG_AUTHOR_NAME_GAD, 63, TAG_DONE,
+ ENDGROUP_KIND,
+ HGROUP_KIND, BBFT_RIDGE,
+ VGROUP_KIND, 0,
+ INTEGER_KIND, (ULONG)TempoClicked, MSG_DEF_TEMPO_GAD, 3, TAG_DONE,
+ INTEGER_KIND, (ULONG)SpeedClicked, MSG_DEF_SPEED_GAD, 3, TAG_DONE,
+ INTEGER_KIND, (ULONG)RestartClicked, MSG_RESTART_GAD, 3, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ NUMBER_KIND, MSG_LENGHT_GAD, 3, TAG_DONE,
+ NUMBER_KIND, MSG_NUM_PATTS_GAD, 3, TAG_DONE,
+ NUMBER_KIND, MSG_NUM_TRACKS_GAD, 3, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ VGROUP_KIND, BBFT_RIDGE,
+ NUMBER_KIND, MSG_TOT_MOD_SIZE_GAD, 7, TAG_DONE,
+ NUMBER_KIND, MSG_TOT_INST_SIZE_GAD, 7, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG SongInfoWinTags[] =
+{
+ XMWIN_NewMenu, (LONG)SongInfoNewMenu,
+ XMWIN_LayoutArgs, (LONG)SongInfoArgs,
+ XMWIN_GCount, SongInfo_CNT,
+ XMWIN_Title, MSG_SONGINFO_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET,
+ XMWIN_IDCMPFlags, NUMBERIDCMP | STRINGIDCMP | LISTVIEWIDCMP | INTEGERIDCMP | BUTTONIDCMP | IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK,
+ XMWIN_PostOpenFunc, (LONG)SongInfoPostOpen,
+ XMWIN_DropIconFunc, (LONG)ToolBoxDropIcon,
+ XMWIN_HelpNode, (LONG)"SongInfo",
+ TAG_DONE
+};
+
+
+
+static void SongInfoPostOpen (void)
+{
+ UpdateSongInfoList();
+ UpdateSongInfo();
+}
+
+
+
+/**********************/
+/* SongInfo Functions */
+/**********************/
+
+
+GLOBALCALL void DetatchSongInfoList (void)
+
+/* Detatches the SongInfo list from the listview gadget.
+ * Call UpdateSongInfoList() to re-attach it when you are done modifying it.
+ */
+{
+ struct WinUserData *wud = WDescr[WID_SONGINFO].Wud;
+
+ if (wud && wud->Win)
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SongList], wud->Win, NULL,
+ GTLV_Labels, ~0,
+ TAG_DONE);
+}
+
+
+
+GLOBALCALL void UpdateSongInfoList (void)
+
+/* Updates the SongInfo listview gadget */
+{
+ struct WinUserData *wud = WDescr[WID_SONGINFO].Wud;
+
+ if (wud && wud->Win)
+ {
+#ifdef OS30_ONLY
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#else
+ /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */
+
+ /* Try to get the shared semaphore */
+ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
+ /* Check if we can get the exclusive version */
+ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
+ /* Oh well, wait for the shared lock */
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+#endif /* OS30_ONLY */
+
+ if (XModuleBase->xm_CurrentSong)
+ {
+ struct SongInfo *tmp = (struct SongInfo *)XModuleBase->xm_Songs.mlh_Head;
+ ULONG songnum = 0;
+
+ /* Find current song number */
+
+ while (tmp != XModuleBase->xm_CurrentSong)
+ {
+ tmp = (struct SongInfo *)tmp->Link.ln_Succ;
+ songnum++;
+ }
+
+ GT_SetGadgetAttrs (wud->Gadgets[GD_SongList], wud->Win, NULL,
+ GTLV_Labels, &XModuleBase->xm_Songs,
+ GTLV_Selected, songnum,
+ GTLV_MakeVisible, songnum,
+ TAG_DONE);
+ }
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ }
+}
+
+
+
+GLOBALCALL void UpdateSongInfo (void)
+
+/* Update information on the current song */
+{
+ struct WinUserData *wud = WDescr[WID_SONGINFO].Wud;
+ struct SongInfo *si;
+ DB(ULONG millisecs);
+
+ if (wud && wud->Win)
+ if (si = xmLockActiveSong (SM_SHARED))
+ {
+ SetGadgets (wud,
+ GD_SongName, si->Title,
+ GD_AuthorName, si->Author,
+ GD_Tempo, si->GlobalTempo,
+ GD_Speed, si->GlobalSpeed,
+ GD_SongLength, si->Length,
+ GD_Patterns, si->NumPatterns,
+ GD_Tracks, si->MaxTracks,
+ GD_Restart, si->RestartPos,
+ GD_TotalSize, CalcSongSize (si),
+ GD_InstSize, CalcInstSize (si),
+ -1);
+
+ /* TODO */
+ DB(millisecs = CalcSongTime (si));
+ DB(xmDisplayMessage (XMDMF_DEBUG, "Song Time: %02ld:%02ld", millisecs / 60000, (millisecs / 1000) % 60));
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UpdateInstrList();
+ UpdatePatternList();
+}
+
+
+
+/********************/
+/* SongInfo Gadgets */
+/********************/
+
+
+static void SongListClicked (struct WinUserData *wud)
+{
+ WORD i;
+ struct SongInfo *tmp;
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+
+ tmp = (struct SongInfo *)XModuleBase->xm_Songs.mlh_Head;
+
+ for (i = IntuiMsg.Code; (i > 0) && tmp ; i--)
+ tmp = (struct SongInfo *)tmp->Link.ln_Succ;
+
+ if (tmp) ObtainSemaphoreShared (&tmp->Lock);
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ if (tmp)
+ {
+ xmActivateSong (tmp);
+ ReleaseSemaphore (&tmp->Lock);
+ }
+}
+
+
+
+static void SongNameClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong(SM_EXCLUSIVE))
+ {
+ DetatchSongInfoList();
+
+ SetAttrs (si,
+ SNGA_Title, GetString (wud->Gadgets[GD_SongName]),
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+
+ UpdateSongInfoList();
+ }
+}
+
+
+
+static void AuthorNameClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong(SM_EXCLUSIVE))
+ {
+ SetAttrs (si,
+ SNGA_Author, GetString (wud->Gadgets[GD_AuthorName]),
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void TempoClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong(SM_EXCLUSIVE))
+ {
+ SetAttrs (si,
+ SNGA_GlobalTempo, GetNumber (wud->Gadgets[GD_Tempo]),
+ TAG_END);
+ GT_SetGadgetAttrs (wud->Gadgets[GD_Tempo], wud->Win, NULL,
+ GTIN_Number, si->GlobalTempo,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void RestartClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong(SM_EXCLUSIVE))
+ {
+ SetAttrs (si,
+ SNGA_RestartPos, GetNumber (wud->Gadgets[GD_Restart]),
+ TAG_END);
+ GT_SetGadgetAttrs (wud->Gadgets[GD_Restart], wud->Win, NULL,
+ GTIN_Number, si->RestartPos,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void SpeedClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong(SM_EXCLUSIVE))
+ {
+ SetAttrs (si,
+ SNGA_GlobalSpeed, GetNumber (wud->Gadgets[GD_Speed]),
+ TAG_END);
+ GT_SetGadgetAttrs (wud->Gadgets[GD_Speed], wud->Win, NULL,
+ GTIN_Number, si->GlobalSpeed,
+ TAG_DONE);
+
+ ReleaseSemaphore (&si->Lock);
+ }
+}
+
+
+
+static void NewSongClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ if (si = xmCreateSong (
+ SNGA_ReadyToUse, TRUE,
+ XMSNG_AddToList, -1,
+ XMSNG_Active, TRUE,
+ TAG_DONE))
+ ReleaseSemaphore (&si->Lock); /* This is for XMSNG_AddToList */
+ else
+ DisplayBeep (NULL);
+}
+
+
+
+static void OpenSongClicked (struct WinUserData *wud)
+{
+ StartFileRequest (FREQ_LOADMOD, ToolBoxOpenModule);
+}
+
+
+
+static void DelSongClicked (struct WinUserData *wud)
+{
+ if (ShowRequestArgs (MSG_DISCARD_CURRENT_SONG, MSG_YES_OR_NO, NULL))
+ {
+ struct SongInfo *si;
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ xmDeleteSong (si);
+
+ if (!XModuleBase->xm_CurrentSong)
+ NewSongClicked(wud);
+ }
+}
+
+
+static void SaveSongClicked (struct WinUserData *wud)
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ if (si = xmLockActiveSong (SM_EXCLUSIVE))
+ {
+ LastErr = xmSaveModuleA (si, si->Path, NULL, NULL);
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ UnlockWindows();
+}
+
+
+
+/******************/
+/* SongInfo Menus */
+/******************/
+
+static void SongInfoMiMergeSongs (void)
+{
+ struct SongInfo *si1, *si2, *si3;
+
+ LockWindows();
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+
+ if (si1 = XModuleBase->xm_CurrentSong)
+ {
+ si2 = (struct SongInfo *)si1->Link.ln_Succ;
+
+ if (si2->Link.ln_Succ)
+ {
+ /* Risking a deadlock?? Hmmm... No!
+ * Shared locks do not prevent other tasks from
+ * locking these two songs.
+ */
+ ObtainSemaphoreShared (&si1->Lock);
+ ObtainSemaphoreShared (&si2->Lock);
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ si3 = MergeSongs (si1, si2);
+
+ ReleaseSemaphore (&si1->Lock);
+ ReleaseSemaphore (&si2->Lock);
+
+ if (si3) xmAddSongA (si3, si2, NULL);
+ }
+ else
+ {
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_MERGE_REQUIRES_TWO_SONGS, NULL);
+ }
+ }
+ else
+ {
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ DisplayBeep (Scr);
+ }
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ UnlockWindows();
+}
+
+
+
+static void SongInfoMiJoinSongs (void)
+{
+ struct SongInfo *si1, *si2, *si3;
+
+ LockWindows();
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+
+ if (si1 = XModuleBase->xm_CurrentSong)
+ {
+ si2 = (struct SongInfo *)si1->Link.ln_Succ;
+
+ if (si2->Link.ln_Succ)
+ {
+ /* Risking a deadlock?? Hmmm... No!
+ * Shared locks do not prevent other tasks from
+ * locking these two songs.
+ */
+ ObtainSemaphoreShared (&si1->Lock);
+ ObtainSemaphoreShared (&si2->Lock);
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ si3 = JoinSongs (si1, si2);
+
+ ReleaseSemaphore (&si1->Lock);
+ ReleaseSemaphore (&si2->Lock);
+
+ xmAddSongA (si3, si2, NULL);
+ }
+ else
+ {
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
+ (APTR)MSG_JOIN_REQUIRES_TWO_SONGS, NULL);
+ }
+ }
+ else
+ {
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+ DisplayBeep (Scr);
+ }
+
+ UnlockWindows();
+}
+
+
+
+static void SongInfoMiClearSong (void)
+{
+ NewWindow (WID_CLEAR);
+}
--- /dev/null
+;**
+;** Startup.asm
+;**
+;** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+;**
+;** Amiga/m68k startup code for XModule
+;**
+
+ INCLUDE "exec/types.i"
+ INCLUDE "exec/alerts.i"
+ INCLUDE "exec/nodes.i"
+ INCLUDE "exec/lists.i"
+ INCLUDE "exec/ports.i"
+ INCLUDE "exec/libraries.i"
+ INCLUDE "exec/tasks.i"
+ INCLUDE "exec/memory.i"
+ INCLUDE "exec/macros.i"
+ INCLUDE "exec/execbase.i"
+ INCLUDE "libraries/dos.i"
+ INCLUDE "libraries/dosextens.i"
+ INCLUDE "workbench/startup.i"
+
+
+AbsExecBase EQU 4
+
+;**************************************************************************
+; External symbol definitions and references
+;**************************************************************************
+
+ xdef _Start
+ xdef @_XCEXIT
+ xdef _SysBase,_DOSBase
+ xdef _ThisTask
+ xdef _StdOut
+ xdef _WBenchMsg
+
+ xdef _SPrintf
+ xdef _VSPrintf
+ xdef _AsmAllocVecPooled
+ xdef _AsmFreeVecPooled
+ xdef _AsmCAllocPooled
+
+ xref _LinkerDB ; linker defined base value
+ xref __BSSBAS ; linker defined base of BSS
+ xref __BSSLEN ; linker defined length of BSS
+ xref ___main ; Name of C entry point
+
+ xref _AsmAllocPooled
+ xref _AsmFreePooled
+
+ IFD DEBUG
+ xref _kprintf
+ ENDC
+
+ IFND STKSIZE
+STKSIZE EQU 8192
+ ENDC
+
+;**************************************************************************
+; Macro definitions
+;**************************************************************************
+
+
+PUSH MACRO ; push to stack
+ move.\0 \1,-(sp) ; push.(w|l) <ea>
+ ENDM
+
+POP MACRO ; pop from stack
+ move.\0 (sp)+,\1 ; pop.(w|l) <ea>
+ ENDM
+
+
+
+;**************************************************************************
+; Code entry point
+;**************************************************************************
+
+ SECTION __MERGED,code
+
+_Start:
+
+ NEAR
+ lea _LinkerDB,a4 ; load base register
+ move.l AbsExecBase.w,a6
+
+; The BSS segment is being cleared by LoadSeg() starting from V37
+;
+; lea __BSSBAS,a3 ; get base of BSS
+; moveq #0,d1
+; move.l #__BSSLEN,d0 ; get length of BSS in longwords
+; bra.s .clr_lp ; and clear for length given
+;.clr_bss:
+; move.l d1,(a3)+
+;.clr_lp:
+; dbf d0,.clr_bss
+
+ move.l a7,_StackPtr(a4) ; Save StackPtr
+ move.l a6,_SysBase(a4) ; Save SysBase
+
+
+; Find our Task (Process) structure
+ suba.l a1,a1
+ JSRLIB FindTask ; FindTask (NULL)
+ move.l d0,a3
+ move.l d0,_ThisTask(a4)
+
+
+;**************************************************************************
+; CPU checking
+;**************************************************************************
+
+ IFD _MC68020_
+ btst.b #AFB_68020,AttnFlags+1(a6)
+ beq.s returnfail
+ ENDC
+
+
+;**************************************************************************
+; Open dos.library
+;**************************************************************************
+
+ lea DOSName(PC),a1
+ moveq.l #37,d0
+ JSRLIB OpenLibrary
+ move.l d0,_DOSBase(a4)
+ beq.s returnfail
+
+; Find output
+ move.l d0,a6 ; Load DOSBase in A6
+ JSRLIB Output ; Call Output()
+ move.l d0,_StdOut(a4) ; Save result
+ move.l _SysBase(a4),a6 ; Restore SysBase in A6
+
+; are we running as a son of Workbench?
+ move.l pr_CurrentDir(a3),__curdir(a4)
+ move.l pr_CLI(a3),d0
+ bne.s cli_stack
+
+
+;**************************************************************************
+;* Workbench Startup Code: get for startup message
+;**************************************************************************
+
+ lea pr_MsgPort(a3),a0 ; our process message port
+ JSRLIB WaitPort
+ lea pr_MsgPort(a3),a0 ; our process message port
+ JSRLIB GetMsg
+ move.l d0,_WBenchMsg(a4)
+
+ move.l d0,a2 ; get first argument
+ move.l sm_ArgList(a2),d0
+ beq.s .wb_stack
+
+ move.l _DOSBase(a4),a6 ; CurrentDir()
+ move.l d0,a0
+ move.l wa_Lock(a0),d1
+ JSRLIB DupLock
+ move.l d0,__curdir(a4)
+ move.l d0,d1
+ JSRLIB CurrentDir
+ move.l _SysBase(a4),a6 ; Restore SysBase in A6
+
+; Now calculate the remaining stack size (SP - TC_SPLOWER)
+
+.wb_stack:
+ move.l sp,d0
+ sub.l TC_SPLOWER(a3),d0
+ bra.s swapstack
+
+
+cli_stack:
+
+; get the size of the stack cli_DefaultStack
+; D0 still holds process->pr_CLI
+
+ lsl.l #2,d0 ; Shift BPTR
+ move.l d0,a0
+ move.l cli_DefaultStack(a0),d0
+ lsl.l #2,d0 ; # longwords -> # bytes
+
+
+
+;**************************************************************************
+;* Stack swapping code
+;**************************************************************************
+
+swapstack:
+ cmpi.l #STKSIZE,d0 ; D0 = current stack size
+ bcc.s do_main
+
+
+; current stack is not as big as STKSIZE says it needs
+; to be. Allocate a new one.
+
+ IFD DEBUG
+
+ lea .msg(pc),a0
+ PUSH.l a0 ; push format string
+ jsr _kprintf ; call kprintf()
+ addq #4,sp ; fix stack
+ bra.s .end_debug
+
+.msg: dc.b 'Startup.asm: Allocating new stack...',$0A, 0
+ even
+.end_debug:
+
+ ENDC
+
+ move.l #STKSIZE,d0
+
+ moveq.l #MEMF_PUBLIC,d1
+ JSRLIB AllocMem
+ tst.l d0
+ bne.s .do_swap
+
+ moveq #RETURN_FAIL,d7 ; Fail
+ bra.s exitdos
+
+.do_swap:
+
+; Call StackSwap to set up the new stack
+
+ lea stackswapstruct(a4),a0
+ move.l d0,(a0) ; stk_Lower
+ add.l #STKSIZE,d0 ; SP to top of new stack (base + size)
+ move.l d0,stk_Pointer(a0) ; stk_Pointer
+ move.l d0,stk_Upper(a0) ; stk_Upper
+ JSRLIB StackSwap
+
+
+
+;**************************************************************************
+; Call main()
+;**************************************************************************
+
+do_main:
+
+ IFD DEBUG
+; Fill all free stack space with a magic number to check
+; maximum program stack usage later.
+
+ move.l #$DEADF00D,d0
+ move.l TC_SPLOWER(a3),a0
+
+.fillstack:
+ move.l d0,(a0)+
+ cmp.l a0,sp
+ bgt.s .fillstack
+
+ ENDC
+
+
+ jsr ___main(pc) ; call C entrypoint
+
+
+@XCEXIT: ; XCEXIT() routine
+@_XCEXIT:
+
+; Save Return Code
+ move.l d0,d7
+
+ IFD DEBUG
+
+; Check maximum stack used during execution.
+; Search the first longword not containing our magic number.
+;
+; <- TC_SPUPPER <- top of stack
+; |xxxxxxxx| \
+; |xxxxxxxx| |- stack used by startup code
+; |xxxxxxxx| /
+; |yyyyyyyy|<- sp \ <- current stack position
+; |yyyyyyyy| |
+; |yyyyyyyy| |
+; |yyyyyyyy| |- stack used by C program
+; |yyyyyyyy| |
+; |yyyyyyyy| |
+; |yyyyyyyy|<- Pivot (d0) / <- last used longword
+; |DEADF00D| \
+; |DEADF00D| |
+; |DEADF00D| |- unused stack
+; |DEADF00D| |
+; |DEADF00D|<- TC_SPLOWER / <- bottom of stack
+;
+
+ move.l TC_SPLOWER(a3),a0
+ move.l #$DEADF00D,d0
+
+.cmpstack:
+ move.l (a0)+,d1 ; read one longword and go on next
+ cmp.l d0,d1 ; is the magic number still there?
+ beq.s .cmpstack ; yes: loop again
+
+ move.l a0,d0 ; make a copy of the Pivot
+
+ move.l TC_SPLOWER(a3),d1
+ sub.l d1,d0 ; Pivot - TC_SPLOWER
+ PUSH.l d0 ; push unused stack size (argument 4)
+
+ move.l TC_SPUPPER(a3),d0
+ sub.l a0,d0 ; TC_SPUPPER - Pivot
+ PUSH.l d0 ; push used stack size (argument 3)
+
+ move.l TC_SPUPPER(a3),d0
+ sub.l d1,d0 ; TC_SPUPPER - TC_SPLOWER
+ PUSH.l d0 ; push total stack size (argument 2)
+
+ pea .msg(pc) ; push format string (argument 1)
+ jsr _kprintf ; output debug string with kprintf()
+
+ lea 16(sp),sp ; fix stack (4 arguments)
+ bra.s .end_debug
+
+.msg: dc.b 'Startup.asm: Total stack size: %ld, Used: %ld, Unused: %ld.',$0A, 0
+ even
+.end_debug:
+
+ move.l d7,d0 ; restore return code
+ ENDC
+
+
+
+; Swap back to original stack
+ move.l _SysBase(a4),a6
+ lea stackswapstruct(a4),a0
+ tst.l (a0)
+ beq.s .noswap
+
+ JSRLIB StackSwap
+
+; free the stack
+ move.l stackswapstruct(a4),a1 ; FreeMem (stk_Lower,STKSIZE)
+ move.l #STKSIZE,d0
+ JSRLIB FreeMem
+
+.noswap:
+ move.l _StackPtr(a4),a2 ; restore original StackPtr
+
+; if we ran from CLI, skip workbench cleanup:
+ tst.l _WBenchMsg(a4)
+ beq.s exitdos
+ move.l _DOSBase(a4),a6
+ move.l __curdir(a4),d1
+ beq.s .nounlock
+ JSRLIB UnLock
+
+.nounlock:
+
+; return the startup message to our parent.
+; We Forbid() so Workbench can't UnLoadSeg() us
+; before we are done.
+
+ move.l _SysBase(a4),a6
+ JSRLIB Forbid
+ move.l _WBenchMsg(a4),a1
+ JSRLIB ReplyMsg
+
+exitdos:
+ move.l _DOSBase(a4),a1 ; Close dos.library
+ JSRLIB CloseLibrary
+ move.l d7,d0 ; Put return code in D0
+ rts ; This RTS sends us back to our caller
+
+returnfail:
+ moveq.l #RETURN_FAIL,d0
+ rts
+
+
+
+;**************************************************************************
+;* Simple (V)SPrintf routines
+;**************************************************************************
+
+_SPrintf:
+ movem.l a2-a3/a6,-(sp) ; Save registers
+
+ move.l 4+12(sp),a3 ; Get destination buffer
+ move.l 8+12(sp),a0 ; Get format string
+ lea.l 12+12(sp),a1 ; Get arguments
+ lea.l StuffChar(pc),a2 ; Get formatting routine
+
+ move.l _SysBase(a4),a6 ; Get ExecBase
+ JSRLIB RawDoFmt ; Format the string
+
+ movem.l (sp)+,a2-a3/a6 ; Restore registers
+
+ rts
+
+
+
+_VSPrintf:
+ movem.l a2/a3/a6,-(sp)
+
+ move.l 4+12(sp),a3
+ move.l 8+12(sp),a0
+ move.l 12+12(sp),a1
+ lea StuffChar(pc),a2
+
+ move.l _SysBase(a4),a6
+ JSRLIB RawDoFmt
+
+ movem.l (sp)+,a2/a3/a6
+
+ rts
+
+StuffChar:
+ move.b d0,(a3)+
+ rts
+
+
+
+;**************************************************************************
+;* Memory pools support
+;**************************************************************************
+
+;
+; AsmAllocVecPooled (Pool, memSize, SysBase)
+; a0 d0 a6
+;
+_AsmAllocVecPooled:
+ addq.l #4,d0 ; Get space for tracking
+ move.l d0,-(sp) ; Save the size
+
+ IFD OS30_ONLY
+ JSRLIB AllocPooled
+ ELSE
+ jsr _AsmAllocPooled ; Call pool...
+ ENDC
+
+ move.l (sp)+,d1 ; Get size back...
+ tst.l d0 ; Check for error
+ beq.s .fail ; If NULL, failed!
+ move.l d0,a0 ; Get pointer...
+ move.l d1,(a0)+ ; Store size
+ move.l a0,d0 ; Get result
+.fail rts ; Return
+
+
+
+;
+; AsmFreeVecPooled (Pool, Memory, SysBase)
+; a0 a1 a6
+
+_AsmFreeVecPooled:
+ move.l a1,d0 ; Test for NULL
+ beq.s .noblock
+ move.l -(a1),d0 ; Get size / ajust pointer
+
+ IFD OS30_ONLY
+ JMPLIB FreePooled
+ ELSE
+ jmp _AsmFreePooled
+ ENDC
+
+.noblock
+ rts
+
+;
+; CAllocPooled (Pool, memSize, SysBase)
+; a0 d0 a6
+
+_AsmCAllocPooled:
+ move.l d0,-(sp) ; Save the size
+ IFD OS30_ONLY
+ JSRLIB AllocPooled ; Allocate memory from pool...
+ ELSE
+ jsr _AsmAllocPooled ; Allocate memory from pool...
+ ENDC
+ move.l (sp)+,d1 ; Get size back...
+ move.l d0,a0
+ tst.l d0 ; Check for error
+ beq.s .fail ; If NULL, failed!
+
+ move.l d0,-(sp) ; Save result
+
+
+ moveq #$F,d0 ; Check for odd length (not multiple of 16)
+ and.w d1,d0
+ beq.s .noneed
+
+ move.l a0,a1
+ add.l d1,a1
+ subq.w #1,d0 ; DBRA loops once too much for us
+
+.clear2 move.b #0,-(a1) ; Clear up to 15 bytes
+ dbra d0,.clear2
+
+.noneed
+ lsr.l #4,d1 ; Divide by 16
+ tst.l d1 ; Check for 0
+ beq.s .endclear
+
+ moveq #0,d0 ; D0 will be used to clear our memory buffer
+ subq.l #1,d1 ; DBRA does one cycle more!
+
+.clear move.l d0,(a0)+ ; Clear memory block
+ move.l d0,(a0)+
+ move.l d0,(a0)+
+ move.l d0,(a0)+
+ dbra d1,.clear
+ swap d1 ; dbra only works on words
+ subq.w #1,d1
+ bmi.s .endclear
+ swap d1
+ bra.s .clear
+.endclear
+
+ move.l (sp)+,d0 ; Get result back...
+.fail rts ; Return
+
+
+
+DOSName dc.b 'dos.library',0
+
+
+
+;**************************************************************************
+;* Data section
+;**************************************************************************
+
+ SECTION __MERGED,BSS
+
+_SysBase ds.l 1
+_DOSBase ds.l 1
+_ThisTask ds.l 1
+_StdOut ds.l 1
+_StackPtr ds.l 1
+_WBenchMsg ds.l 1
+__curdir ds.l 1
+
+stackswapstruct ds.b StackSwapStruct_SIZEOF
+
+ END
--- /dev/null
+/*
+** ToolBoxWin.c
+**
+** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
+**
+** Handle ToolBox panel
+*/
+
+#include <exec/memory.h>
+#include <intuition/intuition.h>
+#include <intuition/gadgetclass.h>
+#include <libraries/gadtools.h>
+#include <libraries/locale.h>
+#include <workbench/workbench.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/wb.h>
+#include <proto/asl.h>
+#include <proto/icon.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* Gadgets IDs */
+
+enum
+{
+ GD_ToolBoxGroup0,
+ GD_ToolBoxGroup1,
+ GD_ToolBoxGroup2,
+ GD_Play,
+ GD_EditSongs,
+ GD_ToolBoxGroup3,
+ GD_EditPatterns,
+ GD_EditInstruments,
+ GD_ToolBoxGroup4,
+ GD_EditSequence,
+ GD_Optimization,
+ ToolBox_CNT
+};
+
+
+
+/*****************************/
+/* Local function prototypes */
+/*****************************/
+
+static void EditInstrumentsClicked (void);
+static void EditSequenceClicked (void);
+static void EditPatternsClicked (void);
+static void OptimizationClicked (void);
+static void PlayClicked (void);
+static void EditSongsClicked (void);
+
+static void ToolBoxMiNew (void);
+static void ToolBoxMiOpen (void);
+static void ToolBoxMiOpenNew (void);
+static void ToolBoxMiSave (void);
+static void ToolBoxMiSaveAs (void);
+static void ToolBoxMiSaveFormat (void);
+static void ToolBoxMiClearMod (void);
+static void ToolBoxMiAbout (void);
+static void ToolBoxMiHelp (void);
+static void ToolBoxMiIconify (void);
+static void ToolBoxMiQuit (void);
+static void ToolBoxMiUserInterface (void);
+static void ToolBoxMiSaveIcons (void);
+static void ToolBoxMiOverwrite (void);
+static void ToolBoxMiAskExit (void);
+static void ToolBoxMiOpenSettings (void);
+static void ToolBoxMiSaveSettings (void);
+static void ToolBoxMiSaveSettingsAs (void);
+
+
+
+static struct NewMenu ToolBoxNewMenu[] = {
+ NM_TITLE, (STRPTR)MSG_PROJECT_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_NEW_MEN, (STRPTR)"N", 0, 0L, (APTR)ToolBoxMiNew,
+ NM_ITEM, (STRPTR)MSG_OPEN_MEN, (STRPTR)"O", 0, 0L, (APTR)ToolBoxMiOpen,
+ NM_ITEM, (STRPTR)MSG_OPEN_NEW_MEN, (STRPTR)"Y", 0, 0L, (APTR)ToolBoxMiOpenNew,
+ NM_ITEM, (STRPTR)MSG_SAVE_MEN, (STRPTR)"S", 0, 0L, (APTR)ToolBoxMiSave,
+ NM_ITEM, (STRPTR)MSG_SAVE_AS_MEN, (STRPTR)"A", 0, 0L, (APTR)ToolBoxMiSaveAs,
+ NM_ITEM, (STRPTR)MSG_SAVE_FORMAT_MEN, (STRPTR)"F", 0, 0L, (APTR)ToolBoxMiSaveFormat,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_CLEAR_MEN, (STRPTR)"K", 0, 0L, (APTR)ToolBoxMiClearMod,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_ABOUT_MEN, (STRPTR)"?", 0, 0L, (APTR)ToolBoxMiAbout,
+ NM_ITEM, (STRPTR)MSG_HELP_MEN, NULL, 0, 0L, (APTR)ToolBoxMiHelp,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_ICONIFY_MEN, (STRPTR)"I", 0, 0L, (APTR)ToolBoxMiIconify,
+ NM_ITEM, (STRPTR)MSG_QUIT_MEN, (STRPTR)"Q", 0, 0L, (APTR)ToolBoxMiQuit,
+ NM_TITLE, (STRPTR)MSG_SETTINGS_MEN, NULL, 0, NULL, NULL,
+ NM_ITEM, (STRPTR)MSG_USER_INTERFACE_MEN, NULL, 0, 0L, (APTR)ToolBoxMiUserInterface,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_SAVE_ICONS_MEN, NULL, CHECKIT|MENUTOGGLE, 0L, (APTR)ToolBoxMiSaveIcons,
+ NM_ITEM, (STRPTR)MSG_CONFIRM_OVERWRITE_MEN, NULL, CHECKIT|MENUTOGGLE, 0L, (APTR)ToolBoxMiOverwrite,
+ NM_ITEM, (STRPTR)MSG_CONFIRM_EXIT_MEN, NULL, CHECKIT|MENUTOGGLE, 0L, (APTR)ToolBoxMiAskExit,
+ NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
+ NM_ITEM, (STRPTR)MSG_OPEN_SETTINGS_MEN, NULL, 0, 0L, (APTR)ToolBoxMiOpenSettings,
+ NM_ITEM, (STRPTR)MSG_SAVE_SETTINGS_MEN, NULL, 0, 0L, (APTR)ToolBoxMiSaveSettings,
+ NM_ITEM, (STRPTR)MSG_SAVE_SETTINGS_AS_MEN, NULL, 0, 0L, (APTR)ToolBoxMiSaveSettingsAs,
+ NM_END, NULL, NULL, 0, 0L, NULL
+};
+
+
+
+static ULONG ToolBoxArgs[] =
+{
+ HGROUP_KIND, 0,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)PlayClicked, MSG_PLAY_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)EditSongsClicked, MSG_SONGS_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)EditPatternsClicked, MSG_PATTERNS_DOTS_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)EditInstrumentsClicked, MSG_INSTRUMENTS_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ VGROUP_KIND, 0,
+ BUTTON_KIND, (ULONG)EditSequenceClicked, MSG_SEQUENCE_DOTS_GAD, TAG_DONE,
+ BUTTON_KIND, (ULONG)OptimizationClicked, MSG_OPTIMIZATION_GAD, TAG_DONE,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND,
+ ENDGROUP_KIND
+};
+
+
+
+XDEF LONG ToolBoxWinTags[] =
+{
+ XMWIN_NewMenu, (LONG)ToolBoxNewMenu,
+ XMWIN_LayoutArgs, (LONG)ToolBoxArgs,
+ XMWIN_GCount, ToolBox_CNT,
+ XMWIN_Title, MSG_TOOLBOX_TITLE,
+ XMWIN_WindowFlags, WFLG_CLOSEGADGET | WFLG_ACTIVATE,
+ XMWIN_IDCMPFlags, BUTTONIDCMP|IDCMP_MENUPICK|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
+ XMWIN_PostOpenFunc, (LONG)UpdateGuiSwitches,
+ XMWIN_HelpNode, (LONG)"ToolBox",
+ TAG_DONE
+};
+
+
+
+
+/*********************/
+/* ToolBox Functions */
+/*********************/
+
+
+
+GLOBALCALL void ToolBoxDropIcon (struct AppMessage *msg)
+{
+ struct WBArg *wba = msg->am_ArgList;
+ BPTR olddir;
+ ULONG i;
+ struct SongInfo *si;
+
+ LockWindows();
+
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+
+ olddir = CurrentDir (wba->wa_Lock);
+
+ for (i = 0; i < msg->am_NumArgs; i++)
+ {
+ CurrentDir (wba->wa_Lock);
+
+ if (si = xmLoadModule (wba->wa_Name,
+ XMSNG_OldSong, i ? NULL : XModuleBase->xm_CurrentSong,
+ XMSNG_AddToList, -1,
+ XMSNG_Active, i == (msg->am_NumArgs - 1),
+ TAG_DONE))
+ ReleaseSemaphore (&si->Lock); /* For XMSNG_AddToList */
+
+ wba++;
+ }
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ CurrentDir (olddir);
+ UnlockWindows();
+}
+
+
+
+GLOBALCALL void ToolBoxOpenModule (CONST_STRPTR file, ULONG num, ULONG count)
+
+/* Handle FileRequester Open Module message */
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ ObtainSemaphore (&XModuleBase->xm_BaseLock);
+
+ DB(kprintf ("opening song %ld of %ld\n", num, count));
+
+ if (si = xmLoadModule (file,
+ XMSNG_AddToList, num ? (ULONG)XModuleBase->xm_CurrentSong : (ULONG)~0,
+ XMSNG_Active, num == (count - 1),
+ XMSNG_OldSong, num ? (ULONG)NULL : (ULONG)XModuleBase->xm_CurrentSong,
+ TAG_DONE))
+ ReleaseSemaphore (&si->Lock); /* This is for XMSNG_AddToList */
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ UnlockWindows();
+}
+
+
+GLOBALCALL void UpdateGuiSwitches ()
+{
+ struct MenuItem *item;
+ struct WinUserData *wud = WDescr[WID_TOOLBOX].Wud;
+
+ if (wud && wud->MenuStrip)
+ {
+ if (wud->Win) ClearMenuStrip (wud->Win);
+
+ item = ItemAddress (wud->MenuStrip, SHIFTMENU(1) | SHIFTITEM(2) );
+
+ /* Save Icons? */
+ if (GuiSwitches.SaveIcons)
+ item->Flags |= CHECKED;
+ else
+ item->Flags &= ~CHECKED;
+
+ /* Confirm Overwrite? */
+ item = item->NextItem;
+ if (GuiSwitches.AskOverwrite)
+ item->Flags |= CHECKED;
+ else
+ item->Flags &= ~CHECKED;
+
+ /* Confirm Exit? */
+ item = item->NextItem;
+ if (GuiSwitches.AskExit)
+ item->Flags |= CHECKED;
+ else
+ item->Flags &= ~CHECKED;
+
+ if (wud->Win) ResetMenuStrip (wud->Win, wud->MenuStrip);
+ }
+}
+
+
+
+/*******************/
+/* ToolBox Gadgets */
+/*******************/
+
+static void EditInstrumentsClicked (void)
+{
+ NewWindow (WID_INSTRUMENTS);
+}
+
+static void EditSequenceClicked (void)
+{
+ NewWindow (WID_SEQUENCE);
+}
+
+static void OptimizationClicked (void)
+{
+ NewWindow (WID_OPTIMIZATION);
+}
+
+static void PlayClicked (void)
+{
+ NewWindow (WID_PLAY);
+}
+
+static void EditSongsClicked (void)
+{
+ NewWindow (WID_SONGINFO);
+}
+
+static void EditPatternsClicked (void)
+{
+ NewWindow (WID_PATTERN);
+}
+
+
+
+/**************/
+/* Menu Items */
+/**************/
+
+static void ToolBoxMiNew (void)
+{
+ struct SongInfo *si;
+
+ if (si = xmCreateSong (
+ SNGA_ReadyToUse, TRUE,
+ XMSNG_AddToList, -1,
+ XMSNG_Active, TRUE,
+ TAG_DONE))
+ ReleaseSemaphore (&si->Lock); /* This is for XMSNG_AddToList */
+ else
+ DisplayBeep (NULL);
+}
+
+
+
+static void ToolBoxMiOpen (void)
+{
+ StartFileRequest (FREQ_LOADMOD, ToolBoxOpenModule);
+}
+
+
+
+static void ToolBoxMiOpenNew (void)
+{
+ ToolBoxMiNew();
+ ToolBoxMiOpen();
+}
+
+
+
+static void ToolBoxMiSave (void)
+{
+ struct SongInfo *si;
+
+ LockWindows();
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+
+ if (si = XModuleBase->xm_CurrentSong)
+ {
+ ObtainSemaphoreShared (&si->Lock);
+
+ if (!(LastErr = xmSaveModuleA (si, si->Path, NULL, NULL)))
+ si->Changes = 0;
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ UnlockWindows();
+}
+
+
+
+static void ToolBoxMiSaveAs (void)
+{
+ struct SongInfo *si;
+ UBYTE pathname[PATHNAME_MAX];
+
+ LockWindows();
+
+ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
+
+ if (si = XModuleBase->xm_CurrentSong)
+ {
+ ObtainSemaphoreShared (&si->Lock);
+
+ if (si->Path)
+ {
+ strncpy (pathname, si->Path, PATHNAME_MAX - 1);
+ pathname[PATHNAME_MAX - 1] = '\0';
+ }
+ else pathname[0] = '\0';
+
+ if (FileRequest (FREQ_SAVEMOD, pathname))
+ {
+ SetAttrs (si,
+ SNGA_Path, pathname,
+ TAG_DONE);
+
+ ToolBoxMiSave();
+ }
+
+ ReleaseSemaphore (&si->Lock);
+ }
+
+ ReleaseSemaphore (&XModuleBase->xm_BaseLock);
+
+ UnlockWindows();
+}
+
+
+
+static void ToolBoxMiClearMod (void)
+{
+ NewWindow (WID_CLEAR);
+}
+
+
+
+static void ToolBoxMiAbout (void)
+{
+ ShowRequest (MSG_ABOUT_TEXT, MSG_CONTINUE,
+ XMODULEVER " " XMODULEDATE " " BUILDMODE,
+ XMODULECOPY,
+ AvailMem (MEMF_CHIP) >> 10,
+ AvailMem (MEMF_FAST) >> 10,
+ ScrInfo.PubScreenName[0] ? ScrInfo.PubScreenName : STR(MSG_DEFAULT),
+ PubPort ? PubPortName : STR(MSG_DISABLED),
+ CxPort ? CxPopKey : STR(MSG_DISABLED),
+ Catalog ? Catalog->cat_Language : (UBYTE *)"English");
+}
+
+
+
+static void ToolBoxMiHelp (void)
+{
+ HandleHelp (NULL);
+}
+
+static void ToolBoxMiIconify (void)
+{
+ Iconify();
+ DoNextSelect = FALSE;
+}
+
+
+
+static void ToolBoxMiQuit (void)
+{
+ DoNextSelect = 0;
+ Quit = 1;
+}
+
+
+
+static void ToolBoxMiSaveFormat (void)
+{
+ NewWindow (WID_SAVERS);
+}
+
+static void ToolBoxMiUserInterface (void)
+{
+ NewWindow (WID_PREFS);
+}
+
+
+
+static void ToolBoxMiSaveIcons (void)
+{
+ GuiSwitches.SaveIcons ^= 1;
+}
+
+
+
+static void ToolBoxMiOverwrite (void)
+{
+ GuiSwitches.AskOverwrite ^= 1;
+}
+
+
+
+static void ToolBoxMiAskExit (void)
+{
+ GuiSwitches.AskExit ^= 1;
+}
+
+
+
+static void ToolBoxMiOpenSettings (void)
+{
+ UBYTE filename[PATHNAME_MAX];
+
+ strcpy (filename, "XModule.prefs");
+
+ if (FileRequest (FREQ_LOADMISC, filename))
+ LastErr = LoadPrefs (filename);
+}
+
+
+
+static void ToolBoxMiSaveSettings (void)
+{
+ LastErr = SavePrefs ("PROGDIR:XModule.prefs");
+}
+
+
+
+static void ToolBoxMiSaveSettingsAs (void)
+{
+ UBYTE filename[PATHNAME_MAX];
+
+ strcpy (filename, "PROGDIR:XModule.prefs");
+
+ if (FileRequest (FREQ_SAVEMISC, filename))
+ LastErr = SavePrefs (filename);
+}
--- /dev/null
+/*
+** TrackerHook.c
+**
+** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
+**
+**
+** Load a Sound/Noise/ProTracker module with 15 or 31 instruments, decode
+** it and store into internal structures.
+**
+** Save internal structures to a SoundTracker module with 15 instruments
+** or to a 31 instruments Noise/ProTracker unpacked module.
+*/
+
+#include <exec/memory.h>
+#include <dos/stdio.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/iffparse.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+
+/* This structure holds a tracker row */
+
+struct StRow
+{
+ UWORD Note;
+ UBYTE InstEff;
+ UBYTE EffVal;
+};
+
+
+
+/* Definitions for various xxxTracker IDs. */
+
+#define ID_SOUNDTRACKER 'ST15' /* "ST15" */
+#define ID_TAKETRACKER '32CH' /* "32CH" */
+#define ID_FASTTRACKER1 '8CHN' /* "8CHN" */
+#define ID_NOISETRACKER 0x4D264B21 /* "M&K!" */
+#define ID_PROTRACKER 0x4D2E4B2E /* "M.K." */
+#define ID_PROTRACKER100 0x4D214B21 /* "M!K!" */
+#define ID_STARTREKKER4 0x464C5434 /* "FLT4" */
+#define ID_STARTREKKER8 0x464C5438 /* "FLT8" */
+#define ID_UNICTRACKER 0x454D5733 /* "EMW3" */
+#define ID_POWERMUSIC 0x21504D21 /* "!PM!" */
+
+
+
+/* Local function prototypes */
+static HOOKCALL struct XMHook *IdentifyTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags));
+static HOOKCALL struct XMHook *IdentifySoundTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags));
+static HOOKCALL LONG SaveTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *saver),
+ REG(a2, ULONG *tags));
+static HOOKCALL LONG LoadTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags));
+
+static void SetGlobalSpeed (struct SongInfo *si, UWORD tracks);
+INLINE UWORD DecodeNote (UWORD Note, UWORD Patt, UWORD Line, UWORD Track);
+INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval);
+
+
+static const ULONG TakeTrackerIDs[32] =
+{
+ 'TDZ1', 'TDZ2', 'TDZ3', 'M.K.', '5CHN', '6CHN', '7CHN', '8CHN',
+ '9CHN', '10CH', '11CH', '12CH', '13CH', '14CH', '15CH', '16CH',
+ '17CH', '18CH', '19CH', '20CH', '21CH', '22CH', '23CH', '24CH',
+ '25CH', '26CH', '27CH', '28CH', '29CH', '30CH', '31CH', '32CH'
+};
+
+
+/* Note conversion table for Sound/Noise/ProTracker */
+static const UWORD TrackerNotes[] =
+{
+ 0x000, /* Null note */
+
+ 0x6B0, 0x650, 0x5F5, 0x5A0, 0x54D, 0x501, /* Octave 0 */
+ 0x4B9, 0x475, 0x435, 0x3F9, 0x3C1, 0x38B,
+
+ 0x358, 0x328, 0x2FA, 0x2D0, 0x2A6, 0x280, /* Octave 1 */
+ 0x25C, 0x23A, 0x21A, 0x1FC, 0x1E0, 0x1C5,
+
+ 0x1AC, 0x194, 0x17D, 0x168, 0x153, 0x140, /* Octave 2 */
+ 0x12E, 0x11d, 0x10D, 0x0FE, 0x0F0, 0x0E2,
+
+ 0x0D6, 0x0CA, 0x0BE, 0x0B4, 0x0AA, 0x0A0, /* Octave 3 */
+ 0x097, 0x08F, 0x087, 0x07F, 0x078, 0x071,
+
+ 0x06B, 0x065, 0x05F, 0x05A, 0x055, 0x050, /* Octave 4 */
+ 0x04C, 0x047, 0x043, 0x040, 0x03C, 0x039,
+
+ 0x035, 0x032, 0x030, 0x02D, 0x02A, 0x028, /* Octave 5 */
+ 0x026, 0x024, 0x022, 0x020, 0x01E, 0x01C
+};
+
+
+
+static const UBYTE Effects[MAXTABLEEFFECTS] =
+{
+/* STRK XModule Val */
+
+ 0x00, /* Null effect $00 */
+
+ 0x01, /* Portamento Up $01 */
+ 0x02, /* Portamento Down $02 */
+ 0x03, /* Tone Portamento $03 */
+ 0x04, /* Vibrato $04 */
+ 0x05, /* ToneP + VolSl $05 */
+ 0x06, /* Vibra + VolSl $06 */
+ 0x07, /* Tremolo $07 */
+ 0x08, /* Set Hold/Decay $08 */
+ 0x09, /* Sample Offset $09 */
+ 0x0A, /* Volume Slide $0A */
+ 0x0B, /* Position Jump $0B */
+ 0x0C, /* Set Volume $0C */
+ 0x0D, /* Pattern break $0D */
+ 0x0E, /* Misc $0E */
+ 0x0F, /* Set Speed $0F */
+ 0x0F, /* Set Tempo $10 */
+ 0x00, /* Arpeggio $11 */
+
+ 0x03, /* Oktalyzer H */
+ 0x03, /* Oktalyzer L */
+};
+
+
+
+static struct XMHook *ProTrackerLoader = NULL;
+
+
+
+static HOOKCALL struct XMHook *IdentifyTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags))
+
+/* Determine if the given file is an XyzTracker module.
+ * Note: the file position will be changed on exit.
+ */
+{
+ LONG id;
+ ULONG i;
+
+ Seek (fh, 1080, OFFSET_BEGINNING);
+ if (FRead (fh, &id, 4, 1) != 1)
+ return NULL;
+
+ /* Check Noise/ProTracker */
+ if ((id == ID_PROTRACKER) || (id == ID_PROTRACKER100) || (id == ID_NOISETRACKER) ||
+ (id == ID_UNICTRACKER) || (id == ID_STARTREKKER4))
+ return loader;
+
+ /* Check for Multi/Fast Tracker */
+ for (i = 0; i < 32; i++)
+ if (id == TakeTrackerIDs[i])
+ return loader;
+
+ return NULL;
+}
+
+
+
+static HOOKCALL struct XMHook *IdentifySoundTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags))
+
+/* Last chance loader: SoundTracker15 or weird ProTracker module */
+{
+ switch (ShowRequestArgs (MSG_UNKNOWN_MOD_FORMAT, MSG_SOUND_PRO_CANCEL, NULL))
+ {
+ case 1:
+ return loader;
+
+ case 2:
+ return ProTrackerLoader;
+
+ default:
+ return NULL;
+ }
+}
+
+
+
+static HOOKCALL LONG SaveTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *saver),
+ REG(a2, ULONG *tags))
+{
+ struct Instrument *instr;
+ ULONG pattcount = 0, maxnumpatt = 64, numtracks = 4, songlength;
+ ULONG l;
+ ULONG i, j, k; /* Loop counters */
+ UBYTE c;
+ struct { UWORD Note; UBYTE InstEff; UBYTE EffVal; }
+ strow; /* Temporary storage for a SoundTracker row */
+
+
+
+ /* Calculate pattcount */
+
+ songlength = min (si->Length, 128);
+
+ for (i = 0 ; i < songlength ; i++)
+ if (si->Sequence[i] > pattcount)
+ pattcount = si->Sequence[i];
+
+ pattcount++; /* Pattern numbering starts from 0 */
+
+
+ /* Write Song name */
+ {
+ UBYTE name[20] = {0};
+
+ /* Tracker modules do not require the NULL terminator */
+ if (si->Title) strncpy (name, si->Title, 20);
+
+ if (FWrite (fh, name, 20, 1) != 1) return ERROR_IOERR;
+ }
+
+
+ /* Write instruments name, length, volume, repeat, replen */
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_WRITING_INSTINFO, NULL);
+
+
+ for ( j = 1 ; j < (((ULONG)saver->xmh_ID == ID_SOUNDTRACKER) ? 16 : 32) ; j++ )
+ {
+ struct
+ {
+ UBYTE namebuf[22];
+ UWORD length;
+ UWORD volume;
+ UWORD repeat;
+ UWORD replen;
+ } modinstr;
+
+
+ memset (&modinstr, 0, sizeof (modinstr));
+
+ if (instr = si->Instr[j])
+ {
+ /* Some Trackers require the instrument buffer to be
+ * padded with 0's.
+ */
+ strncpy (modinstr.namebuf, instr->Name, 22);
+
+ /* SoundTracker cannot handle instruments longer than 64K */
+ if (instr->Length > 0xFFFE)
+ {
+ modinstr.length = 0x7FFF;
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_WARNING,
+ (APTR)MSG_INSTR_TOO_LONG, j);
+ }
+ else modinstr.length = instr->Length >> 1;
+
+ if (!instr->Sample) modinstr.length = 0;
+
+
+ if (saver->xmh_ID == ID_SOUNDTRACKER)
+ modinstr.volume = instr->Volume;
+ else
+ modinstr.volume = instr->Volume | ((instr->FineTune & 0x0F) << 8);
+
+ modinstr.repeat = instr->Repeat >> 1;
+ modinstr.replen = instr->Replen >> 1;
+ if (!modinstr.replen) modinstr.replen = 1;
+ }
+
+ if (FWrite (fh, &modinstr, sizeof (modinstr), 1) != 1)
+ return ERROR_IOERR;
+ }
+
+
+ /******************************************/
+ /* Put number of positions in song (BYTE) */
+ /******************************************/
+
+ if (FPutC (fh, songlength) == ENDSTREAMCH)
+ return ERROR_IOERR;
+
+
+ /*****************************************/
+ /* Put number of patterns in song (BYTE) */
+ /*****************************************/
+
+ switch (saver->xmh_ID)
+ {
+ case ID_SOUNDTRACKER:
+ /* SoundTracker stores the number of patterns here */
+ c = pattcount;
+ break;
+
+ case ID_STARTREKKER4:
+ /* StarTrekker modules store restart value here */
+ c = si->RestartPos;
+ break;
+
+ default:
+ /* Noise/ProTracker stores $7F as the number of patterns.
+ * The correct number of patterns is calculated by looking
+ * for the highest pattern referenced in the position table.
+ * Therefore, unused patterns MUST be removed, or the Tracker
+ * will get confused loading the module.
+ */
+ c = 0x7F;
+ break;
+ }
+
+ if (FPutC (fh, c) == ENDSTREAMCH)
+ return ERROR_IOERR;
+
+
+ /********************/
+ /* Choose module ID */
+ /********************/
+
+ l = saver->xmh_ID;
+
+ switch (l)
+ {
+ case ID_PROTRACKER:
+ if (pattcount > 64)
+ {
+ l = ID_PROTRACKER100;
+ maxnumpatt = 100;
+ xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_EXCEEDS_64_PATTS, NULL);
+ }
+ break;
+
+ case ID_PROTRACKER100:
+ maxnumpatt = 100;
+ if (pattcount <= 64)
+ l = ID_PROTRACKER;
+ break;
+
+ case ID_TAKETRACKER:
+ numtracks = si->MaxTracks;
+ l = TakeTrackerIDs[si->MaxTracks - 1];
+ maxnumpatt = 100;
+ break;
+
+ default:
+ break;
+ }
+
+ if (pattcount >= maxnumpatt)
+ {
+ pattcount = maxnumpatt;
+ xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_EXCEEDS_MAXPAATTS, maxnumpatt);
+ }
+
+
+ /************************/
+ /* Write position table */
+ /************************/
+ {
+ UBYTE postable[128];
+
+ memset (postable, 0, 128);
+
+ /* All this is needed because ProTracker has serious
+ * problems dealing with modules whose postable has
+ * references to inexistant patterns.
+ */
+ for (i = 0; i < songlength; i++)
+ {
+ postable[i] = si->Sequence[i];
+
+ if (postable[i] >= pattcount)
+ postable[i] = 0;
+ }
+
+ if (FWrite (fh, postable, 1, 128) != 128) return ERROR_IOERR;
+ }
+
+
+ /*******************/
+ /* Write module ID */
+ /*******************/
+
+ if (l != ID_SOUNDTRACKER)
+ if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
+
+
+
+ /**********************/
+ /* Write pattern data */
+ /**********************/
+
+ SetGlobalSpeed (si, numtracks);
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_WRITING_PATTS, NULL);
+
+ for (i = 0 ; i < pattcount ; i++)
+ {
+ struct Note **tracks = si->Patt[i]->Notes;
+
+ if (xmDisplayProgress (i, pattcount))
+ return ERROR_BREAK;
+
+ for (j = 0 ; j < 0x40 ; j++)
+ {
+ for (k = 0 ; k < numtracks ; k++)
+ {
+ struct Note *note = &tracks[k][j];
+
+ /* Translate Note */
+ strow.Note = TrackerNotes[note->Note];
+
+ /* Translate instr # (high nibble) & effect (low nibble) */
+ c = note->Inst;
+
+ if ((c > 0x0F) && (saver->xmh_ID != ID_SOUNDTRACKER))
+ {
+ /* Noise/ProTracker stores the high bit of the
+ * instrument number in bit 15 of the note value.
+ */
+ strow.Note |= 0x1000;
+ }
+
+ strow.InstEff = (c << 4) | Effects[note->EffNum];
+
+ /* Copy effect value */
+ strow.EffVal = note->EffVal;
+
+ /* Write the Row */
+ if (FWrite (fh, &strow, 4, 1) != 1) return ERROR_IOERR;
+ }
+ }
+ }
+
+ /* Instruments are written using non buffered I/O because it's
+ * generally more efficient when writing big buffers.
+ */
+ Flush (fh);
+
+ /********************/
+ /* Save Instruments */
+ /********************/
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_WRITING_INSTDATA, NULL);
+
+ for (i = 1 ; i < (saver->xmh_ID == ID_SOUNDTRACKER ? 16 : 32) ; i++)
+ {
+ ULONG len;
+
+ if (!(instr = si->Instr[i])) continue;
+
+ len = instr->Length & (~1);
+
+ /* Adapt instrument to SoundTracker 64K limit */
+ if (len > 0xFFFE) len = 0xFFFE;
+
+ if (xmDisplayProgress (i, (saver->xmh_ID == ID_SOUNDTRACKER) ? 16 : 32))
+ return ERROR_BREAK;
+
+ if (len)
+ if (Write (fh, instr->Sample, len) != len)
+ return ERROR_IOERR;
+ }
+
+ return (0);
+}
+
+
+
+static HOOKCALL LONG LoadTracker (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags))
+{
+ struct Note *note;
+ struct Instrument *instr;
+ struct Pattern *patt;
+ struct StRow *stpatt, /* Temporary storage for a Tracker pattern */
+ *strow;
+
+ ULONG i, j, k; /* Loop counters */
+ ULONG pattsize;
+ UWORD numtracks = 4, numpatterns, songlen;
+ UBYTE c; /* Read buffers */
+
+ ULONG soundtracker = (loader->xmh_ID == ID_SOUNDTRACKER);
+
+
+ /* Get the score name */
+ {
+ UBYTE name[21];
+
+ if (FRead (fh, name, 20, 1) != 1) return ERROR_IOERR;
+ name[20] = '\0'; /* Ensure the string is Null-terminated */
+
+ SetAttrs (si,
+ SNGA_Title, name,
+ TAG_DONE);
+ }
+
+ /* Get the Instruments name, length, cycle, effects */
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_READING_INSTS_INFO, NULL);
+
+ for ( j = 1 ; j < (soundtracker ? 16 : 32); j++ )
+ {
+ struct
+ {
+ UBYTE namebuf[22];
+ UWORD length;
+ BYTE finetune;
+ UBYTE volume;
+ UWORD repeat;
+ UWORD replen;
+ } modinstr;
+
+
+ memset (&modinstr, 0, sizeof (modinstr));
+
+ if (FRead (fh, &modinstr, sizeof (modinstr), 1) != 1)
+ return ERROR_IOERR;
+
+ if (modinstr.namebuf[0] || modinstr.length)
+ {
+ modinstr.namebuf[21] = '\0'; /* Ensure the string is Null-terminated */
+
+ if (!xmAddInstrument (si, j,
+ INSTRA_Name, modinstr.namebuf,
+ INSTRA_Length, modinstr.length << 1,
+ INSTRA_FineTune, (modinstr.finetune & 0x08) ? /* Fix sign */
+ (modinstr.finetune | 0xF0) : modinstr.finetune,
+ INSTRA_Volume, modinstr.volume,
+ INSTRA_Repeat, modinstr.repeat << 1,
+ INSTRA_Replen, (modinstr.replen == 1) ? 0 : (modinstr.replen << 1),
+ TAG_DONE))
+ return ERROR_NO_FREE_STORE;
+ }
+ }
+
+
+ /* Read song length */
+
+ if ((songlen = FGetC (fh)) == ENDSTREAMCH) return ERROR_IOERR;
+
+ /* Read & Ignore number of song patterns.
+ *
+ * Noise/ProTracker stores $7F as the number of patterns.
+ * The correct number of patterns is calculated by looking
+ * for the highest pattern referenced in the position table.
+ * The OctaMED saver wrongly puts the number of patterns
+ * when saving as a ProTracker module.
+ * SoundTracker should save the correct number of patterns,
+ * but some 15 instrument modules I found had incorrect
+ * values here. So we always ignore this field.
+ */
+ if (FGetC (fh) == ENDSTREAMCH) return ERROR_IOERR;
+
+
+ /* Read the song sequence */
+ {
+ UBYTE postable[128];
+
+ if (FRead (fh, postable, 1, 128) != 128) return ERROR_IOERR;
+
+ if (!(xmSetSongLen (si, songlen)))
+ return ERROR_NO_FREE_STORE;
+
+ for (i = 0; i < si->Length; i++)
+ si->Sequence[i] = postable[i];
+
+ /* Search for the highest pattern referenced in the position
+ * table to find out the actual number of patterns.
+ *
+ * Important note: Some modules do have position table
+ * entries beyond the song length. These references *must*
+ * be taken into account to calculate the last pattern, so
+ * looping only <songlen> times here is *wrong*. The
+ * correct behaviour is to always look all of the 128
+ * position table entries.
+ */
+
+ numpatterns = 0;
+
+ for (i = 0 ; i < 128 ; i++)
+ if (postable[i] > numpatterns)
+ numpatterns = postable[i];
+
+ numpatterns++; /* Pattern numbering starts from 0 */
+ }
+
+
+
+ /* Check module ID */
+
+ if (!soundtracker)
+ {
+ ULONG __aligned id = 0;
+
+ /* The module ID could have been stripped away in modules
+ * coming from games and intros to make the module harder
+ * to rip, therefore its absence is acceptable.
+ */
+ if (FRead (fh, &id, 4, 1) != 1) return ERROR_IOERR;
+
+
+ /* Check for FastTracker/TakeTracker IDs */
+
+ for (i = 0; i < 32; i++)
+ {
+ if (id == TakeTrackerIDs[i])
+ {
+ numtracks = i + 1;
+ break;
+ }
+ }
+
+ if (id)
+ {
+ UBYTE buf[5];
+
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
+ (APTR)MSG_MODULE_ID, IDtoStr (id, buf));
+ }
+ }
+
+
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
+ (APTR)MSG_MODULE_HAS_N_CHN, numtracks);
+
+
+ /* Allocate memory for a full SoundTracker pattern */
+
+ si->MaxTracks = numtracks;
+ pattsize = (sizeof (struct StRow) * 0x40) * numtracks;
+
+ if (!(stpatt = AllocMem (pattsize, 0)))
+ return ERROR_NO_FREE_STORE;
+
+
+ /* Read pattern data */
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_READING_PATTS, NULL);
+
+ for (i = 0; i < numpatterns; i++)
+ {
+ if (xmDisplayProgress (i, numpatterns))
+ {
+ FreeMem (stpatt, pattsize);
+ return ERROR_BREAK;
+ }
+
+ /* Read a whole Tracker row */
+ if (FRead (fh, stpatt, pattsize, 1) != 1)
+ {
+ FreeMem (stpatt, pattsize);
+ return ERROR_IOERR;
+ }
+
+ /* Reset note counter */
+ strow = stpatt;
+
+ /* Allocate memory for pattern */
+ if (!(patt = xmAddPattern (si,
+ PATTA_Tracks, numtracks,
+ PATTA_Lines, 64,
+ TAG_DONE)))
+ {
+ FreeMem (stpatt, pattsize);
+ return ERROR_NO_FREE_STORE;
+ }
+
+ for ( j = 0 ; j < 0x40 ; j++ )
+ {
+ for ( k = 0 ; k < numtracks ; k++, strow++ )
+ {
+ /* Get address of the current pattern row */
+ note = &patt->Notes[k][j];
+
+ /* Decode note (highest nibble is cleared) */
+ note->Note = DecodeNote (strow->Note & 0xFFF, i, j, k);
+
+ /* Decode instrument number (high nibble) */
+ c = strow->InstEff >> 4; /* Get instrument nr. */
+ if (!soundtracker && (strow->Note & 0x1000))
+ c |= 0x10; /* High bit of Noise/ProTracker instrument */
+ note->Inst = c;
+
+ /* Decode effect (low nibble) */
+ note->EffNum = DecodeEff (strow->InstEff & 0x0F, strow->EffVal);
+
+ /* Copy effect value */
+ note->EffVal = strow->EffVal;
+ }
+ }
+ }
+
+ FreeMem (stpatt, pattsize);
+
+
+ /* Look for a SetSpeed command ($F) in the first row and
+ * set si->GlobalSpeed. If Speed is higher than 31,
+ * si->GlobalTempo should be initialized instead. (TODO)
+ */
+
+ SetAttrs (si,
+ SNGA_Author, -1,
+ SNGA_GlobalSpeed, 6,
+ SNGA_GlobalTempo, 125,
+ TAG_DONE);
+
+
+ /* Load Instruments */
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_READING_INSTS, NULL);
+
+
+ for (j = 1 ; j < (soundtracker ? 16 : 32) ; j++)
+ {
+ BYTE *sample;
+
+ /* Check if instrument exists */
+
+ if (!(instr = si->Instr[j])) continue;
+ if (instr->Length == 0) continue;
+
+ if (xmDisplayProgress (j, soundtracker ? 16 : 32))
+ return ERROR_BREAK;
+
+ if (!(sample = (BYTE *) AllocVec (instr->Length, MEMF_SAMPLE)))
+ return ERROR_NO_FREE_STORE;
+
+ if (FRead (fh, sample, instr->Length, 1) != 1)
+ {
+ FreeVec (sample);
+
+ /* Clear instrument lengths */
+ for (i = j; i < 31; i++)
+ if (si->Instr[i]) si->Instr[i]->Length = 0;
+
+ if (j == 0)
+ {
+ xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
+ (APTR)MSG_SONG_HAS_NO_INSTS, NULL);
+
+ return RETURN_WARN; /* Tell 'em this is a damn song */
+ }
+
+ return ERROR_IOERR;
+ }
+
+ xmSetInstrument (si, j,
+ INSTRA_Sample, sample,
+ TAG_DONE);
+ }
+
+ /* Check for extra data following the module */
+ if (FGetC (fh) != ENDSTREAMCH)
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_NOTE,
+ (APTR)MSG_EXTRA_DATA_AFTER_MOD, NULL);
+
+ return RETURN_OK;
+}
+
+
+
+INLINE UWORD DecodeNote (UWORD Note, UWORD Patt, UWORD Line, UWORD Track)
+{
+ if (!Note) return 0;
+
+ {
+ ULONG n, mid, low = 1, high = MAXNOTES-1;
+
+ /* The nice binary search learnt at school ;-) */
+ do
+ {
+ mid = (low + high) / 2;
+ if ((n = TrackerNotes[mid]) > Note) low = mid + 1;
+ else if (n < Note) high = mid - 1;
+ else return (UWORD)mid;
+ } while (low <= high);
+ }
+ xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
+ (APTR)MSG_INVALID_NOTE, Note, Patt, Track, Line);
+ return 0;
+}
+
+
+
+INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval)
+{
+ UBYTE i;
+
+ if (eff == 0 && effval)
+ return (EFF_ARPEGGIO);
+
+ if (eff == 0x0F) /* Speed */
+ {
+ if (effval < 0x20)
+ return EFF_SETSPEED;
+ else
+ return EFF_SETTEMPO;
+ }
+
+ for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
+ if (eff == Effects[i])
+ return i;
+
+ return 0;
+}
+
+
+
+static void SetGlobalSpeed (struct SongInfo *si, UWORD tracks)
+
+/* Put a speed command ($F) at the first line played in the song */
+{
+ struct Pattern *patt = si->Patt[si->Sequence[0]];
+ struct Note **pn = patt->Notes;
+ ULONG i;
+
+ tracks = min (tracks, patt->Tracks);
+
+ /* Do it only if required */
+ if (si->GlobalSpeed != DEF_SONGSPEED)
+ {
+ /* Ensure a SetSpeed command does not exist yet in the first row... */
+ for (i = 0 ; i < tracks ; i++)
+ if (pn[i][0].EffNum == EFF_SETSPEED)
+ goto settempo; /* Speed is already set, now for the Tempo... */
+
+ /* Try to find a free effect slot in the row... */
+ for (i = 0 ; i < tracks ; i++)
+ if (pn[i][0].EffNum == EFF_NULL && pn[i][0].EffVal == 0)
+ break;
+
+ pn[i][0].EffNum = EFF_SETSPEED;
+ pn[i][0].EffVal = si->GlobalSpeed;
+ }
+
+settempo:
+ if (si->GlobalTempo != DEF_SONGTEMPO)
+ {
+ /* Ensure a SetTempo command does not exist yet in the first row... */
+ for (i = 0 ; i < tracks ; i++)
+ if (pn[i][0].EffNum == EFF_SETTEMPO)
+ return; /* Tempo is already set, nothing else to do... */
+
+ /* Try to find a free effect slot in the row... */
+ for (i = 0 ; i < tracks ; i++)
+ if (pn[i][0].EffNum == 0 && pn[i][0].EffVal == 0)
+ break;
+
+ pn[i][0].EffNum = EFF_SETTEMPO;
+ pn[i][0].EffVal = si->GlobalTempo;
+ }
+}
+
+
+
+GLOBALCALL void AddTrackerHooks (void)
+
+/* Adds Tracker loaders and savers */
+{
+ ProTrackerLoader = xmAddHook (
+ XMHOOK_Type, NT_XMLOADER,
+ XMHOOK_Name, (LONG)"Tracker",
+ XMHOOK_Priority, 10,
+ XMHOOK_Descr, (LONG)"Sound/Noise/Star/Pro/Fast/TakeTracker",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_PROTRACKER,
+ XMHOOK_Flags, XMHF_INTERNAL,
+ XMHOOK_LoadModFunc, LoadTracker,
+ XMHOOK_IdentifyModFunc, IdentifyTracker,
+ XMHOOK_MaxTracks, 32,
+ XMHOOK_MaxPatterns, 255,
+ XMHOOK_MaxInstruments, 31,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMLOADER,
+ XMHOOK_Name, (LONG)"SoundTracker",
+ XMHOOK_Priority, -100,
+ XMHOOK_Descr, (LONG)"SoundTracker 15 instruments",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_SOUNDTRACKER,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_FIXED_PATT_LEN,
+ XMHOOK_LoadModFunc, LoadTracker,
+ XMHOOK_IdentifyModFunc, IdentifySoundTracker,
+ XMHOOK_MaxTracks, 4,
+ XMHOOK_MaxPatterns, 64,
+ XMHOOK_MaxInstruments, 15,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"TakeTracker",
+ XMHOOK_Priority, 20,
+ XMHOOK_Descr, (LONG)"TakeTracker",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_TAKETRACKER,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
+ XMHOOK_SaveModFunc, SaveTracker,
+ XMHOOK_MaxTracks, 32,
+ XMHOOK_MaxPatterns, 100,
+ XMHOOK_MaxInstruments, 31,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"FastTracker1",
+ XMHOOK_Priority, 20,
+ XMHOOK_Descr, (LONG)"FastTracker 1.0",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_FASTTRACKER1,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
+ XMHOOK_SaveModFunc, SaveTracker,
+ XMHOOK_MaxTracks, 8,
+ XMHOOK_MaxPatterns, 128,
+ XMHOOK_MaxInstruments, 31,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"ProTracker",
+ XMHOOK_Priority, 20,
+ XMHOOK_Descr, (LONG)"ProTracker 2.x - 3.x",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_PROTRACKER,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
+ XMHOOK_SaveModFunc, SaveTracker,
+ XMHOOK_MaxTracks, 4,
+ XMHOOK_MaxPatterns, 64,
+ XMHOOK_MaxInstruments, 31,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"ProTracker100",
+ XMHOOK_Priority, 15,
+ XMHOOK_Descr, (LONG)"ProTracker 2.3 (100 patterns)",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_PROTRACKER100,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
+ XMHOOK_SaveModFunc, SaveTracker,
+ XMHOOK_MaxTracks, 4,
+ XMHOOK_MaxPatterns, 100,
+ XMHOOK_MaxInstruments, 31,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"NoiseTracker",
+ XMHOOK_Priority, 15,
+ XMHOOK_Descr, (LONG)"NoiseTracker 31 instruments",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_NOISETRACKER,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
+ XMHOOK_SaveModFunc, SaveTracker,
+ XMHOOK_MaxTracks, 4,
+ XMHOOK_MaxPatterns, 64,
+ XMHOOK_MaxInstruments, 31,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"StarTrekker4",
+ XMHOOK_Priority, 10,
+ XMHOOK_Descr, (LONG)"StarTrakker 4 channels",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_STARTREKKER4,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
+ XMHOOK_SaveModFunc, SaveTracker,
+ XMHOOK_MaxTracks, 4,
+ XMHOOK_MaxPatterns, 64,
+ XMHOOK_MaxInstruments, 31,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"SoundTracker",
+ XMHOOK_Descr, (LONG)"Old SoundTracker 15 instruments",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_ID, ID_SOUNDTRACKER,
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
+ XMHOOK_SaveModFunc, SaveTracker,
+ XMHOOK_UserData, ID_SOUNDTRACKER,
+ XMHOOK_MaxTracks, 4,
+ XMHOOK_MaxPatterns, 64,
+ XMHOOK_MaxInstruments, 15,
+ XMHOOK_MaxLength, 128,
+ XMHOOK_MaxSampleLen, 65534,
+ XMHOOK_MaxPattLen, 64,
+ TAG_DONE);
+}
--- /dev/null
+ IFND XMODULE_I
+XMODULE_I SET 1
+**
+** XModule replay routine 1.0
+**
+** Copyright (C) 1995 Bernardo Innocenti
+**
+** Assembler structure definitions for player
+**
+
+ IFND EXEC_TYPES_I
+ include exec/types.i
+ ENDC ; EXEC_TYPES_I
+
+ IFND EXEC_NODES_I
+ include exec/nodes.i
+ ENDC ; EXEC_NODES_I
+
+
+
+MAXINSTRUMENTS EQU 64 ; Maximum number of instruments loaded
+MAXTABLENOTE EQU (12*3) ; Number of entries in note conversion table
+MAXTABLEEFFECTS EQU 20 ; Number of entries in effect conversion table
+MAXPATTERNS EQU 128 ; Maximum number of patterns
+MAXTRACKS EQU 32 ; Maximum number of tracks in a pattern
+MAXPATTLINES EQU 32768 ; Maximum number of lines in a pattern
+MAXPOSITIONS EQU 32768 ; Maximum number of song positions
+MAXINSTNAME EQU 32
+MAXSONGNAME EQU 32
+MAXAUTHNAME EQU 64
+MAXPATTNAME EQU 16
+
+PATHNAME_MAX EQU 256
+
+
+**************************************************************************
+** Some definitions transcripted from "XModule.h"
+**************************************************************************
+
+ ENUM 0
+
+ EITEM EFF_NULL
+ EITEM EFF_PORTAMENTOUP
+ EITEM EFF_PORTAMENTODOWN
+ EITEM EFF_TONEPORTAMENTO
+ EITEM EFF_VIBRATO
+ EITEM EFF_TONEPVOLSLIDE
+ EITEM EFF_VIBRATOVOLSLIDE
+ EITEM EFF_TREMOLO
+ EITEM EFF_UNUSED
+ EITEM EFF_SAMPLEOFFSET
+ EITEM EFF_VOLSLIDE
+ EITEM EFF_POSJUMP
+ EITEM EFF_SETVOLUME
+ EITEM EFF_PATTERNBREAK
+ EITEM EFF_MISC
+ EITEM EFF_SETSPEED
+ EITEM EFF_SETTEMPO
+ EITEM EFF_ARPEGGIO
+
+ EITEM EFF_COUNT
+
+
+
+ STRUCTURE Note,0
+ UBYTE note_Note
+ UBYTE note_Inst
+ UBYTE note_Vol
+ UBYTE note_Pad
+ UBYTE note_EffNum
+ UBYTE note_EffVal
+ LABEL Note_SIZEOF
+
+
+ STRUCTURE Instrument,0
+ UWORD in_InstType ; Instrument type (See defs)
+ UWORD in_Volume ; Volume (max $40)
+ STRUCT in_Name,MAXINSTNAME ; Instrument Name
+ APTR in_SampleData ; Sampled data
+ ULONG in_Length ; Length of instr
+ ULONG in_Repeat ; Loop start (No loop = 0)
+ ULONG in_Replen ; Loop size (No loop = 1)
+ WORD in_FineTune ; Instrument FineTune (-8..+7)
+ UWORD in_Flags ; Unused
+ LABEL in_SIZEOF
+
+
+ STRUCTURE Pattern,0
+ UWORD pa_Tracks ; Support for variable number of tracks
+ UWORD pa_Lines ; Number of lines in pattern
+ STRUCT pa_PattName,MAXPATTNAME ; Pattern Name
+ STRUCT pa_Notes,MAXTRACKS * 4 ; Pointers to the lines
+ LABEL pa_SIZEOF
+
+
+ STRUCTURE SongInfo,LN_SIZE
+ UWORD si_Length ; Number of positions in song
+ UWORD si_MaxTracks ; Number of tracks in song
+ UWORD si_NumPatterns ; Number of patterns in song
+ UWORD si_NumInstruments ; Unused
+ UWORD si_GlobalSpeed ; Default song speed
+ UWORD si_GlobalTempo ; Default song tempo
+ UWORD si_Restart ; Position to restart from
+ UWORD si_CurrentPatt
+ UWORD si_CurrentPos
+ UWORD si_CurrentInst
+ UWORD si_Flags ; See definitions below
+ ULONG si_Changes ; Number of changes made to this song
+ APTR si_Sequence ; *UWORD: Pointer to song sequence
+ STRUCT si_PattData,pa_SIZEOF*MAXPATTERNS
+ STRUCT si_Inst,in_SIZEOF*MAXINSTRUMENTS
+ STRUCT si_ActiveTracks,MAXTRACKS ; UBYTE []: Active Tracks (0 = disabled)
+ STRUCT si_SongName,MAXSONGNAME ; UBYTE []: Song name
+ STRUCT si_Author,MAXAUTHNAME ; Author of song
+ STRUCT si_SongPath,PATHNAME_MAX; ; Original song path
+ APTR si_Pool ; The memory pool where song data
+ ; must be allocated from.
+ LABEL si_SIZEOF
+
+
+ ENDC ; !XMODULE_I
--- /dev/null
+/*
+** XModuleHook.c
+**
+** Copyright (C) 1994,95,96,97 Bernardo Innocenti
+**
+** Internal loader/saver hook for the IFF XMOD module format
+*/
+
+#include <exec/memory.h>
+#include <libraries/xmoduleclass.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/iffparse.h>
+#include <proto/xmodule.h>
+
+#include "XModulePriv.h"
+#include "Gui.h"
+
+
+/* Local functions prototypes */
+
+static HOOKCALL struct XMHook *IdentifyXModule (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags));
+static HOOKCALL LONG LoadXModule (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags));
+static HOOKCALL LONG SaveXModule (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *saver),
+ REG(a2, ULONG *tags));
+
+
+
+GLOBALCALL void AddXModuleHooks (void)
+
+/* Adds XModule loader and saver */
+{
+ xmAddHook (
+ XMHOOK_Type, NT_XMLOADER,
+ XMHOOK_Name, (LONG)"XModule",
+ XMHOOK_Priority, 50,
+ XMHOOK_Descr, (LONG)"XModule internal format",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_Flags, XMHF_INTERNAL,
+ XMHOOK_LoadModFunc, LoadXModule,
+ XMHOOK_IdentifyModFunc, IdentifyXModule,
+ TAG_DONE);
+
+ xmAddHook (
+ XMHOOK_Type, NT_XMSAVER,
+ XMHOOK_Name, (LONG)"XModule",
+ XMHOOK_Priority, 50,
+ XMHOOK_Descr, (LONG)"XModule Internal format",
+ XMHOOK_Author, (LONG)"Bernardo Innocenti",
+ XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
+ XMHF_EXCLUDE_NAMES | XMHF_EXCLUDE_SEQUENCE,
+ XMHOOK_SaveModFunc, SaveXModule,
+ TAG_DONE);
+}
+
+
+
+static HOOKCALL struct XMHook *IdentifyXModule (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *loader),
+ REG(a1, ULONG *tags))
+
+/* Determine if the given file is an XModule module.
+ * Note: the file position will be changed on exit.
+ */
+{
+ ULONG id[3];
+
+ Seek (fh, 0, OFFSET_BEGINNING);
+ if (FRead (fh, &id, 12, 1) != 1)
+ return NULL;
+
+ if ((id[0] == ID_FORM) && (id[2] == ID_XMOD))
+ return loader;
+
+ return NULL;
+}
+
+
+
+static HOOKCALL LONG LoadXModule (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *loader),
+ REG(a2, ULONG *tags))
+{
+ struct IFFHandle *iff;
+ struct ContextNode *cn;
+ LONG err;
+
+ if (iff = AllocIFF())
+ {
+ iff->iff_Stream = (ULONG) fh;
+
+ InitIFFasDOS (iff);
+
+ if (!(err = OpenIFF (iff, IFFF_READ)))
+ {
+ struct ModuleHeader mhdr;
+
+ static LONG stopchunks[] =
+ {
+ ID_XMOD, ID_NAME,
+ ID_XMOD, ID_MHDR,
+ ID_SONG, ID_FORM
+ };
+
+ if (err = StopChunks (iff, stopchunks, 3))
+ goto error;
+
+ if (err = StopOnExit (iff, ID_XMOD, ID_FORM))
+ goto error;
+
+ /* Scan module */
+
+ while (1)
+ {
+ if (err = ParseIFF (iff, IFFPARSE_SCAN))
+ {
+ if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
+ break; /* Free resources & exit */
+ }
+
+ if (cn = CurrentChunk (iff))
+ {
+ switch (cn->cn_ID)
+ {
+ case ID_NAME:
+ {
+ UBYTE name[128];
+
+ ReadChunkBytes (iff, name, min(cn->cn_Size, 127));
+ name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */
+ SetAttrs (si,
+ SNGA_Title, name,
+ TAG_DONE);
+ break;
+ }
+
+ case ID_MHDR:
+ if ((err = ReadChunkBytes (iff, &mhdr, sizeof (mhdr))) != sizeof(mhdr))
+ goto error;
+ break;
+
+ case ID_FORM:
+ if (cn->cn_Type == ID_SONG)
+ if (err = LoadSong (iff, si))
+ goto error;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+error:
+ CloseIFF (iff);
+ }
+
+ FreeIFF (iff);
+ }
+ else err = ERROR_NO_FREE_STORE;
+
+ return err;
+}
+
+
+
+GLOBALCALL LONG LoadSong (struct IFFHandle *iff, struct SongInfo *si)
+{
+ LONG err, len;
+ struct ContextNode *cn;
+ struct SongHeader shdr;
+
+ static LONG stopchunks[] =
+ {
+ ID_SONG, ID_NAME,
+ ID_SONG, ID_AUTH,
+ ID_SONG, ID_ANNO,
+ ID_SONG, ID_SHDR,
+ ID_SONG, ID_SEQN,
+ ID_PATT, ID_FORM,
+ ID_8SVX, ID_FORM
+ };
+
+
+ memset (&shdr, 0, sizeof (shdr));
+
+ if (err = StopChunks (iff, stopchunks, 7))
+ return err;
+
+ if (err = StopOnExit (iff, ID_SONG, ID_FORM))
+ return err;
+
+ /* Scan song */
+
+ while (1)
+ {
+ if (err = ParseIFF (iff, IFFPARSE_SCAN))
+ {
+ if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
+ break; /* Free resources & exit */
+ }
+
+ if (cn = CurrentChunk (iff))
+ {
+ switch (cn->cn_ID)
+ {
+ case ID_NAME:
+ {
+ UBYTE name[128];
+
+ ReadChunkBytes (iff, name, min(cn->cn_Size, 127));
+ name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */
+ SetAttrs (si,
+ SNGA_Title, name,
+ TAG_DONE);
+ break;
+ }
+
+ case ID_AUTH:
+ {
+ UBYTE name[128];
+
+ ReadChunkBytes (iff, name, min(cn->cn_Size, 127));
+ name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */
+ SetAttrs (si,
+ SNGA_Author, name,
+ TAG_DONE);
+ break;
+ }
+
+ case ID_ANNO:
+ {
+ UBYTE name[128];
+
+ ReadChunkBytes (iff, name, min(cn->cn_Size, 127));
+ name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */
+ SetAttrs (si,
+ SNGA_Description, name,
+ TAG_DONE);
+ break;
+ }
+
+
+ case ID_SHDR:
+ if ((err = ReadChunkBytes (iff, &shdr, sizeof (shdr))) == 0)
+ return err;
+
+ SetAttrs (si,
+ SNGA_GlobalSpeed, shdr.GlobalSpeed,
+ SNGA_GlobalTempo, shdr.GlobalTempo,
+ SNGA_RestartPos, shdr.RestartPos,
+ SNGA_CurrentPatt, shdr.CurrentPatt,
+ SNGA_CurrentPos, shdr.CurrentPos,
+ SNGA_CurrentInst, shdr.CurrentInst,
+ SNGA_DefaultTracks, shdr.DefNumTracks ? shdr.DefNumTracks : shdr.MaxTracks,
+ SNGA_DefaultPattLen, shdr.DefPattLen ? shdr.DefPattLen : DEF_PATTLEN,
+ SNGA_CreationDate, shdr.CreationDate,
+ SNGA_LastChanged, shdr.LastChanged,
+ SNGA_TotalChanges, shdr.TotalChanges,
+ TAG_DONE);
+ break;
+
+ case ID_SEQN:
+ len = min (cn->cn_Size / 2, MAXPOSITIONS);
+
+ if (xmSetSongLen (si, len))
+ {
+ if ((err = ReadChunkBytes (iff, si->Sequence, si->Length * 2))
+ != si->Length * 2)
+ return err;
+ }
+ else return ERROR_NO_FREE_STORE;
+
+ break;
+
+ case ID_FORM:
+ if (cn->cn_Type == ID_PATT)
+ {
+ if (!si->NumPatterns)
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_READING_PATTS, NULL);
+
+ if (xmDisplayProgress (si->NumPatterns, shdr.NumPatterns))
+ return ERROR_BREAK;
+
+ if (!LoadPattern (si, si->NumPatterns, iff))
+ return IoErr();
+ }
+ else if (cn->cn_Type == ID_8SVX)
+ {
+ if (!si->LastInstrument)
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_READING_INSTS, NULL);
+
+ if (xmDisplayProgress (si->LastInstrument, shdr.LastInstrument))
+ return ERROR_BREAK;
+
+ if (err = Load8SVXInstrument (si, 0, iff, NULL))
+ return err;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ return err;
+}
+
+
+
+GLOBALCALL struct Pattern *LoadPattern (struct SongInfo *si, ULONG num, struct IFFHandle *iff)
+{
+ LONG err, tracksize;
+ struct Pattern *patt = NULL;
+ struct ContextNode *cn;
+ struct PatternHeader phdr;
+ UWORD i;
+ BYTE name[64];
+
+ static LONG stopchunks[] =
+ {
+ ID_PATT, ID_NAME,
+ ID_PATT, ID_PHDR,
+ ID_PATT, ID_BODY
+ };
+
+ name[0] = '\0';
+
+ if (err = StopChunks (iff, stopchunks, 3))
+ {
+ SetIoErr (err);
+ return NULL;
+ }
+
+ if (err = StopOnExit (iff, ID_PATT, ID_FORM))
+ {
+ SetIoErr (err);
+ return NULL;
+ }
+
+ /* Scan Pattern */
+
+ while (1)
+ {
+ if (err = ParseIFF (iff, IFFPARSE_SCAN))
+ {
+ if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
+ break; /* Free resources & exit */
+ }
+
+ if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_PATT))
+ {
+ switch (cn->cn_ID)
+ {
+ case ID_NAME:
+ {
+ ReadChunkBytes (iff, name, min(cn->cn_Size, 63));
+ name[min(cn->cn_Size, 63)] = '\0'; /* Ensure string termination */
+ break;
+ }
+
+ case ID_PHDR:
+ if ((err = ReadChunkBytes (iff, &phdr, sizeof (phdr))) != sizeof(phdr))
+ {
+ SetIoErr (err);
+ return NULL;
+ }
+
+ if (!(patt = xmAddPattern (si,
+ PATTA_Lines, phdr.Lines,
+ PATTA_Tracks, phdr.Tracks,
+ PATTA_Num, num,
+ TAG_DONE)))
+ {
+ SetIoErr (ERROR_NO_FREE_STORE);
+ return NULL;
+ }
+
+ tracksize = phdr.Lines * sizeof (struct Note);
+
+ break;
+
+ case ID_BODY:
+ {
+ if (!patt)
+ {
+ SetIoErr (IFFERR_SYNTAX);
+ return NULL;
+ }
+
+ for (i = 0; i < patt->Tracks; i++)
+ {
+ if ((err = ReadChunkBytes (iff, patt->Notes[i], tracksize)) != tracksize)
+ {
+ SetIoErr (err);
+ return NULL;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (name[0])
+ xmSetPattern (si, si->NumPatterns - 1,
+ PATTA_Name, name,
+ TAG_DONE);
+
+ if (!err && !patt)
+ err = ERROR_OBJECT_NOT_FOUND;
+
+ if (err) SetIoErr (err);
+
+ return patt;
+}
+
+
+
+static HOOKCALL LONG SaveXModule (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *si),
+ REG(a1, struct XMHook *saver),
+ REG(a2, ULONG *tags))
+{
+ struct IFFHandle *iff;
+ LONG err;
+
+ if (iff = AllocIFF())
+ {
+ iff->iff_Stream = (ULONG) fh;
+
+ InitIFFasDOS (iff);
+
+ if (!(err = OpenIFF (iff, IFFF_WRITE)))
+ {
+
+ /* Write XMOD */
+ if (err = PushChunk (iff, ID_XMOD, ID_FORM, IFFSIZE_UNKNOWN))
+ goto error;
+
+ /* Write module NAME */
+ if (err = WriteStringChunk (iff, FilePart (si->Path), ID_NAME))
+ goto error;
+
+ /* Write module ANNO */
+ if (err = WriteStringChunk (iff, VERS, ID_ANNO))
+ goto error;
+
+ /* Write Module Header (MHDR) */
+ {
+ struct ModuleHeader mhdr;
+
+ mhdr.XModuleVersion = VERSION;
+ mhdr.XModuleRevision = REVISION;
+ mhdr.NumSongs = 1;
+ mhdr.ActiveSong = 1;
+ mhdr.MasterVolume = 0xFFFF;
+ mhdr.MixingRate = 44100;
+
+ if (err = PushChunk (iff, ID_XMOD, ID_MHDR, sizeof (mhdr)))
+ goto error;
+ if ((err = WriteChunkBytes (iff, &mhdr, sizeof (mhdr))) != sizeof(mhdr))
+ goto error;
+ if (err = PopChunk (iff)) goto error; /* Pop MHDR */
+ }
+
+ if (err = SaveSong (iff, si))
+ goto error;
+
+ err = PopChunk (iff); /* Pop FORM XMOD */
+
+error:
+ CloseIFF (iff);
+ }
+ else err = IFFERR_NOTIFF;
+
+ FreeIFF (iff);
+ }
+ else err = ERROR_NO_FREE_STORE;
+
+ return (UWORD) err;
+}
+
+
+
+GLOBALCALL LONG SaveSong (struct IFFHandle *iff, struct SongInfo *si)
+{
+ LONG err;
+
+ if (err = PushChunk (iff, ID_SONG, ID_FORM, IFFSIZE_UNKNOWN))
+ return err;
+
+ /* Write Song Name */
+ WriteStringChunk (iff, si->Title, ID_NAME);
+
+ /* Write Author Name */
+ WriteStringChunk (iff, si->Author, ID_AUTH);
+
+ /* Write Annotations */
+ WriteStringChunk (iff, si->Description, ID_ANNO);
+
+ /* Write Song Header (SHDR) */
+ {
+ struct SongHeader shdr;
+
+ if (err = PushChunk (iff, ID_SONG, ID_SHDR, IFFSIZE_UNKNOWN))
+ return err;
+
+
+ /* Set last changed date */
+ {
+ ULONG dummy;
+ CurrentTime (&si->LastChanged, &dummy);
+ }
+
+ shdr.Length = si->Length;
+ shdr.MaxTracks = si->MaxTracks;
+ shdr.NumPatterns = si->NumPatterns;
+ shdr.LastInstrument = si->LastInstrument;
+ shdr.GlobalSpeed = si->GlobalSpeed;
+ shdr.GlobalTempo = si->GlobalTempo;
+ shdr.RestartPos = si->RestartPos;
+ shdr.CurrentPatt = si->CurrentPatt;
+ shdr.CurrentLine = si->CurrentLine;
+ shdr.CurrentTrack = si->CurrentTrack;
+ shdr.CurrentPos = si->CurrentPos;
+ shdr.CurrentInst = si->CurrentInst;
+ shdr.DefNumTracks = si->DefNumTracks;
+ shdr.DefPattLen = si->DefPattLen;
+ shdr.TotalChanges = si->TotalChanges + si->Changes;
+ shdr.CreationDate = si->CreationDate;
+ shdr.LastChanged = si->LastChanged;
+
+
+ if ((err = WriteChunkBytes (iff, &shdr, sizeof (shdr))) != sizeof (shdr))
+ return err;
+
+ if (err = PopChunk (iff)) /* Pop SHDR */
+ return err;
+ }
+
+
+ if (err = SaveSequence (iff, si))
+ return err;
+ if (err = SavePatterns (iff, si))
+ return err;
+ if (err = SaveInstruments (iff, si))
+ return err;
+
+ err = PopChunk (iff); /* Pop FORM SONG */
+
+ return err;
+}
+
+
+
+GLOBALCALL LONG SaveSequence (struct IFFHandle *iff, struct SongInfo *si)
+{
+ LONG err;
+
+ if (err = PushChunk (iff, 0, ID_SEQN, si->Length * 2))
+ return err;
+
+ if ((err = WriteChunkBytes (iff, si->Sequence, si->Length * 2)) != si->Length * 2)
+ return err;
+
+ err = PopChunk (iff);
+
+ return err;
+}
+
+
+
+GLOBALCALL LONG SavePatterns (struct IFFHandle *iff, struct SongInfo *si)
+{
+ LONG err;
+ ULONG i;
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_WRITING_PATTS, NULL);
+
+
+ for (i = 0; i < si->NumPatterns; i++)
+ {
+ if (xmDisplayProgress (i, si->NumPatterns))
+ return ERROR_BREAK;
+
+ if (si->Patt[i])
+ if (err = SavePattern (iff, si->Patt[i]))
+ return err;
+ }
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL LONG SaveInstruments (struct IFFHandle *iff, struct SongInfo *si)
+{
+ LONG err;
+ ULONG i;
+
+ xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
+ (APTR)MSG_WRITING_INSTS, NULL);
+
+ for (i = 1; i <= si->LastInstrument; i++)
+ {
+ if (!(si->Instr[i])) continue;
+
+ if (xmDisplayProgress (i - 1, si->LastInstrument))
+ return ERROR_BREAK;
+
+ if (err = Save8SVXInstrument (si->Instr[i], i, iff))
+ return err;
+ }
+
+ return RETURN_OK;
+}
+
+
+
+GLOBALCALL LONG SavePattern (struct IFFHandle *iff, struct Pattern *patt)
+{
+ LONG err;
+
+ if (err = PushChunk (iff, ID_PATT, ID_FORM, IFFSIZE_UNKNOWN))
+ return err;
+
+ /* Write pattern NAME */
+
+ if (err = WriteStringChunk (iff, patt->Name, ID_NAME))
+ return err;
+
+ /* Write pattern header (PHDR) */
+ {
+ struct PatternHeader phdr;
+
+ phdr.Lines = patt->Lines;
+ phdr.Tracks = patt->Tracks;
+
+ if (err = PushChunk (iff, 0, ID_PHDR, sizeof (struct PatternHeader)))
+ return err;
+
+ if ((err = WriteChunkBytes (iff, &phdr, sizeof (phdr))) != sizeof (phdr))
+ return err;
+
+ if (err = PopChunk (iff)) /* PHDR */
+ return err;
+ }
+
+ /* Write pattern BODY */
+ {
+ ULONG tracksize = sizeof (struct Note) * patt->Lines;
+ ULONG i;
+
+ if (err = PushChunk (iff, 0, ID_BODY, tracksize * patt->Tracks))
+ return err;
+
+ for (i = 0; i < patt->Tracks; i++)
+ {
+ if ((err = WriteChunkBytes (iff, patt->Notes[i], tracksize)) != tracksize)
+ return err;
+ }
+
+ if (err = PopChunk (iff)) /* BODY */
+ return err;
+ }
+
+ err = PopChunk (iff); /* PATT */
+
+ return err;
+}
+
+
+
+GLOBALCALL LONG WriteStringChunk (struct IFFHandle *iff, CONST_STRPTR str, ULONG id)
+{
+ LONG err;
+ ULONG len;
+
+ if (!str || !str[0] || !SaveSwitches.SaveNames)
+ return RETURN_OK;
+
+ if (err = PushChunk (iff, 0, id, len = strlen (str)))
+ return err;
+
+ if ((err = WriteChunkBytes (iff, str, len)) != len)
+ return err;
+
+ err = PopChunk (iff);
+
+ return err;
+}
--- /dev/null
+#ifndef XMODULE_PRIV_H
+#define XMODULE_PRIV_H
+/*
+** XModulePriv.h
+**
+** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
+**
+** Use 4 chars wide TABs to read this source
+**
+** Private definitions for internal XModule use
+*/
+
+#ifndef EXEC_NODES_H
+#include <exec/nodes.h>
+#endif /* !EXEC_NODES_H */
+
+#ifndef EXEC_LIBRARIES_H
+#include <exec/libraries.h>
+#endif /* !EXEC_LIBRARIES_H */
+
+#ifndef DOS_DOS_H
+#include <dos/dos.h>
+#endif /* !DOS_DOS_H */
+
+#ifndef DOS_DOS_H
+#include <dos/dos.h>
+#endif /* !DOS_DOS_H */
+
+#ifndef LIBRARIES_IFFPARSE_H
+#include "libraries/iffparse.h"
+#endif /* LIBRARIES_IFFPARSE_H */
+
+#ifndef LIBRARIES_XMODULE_H
+#include <libraries/xmodule.h>
+#endif /* !LIBRARIES_XMODULE_H */
+
+#ifndef LIBRARIES_SONGCLASS_H
+#include <libraries/songclass.h>
+#endif /* !LIBRARIES_SONGCLASS_H */
+
+#include "XModule_rev.h"
+#include "LocaleStrings.h"
+#include "ListMacros.h"
+#include "CompilerSpecific.h"
+#include "Debug.h" /* Debug support */
+
+/* Single object compilation requires different attributes for global symbols */
+#ifdef SINGLE_OBJECT
+ #undef XREF
+ #undef XDEF
+ #undef GLOBALCALL
+ #define XREF extern
+ #define XDEF static
+ #define GLOBALCALL static
+ #ifdef __SASC
+// #pragma msg 72 ignore
+ #pragma msg 181 ignore
+ #endif
+#endif
+
+
+/* Version information: updated auto-magically on every compilation ;-) */
+
+#define XMODULEVER VERS
+#define XMODULEDATE "(" DATE ")"
+#define XMODULECOPY "Copyright © 1993,94,95,96,97 by Bernardo Innocenti"
+
+#ifdef OS30_ONLY
+ #ifdef _M68020
+ #define BUILDMODE "[M68020 OS3.0]"
+ #else
+ #define BUILDMODE "[M68000 OS3.0]"
+ #endif
+#else
+ #ifdef _M68020
+ #define BUILDMODE "[M68020 OS2.0]"
+ #else
+ #define BUILDMODE "[M68000 OS2.0]"
+ #endif
+#endif
+
+
+
+/*************************/
+/* Constants Definitions */
+/*************************/
+
+/* Maximum values */
+#define MAXTABLEEFFECTS 20 /* Number of entries in effect conversion table */
+#define PATHNAME_MAX 108 /* Maximum length of an AmigaDOS path name */
+
+
+/* Specific error codes */
+#define ERROR_NOTMODULE ERROR_OBJECT_WRONG_TYPE /* File format not recognized */
+#define ERROR_IOERR RETURN_FAIL /* A DOS call (e.g.: Read()) failed.
+ * check IoErr() for specific error.
+ */
+
+/* Default directory for external hooks */
+#define DEF_HOOKSDIR "PROGDIR:Hooks/"
+
+
+/***********************/
+/* Function Prototypes */
+/***********************/
+
+/* From "Audio.c" */
+GLOBALCALL void HandleAudio (void);
+GLOBALCALL void PlaySample (BYTE *samp, ULONG len, UWORD vol, UWORD per);
+GLOBALCALL ULONG SetupAudio (void);
+GLOBALCALL void CleanupAudio (void);
+
+/* From "Misc.c" */
+GLOBALCALL struct Library *MyOpenLibrary (CONST_STRPTR name, ULONG ver);
+GLOBALCALL void CantOpenLib (CONST_STRPTR name, LONG vers);
+GLOBALCALL void KillMsgPort (struct MsgPort *mp);
+GLOBALCALL struct TextAttr *CopyTextAttrPooled (void *pool, const struct TextAttr *source, struct TextAttr *dest);
+GLOBALCALL UWORD CmpTextAttr (const struct TextAttr *ta1, const struct TextAttr *ta2);
+GLOBALCALL STRPTR DupStringPooled (void *pool, CONST_STRPTR source, STRPTR *dest);
+GLOBALCALL void FilterName (STRPTR name);
+GLOBALCALL struct DiskObject *GetProgramIcon (void);
+GLOBALCALL LONG PutIcon (CONST_STRPTR source, CONST_STRPTR dest);
+GLOBALCALL LONG BackupFile (CONST_STRPTR src, CONST_STRPTR template, ULONG versions);
+
+/* From "Gui.c" (More functions prototyped in "Gui.h") */
+GLOBALCALL LONG HandleGui (void);
+
+/* From "Rexx.c" */
+GLOBALCALL void HandleRexxMsg (void);
+GLOBALCALL LONG CreateRexxPort (void);
+GLOBALCALL void DeleteRexxPort (void);
+
+/* From "ToolBoxWin.c" */
+GLOBALCALL void ToolBoxOpenModule (CONST_STRPTR file, ULONG num, ULONG count);
+
+/* From "Help.c" */
+GLOBALCALL void HandleHelp (struct IntuiMessage *msg);
+GLOBALCALL void HandleAmigaGuide (void);
+GLOBALCALL void CleanupHelp (void);
+
+/* From "Cx.c" */
+GLOBALCALL LONG SetupCx (void);
+GLOBALCALL void HandleCx (void);
+GLOBALCALL void CleanupCx (void);
+
+/* From "ProgressWin.c" */
+GLOBALCALL void DisplayAction (ULONG msg);
+GLOBALCALL void DisplayActionStr (CONST_STRPTR str);
+GLOBALCALL LONG DisplayProgress (LONG Num, LONG Max);
+GLOBALCALL void ShowMessage (ULONG msg, ...);
+GLOBALCALL void ShowString (CONST_STRPTR s, LONG *args);
+
+/* From "PlayWin.c" */
+GLOBALCALL LONG SetupPlayer (void);
+GLOBALCALL void CleanupPlayer (void);
+
+/* From "Compress.c" */
+GLOBALCALL BPTR DecompressFile (CONST_STRPTR name, UWORD type);
+GLOBALCALL void DecompressFileDone (void);
+GLOBALCALL LONG CruncherType (BPTR file);
+
+/* From "Song.c" */
+GLOBALCALL Class * InitSongClass (void);
+GLOBALCALL void FreeSongClass (Class *cl);
+GLOBALCALL void FixSong (struct SongInfo *si);
+GLOBALCALL ULONG CalcInstSize (struct SongInfo *si);
+GLOBALCALL ULONG CalcSongSize (struct SongInfo *si);
+GLOBALCALL ULONG CalcSongTime (struct SongInfo *si);
+
+/* From "Instr.c" */
+GLOBALCALL LONG LoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename);
+GLOBALCALL LONG SaveInstrument (struct Instrument *inst, CONST_STRPTR filename);
+GLOBALCALL LONG Load8SVXInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename);
+GLOBALCALL LONG Save8SVXInstrument (struct Instrument *inst, ULONG num, struct IFFHandle *iff);
+GLOBALCALL void OptimizeInstruments (struct SongInfo *si);
+GLOBALCALL void RemDupInstruments (struct SongInfo *si);
+GLOBALCALL void RemUnusedInstruments (struct SongInfo *si);
+GLOBALCALL void RemapInstruments (struct SongInfo *si);
+GLOBALCALL void SampChangeSign8 (UBYTE *samp, ULONG len);
+
+/* From "Operators.c" */
+GLOBALCALL ULONG InsertPattern (struct SongInfo *si, struct Pattern *patt, UWORD patnum);
+GLOBALCALL struct SongInfo *MergeSongs (struct SongInfo *songa, struct SongInfo *songb);
+GLOBALCALL struct SongInfo *JoinSongs (struct SongInfo *songa, struct SongInfo *songb);
+GLOBALCALL void RemDupPatterns (struct SongInfo *si);
+GLOBALCALL void DiscardPatterns (struct SongInfo *si);
+GLOBALCALL void CutPatterns (struct SongInfo *si);
+GLOBALCALL LONG ResizePatterns (struct SongInfo *si, ULONG min, ULONG max);
+GLOBALCALL struct Pattern *CopyPattern (struct SongInfo *si, struct Pattern *src, ULONG destNum);
+
+/* From "Prefs.c" */
+GLOBALCALL LONG LoadPrefs (CONST_STRPTR filename);
+GLOBALCALL LONG SavePrefs (CONST_STRPTR filename);
+
+/* From "Library.c" */
+GLOBALCALL ULONG MakeXModuleLibrary (void);
+GLOBALCALL void DisposeXModuleLibrary (void);
+
+/* From "Locale.c" */
+GLOBALCALL void SetupLocale (void);
+GLOBALCALL void CleanupLocale (void);
+
+/* From "XModuleHook.c" */
+GLOBALCALL void AddXModuleHooks (void);
+GLOBALCALL LONG LoadSong (struct IFFHandle *iff, struct SongInfo *si);
+GLOBALCALL struct Pattern *LoadPattern (struct SongInfo *si, ULONG num, struct IFFHandle *iff);
+GLOBALCALL LONG SaveSong (struct IFFHandle *iff, struct SongInfo *si);
+GLOBALCALL LONG SaveSequence (struct IFFHandle *iff, struct SongInfo *si);
+GLOBALCALL LONG SavePatterns (struct IFFHandle *iff, struct SongInfo *si);
+GLOBALCALL LONG SaveInstruments (struct IFFHandle *iff, struct SongInfo *si);
+GLOBALCALL LONG SavePattern (struct IFFHandle *iff, struct Pattern *patt);
+GLOBALCALL LONG WriteStringChunk (struct IFFHandle *iff, CONST_STRPTR name, ULONG id);
+
+/* From "TrackerHook.c" */
+GLOBALCALL void AddTrackerHooks (void);
+
+/* From "Startup.asm" */
+extern void _XCEXIT (LONG err);
+extern void SPrintf (UBYTE *buf, CONST_STRPTR formatstr, ...);
+extern void STDARGS VSPrintf (UBYTE *buf, CONST_STRPTR formatstr, void *argv);
+extern struct WBStartup *WBenchMsg;
+extern struct Process *ThisTask;
+
+
+#ifndef OS30_ONLY
+
+/* Memory pools support for pre-V39 OS */
+
+#define CreatePool(f,p,t) LibCreatePool((f),(p),(t))
+#define DeletePool(p) LibDeletePool(p)
+#define AllocPooled(p,s) AsmAllocPooled((p), (s), SysBase)
+#define FreePooled(p,m,s) AsmFreePooled((p), (m), (s), SysBase)
+
+extern ASMCALL void *AsmAllocPooled (REG(a0, void *pool), REG(d0, ULONG size), REG(a6, struct ExecBase *SysBase));
+extern ASMCALL void *AsmFreePooled (REG(a0, void *pool), REG(a1, void *drop), REG(d0, ULONG size), REG(a6, struct ExecBase *SysBase));
+
+#endif /* !OS30_ONLY */
+
+
+/* More memory pools support */
+
+#ifdef PORTABLE
+ GLOBALCALL void *CAllocPooled (void *pool, ULONG size);
+ GLOBALCALL void *AllocVecPooled (void *pool, ULONG size);
+ GLOBALCALL void FreeVecPooled (void *pool, void *drop);
+#else
+ #define AllocVecPooled(p,s) AsmAllocVecPooled((p), (s), SysBase)
+ #define FreeVecPooled(p,m) AsmFreeVecPooled((p), (m), SysBase)
+ #define CAllocPooled(p,s) AsmCAllocPooled((p), (s), SysBase)
+
+ extern ASMCALL void *AsmCAllocPooled (REG(a0, void *pool), REG(d0, ULONG size), REG(a6, struct ExecBase *SysBase));
+ extern ASMCALL void *AsmAllocVecPooled (REG(a0, void *pool), REG(d0, ULONG size), REG(a6, struct ExecBase *SysBase));
+ extern ASMCALL void AsmFreeVecPooled (REG(a0, void *pool), REG(a1, void *drop), REG(a6, struct ExecBase *SysBase));
+#endif
+
+
+/* Use these macros when the corresponding functions are not available.
+ * GLOBALCALL void InstallGfxFunctions (void);
+ * GLOBALCALL ULONG (*ReadAPen)(struct RastPort *RPort);
+ * GLOBALCALL ULONG (*ReadBPen)(struct RastPort *RPort);
+ * GLOBALCALL ULONG (*ReadDrMd)(struct RastPort *RPort);
+ */
+
+
+
+/********************/
+/* Global variables */
+/********************/
+
+XREF struct Catalog *Catalog;
+XREF void *Pool;
+XREF STRPTR AppStrings[];
+XREF BOOL Verbose;
+XREF BPTR StdOut;
+XREF LONG LastErr;
+XREF UBYTE PubPortName[];
+
+XREF BYTE CxPri;
+XREF UBYTE CxPopKey[32];
+XREF BOOL CxPopup;
+
+XREF LONG IconX;
+XREF LONG IconY;
+XREF UBYTE IconName[16];
+
+XREF const UBYTE Version[];
+XREF const UBYTE BaseName[]; /* All caps */
+XREF const UBYTE PrgName[]; /* Mixed case */
+
+#endif /* !XMODULE_PRIV_H */
--- /dev/null
+/* This source file is automatically generated - do not edit */
+#include "Main.c"
+#include "Misc.c"
+#include "Rexx.c"
+#include "Gui.c"
+#include "Requesters.c"
+#include "Prefs.c"
+#include "Locale.c"
+#include "Audio.c"
+#include "Compress.c"
+#include "App.c"
+#include "Cx.c"
+#include "Help.c"
+#include "Operators.c"
+#include "Instr.c"
+#include "SongClass.c"
+#include "Library.c"
+#include "XModuleHook.c"
+#include "TrackerHook.c"
+#include "CustomClasses.c"
+#include "ToolBoxWin.c"
+#include "OptimizationWin.c"
+#include "ClearWin.c"
+#include "InstrumentsWin.c"
+#include "SongInfoWin.c"
+#include "SequenceWin.c"
+#include "ProgressWin.c"
+#include "SaversWin.c"
+#include "PlayWin.c"
+#include "PrefsWin.c"
+#include "PatternWin.c"
+#include "PattPrefsWin.c"
--- /dev/null
+/* C Headerfile generated by RevUp 1.2 */
+
+#define VERSION 3
+#define REVISION 5
+#define DATE "24.6.95"
+#define VERS "XModule 3.5"
+#define VSTRING "XModule 3.5 (24.6.95)\r\n"
+#define VERSTAG "\0$VER: XModule 3.5 (24.6.95)"
+#define TIME "01:28:57"
+#define PRGNAME "XModule"
+#define BASENAME "XMODULE"
+#define VSTR "XModule 3.5 (24.6.95)"
--- /dev/null
+6
\ No newline at end of file
--- /dev/null
+##
+## $Id:$
+##
+## Build environment configuration parameters
+## Copyright (C) 1999 by Bernardo Innocenti
+
+###########################################################
+# Package configuration
+###########################################################
+
+# set to OS30_ONLY to leave out support for old V37
+# set to ANY_OS to make an executable for V37 with V39 support
+#
+OSVER := OS30_ONLY
+
+# CPU to compile for (eg: "68020").
+#
+CPU := 68020
+
+# Compiler to use. Possible options are:
+# sc - SAS/C 6.58 or better
+# gcc - gcc 2.7.2 or EGCS 1.1b
+# vbcc - Not yet supported
+# stormc - Not yet supported
+#
+COMPILER := gcc
+
+# Set to 1 to build debug executables
+#
+DEBUG := 0
+
+# Minimum stack size to allocate
+#
+STACKSIZE := 16384
+
+###########################################################
+# Paths
+###########################################################
+#
+PROJNAME := XModule
+
+ifeq ($(COMPILER),gcc)
+TOP := /SC/src/xm
+else
+TOP := SC:src/xm
+endif
+
+OBJDIR := $(TOP)/obj
+INCDIR := $(TOP)/include
+DISTPREFIX := $(TOP)/dist
+ARCNAME := $(DISTPREFIX)/$(PROJNAME).lzx
+SRCARCNAME := $(DISTPREFIX)/$(PROJNAME)_src.lzx
+
+###########################################################
+# Tools used in the Makefiles
+###########################################################
+#
+MAKEINFO := GG:bin/makeinfo
+FD2PRAGMA := fd2pragma
+FLEXCAT := FlexCat
+ARCHIVER := lzx -3 -e -r a
+
+ifeq ($(COMPILER),sc)
+ CC := sc
+ AS := PhxAss
+ OBJCONV := echo >NIL:
+ LD := PhxLnk
+ CP := copy CLONE
+ MV := rename
+ RM := delete
+endif
+
+ifeq ($(COMPILER),gcc)
+ CC := gcc
+ AS := PhxAss
+ OBJCONV := hunk2aout
+ LD := gcc
+ CP := cp -p
+ MV := mv
+ RM := rm
+endif
+
+###########################################################
+# SAS/C compiler, assembler and linker flags
+###########################################################
+#
+ifeq ($(COMPILER),sc)
+ CMN_CFLAGS := CODE=FAR PARM=REGISTERS STRMERGE STRINGSCONST NOSTKCHK AFP \
+ UTILLIB MCCONS NOMINC NOLINK CNEST INCDIR=$(TOP) INCDIR=$(INCDIR)
+ OPT_CFLAGS := OPTIMIZE OPTTIME OPTINLINELOCAL
+ DBG_CFLAGS := DEBUG=FULLFLUSH DEF DEBUG
+ CC_OUT := OBJNAME
+
+ CMN_ASFLAGS := SMALLDATA SMALLCODE ALIGN MACHINE=$(CPU) \
+ INCPATH=INCLUDE:,$(INCDIR) NOEXE QUIET
+ OPT_ASFLAGS := OPT !
+ DBG_ASFLAGS := SYMDEBUG LINEDEBUG SET "DEBUG"
+
+ CMN_LDFLAGS := SMALLCODE SMALLDATA DEFINE \
+ "__CXM33=__UCXM33,__CXD33=__UCXD33,__CXM22=__UCXM22,__CXD22=__UCXD22"
+ OPT_LDFLAGS := NODEBUG
+ DBG_LDFLAGS := NOSHORTRELOCS
+
+ CMN_LIBS := LIB:sc.lib LIB:small.lib
+ OPT_LIBS :=
+ DBG_LIBS := LIB:debug.lib
+
+# GST usage must be disabled due to bugs in SAS/C 6.58
+# GST := $(OBJDIR)/$(PROJNAME).gst
+# CMN_CFLAGS += GST=$(GST)
+
+# Old flags used with slink:
+# CMN_LDFLAGS += SMALLCODE SMALLDATA NOALVS NOICONS XREF \
+# DEFINE __CXM33=__UCXM33 DEFINE __CXD33=__UCXD33 \
+# DEFINE __CXM22=__UCXM22 DEFINE __CXD22=__UCXD22
+# DBG_LDFLAGS += BATCH ADDSYM
+
+endif
+
+
+###########################################################
+# GCC compiler, assembler and linker flags
+###########################################################
+#
+ifeq ($(COMPILER),gcc)
+ CMN_CFLAGS := -c -Wall -W -Wundef -Wimplicit -Wreturn-type \
+ -m$(CPU) -D$(OSVER) -I$(INCDIR) -I$(TOP) -I/gg/include -I/include
+ OPT_CFLAGS := -O2 -msmall-code -mregparm -fomit-frame-pointer \
+ -funroll-loops -fstrict-aliasing -finline-functions \
+ -fno-implement-inlines
+ DBG_CFLAGS := -DDEBUG -g
+ CC_OUT := -o
+
+ CMN_ASFLAGS := SMALLDATA SMALLCODE ALIGN MACHINE=$(CPU) \
+ INCPATH=INCLUDE:,$(INCDIR) NOEXE QUIET
+ OPT_ASFLAGS := OPT !
+ DBG_ASFLAGS := SYMDEBUG LINEDEBUG SET "DEBUG"
+
+ OPT_LDFLAGS := -s
+ DBG_LDFLAGS :=
+ CMN_LDFLAGS :=
+endif
+
+###########################################################
+# Build flags for Debug/Release version
+###########################################################
+#
+ifeq ($(DEBUG),1)
+ CFLAGS := $(CMN_CFLAGS) $(DBG_CFLAGS)
+ ASFLAGS := $(CMN_ASFLAGS) $(DBG_ASFLAGS)
+ LDFLAGS := $(CMN_LDFLAGS) $(DBG_LDFLAGS)
+ LIBS := $(CMN_LIBS) $(DBG_LIBS)
+else
+ CFLAGS := $(CMN_CFLAGS) $(OPT_CFLAGS)
+ ASFLAGS := $(CMN_ASFLAGS) $(OPT_ASFLAGS)
+ LDFLAGS := $(CMN_LDFLAGS) $(OPT_LDFLAGS)
+ LIBS := $(CMN_LIBS) $(OPT_LIBS)
+endif
+
+###########################################################
+# GNU make quirks
+###########################################################
+#
+# clear implicit rules for known suffixes such as .c and .o
+.SUFFIXES:
--- /dev/null
+**
+** Delta.asm
+**
+** Copyright (C) 1995 Bernardo Innocenti
+**
+** Delta encoding/decoding routine
+**
+
+_DeltaDecode:
+
+* void DeltaDecode (BYTE *dataBlock, ULONG blockSize);
+* A0 D0
+*
+* old=0
+* for i=0 to blockSize-1
+* new=dataBlock[i]+old
+* dataBlock[i]=new
+* old=new
+
+ MOVEQ #0,D1
+
+$1
+ ADD.B (A0),D1
+ MOVE.B D1,(A0)+
+
+ SUBQ.L #1,D0 ; Loop
+ BNE $1
+
+ RTS
+
+
+_DeltaEncode:
+
+* void DeltaEncode (BYTE *dataBlock, ULONG blockSize);
+* A0 D0
+*
+* old=0
+* for i=0 to blockSize-1
+* old=dataBlock[i]
+* new=old-new
+* datablock[i]=new
+
+
+ MOVE.W D2,-(SP)
+ MOVEQ #0,D1
+
+$1
+ MOVE.B (A0),D2
+ SUB.B D2,D1
+ MOVE.B D1,(A0)+
+
+ SUBQ.L #1,D0 ; Loop
+ BNE $1
+
+ MOVE.W (SP)+,D2
+ RTS
--- /dev/null
+#ifndef CLIB_XMODULE_PROTOS_H
+#define CLIB_XMODULE_PROTOS_H
+
+/*
+** $VER: xmodule_protos.h 4.0 (16.1.96)
+**
+** C prototypes. For use with 32 bit integers only.
+**
+** (C) Copyright 1995,96 Bernardo Innocenti
+** All Rights Reserved
+*/
+
+#ifndef EXEC_TYPES_H
+#include <exec/types.h>
+#endif
+#ifndef UTILITY_TAGITEM_H
+#include <utility/tagitem.h>
+#endif
+#ifndef LIBRARIES_XMODULE_H
+#include <libraries/xmodule.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*--- functions in V1 or higher (Release 4.0) ---*/
+
+/* Public entries */
+
+struct SongInfo *xmCreateSongA( struct TagItem * tagList );
+struct SongInfo *xmCreateSong( ULONG tag1, ... );
+BOOL xmDeleteSong( struct SongInfo *si );
+ULONG xmAddSongA( struct SongInfo *si, struct SongInfo *position, struct TagItem *tagList );
+ULONG xmRemSong( struct SongInfo *si );
+ULONG xmActivateSong( struct SongInfo *si );
+struct SongInfo *xmLockActiveSong( UWORD mode );
+struct XMHook *xmAddHookA( struct TagItem * tagList );
+struct XMHook *xmAddHook( ULONG tag1, ... );
+void xmRemHook ( struct XMHook *hook );
+struct XMHook *xmIdentifyModule( BPTR fh, struct TagItem * tagList );
+struct SongInfo *xmLoadModuleA( CONST_STRPTR fileName, struct TagItem * tagList );
+struct SongInfo *xmLoadModule( CONST_STRPTR fileName, ULONG tag1, ... );
+LONG xmSaveModuleA( struct SongInfo *si, CONST_STRPTR filename, struct XMHook *saver, struct TagItem * tagList );
+LONG xmSaveModule( struct SongInfo *si, CONST_STRPTR filename, struct XMHook *saver, ULONG tag1, ... );
+UWORD *xmSetSongLen( struct SongInfo *si, UWORD length );
+struct Pattern *xmAddPatternA( struct SongInfo *, struct TagItem *);
+struct Pattern *xmAddPattern( struct SongInfo *, LONG Tag1, ...);
+struct Pattern *xmSetPatternA( struct SongInfo *, ULONG pattNum, struct TagItem * );
+struct Pattern *xmSetPattern( struct SongInfo *, ULONG pattNum, LONG Tag1, ...);
+void xmRemPattern( struct SongInfo *, LONG, LONG );
+struct Instrument *xmAddInstrumentA( struct SongInfo *, LONG instrNum, struct TagItem * );
+struct Instrument *xmAddInstrument( struct SongInfo *, LONG instrNum, LONG, ... );
+struct Instrument *xmSetInstrumentA( struct SongInfo *, LONG instrNum, struct TagItem * );
+struct Instrument *xmSetInstrument( struct SongInfo *, LONG instrNum, LONG, ... );
+void xmRemInstrument( struct SongInfo *, LONG );
+LONG xmProcessSongA( struct SongInfo *, void *, struct TagItem * );
+LONG xmProcessSong( struct SongInfo *, void *, LONG, ... );
+LONG xmDisplayMessageA( LONG level, APTR messsage, APTR args );
+LONG xmDisplayMessage( LONG level, APTR messsage, ... );
+LONG xmDisplayProgress( ULONG actual, ULONG total );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CLIB_XMODULE_PROTOS_H */
--- /dev/null
+#ifndef LIBRARIES_PATTEDITCLASS_H
+#define LIBRARIES_PATTEDITCLASS_H
+/*
+** PattEditClass.h
+**
+** Copyright (C) 1995 by Bernardo Innocenti
+**
+** Pattern editor class built on top of the "gadgetclass".
+**
+** Note: Use 4 chars wide TABs to read this file.
+*/
+
+#ifndef DATATYPES_DATATYPESCLASS_H
+#include <datatypes/datatypesclass.h>
+#endif
+
+
+#define PATTEDITNAME "gadgets/pattedit.gadget"
+#define PATTEDITCLASS "patteditclass"
+#define PATTEDITVERS 2
+
+
+/********************/
+/* Class Attributes */
+/********************/
+
+#define PEA_Dummy (TAG_USER + 'PE' << 8)
+
+#define PEA_CursTrack (PEA_Dummy+1)
+#define PEA_CursColumn (PEA_Dummy+2)
+#define PEA_CursLine (PEA_Dummy+3)
+ /* (IGSNU) Cursor position.
+ */
+
+#define PEA_LeftTrack DTA_TopHoriz
+#define PEA_TopLine DTA_TopVert
+ /* (IGSNU) Top line and leftmost track of the editor view.
+ */
+
+#define PEA_Left (PEA_Dummy+6)
+#define PEA_Right (PEA_Dummy+7)
+#define PEA_Up (PEA_Dummy+8)
+#define PEA_Down (PEA_Dummy+9)
+#define PEA_CursLeft (PEA_Dummy+10)
+#define PEA_CursRight (PEA_Dummy+11)
+#define PEA_CursUp (PEA_Dummy+12)
+#define PEA_CursDown (PEA_Dummy+13)
+ /* (S) PEA_Left and PEA_Right scroll the view left and right one
+ * line. The cursor is positioned to the leftmost or rightmost
+ * position, respectively. PEA_Up and PEA_Down scroll the view up
+ * and down of one line. The cursor is positioned to the upper or
+ * lower visible line, respectively. PEA_Curs#? attributes move
+ * the cursor one position towards the direction specified. The
+ * contents of ti_Data is meaningless for all these attributes.
+ */
+
+#define PEA_UndoChange (PEA_Dummy+14)
+ /* (S) If ti_Data is > 0, one change made to the pattern is
+ * undone. If ti_Data is < 0, a previously undone change is
+ * redone. The cursor is always moved on the affected position.
+ */
+
+#define PEA_Changes (PEA_Dummy+15)
+ /* (IGS) ti_Data contains the number of changes made to the pattern.
+ */
+
+#define PEA_MarkRegion (PEA_Dummy+16)
+ /* (GS) ti_Data points to a struct Rectangle containing the limits
+ * (tracks and lines) of a pattern sub-region. When set, this
+ * attribute automatically starts range mode. Passing NULL in
+ * ti_Data causes the editor to clear the marked region. Passing
+ * ~0 in ti_Data toggles mark mode on and off.
+ * Getting this attribute returns a copy of the currently
+ * marked region, or {0,0,0,0} if no region is currently
+ * selected. The following relations must be true when setting
+ * this attribute and are guaranteed when getting it:
+ *
+ * MinX <= MaxX
+ * MinY <= MaxY
+ * MaxX < Patt->Tracks
+ * MaxY < Patt->Lines
+ */
+
+#define PEA_Flags (PEA_Dummy+17)
+ /* (IGSN) See PEF_#? flags definitions below.
+ */
+
+#define PEA_DisplayTracks DTA_VisibleHoriz
+#define PEA_DisplayLines DTA_VisibleVert
+ /* (GN) Maximum number of tracks and lines that fit in the gadget
+ * bounds.
+ */
+
+#define PEA_Pattern (PEA_Dummy+20)
+ /* (ISGU) ti_Data is a pointer to the Pattern
+ * structure to be displayed by the PatternEditor.
+ */
+
+#define PEA_CurrentInst (PEA_Dummy+21)
+ /* (IS) ti_Data is the default instrument
+ * number to be used when entering notes in the
+ * pattern editor.
+ */
+
+#define PEA_MaxUndoLevels (PEA_Dummy+22)
+ /* (IS) ti_Data is the maximum size of the undo buffer in number
+ * of slots. 0 disables undo feature. Defaults to 16.
+ */
+
+#define PEA_MaxUndoMem (PEA_Dummy+23)
+ /* (IS) ti_Data is the maximum memory used by the undo buffers.
+ * Setting it to 0 means unlimited memory. Defaults to 8192 bytes.
+ */
+
+#define PEA_TextFont DTA_TextFont
+ /* (I) ti_Data points to the TextFont to be
+ * used with the PatternEditor. The font must
+ * be mono spaced.
+ */
+
+#define PEA_AdvanceCurs (PEA_Dummy+25)
+ /* (IS) The lower 16 bits of ti_Data contain the number of lines
+ * the cursor moves when a note is typed. The upper 16 bits contain
+ * the number of tracks. Both numbers are signed, so backwards
+ * cursor advancement is possible.
+ */
+
+#define PEA_CursWrap (PEA_Dummy+26)
+ /* (IS) Can be 0 for no wrapping, PEF_HWRAP, PEF_VWRAP or both.
+ */
+
+#define PEA_BGPen (PEA_Dummy+27)
+#define PEA_TextPen (PEA_Dummy+28)
+#define PEA_LinesPen (PEA_Dummy+29)
+#define PEA_TinyLinesPen (PEA_Dummy+30)
+ /* (IS) Pens to be used to render the various editor elements.
+ * PEA_TextPen must be a power of two (1, 2, 4, 8...) because
+ * the text is rendered on one bitplane only. PEA_LinesPen
+ * and PEA_TinyLinesPen must not have the PEA_TextPen bit
+ * set because text scrolling happens on the text bitplane only
+ * and must not disturb the other elements. The defaults are
+ * 1 for PEA_TextPen and 2 for both PEA_LinesPen and
+ * PEA_TinyLinesPen.
+ */
+
+#define PEA_KeyboardMap (PEA_Dummy+31)
+ /* (IS) ti_Data points to a table which describes the notes associated
+ * to of the keys. The table starts with 1 Word specifying the number
+ * of keys following.
+ *
+ * Each key definition has this format:
+ *
+ * 1 Byte with the note associated with the first key.
+ * 1 Byte with the instrument associated with the first key.
+ * This value may be 0, in which case the current instrument
+ * will be used.
+ *
+ * Passing NULL in ti_Data disables keyboard mapping.
+ *
+ * NOTE: The table is referenced, not copied, so it must stay in
+ * memory until the pattern editor is disposed or the table is
+ * disabled.
+ */
+
+#define PEA_TrackChars (PEA_Dummy+32)
+ /* (IGSNU) Number of straight characters in a track. Minimum
+ * value is MINTRACKCHARS, maximum is MAXTRACKCHARS.
+ * Defaults to 10.
+ */
+
+#define PEA_ChangeNote (PEA_Dummy+33)
+ /* (GNU) ti_Data contains a packed track/line pair of coordinates
+ * (the line in the low order 16 bits and the track in the upper
+ * 16bits). The editor sends update messages containing this tag
+ * whenever a note has been changed, and redraws the specified note
+ * when it receives this tag. This is a way to keep two or more
+ * editors synchronized while they display the same pattern. It can
+ * also be used to count the changes made to the song.
+ * ti_Data will be -1L when more than one note has changed (for
+ * istance when the user has pasted a block), and other editors
+ * will have to redraw their display completely.
+ */
+
+#define PEA_SongInfo (PEA_Dummy+34)
+ /* (ISGU) ti_Data is a pointer to the songclass object which
+ * contains the pattern to be displayed. Without this tag, the
+ * pattern editor is unable to do some operations.
+ */
+
+#define PEA_PattNum (PEA_Dummy+35)
+ /* (ISGNU) The pattern number to display. To use this tag,
+ * the editor needs to be passed the PEA_SongInfo tag too.
+ */
+
+
+/* Definitions for PEA_Flags attribute */
+
+#define PEF_MARKING (1<<0) /* Range mode */
+#define PEF_HWRAP (1<<1) /* Horizontal cursor wrap */
+#define PEF_VWRAP (1<<2) /* Vertical cursor wrap */
+#define PEF_HEXMODE (1<<3) /* Use hexadecimal numbers */
+#define PEF_BLANKZERO (1<<4) /* Blank zero digits */
+#define PEF_INVERSETEXT (1<<5) /* Show backfilled text */
+#define PEF_DOTINYLINES (1<<6) /* Show tiny separator lines */
+#define PEF_DOCURSORRULER (1<<7) /* Show a ruler under the cursor */
+#define PEF_MARKFULLTRACKS (1<<8) /* Mark full tracks only */
+
+/* Private flags - Not settable by application */
+#define PEF_ADJUSTSCROLLBOX (1<<28) /* User is adjusting the scroll box */
+#define PEF_RESIZETRACKS (1<<29) /* User is resizing the tracks */
+#define PEF_SCROLLING (1<<30) /* View is scrolling (read only) */
+#define PEF_DRAGGING (1<<31) /* Cursor drag mode (read only) */
+
+
+
+/* Width of a track expressed in chars */
+#define MAXTRACKCHARS 14
+#define MINTRACKCHARS 4
+
+
+/* Cursor column names */
+enum {
+ COL_NOTE, /* Cursor on note field */
+ COL_INSTH, /* Cursor on instrument high nibble */
+ COL_INSTL, /* Cursor on instrument low nibble */
+ COL_EFF, /* Cursor on effect field */
+ COL_VALH, /* Cursor on effect value high nibble */
+ COL_VALL, /* Cursor on effect value low nibble */
+
+ COL_COUNT
+};
+
+#endif /* !LIBRARIES_PATTEDITCLASS_H */
--- /dev/null
+#ifndef SAMPEDITCLASS_H
+#define SAMPEDITCLASS_H
+/*
+** SampEditClass.h
+**
+** Copyright (C) 1995,96 by Bernardo Innocenti
+**
+** Sample editor class built on top of the "gadgetclass".
+**
+** Note: Use 4 chars wide TABs to read this file.
+*/
+
+
+#define SAMPEDITNAME "gadgets/sampedit.gadget"
+#define SAMPEDITCLASS "sampeditclass"
+#define SAMPEDITVERS 1
+
+
+/********************/
+/* Class Attributes */
+/********************/
+
+#define SEA_CursXPos (TAG_USER+1)
+#define SEA_CursYPos (TAG_USER+2)
+ /* (IGSNU) Cursor position. CursYPos is unused.
+ */
+
+#define SEA_XStart (TAG_USER+2)
+#define SEA_YStart (TAG_USER+3)
+ /* (IGSNU) Left and top offsett of the display relative to the start of the sample.
+ */
+
+#define SEA_XRatio (TAG_USER+4)
+#define SEA_YRatio (TAG_USER+5)
+ /* (ISGNU) Fixed point 16+16 bit number for the zoom factor. The upper word holds
+ * the integer part and the low word represents the decimals. Setting the ratio
+ * to 65536 sets the zoom factor to 1:1. Bigger numbers produce an higher zoom
+ * factor (e.g.: 131072 is 2:1).
+ */
+
+#define SEA_ScrollLeft (TAG_USER+6)
+#define SEA_ScrollRight (TAG_USER+7)
+#define SEA_ScrollUp (TAG_USER+8)
+#define SEA_ScrollDown (TAG_USER+9)
+#define SEA_CursLeft (TAG_USER+10)
+#define SEA_CursRight (TAG_USER+11)
+#define SEA_CursUp (TAG_USER+12)
+#define SEA_CursDown (TAG_USER+13)
+ /* (S) SEA_ScrollLeft and SEA_ScrollRight scroll the display left
+ * and right one unit. SEA_ScrollUp and SEA_ScrollDown move the
+ * display up and down of one unit. SEA_Curs#? attributes move
+ * the cursor one position towards the direction specified. The
+ * contents of ti_Data is meaningless for all these attributes.
+ */
+
+#define SEA_UndoChange (TAG_USER+14)
+ /* (S) If ti_Data is > 0, one change made to the pattern is
+ * undone. If ti_Data is < 0, a previously undone change is
+ * redone. The cursor is always moved on the affected position.
+ */
+
+#define SEA_Changes (TAG_USER+15)
+ /* (IGS) ti_Data contains the number of changes made to the sample.
+ */
+
+#define SEA_MarkRegion (TAG_USER+16)
+ /* (GS) ti_Data points to a struct Rectangle containing the
+ * limits (tracks and lines) of a pattern sub-region.
+ * When set, this attribute automatically starts range mode.
+ * Passing NULL in ti_Data causes the editor to clear the
+ * marked region. Passing ~0 in ti_Data toggles mark mode.
+ * Getting this attribute returns a copy of the currently
+ * marked region, or {0,0,0,0} if no region is currently
+ * selected.
+ */
+
+#define SEA_Flags (TAG_USER+17)
+ /* (IGSN) See PEF_#? flags definitions below.
+ */
+
+#define SEA_Instrument (TAG_USER+18)
+ /* (ISGU) ti_Data is a pointer to the Instrument
+ * structure to be displayed by the SampleEditor.
+ */
+
+#define SEA_MaxUndoLevels (TAG_USER+19)
+ /* (IS) ti_Data is the maximum size of the undo buffer in number
+ * of slots. 0 disables undo feature. Defaults to 8.
+ */
+
+#define SEA_MaxUndoMem (TAG_USER+20)
+ /* (IS) ti_Data is the maximum memory used by the undo buffers.
+ * Setting it to 0 means unlimited memory. Defaults to 32768 bytes.
+ */
+
+#define SEA_BackgroundPen (TAG_USER+21)
+#define SEA_LinePen (TAG_USER+22)
+#define SEA_FillPen (TAG_USER+23)
+#define SEA_GridPen (TAG_USER+24)
+ /* (IS) Pens to be used to render the various editor elements.
+ * SEA_TextPen must be a power of two (1, 2, 4, 8...) because
+ * the text is rendered on one bitplane only. SEA_LinesPen
+ * and SEA_TinyLinesPen must not have the SEA_TextPen bit
+ * set because text scrolling happens on the text bitplane only
+ * and must not disturb the other elements. The defaults are
+ * 1 for SEA_TextPen and 2 for both SEA_LinesPen and
+ * SEA_TinyLinesPen.
+ */
+
+
+
+/* Definitions for SEA_Flags attribute */
+
+#define SEF_MARKING (1<<0) /* Range mode */
+#define SEF_LOGMODE (1<<1) /* Logaritmic mode on */
+#define SEF_DOGRID (1<<2) /* Show superimposed grid */
+
+/* Private flags - Not settable by application */
+#define SEF_SCROLLING (1<<30) /* View is scrolling (read only) */
+#define SEF_DRAGGING (1<<31) /* Cursor drag mode (read only) */
+
+
+#endif /* SAMPEDITCLASS_H */
--- /dev/null
+#ifndef LIBRARIES_SONGCLASS_H
+#define LIBRARIES_SONGCLASS_H
+/*
+** $VER: songclass.h 3.9 (16.1.96)
+** Copyright (C) 1996 Bernardo Innocenti
+**
+** Set TAB size to 4 chars to read this header file
+**
+** Song class definition.
+*/
+
+#ifndef EXEC_SEMAPHORES_H
+#include <exec/semaphores.h>
+#endif /* EXEC_SEMAPHORES_H */
+
+#ifndef INTUITION_CLASSES_H
+#include <intuition/classes.h>
+#endif /* INTUITION_CLASSES_H */
+
+
+
+/* The name of the songclass */
+#define SONGCLASS "songclass"
+
+
+
+/* Song methods */
+#define SNGM_Dummy (TAG_USER + ('SG'<<8))
+#define SNGM_ADDPATTERN (SNGM_Dummy + 1)
+#define SNGM_SETPATTERN (SNGM_Dummy + 2)
+#define SNGM_REMPATTERN (SNGM_Dummy + 3)
+#define SNGM_ADDINSTRUMENT (SNGM_Dummy + 4)
+#define SNGM_SETINSTRUMENT (SNGM_Dummy + 5)
+#define SNGM_REMINSTRUMENT (SNGM_Dummy + 6)
+#define SNGM_SWAPINSTRUMENTS (SNGM_Dummy + 7)
+
+
+#define spAddPattern opSet /* No GadgetInfo field required */
+
+struct spSetPattern
+{
+ ULONG MethodID;
+ ULONG spsp_PattNum;
+ struct TagItem *spsp_AttrList;
+};
+
+struct spRemPattern
+{
+ ULONG MethodID;
+ ULONG sprp_PattNum;
+ ULONG sprp_NewPatt;
+};
+
+struct spSetInstrument
+{
+ ULONG MethodID;
+ ULONG spsi_InstrNum;
+ struct TagItem *spsi_AttrList;
+};
+
+#define spAddInstrument spSetInstrument
+
+struct spRemInstrument
+{
+ ULONG MethodID;
+ ULONG spri_Num;
+};
+
+struct spSwapInstruments
+{
+ ULONG MethodID;
+ ULONG spsi_Num1;
+ ULONG spsi_Num2;
+};
+
+
+
+/* Song attributes */
+
+#define SNGA_Dummy (TAG_USER + 'SG'<<8) /* use */
+
+#define SNGA_CurrentPatt (SNGA_Dummy + 1) /* ISG */
+#define SNGA_CurrentPos (SNGA_Dummy + 2) /* ISG */
+#define SNGA_CurrentInst (SNGA_Dummy + 3) /* ISG */
+#define SNGA_CurrentLine (SNGA_Dummy + 4) /* ISG */
+#define SNGA_CurrentTrack (SNGA_Dummy + 5) /* ISG */
+
+#define SNGA_Length (SNGA_Dummy + 6) /* ISG */
+#define SNGA_NumPatterns (SNGA_Dummy + 7) /* G */
+#define SNGA_NumInstruments (SNGA_Dummy + 8) /* G */
+#define SNGA_MaxTracks (SNGA_Dummy + 9) /* G */
+
+#define SNGA_Title (SNGA_Dummy + 10) /* ISG */
+#define SNGA_Author (SNGA_Dummy + 11) /* ISG */
+#define SNGA_Description (SNGA_Dummy + 12) /* ISG */
+/* (UBYTE *) Long description/comment for the
+ * song. The string may contain newlines and there is no
+ * length limit.
+ */
+
+#define SNGA_Path (SNGA_Dummy + 13) /* ISG */
+/* The complete path name of the file the song was
+ * loaded from.
+ */
+
+#define SNGA_Changes (SNGA_Dummy + 14) /* ISG */
+/* (ULONG) Sets the number of changes made to the song
+ * since the last time it was saved.
+ */
+
+#define SNGA_TotalChanges (SNGA_Dummy + 15) /* ISG */
+/* (ULONG) Total number of changes made to the song
+ * since it has been created.
+ */
+
+#define SNGA_CreationDate (SNGA_Dummy + 16) /* ISG */
+#define SNGA_LastChanged (SNGA_Dummy + 17) /* ISG */
+/* (ULONG) Datestamp of last time the song was saved
+ */
+
+#define SNGA_DefaultTracks (SNGA_Dummy + 32) /* ISG */
+#define SNGA_DefaultPattLen (SNGA_Dummy + 33) /* ISG */
+#define SNGA_GlobalSpeed (SNGA_Dummy + 34) /* ISG */
+#define SNGA_GlobalTempo (SNGA_Dummy + 35) /* ISG */
+#define SNGA_RestartPos (SNGA_Dummy + 36) /* ISG */
+
+#define SNGA_ReadyToUse (SNGA_Dummy + 37) /* I */
+/* (BOOL) Add one position and one pattern when
+ * creating a new song.
+ */
+
+#define PATTA_Dummy (TAG_USER + 'PA'<<8)
+#define PATTA_Lines (PATTA_Dummy + 1) /* I */
+#define PATTA_Tracks (PATTA_Dummy + 2) /* I */
+#define PATTA_Name (PATTA_Dummy + 3) /* IS */
+#define PATTA_Pattern (PATTA_Dummy + 4) /* I */
+#define PATTA_Num (PATTA_Dummy + 5) /* I */
+#define PATTA_Replace (PATTA_Dummy + 6) /* I */
+
+
+#define INSTRA_Dummy (TAG_USER + 'IN'<<8)
+#define INSTRA_Type (INSTRA_Dummy + 1) /* I */
+#define INSTRA_Name (INSTRA_Dummy + 2) /* IS */
+#define INSTRA_Volume (INSTRA_Dummy + 3) /* IS */
+#define INSTRA_Sample (INSTRA_Dummy + 4) /* IS */
+#define INSTRA_Length (INSTRA_Dummy + 5) /* IS */
+#define INSTRA_Flags (INSTRA_Dummy + 6) /* IS */
+#define INSTRA_LoopStart (INSTRA_Dummy + 7) /* IS */
+#define INSTRA_LoopLen (INSTRA_Dummy + 8) /* IS */
+#define INSTRA_LoopEnd (INSTRA_Dummy + 9) /* IS */
+#define INSTRA_FineTune (INSTRA_Dummy + 10) /* IS */
+
+
+/* ProTracker-style aliases for loop tags */
+#define INSTRA_Repeat INSTRA_LoopStart
+#define INSTRA_Replen INSTRA_LoopLen
+#define INSTRA_Repend INSTRA_LoopEnd
+
+
+/* Default values for some song attributes */
+#define DEF_PATTLEN 64
+#define DEF_NUMTRACKS 4
+#define DEF_SONGSPEED 6
+#define DEF_SONGTEMPO 125
+
+
+/* Maximum values */
+#define MAXOCTAVES 6 /* Number of octaves currently supported */
+#define MAXNOTES (12*MAXOCTAVES+1) /* Number of entries in notes table */
+#define MAXTRACKS 255 /* Maximum number of tracks in a pattern */
+#define MAXINSTRUMENTS 255 /* Maximum number of instruments in a song */
+#define MAXPATTERNS 32767 /* Maximum number of patterns */
+#define MAXPATTLINES 32767 /* Maximum number of lines in a pattern */
+#define MAXPOSITIONS 32767 /* Maximum number of song positions */
+
+#define SEQUENCE_QUANTUM 32 /* Multiples of SEQUENCE_QUANTUM positions are
+ * allocated in the sequence array
+ */
+#define PATTERNS_QUANTUM 16 /* Multiples of PATTERNS_QUANTUM pointers are
+ * allocated in the patterns array
+ */
+
+struct Instrument
+{
+ UWORD Type; /* Instrument type (See defs) */
+ UWORD Volume; /* Volume (max $40) */
+ STRPTR Name; /* Instrument Name */
+ ULONG Length; /* Length of instr */
+ BYTE *Sample; /* Sampled data */
+ ULONG Repeat; /* Loop start (No loop = 0) */
+ ULONG Replen; /* Loop size (No loop = 1) */
+ WORD FineTune; /* Instrument FineTune (-8..+7) */
+ UWORD Flags; /* Unused */
+};
+
+
+#ifndef LIBRARIES_XMODULECLASS_H
+
+/* Possibile values for Instrument->InstType */
+enum {
+ ITYPE_SAMPLE8, /* 8 bit sampled waveform */
+ ITYPE_SAMPLE16, /* TODO: 16 bit sampled waveform */
+ ITYPE_SYNTH, /* TODO: Synthetic instrument */
+ ITYPE_HYBRID, /* TODO: Both Synth & Sample */
+ ITYPE_MIDI /* TODO: Played by external MIDI device */
+};
+#endif /* !LIBRARIES_XMODULECLASS_H */
+
+
+
+/* Pass this value to AllocVec() to allocate sample memory */
+#define MEMF_SAMPLE MEMF_ANY
+
+
+#ifndef LIBRARIES_XMODULECLASS_H
+struct Note
+{
+ UBYTE Note; /* See below for more info. */
+ UBYTE Inst; /* Instrument number */
+ UBYTE Vol; /* Volume for this note */
+ UBYTE Pad; /* Unused */
+ UBYTE EffNum; /* See definitions below. */
+ UBYTE EffVal; /* Effect value ($00-$FF) */
+};
+#endif /* !LIBRARIES_XMODULECLASS_H */
+
+
+
+struct Pattern
+{
+ UWORD Tracks; /* Support for variable number of tracks */
+ UWORD Lines; /* Number of lines in pattern */
+ STRPTR Name; /* Name of this pattern */
+ ULONG Reserved[2]; /* Reserved for future enhancements */
+ struct Note *Notes[0]; /* Pointers to the lines */
+
+ /* Note: dynamic size structure! */
+};
+
+
+
+/*
+ * Effects
+ */
+#ifndef LIBRARIES_XMODULECLASS_H
+enum {
+ EFF_NULL, /* $00 */
+ EFF_PORTAMENTOUP, /* $01 */
+ EFF_PORTAMENTODOWN, /* $02 */
+ EFF_TONEPORTAMENTO, /* $03 */
+ EFF_VIBRATO, /* $04 */
+ EFF_TONEPVOLSLIDE, /* $05 */
+ EFF_VIBRATOVOLSLIDE, /* $06 */
+ EFF_TREMOLO, /* $07 */
+ EFF_UNUSED, /* $08 */
+ EFF_SAMPLEOFFSET, /* $09 */
+ EFF_VOLSLIDE, /* $0A */
+ EFF_POSJUMP, /* $0B */
+ EFF_SETVOLUME, /* $0C */
+ EFF_PATTERNBREAK, /* $0D */
+ EFF_MISC, /* $0E */
+ EFF_SETSPEED, /* $0F */
+ EFF_SETTEMPO, /* $10 */
+ EFF_ARPEGGIO, /* $11 */
+
+ EFF_COUNT /* $12 */
+};
+#endif /* !LIBRARIES_XMODULECLASS_H */
+
+
+
+/* Song class white box istance
+ *
+ * You can read from this structure directly for speed critical operations.
+ * Modifying the data contained in the SongInfo structure is only allowed
+ * by means of object methods.
+ *
+ * Before using a public song, please protect yourself against other tasks
+ * modifying these fields. Always obtainin a (shared) lock on the
+ * SignalSemaphore contained in the SongInfo structure before doing anything
+ * with it. The methods provided by the song class will not handle access
+ * arbitration for you.
+ */
+struct SongInfo
+{
+ struct Node Link; /* Link for the song list */
+ struct SignalSemaphore Lock; /* Prevent other tasks from disturbing you! */
+ UWORD Length; /* Number of positions in song */
+ UWORD MaxTracks; /* Maximum number of tracks in song */
+ UWORD NumPatterns; /* Number of patterns in song */
+ UWORD LastInstrument; /* Unused */
+ UWORD CurrentPatt;
+ UWORD CurrentPos;
+ UWORD CurrentInst;
+ UWORD CurrentLine;
+ UWORD CurrentTrack;
+ UWORD DefNumTracks; /* Default number of tracks in new patterns */
+ UWORD DefPattLen; /* Default number of lines in new patterns */
+ UWORD GlobalSpeed; /* Default song speed */
+ UWORD GlobalTempo; /* Default song tempo */
+ UWORD RestartPos; /* Position to restart from */
+ UWORD Reserved[4]; /* Reserved for future enhancements */
+
+ /* Note: all data beyond this point is longword aligned */
+
+ ULONG Flags; /* See definitions below */
+ void *Pool; /* Memory pool from which all song data
+ * must be allocated from.
+ */
+ UWORD *Sequence; /* Pointer to song sequence */
+ struct Pattern **Patt; /* Pointer to patterns array */
+ struct Instrument **Instr; /* Pointer to instruments array */
+ UWORD *ActiveTracks; /* (-1 = disabled) *yet unused* */
+ STRPTR Title; /* Song name */
+ STRPTR Author; /* Name of the author */
+ STRPTR Path; /* Original song path */
+ STRPTR Description; /* Verbose song description */
+
+ ULONG Changes; /* Changes made since song was last saved */
+ ULONG TotalChanges; /* Total number of changes made so far */
+ ULONG CreationDate; /* Date of OM_NEW */
+ ULONG LastChanged; /* Date of last saving */
+
+
+ /* PRIVATE DATA --- HANDS OFF!
+ *
+ * All fields beyond this point are subject to change and
+ * *will* change in future releases of XModule. Do not
+ * even think of using them for whatever reason. Right? :-)
+ */
+ struct Instrument *InstrumentsTable[MAXINSTRUMENTS + 1];
+};
+
+
+
+/* Flags for SongInfo->Flags */
+/* No flags are defined yet */
+
+
+
+#endif /* !LIBRARIES_SONGCLASS_H */
--- /dev/null
+ IFND LIBRARIES_SONGLCASS_I
+LIBRARIES_SONGCLASS_I SET 1
+**
+** $VER: songclass.i 3.9 (7.1.96)
+** Copyright (C) 1995,96 Bernardo Innocenti
+**
+** Set TAB size to 8 chars to read this header file
+**
+** Assembler structure definitions for the song class
+**
+
+
+ IFND EXEC_TYPES_I
+ include exec/types.i
+ ENDC ; EXEC_TYPES_I
+
+ IFND EXEC_NODES_I
+ include exec/nodes.i
+ ENDC ; EXEC_NODES_I
+
+ IFND EXEC_SEMAPHORES_I
+ include exec/semaphores.i
+ ENDC ; EXEC_SEMAPHORES_I
+
+
+MAXOCTAVES EQU 6 ; Number of octaves currently supported
+MAXNOTES EQU (12*MAXOCTAVES+1) ; Entries in note conversion table
+MAXTRACKS EQU 255 ; Maximum number of tracks in a pattern
+MAXINSTRUMENTS EQU 255 ; Maximum number of instruments in a song
+MAXPATTLINES EQU 32767 ; Maximum number of lines in a pattern
+MAXPOSITIONS EQU 32767 ; Maximum number of song positions
+MAXPATTERNS EQU 32767 ; Maximum number of patterns
+
+
+
+**************************************************************************
+** Some definitions transcripted from "XModule.h"
+**************************************************************************
+
+ ENUM 0
+
+ EITEM EFF_NULL
+ EITEM EFF_PORTAMENTOUP
+ EITEM EFF_PORTAMENTODOWN
+ EITEM EFF_TONEPORTAMENTO
+ EITEM EFF_VIBRATO
+ EITEM EFF_TONEPVOLSLIDE
+ EITEM EFF_VIBRATOVOLSLIDE
+ EITEM EFF_TREMOLO
+ EITEM EFF_UNUSED
+ EITEM EFF_SAMPLEOFFSET
+ EITEM EFF_VOLSLIDE
+ EITEM EFF_POSJUMP
+ EITEM EFF_SETVOLUME
+ EITEM EFF_PATTERNBREAK
+ EITEM EFF_MISC
+ EITEM EFF_SETSPEED
+ EITEM EFF_SETTEMPO
+ EITEM EFF_ARPEGGIO
+
+ EITEM EFF_COUNT
+
+
+
+ STRUCTURE Note,0
+ UBYTE note_Note
+ UBYTE note_Inst
+ UBYTE note_Vol
+ UBYTE note_Pad
+ UBYTE note_EffNum
+ UBYTE note_EffVal
+ LABEL Note_SIZEOF
+
+
+ STRUCTURE Instrument,0
+ UWORD in_InstType ; Instrument type (See defs)
+ UWORD in_Volume ; Volume (max $40)
+ APTR in_Name ; Instrument Name
+ APTR in_SampleData ; Sampled data
+ ULONG in_Length ; Length of instr
+ ULONG in_Repeat ; Loop start (No loop = 0)
+ ULONG in_Replen ; Loop size (No loop = 1)
+ WORD in_FineTune ; Instrument FineTune (-8..+7)
+ UWORD in_Flags ; Unused
+ LABEL in_SIZEOF
+
+
+ STRUCTURE Pattern,0
+ UWORD pa_Tracks ; Support for variable number of tracks
+ UWORD pa_Lines ; Number of lines in pattern
+ APTR pa_Name ; Pattern Name
+ STRUCT pa_Reserved,8 ; Reserved for future enhancements
+ LABEL pa_Notes,0 ; Pointers to all tracks follow
+ LABEL pa_SIZEOF
+
+
+* Song class white box istance
+*
+* You can read from this structure directly for speed critical operations.
+* Modifying the data contained in the SongInfo structure is only allowed
+* by means of object methods.
+*
+* Before using a public song, please protect yourself against other tasks
+* modifying these fields. Always obtainin a (shared) lock on the
+* SignalSemaphore contained in the SongInfo structure before doing anything
+* with it. The methods provided by the song class will not handle access
+* arbitration for you.
+*
+ STRUCTURE SongInfo,LN_SIZE
+ STRUCT si_Lock,SS_SIZE
+ UWORD si_Length ; Number of positions in song
+ UWORD si_MaxTracks ; Number of tracks in song
+ UWORD si_NumPatterns ; Number of patterns in song
+ UWORD si_LastInstrument ; Unused
+ UWORD si_CurrentPatt
+ UWORD si_CurrentPos
+ UWORD si_CurrentInst
+ UWORD si_CurrentLine
+ UWORD si_CurrentTrack
+ UWORD si_DefNumTracks ; Default number of tracks in new patterns
+ UWORD si_DefPattLen ; Default number of lines in new patterns
+
+ UWORD si_GlobalSpeed ; Default song speed
+ UWORD si_GlobalTempo ; Default song tempo
+ UWORD si_RestartPos ; Position to restart from
+ STRUCT si_Reserved,8 ; Reserved for future enhancements
+
+* Note: all data beyond this point is longword aligned
+
+ ULONG si_Flags ; See definitions below
+ APTR si_Pool ; The memory pool where song data
+ ; must be allocated from.
+ APTR si_Sequence ; UWORD *: Pointer to song sequence
+ APTR si_Patt
+ APTR si_Instr
+ APTR si_ActiveTracks ; UWORD *: Active Tracks (-1 = disabled)
+ APTR si_Title ; Song name
+ APTR si_Author ; Author of song
+ APTR si_Path ; Original song path
+ APTR si_Description ; Verbose song description
+
+ ULONG si_Changes ; Number of changes made to this song
+ ULONG si_TotalChanges ; Total number of changes made so far
+ ULONG si_CreationDate ; Date of OM_NEW
+ ULONG si_LastChanged ; Date of last saving
+
+ LABEL si_SIZEOF
+
+
+
+ ENDC ; !LIBRARIES_SONGCLASS_I
--- /dev/null
+#ifndef LIBRARIES_XMODULE_H
+#define LIBRARIES_XMODULE_H
+/*
+** XModule.h
+**
+** Copyright (C) 1995,96,97 Bernardo Innocenti
+**
+** Set TAB size to 8 chars to read this header file
+**
+** Public structures and constants for the xmodule.library
+*/
+
+#ifndef DOS_DOS_H
+#include <dos/dos.h>
+#endif /* DOS_DOS_H */
+
+#ifndef EXEC_LISTS_H
+#include <exec/lists.h>
+#endif /* EXEC_LISTS_H */
+
+#ifndef EXEC_LIBRARIES_H
+#include <exec/libraries.h>
+#endif /* EXEC_LIBRARIES_H */
+
+#ifndef EXEC_SEMAPHORES_H
+#include <exec/semaphores.h>
+#endif /* EXEC_SEMAPHORES_H */
+
+#ifndef INTUITION_CLASSES_H
+#include <intuition/classes.h>
+#endif /* INTUITION_CLASSES_H */
+
+#ifndef COMPILERSPECIFIC_H
+#include "CompilerSpecific.h"
+#endif /* COMPILERSPECIFIC_H */
+
+
+/***************/
+/* XModuleBase */
+/***************/
+
+struct XModuleBase
+{
+ struct Library xm_Library; /* Standard Library node structure */
+ UWORD xm_Pad0; /* Struct Library is misaligned! */
+ struct SignalSemaphore xm_BaseLock; /* Obtain this before browsing
+ * in any of the fields!
+ */
+ UWORD xm_Pad1; /* SignalSemaphore is misaligned! */
+
+ /* The following data is longword aligned */
+
+ struct MinList xm_Songs; /* List of public SongInfo structs */
+ struct MinList xm_Loaders; /* List of available loaders */
+ struct MinList xm_Savers; /* List of available savers */
+
+ void *xm_Pool; /* Public mem pool used by XModule */
+ Class *xm_SongClass; /* Song boopsi class */
+ struct SongInfo *xm_CurrentSong; /* Pointer to currently active song */
+ struct XMHook *xm_DefaultSaver; /* Currently selected saver hook */
+ struct Screen *xm_Screen; /* XModule screen (might be NULL) */
+ ULONG xm_Flags; /* See definitions below */
+ STRPTR xm_ScreenName; /* Name of XModule's public screen */
+ STRPTR xm_RexxName; /* Name of XModule's ARexx port */
+
+ ULONG xm_Reserved[4];
+
+ struct DosLibrary *xm_DOSBase; /* Some useful library bases that */
+ struct UtilityBase *xm_UtilityBase; /* can be grabbed and used by */
+ struct Library *xm_IFFParseBase; /* external hooks. These four are */
+ struct IntuitionBase *xm_IntuitionBase; /* guaranteed to be always open. */
+};
+
+
+
+struct XMHook
+{
+ struct Node xmh_Link; /* Link to other hooks in a list.
+ * ln_Name contains the Hook name.
+ */
+ UWORD xmh_Version; /* Will be set to XMHOOK_VERSION */
+ STRPTR xmh_Descr; /* Short description of this hook */
+ STRPTR xmh_Author; /* Author's name */
+ ULONG xmh_Flags; /* See below for definitions */
+ ULONG xmh_ID; /* IFF-style ID. Set it to the FORM
+ * type for IFF loaders/savers.
+ */
+ APTR xmh_UserData; /* User's private data */
+ struct Library *xmh_LibraryBase; /* External hook's library base */
+ ASMCALL void (*xmh_RemoveHook) (
+ REG(a0,struct XMHook *hook)); /* Hook remotion entrypoint */
+ ASMCALL LONG (*xmh_LoadMod) (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *song),
+ REG(a1, struct XMHook *loader),
+ REG(a2, struct TagItem *tags)); /* Module loader entrypoint */
+ ASMCALL LONG (*xmh_SaveMod) (
+ REG(d0, BPTR fh),
+ REG(a0, struct SongInfo *song),
+ REG(a1, struct XMHook *saver),
+ REG(a2, struct TagItem *tags)); /* Module saver entrypoint */
+ ASMCALL struct XMHook * (*xmh_IdentifyMod) (
+ REG(d0, BPTR fh),
+ REG(a0, struct XMHook *hook),
+ REG(a1, struct TagItem *tags)); /* Module identification entrypoint */
+
+ ULONG xmh_MaxTracks; /* Max. number of tracks */
+ ULONG xmh_MaxPatterns; /* Max. number of patterns */
+ ULONG xmh_MaxInstruments; /* Max. number of instruments */
+ ULONG xmh_MaxLength; /* Max. number of positions */
+ ULONG xmh_MaxSampleLen; /* Max. length of each sample */
+ ULONG xmh_MaxPattLen; /* Max. lines in a pattern */
+
+ ULONG xmh_Reserved[2]; /* Avoid recompilation :-) */
+};
+
+
+
+#define XMHOOK_VERSION 0
+
+#define NT_XMSONG 'X'
+#define NT_XMLOADER 'L'
+#define NT_XMSAVER 'S'
+
+
+
+/* Flag definitions for XMHook->xmh_Flags */
+
+/* Basic type */
+#define XMHF_BINARY 0x0000
+#define XMHF_ASCII 0x0001
+#define XMHF_IFF 0x0002
+#define XMHF_MISC 0x0003
+
+/* These specify that the saver is able to exclude
+ * the corresponding items when saving in its own format.
+ */
+#define XMHF_EXCLUDE_SEQUENCE (1<<5)
+#define XMHF_EXCLUDE_INSTRUMENTS (1<<6)
+#define XMHF_EXCLUDE_PATTERNS (1<<7)
+#define XMHF_EXCLUDE_NAMES (1<<8)
+
+/* This module format does not support variable length
+ * patterns. Use in conjunction with XMHOOK_MaxPattLen tag.
+ */
+#define XMHF_FIXED_PATT_LEN (1<<9)
+
+/* This is an internal hook */
+#define XMHF_INTERNAL (1<<31)
+
+
+
+/* Flags for xmDisplayMessage() */
+
+
+
+#define XMDMF_ALERT 0x00000000
+#define XMDMF_FAIL 0x00000001
+#define XMDMF_ERROR 0x00000002
+#define XMDMF_WARNING 0x00000003
+#define XMDMF_NOTE 0x00000004
+#define XMDMF_INFORMATION 0x00000005
+#define XMDMF_COMMENT 0x00000006
+#define XMDMF_DEBUG 0x00000007
+#define XMDMF_LEVELMASK 0x00000007
+
+#define XMDMF_DOSERRORMASK 0x0000FFFF
+#define XMDMF_DOSFAULT (1<<28)
+#define XMDMF_ACTION (1<<29)
+#define XMDMF_USEREQUESTER (1<<30)
+#define XMDMF_USECATALOG (1<<31)
+
+
+
+/* Specific error codes */
+
+#define ERROR_NOTMODULE ERROR_OBJECT_WRONG_TYPE /* File format not recognized */
+#define ERROR_IOERR RETURN_FAIL /* A DOS call (e.g.: Read()) failed.
+ * check IoErr() for specific error.
+ */
+
+
+/********/
+/* Tags */
+/********/
+
+#define XMSNG_Dummy (TAG_USER + 'XS'<<8)
+
+#define XMSNG_AddToList (XMSNG_Dummy + 1)
+#define XMSNG_OldSong (XMSNG_Dummy + 2)
+#define XMSNG_Loader (XMSNG_Dummy + 3)
+#define XMSNG_FileHandle (XMSNG_Dummy + 4)
+#define XMSNG_Active (XMSNG_Dummy + 5)
+
+/* Tags for xmOptimizeSongA() */
+
+#define XMSNG_Optimize (XMSNG_Dummy + 16)
+#define XMSNG_RemapInstruments (XMSNG_Dummy + 17)
+#define XMSNG_LimitPatterns (XMSNG_Dummy + 18)
+#define XMSNG_Join (XMSNG_Dummy + 19)
+#define XMSNG_Merge (XMSNG_Dummy + 20)
+
+/* Flags for XMSNG_Optimize */
+#define XMOF_DEFAULT (1<<0)
+#define XMOF_REM_UNUSED_PATTERNS (1<<1)
+#define XMOF_REM_DUP_PATTERNS (1<<2)
+#define XMOF_REM_UNUSED_INSTRS (1<<3)
+#define XMOF_REM_DUP_INSTRS (1<<4)
+#define XMOF_CUT_INSTR_LOOPS (1<<5)
+#define XMOF_CUT_INSTR_TAILS (1<<6)
+#define XMOF_CUT_PATTERNS (1<<7)
+#define XMOF_REMAP_INSTRS (1<<8)
+
+
+/* XModule hook tags */
+
+#define XMHOOK_Dummy (TAG_USER + 'XH'<<8)
+
+#define XMHOOK_Type (XMHOOK_Dummy + 1)
+#define XMHOOK_Name (XMHOOK_Dummy + 2)
+#define XMHOOK_Priority (XMHOOK_Dummy + 3)
+#define XMHOOK_Descr (XMHOOK_Dummy + 4)
+#define XMHOOK_Author (XMHOOK_Dummy + 5)
+#define XMHOOK_ID (XMHOOK_Dummy + 6)
+#define XMHOOK_Flags (XMHOOK_Dummy + 7)
+#define XMHOOK_UserData (XMHOOK_Dummy + 8)
+#define XMHOOK_LibraryBase (XMHOOK_Dummy + 9)
+#define XMHOOK_RemoveFunc (XMHOOK_Dummy + 10)
+#define XMHOOK_LoadModFunc (XMHOOK_Dummy + 11)
+#define XMHOOK_SaveModFunc (XMHOOK_Dummy + 12)
+#define XMHOOK_IdentifyModFunc (XMHOOK_Dummy + 13)
+#define XMHOOK_MaxTracks (XMHOOK_Dummy + 14)
+#define XMHOOK_MaxPatterns (XMHOOK_Dummy + 15)
+#define XMHOOK_MaxInstruments (XMHOOK_Dummy + 16)
+#define XMHOOK_MaxLength (XMHOOK_Dummy + 17)
+#define XMHOOK_MaxSampleLen (XMHOOK_Dummy + 18)
+#define XMHOOK_MaxPattLen (XMHOOK_Dummy + 19)
+
+
+#endif /* !LIBRARIES_XMODULE_H */
--- /dev/null
+ IFND XMODULE_I
+XMODULE_I SET 1
+**
+** XModule replay routine 1.0
+**
+** Copyright (C) 1995 Bernardo Innocenti
+**
+** Assembler structure definitions for player
+**
+
+ IFND EXEC_TYPES_I
+ include exec/types.i
+ ENDC ; EXEC_TYPES_I
+
+ IFND EXEC_NODES_I
+ include exec/nodes.i
+ ENDC ; EXEC_NODES_I
+
+
+
+MAXINSTRUMENTS EQU 64 ; Maximum number of instruments loaded
+MAXTABLENOTE EQU (12*3) ; Number of entries in note conversion table
+MAXTABLEEFFECTS EQU 20 ; Number of entries in effect conversion table
+MAXPATTERNS EQU 128 ; Maximum number of patterns
+MAXTRACKS EQU 32 ; Maximum number of tracks in a pattern
+MAXPATTLINES EQU 32768 ; Maximum number of lines in a pattern
+MAXPOSITIONS EQU 32768 ; Maximum number of song positions
+MAXINSTNAME EQU 32
+MAXSONGNAME EQU 32
+MAXAUTHNAME EQU 64
+MAXPATTNAME EQU 16
+
+PATHNAME_MAX EQU 256
+
+
+**************************************************************************
+** Some definitions transcripted from "XModule.h"
+**************************************************************************
+
+ ENUM 0
+
+ EITEM EFF_NULL
+ EITEM EFF_PORTAMENTOUP
+ EITEM EFF_PORTAMENTODOWN
+ EITEM EFF_TONEPORTAMENTO
+ EITEM EFF_VIBRATO
+ EITEM EFF_TONEPVOLSLIDE
+ EITEM EFF_VIBRATOVOLSLIDE
+ EITEM EFF_TREMOLO
+ EITEM EFF_UNUSED
+ EITEM EFF_SAMPLEOFFSET
+ EITEM EFF_VOLSLIDE
+ EITEM EFF_POSJUMP
+ EITEM EFF_SETVOLUME
+ EITEM EFF_PATTERNBREAK
+ EITEM EFF_MISC
+ EITEM EFF_SETSPEED
+ EITEM EFF_SETTEMPO
+ EITEM EFF_ARPEGGIO
+
+ EITEM EFF_COUNT
+
+
+
+ STRUCTURE Note,0
+ UBYTE note_Note
+ UBYTE note_Inst
+ UBYTE note_Vol
+ UBYTE note_Pad
+ UBYTE note_EffNum
+ UBYTE note_EffVal
+ LABEL Note_SIZEOF
+
+
+ STRUCTURE Instrument,0
+ UWORD in_InstType ; Instrument type (See defs)
+ UWORD in_Volume ; Volume (max $40)
+ STRUCT in_Name,MAXINSTNAME ; Instrument Name
+ APTR in_SampleData ; Sampled data
+ ULONG in_Length ; Length of instr
+ ULONG in_Repeat ; Loop start (No loop = 0)
+ ULONG in_Replen ; Loop size (No loop = 1)
+ WORD in_FineTune ; Instrument FineTune (-8..+7)
+ UWORD in_Flags ; Unused
+ LABEL in_SIZEOF
+
+
+ STRUCTURE Pattern,0
+ UWORD pa_Tracks ; Support for variable number of tracks
+ UWORD pa_Lines ; Number of lines in pattern
+ STRUCT pa_PattName,MAXPATTNAME ; Pattern Name
+ STRUCT pa_Notes,MAXTRACKS * 4 ; Pointers to the lines
+ LABEL pa_SIZEOF
+
+
+ STRUCTURE SongInfo,LN_SIZE
+ UWORD si_Length ; Number of positions in song
+ UWORD si_MaxTracks ; Number of tracks in song
+ UWORD si_NumPatterns ; Number of patterns in song
+ UWORD si_NumInstruments ; Unused
+ UWORD si_GlobalSpeed ; Default song speed
+ UWORD si_GlobalTempo ; Default song tempo
+ UWORD si_Restart ; Position to restart from
+ UWORD si_CurrentPatt
+ UWORD si_CurrentPos
+ UWORD si_CurrentInst
+ UWORD si_Flags ; See definitions below
+ ULONG si_Changes ; Number of changes made to this song
+ APTR si_Sequence ; *UWORD: Pointer to song sequence
+ STRUCT si_PattData,pa_SIZEOF*MAXPATTERNS
+ STRUCT si_Inst,in_SIZEOF*MAXINSTRUMENTS
+ STRUCT si_ActiveTracks,MAXTRACKS ; UBYTE []: Active Tracks (0 = disabled)
+ STRUCT si_SongName,MAXSONGNAME ; UBYTE []: Song name
+ STRUCT si_Author,MAXAUTHNAME ; Author of song
+ STRUCT si_SongPath,PATHNAME_MAX; ; Original song path
+ APTR si_Pool ; The memory pool where song data
+ ; must be allocated from.
+ LABEL si_SIZEOF
+
+
+ ENDC ; !XMODULE_I
--- /dev/null
+ IFND LIBRARIES_XMODULE_LIB_I
+LIBRARIES_XMODULE_LIB_I SET 1
+
+
+_LVOxmCreateSongA EQU -54
+_LVOxmDeleteSong EQU -60
+_LVOxmAddSongA EQU -66
+_LVOxmRemSong EQU -72
+_LVOxmActivateSong EQU -78
+_LVOxmLockActiveSong EQU -84
+_LVOxmAddHookA EQU -90
+_LVOxmRemHook EQU -96
+_LVOxmIdentifyModule EQU -102
+_LVOxmLoadModuleA EQU -108
+_LVOxmSaveModuleA EQU -114
+_LVOxmSetSongLen EQU -120
+_LVOxmAddPatternA EQU -126
+_LVOxmSetPatternA EQU -132
+_LVOxmRemPattern EQU -138
+_LVOxmAddInstrumentA EQU -144
+_LVOxmSetInstrumentA EQU -150
+_LVOxmRemInstrument EQU -156
+_LVOxmProcessSongA EQU -162
+_LVOxmDisplayMessageA EQU -168
+_LVOxmDisplayProgress EQU -174
+
+ ENDC
\ No newline at end of file
--- /dev/null
+#ifndef LIBRARIES_XMODULECLASS_H
+#define LIBRARIES_XMODULECLASS_H
+/*
+** XModuleClass.h
+**
+** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
+**
+** Use 4 chars wide TABs to read this file
+**
+** IFF XMOD file format definition.
+*/
+
+
+#ifndef IFF_IFFPARSE_H
+#include <libraries/iffparse.h>
+#endif /* !IFF_IFFPARSE_H */
+
+#ifndef DATATYPES_SOUNDCLASS_H
+#include <datatypes/soundclass.h>
+#endif /* !DATATYPES_SOUNDCLASS_H */
+
+
+#define ID_AUTH MAKE_ID('A','U','T','H')
+#define ID_ANNO MAKE_ID('A','N','N','O')
+
+#define ID_XMOD MAKE_ID('X','M','O','D')
+
+/* The following chunk ID definition conflicts with <libraries/maud.h> */
+#ifndef ID_MHDR
+#define ID_MHDR MAKE_ID('M','H','D','R')
+#endif
+
+#define ID_SONG MAKE_ID('S','O','N','G')
+#define ID_SHDR MAKE_ID('S','H','D','R')
+#define ID_SEQN MAKE_ID('S','E','Q','N')
+
+#define ID_PATT MAKE_ID('P','A','T','T')
+#define ID_PHDR MAKE_ID('P','H','D','R')
+
+#define ID_INST MAKE_ID('I','N','S','T')
+
+
+struct ModuleHeader
+{
+ UWORD XModuleVersion; /* XModule version used to save this file */
+ UWORD XModuleRevision; /* XModule revision used to save this file */
+ UWORD NumSongs; /* Number of songs in this module */
+ UWORD ActiveSong;
+ UWORD MasterVolume;
+ UWORD MixingMode;
+ ULONG MixingRate;
+};
+
+
+
+struct SongHeader
+{
+ UWORD MaxTracks; /* Number of tracks in song */
+ UWORD Length; /* Number of positions in song */
+ UWORD NumPatterns; /* Number of patterns in song */
+ UWORD LastInstrument; /* Last used instrument */
+ UWORD GlobalSpeed; /* Global song speed */
+ UWORD GlobalTempo; /* Global song tempo */
+ UWORD RestartPos; /* Position to restart from */
+ UWORD CurrentPatt;
+ UWORD CurrentLine;
+ UWORD CurrentTrack;
+ UWORD CurrentPos;
+ UWORD CurrentInst;
+
+ /* New fields added in XModule version 3.5 -
+ * not present in modules saved by version 3.4 and below
+ */
+ UWORD DefNumTracks; /* Default number of tracks in new patterns */
+ UWORD DefPattLen; /* Default number of lines in new patterns */
+ ULONG TotalChanges; /* Total number of changes made so far */
+ ULONG CreationDate; /* Date of OM_NEW */
+ ULONG LastChanged; /* Date of last saving */
+};
+
+
+
+struct PatternHeader
+{
+ UWORD Tracks; /* Number of tracks in pattern */
+ UWORD Lines; /* Number of lines in pattern */
+};
+
+
+
+#ifndef LIBRARIES_SONGCLASS_H
+
+struct Note
+{
+ UBYTE Note; /* See below for more info */
+ UBYTE Inst; /* Instrument number */
+ UBYTE Vol; /* Volume for this note */
+ UBYTE Pad; /* Unused */
+ UBYTE EffNum; /* See definitions below. */
+ UBYTE EffVal; /* Effect value ($00-$FF) */
+};
+/* Note values:
+ *
+ * 0 - no note
+ * 1 - C-0
+ * 2 - C#0
+ * ...
+ * 13 - C-1
+ * ...
+ * 72 - B-5
+ */
+
+
+/* Values for Note->EffNum */
+
+enum {
+ EFF_NULL, /* $00 */
+ EFF_PORTAMENTOUP, /* $01 */
+ EFF_PORTAMENTODOWN, /* $02 */
+ EFF_TONEPORTAMENTO, /* $03 */
+ EFF_VIBRATO, /* $04 */
+ EFF_TONEPVOLSLIDE, /* $05 */
+ EFF_VIBRATOVOLSLIDE, /* $06 */
+ EFF_TREMOLO, /* $07 */
+ EFF_UNUSED, /* $08 */
+ EFF_SAMPLEOFFSET, /* $09 */
+ EFF_VOLSLIDE, /* $0A */
+ EFF_POSJUMP, /* $0B */
+ EFF_SETVOLUME, /* $0C */
+ EFF_PATTERNBREAK, /* $0D */
+ EFF_MISC, /* $0E */
+ EFF_SETSPEED, /* $0F */
+ EFF_SETTEMPO, /* $10 */
+ EFF_ARPEGGIO, /* $11 */
+
+ EFF_COUNT /* $12 */
+};
+
+#endif /* !LIBRARIES_SONGCLASS_H */
+
+
+
+struct InstrumentHeader
+{
+ WORD Num; /* Instrument slot number */
+ UWORD Type; /* Instrument type (see defs) */
+ WORD FineTune; /* Instrument FineTune (-8..+7) */
+};
+
+
+
+#ifndef LIBRARIES_SONGCLASS_H
+
+/* Possible values for Instrument->InstType */
+enum {
+ ITYPE_SAMPLE8, /* signed 8bit sample */
+ ITYPE_SAMPLE16, /* TODO: signed 16bit sample */
+ ITYPE_SYNTH, /* TODO: Synthetic instrument */
+ ITYPE_HYBRID, /* TODO: Both Synth & Sample */
+ ITYPE_MIDI /* TODO: Played by external MIDI device */
+};
+#endif /* !LIBRARIES_SONGCLASS_H */
+
+#endif /* !LIBRARIES_XMODULECLASS_H */
--- /dev/null
+#ifndef _INCLUDE_PRAGMA_XMODULE_LIB_H
+#define _INCLUDE_PRAGMA_XMODULE_LIB_H
+
+#ifndef CLIB_XMODULE_PROTOS_H
+#include <clib/xmodule_protos.h>
+#endif
+
+#if defined(AZTEC_C) || defined(__MAXON__) || defined(__STORM__)
+#pragma amicall(XModuleBase,0x036,xmCreateSongA(a0))
+#pragma amicall(XModuleBase,0x03C,xmDeleteSong(a0))
+#pragma amicall(XModuleBase,0x042,xmAddSongA(a0,a1,a2))
+#pragma amicall(XModuleBase,0x048,xmRemSong(a0))
+#pragma amicall(XModuleBase,0x04E,xmActivateSong(a0))
+#pragma amicall(XModuleBase,0x054,xmLockActiveSong(d0))
+#pragma amicall(XModuleBase,0x05A,xmAddHookA(a0))
+#pragma amicall(XModuleBase,0x060,xmRemHook(a0))
+#pragma amicall(XModuleBase,0x066,xmIdentifyModule(d0,a0))
+#pragma amicall(XModuleBase,0x06C,xmLoadModuleA(a0,a1))
+#pragma amicall(XModuleBase,0x072,xmSaveModuleA(a0,a1,a2,a3))
+#pragma amicall(XModuleBase,0x078,xmSetSongLen(a0,d0))
+#pragma amicall(XModuleBase,0x07E,xmAddPatternA(a0,a1))
+#pragma amicall(XModuleBase,0x084,xmSetPatternA(a0,d0,a1))
+#pragma amicall(XModuleBase,0x08A,xmRemPattern(a0,d0,d1))
+#pragma amicall(XModuleBase,0x090,xmAddInstrumentA(a0,d0,a1))
+#pragma amicall(XModuleBase,0x096,xmSetInstrumentA(a0,d0,a1))
+#pragma amicall(XModuleBase,0x09C,xmRemInstrument(a0,d0))
+#pragma amicall(XModuleBase,0x0A2,xmProcessSongA(a0,a1,a2))
+#pragma amicall(XModuleBase,0x0A8,xmDisplayMessageA(d0,a0,a1))
+#pragma amicall(XModuleBase,0x0AE,xmDisplayProgress(d0,d1))
+#endif
+#if defined(_DCC) || defined(__SASC)
+#pragma libcall XModuleBase xmCreateSongA 036 801
+#pragma libcall XModuleBase xmDeleteSong 03C 801
+#pragma libcall XModuleBase xmAddSongA 042 A9803
+#pragma libcall XModuleBase xmRemSong 048 801
+#pragma libcall XModuleBase xmActivateSong 04E 801
+#pragma libcall XModuleBase xmLockActiveSong 054 001
+#pragma libcall XModuleBase xmAddHookA 05A 801
+#pragma libcall XModuleBase xmRemHook 060 801
+#pragma libcall XModuleBase xmIdentifyModule 066 8002
+#pragma libcall XModuleBase xmLoadModuleA 06C 9802
+#pragma libcall XModuleBase xmSaveModuleA 072 BA9804
+#pragma libcall XModuleBase xmSetSongLen 078 0802
+#pragma libcall XModuleBase xmAddPatternA 07E 9802
+#pragma libcall XModuleBase xmSetPatternA 084 90803
+#pragma libcall XModuleBase xmRemPattern 08A 10803
+#pragma libcall XModuleBase xmAddInstrumentA 090 90803
+#pragma libcall XModuleBase xmSetInstrumentA 096 90803
+#pragma libcall XModuleBase xmRemInstrument 09C 0802
+#pragma libcall XModuleBase xmProcessSongA 0A2 A9803
+#pragma libcall XModuleBase xmDisplayMessageA 0A8 98003
+#pragma libcall XModuleBase xmDisplayProgress 0AE 1002
+#endif
+#ifdef __STORM__
+#pragma tagcall(XModuleBase,0x036,xmCreateSong(a0))
+#pragma tagcall(XModuleBase,0x042,xmAddSong(a0,a1,a2))
+#pragma tagcall(XModuleBase,0x05A,xmAddHook(a0))
+#pragma tagcall(XModuleBase,0x066,xmIdentifyModuleTags(d0,a0))
+#pragma tagcall(XModuleBase,0x06C,xmLoadModule(a0,a1))
+#pragma tagcall(XModuleBase,0x072,xmSaveModule(a0,a1,a2,a3))
+#pragma tagcall(XModuleBase,0x07E,xmAddPattern(a0,a1))
+#pragma tagcall(XModuleBase,0x084,xmSetPattern(a0,d0,a1))
+#pragma tagcall(XModuleBase,0x090,xmAddInstrument(a0,d0,a1))
+#pragma tagcall(XModuleBase,0x096,xmSetInstrument(a0,d0,a1))
+#pragma tagcall(XModuleBase,0x0A2,xmProcessSong(a0,a1,a2))
+#pragma tagcall(XModuleBase,0x0A8,xmDisplayMessage(d0,a0,a1))
+#endif
+#ifdef __SASC_60
+#pragma tagcall XModuleBase xmCreateSong 036 801
+#pragma tagcall XModuleBase xmAddSong 042 A9803
+#pragma tagcall XModuleBase xmAddHook 05A 801
+#pragma tagcall XModuleBase xmIdentifyModuleTags 066 8002
+#pragma tagcall XModuleBase xmLoadModule 06C 9802
+#pragma tagcall XModuleBase xmSaveModule 072 BA9804
+#pragma tagcall XModuleBase xmAddPattern 07E 9802
+#pragma tagcall XModuleBase xmSetPattern 084 90803
+#pragma tagcall XModuleBase xmAddInstrument 090 90803
+#pragma tagcall XModuleBase xmSetInstrument 096 90803
+#pragma tagcall XModuleBase xmProcessSong 0A2 A9803
+#pragma tagcall XModuleBase xmDisplayMessage 0A8 98003
+#endif
+
+#endif /* _INCLUDE_PRAGMA_XMODULE_LIB_H */
\ No newline at end of file
--- /dev/null
+#ifndef _PROTO_XMODULE_H
+#define _PROTO_XMODULE_H
+
+#include <exec/types.h>
+
+extern struct XModuleBase *XModuleBase;
+
+#include <clib/xmodule_protos.h>
+#include <pragmas/xmodule_pragmas.h>
+
+#endif
--- /dev/null
+Note: The history log is reverse sorted: newer changes are added from the top.
+
+
+New for version 4.0
+===================
+
+CHANGE: Split the Makefile into several smaller Makefiles, one for each
+ subdirectory. Added "config.mk" file with global definitions for
+ make.
+
+CHANGE: Recompiled with SAS/C 6.58. I had to quit using GST because it
+ seems whis new version has bugs with them.
+
+ADD: Added 3 new versions of the XModule main executable:
+
+ - XModule_020 optimized for 68020 or better processors
+ - XModule_OS30 does not include support code for V36-38
+ - XModule_020_OS30 both 68020 code and OS 3.0-only code
+
+ Patch files are distributed to generate these new versions from
+ the generic executable. The generic version still has support
+ for OS 3.0 and works fine with any 68K CPU.
+
+CHANGE: Changed the Makefile to use PhxLnk instead of Slink. This made
+ the exetuable a bit smaller and the linking process much faster.
+
+CHANGE: Recompiled with SAS/C 6.57... Those guys at SAS again did a
+ very good job, and thanks to the new warnings, I was able to
+ catch a couple of nasty bugs hidden inside the code.
+
+ADD: The installer script will now choose a sensible default path
+ for the XModule drawer.
+
+CHANGE: Changed the Makefile to support GNU make and to use the ADE
+ version of makeinfo. Hopefully one day I'll switch
+ definitely to GCC...
+
+CHANGE: Changed the installation script to use the spatch program to
+ generate 020 versions of some executables.
+ This makes the distribution archive a bit smaller and the
+ users a bit happier :)
+
+
+New for version 3.9
+===================
+
+FIX: Fixed assembler startup to use FindTask(NULL) instead of
+ grabbing the pointer from ExecBase->ThisTask because this
+ field has been declared private by Amiga Technologies.
+
+ADD: Brand new user interface with scalable windows (like MUI)
+ and localized labels! WOW!
+
+CHANGE: Songs are now represented internally as `boopsi' objects.
+ And, once again, XModule has been almost rewritten! :-)
+
+ADD: External loader/saver hooks are now supported through the
+ xmodule.library interface routines. Other kinds of
+ external hooks are possible, but not used at the moment.
+ Useless to say, it required very hard work.
+
+ADD: xmodule.library. This library is contained inside the XModule
+ executable, so you can't see it in your Libs: drawer. It is used
+ by all external hooks and by internal functions. Implementing it
+ almost required a total rewrite of all the XModule code.
+
+ADD: Internal support for volume column. The pattern editor does
+ not show it yet, but it's there.
+
+FIX: Fixed a stupid bug in automatic pattern breaking routines.
+ (It inserted a '0' effect instead of 'D' to break the pattern :-)
+
+ADD: Copying, cutting and pasting in the pattern editor is now possible.
+
+ADD: Patterns can now be loaded/saved.
+
+ADD: Menu items are now localized.
+
+ADD: Added french catalog translation. Thanks to Julien Wilk.
+
+ADD: Added german catalog translation and Installer script. Thanks to
+ Michael Reichenbach.
+
+FIX: Fixed font handling problems. XModule does not require
+ diskfont.library anymore.
+
+CHANGE: Changed the "Get" buttons in the Prefs window into nice vectorial
+ images.
+
+FIX: Fixed a tiny bug in the #?Tracker loader. It would not look for
+ position table entries beyond the end when calculating the
+ number of patterns, so it would fail when loading some modules.
+
+
+New for version 3.5
+===================
+
+ Version 3.5 was released to beta testers only, therefore version
+3.9 is the official release version of 3.5.
+
+
+
+New for version 3.4
+===================
+
+ADD: Full featured Pattern Editor!!! Another big step towards a
+ complete n-channels music editor...
+
+ADD: New loader for ScreamTracker 3.01! Conversion is not very good,
+ but at least it works.
+
+ADD: Supports loading and saving TakeTracker with up to 32 channels.
+
+ADD: Locale support. There is only an italian catalog yet, translated
+ by my friend Steven Cantini. If you want more translations, please
+ do them yourself and send me the .ct files! :-)
+
+ADD: The installer script has been localized. Thanks to Steven Cantini
+ for translating Italiano strings.
+
+FIX: XModule did not load the last sample in (Octa)MED modules. Thanks
+ to Brian Tietz for reporting this.
+
+ADD: XModule now stores the current directory and pattern of all
+ file requesters in the settings file.
+
+ADD: Added window cycling through ALT-TAB and ALT-SHIFT-TAB.
+
+CHANGE: Rearranged song sequence handling. Now it is possible to have
+ an arbitrary song length (let's say 32768) and it will be easy
+ to add support for song Sections ala OctaMED 5, but I don't
+ think this is a useful option. Tell me if you want it.
+
+CHANGE: Now uses memory pools for storing songs. This will
+ reduce memory fragmentation and will make XModule a little
+ bit faster at the cost of a little (say 15%) memory waste.
+
+ADD: Fonts preferences are now loaded and saved (sorry, I forgot
+ to do it in the last version :-).
+
+FIX: Fixed a bug in the ASL file requester handling routines which
+ caused XModule to save songs to its own current directory
+ instead of the directories where they were loaded from.
+
+FIX: Loading garbage as a Sound/ProTracker module will no longer lead
+ to a crash. I've put a lot of efford in trying to catch all
+ dangerous situations, like modules with no patterns or with
+ invalid sequence.
+
+ADD: Added support for MMD1 (OctaMED) modules. Improved MED loader and
+ saver.
+
+FIX: The "Play All" button in the Sample Editor no longer crashes.
+ I'm sorry, it was a really stupid bug. :-))
+
+FIX: Fixed a nasty bug in the instrument clearing routine that also
+ affected module optimization. The Repeat/Replen of killed
+ instruments was not cleared, causing ProTracker 3.0 to refuse
+ loading them. Added some sanity checks in the
+ Sound/Noise/ProTracker loader to fix these corrupted modules.
+ Thanks to Steven Cantini for reporting this bug.
+
+CHANGE: Default icons are now kept in a separate "Icons" drawer.
+
+ADD: The SaveIcons switch in the Instruments window does now work
+ properly.
+
+CHANGE: Double clicking in the Instruments list brings up the Sample Editor.
+
+CHANGE: Changed the way to insert patterns in the song sequence. Now when
+ double-click on the song position, the current pattern gets inserted
+ there. Double clicking on a pattern causes opens the pattern editor
+ window.
+
+FIX: The SongInfo ListView would not hilight the correct song when opened.
+
+FIX: Some string gadgets (The Song Name string gadget in the SongInfo
+ window, to name one) allowed too many characters.
+
+ADD: Context sensitive help in all windows: pressing HELP now brings up
+ help on the active window.
+
+CHANGE: When cloning the default screen, the overscan ClipRect is used to
+ calculate the screen size. This behavior is more pleasing for those
+ who use a very big autoscrolling workbench screen. (Hi Alfonso!)
+
+FIX: OctaMed loader now recognizes and converts decimal volumes.
+
+ADD: Instruments window "Add" button now spawns a file requester and lets
+ you load an instrument.
+
+CHANGE: When an instrument has no name and has no sample associated, it
+ will be marked "--empty--" in the Instruments list.
+
+FIX: Removed a serious bug that could crash the machine when a
+ compressed module was loaded.
+
+ADD: The Instruments window is now an AppWindow and you can drop sample
+ icons over it.
+
+ADD: The SongInfo window is now an AppWindow and you can drop module
+ icons over it.
+
+FIX: Dropping multiple module icons in the ToolBox window or on XModule's
+ AppIcon now loads all the modules correctly instead of loading all
+ them one over the other.
+
+FIX: Low memory handling generally improved: XModule now has much more
+ chances of surviving when memory is very short.
+
+FIX: Fixed a really serious bug in Clear Pattern option of Clear Module
+ panel. The song was left with no patterns at all. Now one empty
+ pattern is always allocated after clearing the patterns.
+
+FIX: When you delete an instrument, the "Total Module Size" and "Total
+ Instrument Size" boxes in the SongInfo panel are now correctly
+ updated to show the change.
+
+FIX: The setting editor did not notice when one of the fonts was changed
+ with another font with the same width and heigth.
+
+CHANGE: The progress indicator will not be updated when the user is playing
+ with Intuition. This way operations do not lock while a window is
+ being resized or dragged.
+
+ADD: Menu items in Sample window are now updated correctly when
+ preferences are loaded/saved.
+
+
+New for versions 3.2, 3.3
+=========================
+
+ Version 3.2 and 3.3 were never officially released. Some of the changes
+logged for version 3.4 were actually introduced in these internal versions.
+
+
+New for version 3.1
+===================
+
+ Version 3.0 was released to beta testers only, therefore version
+3.1 is the official release version of 3.0.
+
+
+New for version 3.0
+===================
+
+ Version 3.0 took a lot more time than I originally planned. My machine
+has been used for an animation for a local TV, and the people working on it
+have been so clever that they deleted almost all my WORK: partition.
+I had a backup on a streamer tape, but somehow I couldn't restore it. The
+only thing that actually survived the catastrophe was XModule and its source,
+except the history log and a short assembly file. I've rewritten the
+assembler part, but I'm not going to type the history again (who remembers?).
+There were really lots of news (the list was two pages long), but you'll
+have to discover all them by yourself.
+
+
+New for version 2.8
+===================
+
+FIX: Fixed a problem with NT/PT FineTune.
+
+FIX: Some memory did not free when user aborted loading/saving of a
+ ST/NT/PT module.
+
+ADD: Support for 6CHN/8CHN modules (not tested). Thanks to A.Booker
+ for suggesting this.
+
+ADD: XModule now supports loading signed and unsigned 8-bit samples.
+
+FIX: Instrument longer than 128K are correctly clipped when saving
+ to ST/NT/PT.
+
+FIX: Memory wasn't freed when loading an instrument over an already
+ existing one.
+
+CHANGE: Windows are now simple refreshed. Slower but eats a lot less memory.
+
+FIX: Now the DefaultTool for icons created by XModule contain the
+ correct pathname.
+
+CHANGE: Code has been optimized and rearranged in many parts,
+ especially the user interface. This should fix some potential
+ bugs and make it easier to add new features quickly. Locale
+ support and context-sensitive help are almost ready.
+
+ADD: Some new shortcut keys; more keyboard control over gadgets
+ available for Kickstart V39+.
+
+ADD: Documentation cleaned up and some parts added.
+
+FIX: Possible small memory loss when closing a window.
+
+FIX: When iconifies, XModule now tries to use its-own icon instead of the
+ Workbench default Tool icon.
+
+FIX: Screen re-opening bug which caused XModule to illegally close
+ someone else's screen in some cases.
+
+ADD: Many new gadgets in Sample editor (yet some are inactive).
+
+ADD: Range marker in Sample window.
+
+FIX: SaveFormat window text positions were incorrect.
+
+FIX: Newly created songs now have 1 empty pattern allocated.
+ This fixed enforcer hits when saving empty songs.
+
+ADD: Three different methods for rendering samples (the code was already
+ there since version 2.7 but I didn't add the menu items).
+
+CHANGE: Removed DETACH Shell option, it caused too many bugs.
+
+ADD: Some new (undocumented) ARexx commands.
+
+ADD: Added settings file saving/loading.
+
+ADD: Added Log window.
+
+
+New for version 2.7
+===================
+
+FIX: Fixed occasional hits when closing a window.
+
+FIX: Bug in IFF 8SVX saver found & killed.
+
+CHANGE: Faster sample graph drawing under V39+.
+
+ADD: ScreenMode requester.
+
+
+New for version 2.6
+===================
+
+CHANGE: XModule now requires Kickstart V37 instead of V36.
+
+FIX: Instruments ViewList under Kick 2.0 is now displayed correctly.
+
+CHANGE: Assembler startup as been rewritten (smaller & bugs fixed).
+
+ADD: Sequence editor is now fully operative!
+
+ADD: Progress indicator finally sports a percentage bar!
+
+ADD: Can now detach from the Shell.
+
+FIX: The documentation has been revised and corrected.
+
+FIX: Enforcer hits occurrred in Progress Panel under some rare conditions.
+
+
+New for version 2.5
+===================
+
+FIX: Problem (read: guru) when trying to save as Sound/Noise/ProTracker
+ a module having the first pattern longer than 64 lines.
+
+FIX: Oktalyzer saver used to set mode 8 for all instruments.
+
+FIX: Sample editor did not update Repeat and Replen when opened.
+
+FIX: the Repeat value was doubled when loading SoundTracker modules and
+ halved when saving.
+
+CHANGE: Numbers in sequence list are now displayed in decimal format.
+
+FIX: Some weird SoundTracker 15 instrument module did not
+ load correctly.
+
+ADD: Installer script included in the distribution.
+
+ADD: MagicWB icons for XModule and its documentation.
+
+CHANGE: Documentation improved a little.
+
+
+New for version 2.4
+===================
+
+ADD: Can now load raw samples.
+
+ADD: Added DataTypes support to load instruments.
+
+ADD: Can now load raw data as instrument.
+
+FIX: Illegal characters in instrument names are now filtered out.
+
+FIX: Sample graphic now clips correctly.
+
+FIX: Fine tune was not saved in SoundTracker modules.
+
+
+New for version 2.3
+===================
+
+ADD: Asyncronous FileRequesters.
+
+ADD: Asl & ReqTools FileRequester support.
+
+FIX: Didn't accept some SoundTracker effects.
+
+
+New for version 2.2
+===================
+
+ADD: XModule now sports an ARexx port featuring two (2) commands,
+ one of which does not work yet!
+
+ADD: When opening on the Workbench screen, the main window is now an
+ AppWindow. You can drop module icons on it and see them loaded
+ auto-magically!
+
+ADD: Command line & Tool Types arguments are now supported.
+
+FIX: Save Fromat panel works a little better now.
+
+
+New for version 2.1
+===================
+
+FIX: Status display routines did sometimes crash.
+
+ADD: Sample graphic display is now a little nicer and clips (almost)
+ correctly.
+
+ADD: There is now a Save Format requester, but it is still not fully
+ functional.
+
+ADD: Sound/Noise/ProTracker loader now warns notes with 0 as instrument
+ number. I discovered that some composers (for istance
+ AudioMonster) use this trick instead of command C00 to kill sound
+ on a track. The problem is that some players (e.g.: MultiPlayer 1.32)
+ will play some random data instead. I had no time to investigate
+ deeper, but a solution could be to replace such notes with a C00
+ effect, loosing the original effect if any (such as speed commands).
+ In this cases you will have to fix the module manually.
+
+CHANGE: Sequence window look has drastically changed and includes a
+ list to pick patterns from ( It is not operative, thoug :-( ).
+
+CHANGE: Repeat and Replen moved to Sample window.
+
+
+*** Versions below 2.1 have never been released ***
--- /dev/null
+###########################################################
+# Make the AmigaGuide and ASCII documentation
+###########################################################
+
+include $(TOP)/config.mk
+
+ALL_DOCS := XModule.guide_34 XModule.guide_39 XModule.guide_40 XModule.doc XModule.txt
+
+all: $(ALL_DOCS)
+
+clean:
+ delete $(ALL_DOCS)
+
+XModule.guide_34: XModule.texi
+# $(MAKEINFO) --amiga-34 --no-split -D "agver 34" --output XModule.guide XModule.texi
+# Delete $@
+# Rename XModule.guide $@
+
+XModule.guide_39: XModule.texi
+# $(MAKEINFO) --amiga-39 --no-split -D "agver 39" --output XModule.guide XModule.texi
+# Delete $@
+# Move XModule.guide $@
+
+XModule.guide_40: XModule.texi
+ $(MAKEINFO) --amiga --no-split -D "agver 40" --output XModule.guide XModule.texi
+ Delete $@
+ Move XModule.guide $@
+
+XModule.doc: XModule.texi
+ $(MAKEINFO) --amiga --no-headers --no-split --output $@ XModule.texi
+
+XModule.txt: XModule.texi
+ $(MAKEINFO) --no-headers --no-split --output $@ XModule.texi
--- /dev/null
+XModule beta release notes
+**************************
+
+
+Installation
+============
+
+ The "Install" drawer contains the XModule installation script.
+Double-click on your preferred language and follow the instrutions.
+The installation requires the Commodore Installer utility located
+somewhere in the Workbench path (usually in "SYS:Utilities/").
+We recommend version 43.3 or better of the Installer program, which
+can be retrieved on Aminet. The installation script will probably
+work with older versions of the Installer, but it has not been tested.
+
+ To update from a previous version of XModule, follow the
+instructions in the installatation program. To uninstall XModule,
+just delete its drawer and all its contents.
+
+ If you don't have the Installer or if you don't want to use it,
+you can still install XModule manually. Just make a new drawer and
+copy all the XModule files in it. Manual installation is strongly
+discouraged because you will miss the automatic tooltypes substituitions
+and the 020+/OS30 patches made for you by the Installer.
+
+
+New Pattern Editor
+==================
+
+ Starting with release 3.2, XModule incorporates a full featured pattern
+editor with keyboard and mouse driven scrolling, range selection and
+multiple undo/redo function. The supported keyboard sequences are
+described in the documentation (have a look at it). I'm sorry that there
+is still no replay routine, but I have already contacted Jarno Paanen (the
+author of PS3M) and Peter Kunath (the author of DeliTracker). Both of them
+have been so kind to give me all the sources and help I needed to add their
+32 channel mixing engines to XModule. Please, be patient.
+
+
+Beta Version
+============
+
+ This package contains a *BETA* release of XModule.
+Before running it, you should be warned about a few things:
+
+- Many gadgets and menu items are still no-ops.
+
+- The documentation is sometimes incorrect and some parts
+ have not been written yet.
+
+- Please do not base your opinion of XModule on this beta release.
+ The final version will be fully operative and bug free.
+
+- Do not report obvious bugs: I know that the Sample editor is
+ not working. If you see a little, hard to discover, bug
+ such as a small memory leak or strange errors when converting a
+ particular module, then do not hesitate reporting it immediatly.
+ Failure to do so will probably result in the bug being
+ still there in the next release of XModule.
+
+
+ Thank you for beta testing my hard work.
+
+
+ // Bernardo Innocenti
+ \X/ bernie@shock.nervous.com
--- /dev/null
+
+ Description Pri
+ -------------------------------------------------
+ Add volume column to notes 4
+ Split view in pattern editor 2
+ Backdrop window in pattern editor 2
+ Measure in pattern editor 3
+ Infobar in pattern editor 4
+ Separate rendering task for pattern editor 3
+ Scrollbars position in prefs 3
+ Play routine in C 7
+ XM loader 6
+ XM saver 5
+ S3M saver 4
+ Pack patterns in XMOD format 2
+ Compression options 3
+ Fix GUI bugs 7
+ SampEditClass 6
+ SplineEditClass 3
+ ARexx commands 5
+ ARexx menu 4
+ Recently used mods quick menu 3
+ Long text comment to modules 4
+ Module Datatype 1
+ Autosave 3
+ Load instruments inside module 2
+ Optimize sample usage 4
+ Fix Playtime 5
+ Operator hooks 5
+ Move edit variables away from MHDR chunk 3
+ Move MasterVolume inside SONG 3
+
+ song.datatype proposal:
+
+ +-------------------------------------------+
+ | +------------+ View: * Patterns |
+ | | Tapedeck | |
+ | +------------+ * Sequence |
+ | +------------+ |
+ | | Volume | * Instruments |
+ | +------------+ |
+ | +---------------------------------------+ |
+ | | | |
+ | | | |
+ | | Show patterns, sequence, instruments | |
+ | | | |
+ | | | |
+ | +---------------------------------------+ |
+ +-------------------------------------------+
--- /dev/null
+\input amigatexinfo
+\input texinfo
+@c $VER: XModule.texi 3.9 (21.1.97)
+
+@c Remember to update these variables with each release
+@set prgname XModule
+@set prgver 3.9
+
+@setfilename XModule.guide
+@settitle Documentation for XModule @value{prgver} (@value{prgdate})
+@setchapternewpage odd
+@c @set amiga_convert_nodes <-- this makes node names look like shit!
+
+@titlepage
+
+@title CrossModule
+@subtitle @value{prgver} @today{}
+@author by Bernardo Innocenti
+
+Copyright @copyright{} 1993,94,95,96,97 by Bernardo Innocenti, all rights reserved.
+Freely distributable.
+
+@page
+@vskip 0pt plus 1filll
+
+@end titlepage
+
+@c ******************
+@c * Top *
+@c ******************
+
+@ifinfo
+@node Top, Overview, , (dir)
+@top
+
+This manual documents @value{prgname} version @value{prgver} released on
+@w{@today{}}. It is formatted for viewing with @w{AmigaGuide V@value{agver}}.
+
+Copyright @copyright{} 1993,94,95,96,97 by Bernardo Innocenti, all rights reserved.
+Freely distributable.
+
+If you haven't done it yet, please take a look to the release notes first.
+@c does not work with latest GeekGadgets snapshot
+@c @ref{Top, , release notes, README, README file}.
+
+@menu
+* Overview:: An overview of @value{prgname}.
+* Distribution:: Licence & Disclaimer.
+* Getting Started:: How to run @value{prgname}.
+* User Interface:: General notes on the user interface.
+
+* Panels:: Description of all @value{prgname} windows.
+
+* Effects:: List of all the player effects.
+* Format Conversion:: Description of module conversion with @value{prgname}.
+* Optimization:: How to save memory and disk space with modules.
+* ARexx Interface:: Every program should have one nowadays, doesn't it?
+
+* Known Bugs:: Notes on things that are still not working@dots{}
+* Future Plans:: What @value{prgname} should feature in the next releases.
+* Programmers:: Notes on the C source and TexInfo documentation.
+* XModule Format:: Description of the module format used by @value{prgname}.
+
+* History:(History)Main. See what has changed since the last version of @value{prgname}. READ THIS!
+
+* Credits:: Some people I wish to express my gratitude to.
+* Author Info:: How to contact me.
+
+* Concept Index::
+@end menu
+@end ifinfo
+
+@c ******************
+@c * Overview *
+@c ******************
+
+@node Overview, Distribution, Top, Top
+@chapter Overview
+@cindex Overview of @value{prgname}
+@cindex Introduction
+
+ The Amiga computer has standard file formats for almost
+everything. Graphics, text, animation, hypertext and many other
+data types can be easily imported into any program that supports the
+given standards, but this is not true for music. Too bad the music
+interchange file format defined by Electronic Arts (the old IFF SMUS)
+was too poor to be usable in high-quality music sequencers. Therefore,
+every music editor created its own proprietary module format, so that,
+for instance, you cannot load into ProTracker music written with
+Oktalyzer.
+
+@dfn{@value{prgname}} (pronounced @dfn{Cross Module}) born as a simple
+music module conversion utility that tried to solve this problem. The
+project started in early 1993, as a friend of mine (FBY) needed to
+convert his modules to ProTracker to perform some effects. He had an
+old PD conversion utility called @dfn{Tracker} that worked only in
+particular conditions and was very bogus. I decided to contact
+the author and he let me have the source for Lit.30000 (about $20).
+
+The original program has been completely rewritten from scratch, as the
+source was poorly coded. Now @value{prgname} is a completely different
+program, which sports many other features, as module optimization, a
+nice user interface and complex module editing functions and it is
+quickly turning into a full featured music editor.
+
+This is why I like to call @value{prgname} a @dfn{module processor}. @value{prgname}
+is to music processing what ADPro and ImageFX are to image processing.
+
+
+
+@c ******************
+@c * Distribution *
+@c ******************
+
+@node Distribution, Getting Started, Overview, Top
+@chapter Distribution
+@cindex Distribution
+
+@value{prgname} is a @dfn{Freeware} program. This means that you (probably)
+have got it free and @strong{you must not ask any money} if you
+decide to copy it for someone else. If, instead, someone sold you
+@value{prgname} for a price higher than the media used to store it on
+(i.e.@:, $1 for a DD disk), tell him he should try making money honestly.
+
+I am not asking you to pay for @value{prgname} because I hate the
+Shareware concept. I use a lot of great PD software on my computer,
+so I feel that I somehow have to pay my debt and I want to distribute
+@value{prgname} as free software; a little gift to all the Amiga
+community. Feel free to distribute @value{prgname} to your friends and
+enemies, but, if you do so, you @strong{must} keep the documentation and
+the other distribution files together with the executable. The source
+code is not required, but I am distributing it to let others take advantage
+of my work, so, if possible, try to also include the source.
+
+Aminet and @w{Fred Fish} are especially granted permission to include
+@value{prgname} into their PD libraries. Other PD libraries
+are welcome as well.
+
+If you intend to include @value{prgname} or parts of it in a commercial package,
+please at least send me a free copy of the product. I would be honored
+to see one of my programs being part of a commercial package.
+
+You can modify or improve @value{prgname} (@pxref{Programmers}), only be
+so kind to return the new source code to me, so I can put the new features
+in the next release. @emph{Please}, do not strip my name from the
+documentation, the program source, or the executable.
+
+This product is provided @strong{as is}@footnote{In other words:
+@value{prgname} never formatted my hard disk nor killed my dog, so I suppose
+it won't cause any damage to your system either, but, just in case, do not
+blame me if something nasty happens.}, without warranties of any kind: the
+author of this program cannot be held liable for any defects in the executable
+nor in the documentation or in any other files contained in this package.
+Any damage directly or indirectly caused by the use/misuse of @value{prgname}
+is the sole responsibility of the user her/him-self.
+
+
+
+@c *******************
+@c * Getting Started *
+@c *******************
+
+@node Getting Started, User Interface, Distribution, Top
+@chapter Getting Started
+@cindex Usage
+@cindex ToolTypes
+@cindex Arguments, command line
+@cindex Starting @value{prgname}
+
+
+@value{prgname} requires Kickstart version 2.0 or greater. The reason is
+that I hate programming on old 1.3 and I will not do it any more. If you
+still have not upgraded to 2.0, you'll miss the chance to run
+@value{prgname} and many other wonderful programs. So what are you
+waiting for?
+
+@value{prgname} also takes advantage of some 2.1 and 3.0 features, such as
+DataTypes, AmigaGuide, Locale and many enhancements for the user
+interface.
+
+You can invoke @value{prgname} either from the Shell or Workbench.
+@value{prgname} accepts both command line and ToolTypes arguments.
+Workbench and Shell parameters are the same. The full AmigaDOS
+template is:
+
+@example
+@group
+@value{prgname} FROM/M,PUBSCREEN/K,PORTNAME/K,SETTINGS/K,
+ CX_POPUP/T,CX_POPKEY/K,CX_PRIORITY/K/N,
+ ICONXPOS/K/N,ICONYPOS/K/N,ICONNAME/K
+@end group
+@end example
+
+@noindent
+The @code{FROM} keyword specifies modules to load at startup time.
+Standard AmigaDOS wildcards can be used to match more than one module.
+From Workbench, you multi-select module icons instead of specifying a
+ToolType.
+
+Specifying the @code{PUBSCREEN} keyword, followed by a public screen name,
+allows you to open @value{prgname} on an existing public screen. The given
+name should match exactly (case matters), or the system won't be able to
+find the requested screen. If the requested screen does not exist, a new
+public screen will be opened, cloning the resolution and colors of the default
+public screen (usually the Workbench screen). Specifying an empty string
+(@samp{""}) you will automatically pick the default public screen.
+Note that this behavior is not standard: most commodities will abort if the
+requested public screen does not exist. The default is to use or create a
+public screen called @samp{@value{prgname}}.
+
+@code{PORTNAME} will change the name of @value{prgname}'s ARexx port.
+If a port with this name already exists, a number (eg: @samp{.1}) will be
+appended to the name, until a unique name is found. You can disable the ARexx
+port providing an empty string (eg: @samp{""}) for @code{PORTNAME}. The port name
+defaults to @samp{XMODULE}. For more information about the ARexx port, see
+@ref{ARexx Interface}.
+
+@code{SETTINGS} causes @value{prgname} to use the given settings file instead
+of looking for the default preferences file.
+
+@code{CX_POPUP} controls wether the user interface should show when
+@value{prgname} starts. The default is to always show the interface. When used
+from the shell, @code{CX_POPUP} acts as a toggle, turning off the user interface.
+From Workbench, you specify @code{CX_POPUP=YES} or @code{CX_POPUP=NO} to show and
+hide the user interface, respectively. @xref{Commodities}.
+
+@code{CX_POPKEY} specifies the hotkey that pops up @value{prgname}'s user interface.
+The given string should be a valid Commodities input description. You can disable
+@value{prgname}'s Commodities broker specifying an empty string (eg: @samp{""})
+for @code{CX_POPUP}. The default is @samp{ctrl alt x}. @xref{Commodities}.
+
+@code{CX_PRIORITY} sets the Commodities priority for the @value{prgname} broker.
+An high priority causes the input to be processed by @value{prgname} before other
+commodities with a lower priority. The priority ranges from +127 to -128.
+The default priority is 0. @xref{Commodities}.
+
+@code{ICONXPOS} specifies the X position of @value{prgname}'s AppIcon. The default is
+to let Workbench choose a suitable position for the icon.
+
+@code{ICONYPOS} specifies the Y position of @value{prgname}'s AppIcon. The default is
+to let Workbench choose a suitable position for the icon.
+
+@code{ICONNAME} specifies the name of @value{prgname}'s AppIcon. The default name is
+@samp{@value{prgname}}.
+
+
+@c ******************
+@c * User Interface *
+@c ******************
+
+@node User Interface, Panels, Getting Started, Top
+@chapter User Interface
+@cindex User Interface
+@cindex GUI
+
+@value{prgname}'s GUI follows Commodore's 2.0 style guidelines. Under Kickstart 3.0
+and above, @value{prgname} takes advantage of new OS capabilities to enhance some
+aspects of the GUI.
+
+@menu
+* Windows Layout::
+* Shortcut Keys::
+* On-Line Help::
+* Localization::
+* Commodities::
+* Default Icons::
+@end menu
+
+
+@c ******************
+@c * Windows Layout *
+@c ******************
+
+@node Windows Layout, Shortcut Keys, , User Interface
+@section Windows Layout
+@cindex Windows Layout
+@cindex Layout, of Windows
+@cindex Font Sensivity
+
+Gadget and menu layout is font sensitive, but using some fancy fonts could
+result in a somewhat weird look. If you are using a very big font and a
+window becomes too big to fit in the screen, @file{topaz/8} will be used as
+a fallback for rendering that window.
+
+Most windows are completely scalable and their contents will adapt to the
+size you choose, just like a MUI application. But @value{prgname} does
+not use MUI, so I had to do all the GUI engine myself, and it was quite an
+hard work for me. @value{prgname} GUI is not as nice as can be MUI, but
+it's much faster and requires much less memory.
+
+When you close a window, it will remember its position and size when you
+open it again. The same is true for window zooming operations.
+
+@value{prgname} will also try to make the active window visible by
+scrolling the screen automatically. This feature comes handy if you use
+a virtual screen which is wider and/or taller than the default.
+
+
+@c ******************
+@c * Shortcut Keys *
+@c ******************
+
+@node Shortcut Keys, On-Line Help, Windows Layout, User Interface
+@section Shortcut Keys
+@cindex Shortcut Keys
+@cindex Keys, Shortcuts
+
+
+ In all windows, the following shortcut keys are active:
+
+@table @kbd
+@item ESC
+Close the active window. Does not work with ToolBox window, as it would
+make it too easy to accidentally exit the program.
+
+@item HELP
+Bring up AmigaGuide on-line help. @xref{On-Line Help}.
+
+@item TAB
+Cycles forward through string and numeric gadgets within the active
+window. @kbd{SHIFT-TAB} will cycle backwards. @kbd{TAB} also
+activates the first string or numeric gadget if none is selected.
+
+@item Alt-TAB
+Activates the next @value{prgname} window. @kbd{ALT-SHIFT-TAB}
+activates the previous window.
+
+@item RETURN
+Activate the first string or numeric gadget in the active window.
+
+@item Cursor UP/DOWN
+Move up and down in a viewlist whenever the active window contains one.
+@kbd{SHIFT-Cursor} moves 5 items up/down. @kbd{ALT-Cursor} moves to the
+top or to the bottom of the list. This shortcut requires at least
+Kickstart 3.0.
+@end table
+
+In addition, gadgets with underscored characters in labels, can be
+operated hitting the highlighted key. @kbd{SHIFT-key} acts the opposite
+of the unshifted key (sliders are decremented by one, cycle gadgets will
+cycle backwards, etc.@:). Keyboard shortcuts will not work for all gadget
+kinds under Kickstart 2.0.
+
+
+
+@c ******************
+@c * On-Line Help *
+@c ******************
+
+@node On-Line Help, Localization, Shortcut Keys, User Interface
+@section On-Line Help
+@cindex On-Line Help
+@cindex Help, On-Line
+
+This feature requires @file{amigaguide.library}, which is distributed
+with Workbench version 2.1 and up, and @file{@value{prgname}.guide}
+located in the current directory or in the same directory of
+@value{prgname}'s executable or in the @file{HELP:language/} directory,
+where @samp{language} is any of your Locale preferred languages.
+AmigaGuide(TM) is also available as a freely distributable package
+for 1.3-2.0 users.
+
+To get help on a gadget, press @key{HELP} while the mouse pointer is
+over the gadget. To get help on a string gadget, activate it and
+press @key{HELP}. Hitting @key{HELP} while the mouse isn't over a
+gadget, will give you help on the active window.
+
+To get help on a menu item, hilight the item and press @key{HELP} while
+keeping the right mouse button pressed.
+
+
+
+@c ******************
+@c * Localization *
+@c ******************
+
+@node Localization, Commodities, On-Line Help, User Interface
+@section Localization
+@cindex Localization
+@cindex Language selection
+@cindex Writing a new language catalog
+
+Localization requires @file{locale.library}, which is distributed
+with Workbench version 2.1 and up, and the catalog file
+@file{@value{prgname}.catalog} located in the same directory of
+@value{prgname}'s executable or in the @file{LOCALE:catalogs/language/}
+directory, where @samp{langage} is is any of your Locale preferred
+languages.
+
+If you speak a language for which a catalog isn't yet available, please
+consider writing it yourself and then send it to me, so it will be
+included in the next release of @value{prgname}. Look at the empty
+catalog translation file @file{Empty.ct} in the @file{Catalogs/}.
+It can be used to create new catalog descriptions for @value{prgname}.
+Then, use a tool like @cite{FlexCat}, @cite{KitKat} or @cite{CatComp}
+to compile it into a catalog file. Please read the @cite{HowToTranslate.doc}
+before you start translating: it contains many tips and suggestions
+which may come useful.
+
+
+@c ******************
+@c * Commodities *
+@c ******************
+
+@node Commodities, Default Icons, Localization, User Interface
+@section Commodities support
+@cindex Break signals
+
+@value{prgname} is a Commodity and can be controlled by the
+Commodities Exchange program.
+
+@value{prgname} will pop up when its hotkey is typed; the main window
+will activate and the screen will move to the front. If
+@value{prgname}'s interface is hidden, it will be revealed.
+
+The user interface can be hidden with the Exchange program.
+When the user interface is hidden, the only ways you have to still
+operate @value{prgname} is through the ARexx port or the AppIcon.
+
+You can also control @value{prgname} by sending the following
+signals to its process:
+
+@table @bullet
+@item @kbd{CTRL-C}
+Quit @value{prgname}.
+
+@item @kbd{CTRL-D}
+Disable the Commodities broker (equivalent to selecting @code{Inactive}
+with Commodities Exchange).
+
+@item @kbd{CTRL-E}
+Enable the Commodities broker (equivalent to selecting @code{Active}
+with Commodities Exchange).
+
+@item @kbd{CTRL-F}
+Deiconify @value{prgname} (equivalent to double-clicking on the AppIcon).
+@end table
+
+
+
+@c ******************
+@c * Default Icons *
+@c ******************
+
+@node Default Icons, , Commodities, User Interface
+@section Default Icons
+@cindex Icons, changing defaults
+@cindex def_Instrument
+@cindex def_Module
+
+When @value{prgname} creates an icon for a file, it looks for the user
+preferred icon for the type of data being saved.
+
+The template icons can be placed in the @file{Icons} drawer which is
+located in the same drawer where the @value{prgname} executable resides.
+@value{prgname} will also look for its icons in the @file{ENV:Sys/}
+directory. If you choose to use @file{ENV:Sys/}, you will probably want
+to store the icons in @file{ENVARC:Sys/} to have them copied automatically
+to @file{ENV:Sys/} every time the system boots up.
+
+Template icons should be only of type @dfn{Project}. When @value{prgname}
+does not find a template icon, it uses the default Project icon instead.
+
+Whenever the default icon does not have its own default tool, it will be
+set to @value{prgname}'s executable.
+
+The following default icons are supported:
+
+@table @minus
+@item @file{def_Module}
+@item @file{def_Instrument}
+@end table
+
+
+
+@c ************
+@c * Panels *
+@c ************
+
+@node Panels, Effects, User Interface, Top
+@chapter Panels
+@cindex Panels
+@cindex Windows
+
+Sorry, XModule's documentation is still incomplete. Not all
+the following links are finished.
+
+@menu
+* ToolBox::
+* Pattern Editor::
+* Instruments::
+* Song Information::
+* Sequence::
+* Save Format::
+* Clear Song::
+* Sample Editor::
+@end menu
+
+
+
+@c ******************
+@c * ToolBox Window *
+@c ******************
+
+@node ToolBox, Pattern Editor, , Panels
+@section ToolBox Window
+
+@subsection Gadgets
+
+@vtable @code
+@item Patterns (P)
+Opens or activates the @ref{Pattern Editor} window.
+
+@item Instruments (I)
+Opens or activates the @ref{Instruments} window.
+
+@item Sequence (Q)
+Opens or activates the @ref{Sequence} editor.
+
+@item Songs (S)
+Opens or activates the @ref{Song Information} window.
+
+@item Optimization (O)
+Opens or activates the @ref{Optimization} window.
+
+@item Play (P)
+This option has not been implemented yet.
+@end vtable
+
+
+
+@subsection Project Menu
+
+@vtable @code
+@item New (Amiga-N)
+Creates a new empty song.
+
+@item Open (Amiga-O)
+Opens a song over the current one. A file requester will be put
+up to let you choose the module. The file requester is asynchronous,
+so you can continue operating @value{prgname} while keeping the requester
+open. You can use multiselection to open several modules at once.
+
+@item Save (Amiga-S)
+Saves the current song to the same path it was loaded from.
+
+@item Save As (Amiga-A)
+Opens a file requester and saves the current song to the selected file
+name and path.
+
+@item Clear Module (Amiga-K)
+Opens or activates the @ref{Clear Song} panel.
+
+@item About (Amiga-?)
+Brings up a requester showing various information on @value{prgname}.
+
+@item Help (HELP)
+Shows the main node of the AmigaGuide on-line help. @xref{On-Line Help}.
+
+@item Iconify (Amiga-I)
+Closes all open windows and screens and puts an AppIcon on the Workbench.
+You can De-Iconify @value{prgname} by double clicking on the icon or by
+using @code{Show Interface} in the Commodities Exchange program.
+@xref{Commodities}.
+
+@item Quit (Amiga-Q)
+Quits @value{prgname}. A requester will ask if you are sure if the
+@code{Settings/Confirm Exit} menu item is checked.
+@end vtable
+
+
+
+@subsection Settings Menu
+
+@vtable @code
+@item Save Format
+Opens or activates the @ref{Save Format} window.
+
+@item User Interface
+Opens or activates the @ref{User Interface} window.
+
+@item Save Icons?
+When this menu item is checked, @value{prgname} will automatically add
+an icon to the files it saves. @xref{Default Icons}.
+
+@item Confirm Overwrite?
+When this menu item is checked, @value{prgname} will ask before
+overwriting an existing file when saving something.
+
+@item Confirm Exit?
+When this menu item is checked, @value{prgname} will ask
+if you are really sure before quitting.
+
+@item Verbose?
+When this menu item is checked, @value{prgname} will be output more
+detailed information on various operations.
+
+@item Open Settings
+Opens a file requester asking for an XModule preferences file. The
+changes will take place immediately.
+
+@item Save Settings
+Saves the current settings to @file{PROGDIR:XModule.prefs}, which
+is where preferences are looked for first.
+
+@item Save Settings As
+Opens a file requester and saves the current settings to the selected
+file and path name.
+@end vtable
+
+
+
+@c ******************
+@c * Pattern Editor *
+@c ******************
+
+@node Pattern Editor, Instruments, ToolBox, Panels
+@section Pattern Editor Window
+@cindex Pattern Editor Window
+@cindex Notation
+
+@subsection Overview
+
+ The pattern editor is used to actually edit notes into your song.
+A song is made up of several patterns which are played one after the
+other following the order defined in the @ref{Sequence} editor.
+
+ A pattern is subdivided into @dfn{tracks}. Each track is made of
+several note slots. Each note slot has four basic fields: the
+actual note, the number of the instrument used to play this note,
+a field for the @dfn{effect} (also called @dfn{command}) and a
+@dfn{value} for the effect, the meaning of which depends from
+the type of effect (@pxref{Effects}). A @dfn{line} is an horizontal
+set of note slots. The lines are numbered starting from 0 (line
+numbers are displayed in the leftmost column of the pattern editor).
+
+ A pattern can have 1 to 32 tracks. The more the tracks, the more the
+notes that can be played at the same time. When @value{prgname} plays
+a pattern, it reads the lines one by one, playing all the notes and
+executing all the effects as soon as it encounters them. A pattern
+line can be imagined as one sixteenth of a beat when the pattern is
+played at the default speed and tempo.
+
+ A pattern row looks like this:
+
+@example
+C-2 5 C40
+~~~ | |~~
+| | ||
+| | |+---- Effect Value
+| | +----- Effect Command
+| +------- Instrument
++----------- Note
+@end example
+
+ Notes are shown in anglosaxon notation, followed by the octave number.
+So @code{C-2} is a second octave @code{DO} in classic notation and
+@code{A#3} is a third octave diesis @code{LA}. There are 5 octaves
+numbered from 0 to 4; a @code{C-2} note is twice the frequency of a
+@code{C-1}, and it's instrument will be played at double speed.
+
+ The duration of the note is determined by the space left from one note
+to the next. For example, if a note is immediately followed by another
+note on the same track, it's duration will be one sixteenth of a beat
+at four quarters. If two notes are put one at line n and the other
+at line n+4 (leaving three blank spaces between), the duration of the
+first note will be exactly one quarter.
+
+ Calculating the duration of a note can become trickier when weird
+combinations of speed and tempo are used. Also, notes can be
+associated to instruments which have an endless loop (like a flute)
+or to instruments that only last for a short time and then stop
+(like a snare drum). In the latter case, the duration of a note could
+be shorter than it's distance from the following note. Experience
+will help understanding these simple rules.
+
+
+@subsection Editor Keys
+
+@table @kbd
+@item up
+@itemx down
+@itemx left
+@itemx right
+Move the cursor one position towards the respective direction. Keeping
+cursor keys pressed will repeat the movement until the key is released.
+
+ Cursor keys in combination with the @kbd{SHIFT} key will move the cursor
+a whole page up/down or one track left/right, keeping it on the same
+column.
+
+ Cursor keys in combination with the @kbd{ALT} key will move the cursor
+to the top/bottom/leftmost/rightmost position.
+
+ Cursor keys in combination with the @kbd{CTRL} key will scroll the view
+without moving the cursor. This is useful to reveal something without
+having to reposition the cursor.
+
+@strong{NOTE:} You can adjust the repeat speed and delay with the
+@cite{Input} system preferences.
+
+@item TAB
+Move to the note column of the next track. Pressing @kbd{TAB} on the last
+track, will bring the cursor to the first track. @kbd{SHIFT-TAB} moves
+backwards.
+
+@item DEL
+Delete the field under the cursor. @kbd{SHIFT-DEL} kills all the note slot
+(note, effect and value).
+@end table
+
+
+
+@subsection Project Menu
+
+@vtable @code
+@item Open Pattern (Amiga-O)
+Opens a file requester where you can select a pattern to be loaded in
+place of the current one.
+
+@item Save Pattern (Amiga-S)
+Saves the current pattern to a file with the same name of the pattern.
+If the pattern has no name, a file requester will open.
+
+@item Save Pattern As (Amiga-A)
+Opens a file requester where you can select the file and path name where
+the current pattern will be saved to.
+@end vtable
+
+
+
+@subsection Edit Menu
+
+@vtable @code
+@item Mark (Amiga-M)
+Toggles mark mode in the pattern editor.
+
+@item Cut (Amiga-X)
+Copies the currently marked region to the clipboard and erases it from
+the pattern.
+
+@item Copy (Amiga-C)
+Copies the currently marked region to the clipboard.
+
+@item Paste (Amiga-V)
+Gets the last pattern stored into the clipboard and pastes it in the
+current pattern starting at the current cursor position.
+
+@item Erase (Amiga-E)
+Clears the currently marked region.
+
+@item Undo (Amiga-U)
+Restores the pattern to the state it was before the last
+modification.
+
+@item Redo (Amiga-Y)
+Remakes the last change undone with the Undo function.
+@end vtable
+
+
+
+@c ******************
+@c * Instruments *
+@c ******************
+
+@node Instruments, Song Information, Pattern Editor, Panels
+@section Instruments Editor Window
+@cindex Instruments Editor Window
+
+@subsection Gadgets
+
+@vtable @code
+@item Instruments List (Cursor up/down)
+This list shows all the instruments of a song. The leftmost column
+is the instrument number (hexadecimal), followed by its name.
+Some musicians usually use the instrument names to include short
+texts inside the modules they distribute. Picking an instrument
+from this list will set it as the current instrument. An empty
+instrument slot with no name will have the word @samp{--empty--}
+in place of its name.
+
+@item Instrument Name (RETURN or TAB)
+(The string gadget immediately below the Instruments list). Allows
+editing the name of the currently selected instrument.
+
+@item Volume (V)
+Default instrument volume. Ranges from 0 (mute) to 64 (maximum
+volume).
+
+@item Fine Tune (F)
+Sets the instrument fine tune. 0 is the default tuning. Negative
+values cause the instrument to be played at a slightly lower
+frequency. Positive values cause the instrument to be played at
+a slightly higher frequency.
+
+@item Lenght
+Displays the instrument lenght in bytes.
+
+@item Kind (K)
+This function has not yet been implemented.
+
+@item Edit (E)
+Opens or activates the @ref{Sample Editor} panel.
+@end vtable
+
+
+
+@subsection Instruments Menu
+
+@vtable @code
+@item Load (Amiga-L)
+Loads an instrument in the current intrument slot. A file requester
+will be put up to let you choose the instrument. The file requester
+is asynchronous, so you can continue operating @value{prgname} while
+keeping the requester open.
+@item Save (Amiga-S)
+Saves the current instrument to a file with its current name.
+
+@item SaveAs (Amiga-A)
+Opens a file requester and saves the current instrument to the
+selected file name and path.
+
+@item Remap (Amiga-R)
+Remaps all the instruments removing all empty slots between them.
+The song patterns are updated to keep the correct instruments.
+This function is useful to save a module to a format which has a
+limited number of instruments, like @ref{SoundTracker}.
+@end vtable
+
+
+
+@c ********************
+@c * Song Information *
+@c ********************
+
+@node Song Information, Sequence, Instruments, Panels
+@section Song Information Window
+@cindex Song Information Window
+
+@subsection Gadgets
+
+@vtable @code
+@item New
+Creates a new module and makes it the current module.
+
+@item Del
+Deletes the current module from memory. If the module has been
+modified since last saving, a requester will give you the chance
+to abort the operation.
+
+@item Open... (O)
+Opens a song over the current one. A file requester will be put
+up to let you choose the module. The file requester is asynchronous,
+so you can continue operating @value{prgname} while keeping the requester
+open. You can use multiselection to open several modules at once.
+This gadget behaves exactly like the @code{Open} menu item in the @ref{ToolBox}
+panel.
+
+@item Save (S)
+Saves the current song to the same path it was loaded from.
+This gadget behaves exactly like the @code{Save} menu item in the @ref{ToolBox}
+panel.
+
+@item Song Name (N)
+Displays the name of the current song. You can use any character in the song
+name, but keep in mind that the name you type will be used by default as the
+filename for the song, so be careful with special characters such as @samp{/}
+and @samp{:}!
+
+@item Author (A)
+Displays the author of the current song. @value{prgname} tries to guess the
+author when it loads a module whose format has no direct support for embedded
+author name. @value{prgname} scans the instruments names looking for a line
+beginning with a pound sign (@samp{#}). This convention started with
+@cite{IntuiTracker}, an old module player for the Amiga. If the author name
+can't still be found, @value{prgname} looks again for an occurence of @samp{by },
+@samp{by:} or @samp{(c)}, which is usually followed by the author name.
+The author name is ignored when saving to a format which does not support
+embedding the author name.
+
+@item Tempo (T)
+Displays the default tempo for the current song. Valid range is 32-255, the
+dafult is 125. The default tempo is used when the song starts playing and can
+be overridden by the Set Tempo command. @xref{Effects}.
+
+@item Speed (P)
+Displays the default speed for the current song. Valid range is 1-31, the
+dafult is 6. The default speed is used when the song starts playing and can
+be overridden by the Set Speed command. @xref{Effects}.
+
+@item Restart (R)
+Displays the position where playback will continue once the song end has
+been reached. The restart position is lost when saving to formats which
+do not support it.
+
+@item Patterns
+Displays the total number of patterns in the song.
+
+@item Tracks
+Displays the maximum number of tracks in the song.
+
+@item Length
+Displays the number of positions in the song sequence.
+
+@item Total Module Size
+Shows an approximation of the memory used up by the song and its data.
+This value will usually be similar to the file size.
+
+@item Total Instruments Size
+Shows an approximation of the memory used by all the instruments of the
+current song.
+@end vtable
+
+@subsection Song Menu
+
+@vtable @code
+@item Merge Songs (Amiga-M)
+
+@item Join Songs (Amiga-J)
+
+@item Clear (Amiga-K)
+
+@end vtable
+
+
+
+@c ************
+@c * Sequence *
+@c ************
+
+@node Sequence, Save Format, Song Information, Panels
+@section Sequence Editor Window
+@cindex Sequence Editor Window
+
+@subsection Gadgets
+
+ Had no time to write it, sorry. ;-)
+
+@subsection Menu
+
+ Had no time to write it, sorry. ;-)
+
+
+
+@c ***************
+@c * Save Format *
+@c ***************
+
+@node Save Format, Clear Song, Sequence, Panels
+@section Save Format Window
+@cindex Save Format Window
+
+@subsection Gadgets
+
+ Had no time to write it, sorry. ;-)
+
+@subsection Menu
+
+ Had no time to write it, sorry. ;-)
+
+
+
+@c **************
+@c * Clear Song *
+@c **************
+
+@node Clear Song, Sample Editor, Save Format, Panels
+@section Clear Song Window
+
+@subsection Gadgets
+
+ Had no time to write it, sorry. ;-)
+
+@subsection Menu
+
+ Had no time to write it, sorry. ;-)
+
+
+
+@c *****************
+@c * Sample Editor *
+@c *****************
+
+@node Sample Editor, , Clear Song, Panels
+@section Sample Editor Window
+
+@subsection Gadgets
+
+ Had no time to write it, sorry. ;-)
+
+@subsection Menu
+
+ Had no time to write it, sorry. ;-)
+
+
+
+@c *********************
+@c * Effects *
+@c *********************
+
+@node Effects, Format Conversion, Panels, Top
+@chapter Effects
+@cindex Effects
+@cindex Commands
+
+ Had no time to write it, sorry. ;-)
+
+
+
+@c *********************
+@c * Format Conversion *
+@c *********************
+
+@node Format Conversion, Optimization, Effects, Top
+@chapter Format Conversion
+@cindex Format Conversion
+@cindex Module Formats
+
+@value{prgname} can load and save modules created by other
+music editors. The following is a list of all supported module formats:
+
+@menu
+* XModule: XModule Format.
+* NoiseTracker::
+* ProTracker::
+* SoundTracker::
+* StarTrekker::
+* UNIC-Tracker::
+* FastTracker::
+* TakeTracker::
+* Oktalyzer::
+* MED::
+* OctaMED::
+* ScreamTracker::
+* MIDI File::
+@end menu
+
+
+As a matter of fact, there are things that simply cannot be done
+because of the big differences between different module formats.
+Below is a list of the internal limitations of each module format.
+When you save to a particular format and the module exceeds one of
+these values, or makes use of features not implemented, some data
+will be stripped away to produce at least a partial conversion.
+
+@example
+@group
+@c Is there a better way to make a table like this?
+
+Program Instr MaxPatt PattLen SongLen InstrLen Tracks
+---------------------------------------------------------------
+Oktalyzer 36 128 128 128 128K 4
+SoundTracker 15 64 64 128 64K 4
+NoiseTracker 31 64 64 128 64K 4
+StarTrekker 31 64 64 128 64K 4/8
+ProTracker 31 100 64 128 64K 4
+Fast Tracker I 31 100 64 128 64K 4/6/8
+MED/OctaMED 63 256 256 256 No Limit 1-64
+
+XModule 63 256 32768 32768 No Limit 1-32
+
+
+Where:
+#Instr is the maximum number of instruments,
+MaxPatt is the maximum number of patterns,
+PattLen is the maximum number of lines per pattern,
+SongLen is the maximum number of positions in a song,
+InstrLen is the maximum size of an instrument in bytes,
+Tracks is the maximum number of tracks in a pattern.
+@end group
+@end example
+
+ Effects are the hardest thing to convert. While Speed and Volume
+are almost the same between different music editors, some effects (such as
+Oktalyzer's H and L) cannot be converted because there are no equivalents in
+other music editors. Therefore, you will have to modify the score manually
+if you intend to convert a module while keeping such effects. In addition,
+some effects behave in different ways even if they claim to do the same
+thing.
+
+ Last but not least, some music editors support synthetic or hybrid
+instruments. @value{prgname} does not convert such instruments,
+because I can hardly think of a way to implement a sample to synth
+conversion (any ideas?). I used to like synthetic music, but it
+seems that all those great composers on the good old C64 have
+vanished@dots{} Why have you musicians become so lazy? 8-(
+
+
+
+@c ******************
+@c * NoiseTracker *
+@c ******************
+
+@node NoiseTracker, ProTracker, , Format Conversion
+@section NoiseTracker
+@cindex NoiseTracker
+
+Noise/ProTracker 31 instruments module. This is absolutely the
+most common module format; almost any music editor and module
+player (even the crap ones for the PeeCee) understand this
+format. Noise/ProTracker modules can be recognized by looking for
+the ID @samp{M.K.} at offset 1080 ($438 in hex) in the file.
+Andrew Scott, the author of MidiMod, says that @samp{M.K.} are
+the initials of Mahoney & Kaktus, the two guys who designed this
+format. Peter Kunath, the author of DeliTracker, points out that
+@samp{M.K.} are more likely to be the initials of Michael Kleps
+(aka Unknown/DOC).
+
+Unfortunately, there is an infinite variety of standard
+NoiseTracker modules with fancy IDs. In fact, most tracker
+clones put their own ID instead of the standard one even if
+there is no reason to do it. If you find such a module, try
+replacing the ID with an hex editor and you will be able to
+load it most of the times.
+
+
+
+@c ******************
+@c * ProTracker *
+@c ******************
+
+@node ProTracker, SoundTracker, NoiseTracker, Format Conversion
+@section ProTracker 100 Patterns
+@cindex ProTracker
+@cindex M!K!
+@cindex 100 Patterns ProTracker modules
+
+@value{prgname} will automatically switch to @code{ProTracker 100}
+whenever the module being saved exceeds the 64 patterns limit of
+NoiseTracker modules.
+Modules with more than 64 patterns have the ID @samp{M!K!} instead
+of the standard Noise/ProTracker ID (@samp{M.K.}).
+
+@strong{Note:} You @emph{must} load such a module with ProTracker 2.3 or
+better in order to play/edit it. Older players will either refuse
+to load it or they'll turn the module into scratch dance.
+
+ProTracker 3.0+ has a completely new file format, but this isn't
+supported yet, as I've never seen a module of this kind yet.
+
+
+
+@c ******************
+@c * SoundTracker *
+@c ******************
+
+@node SoundTracker, StarTrekker, ProTracker, Format Conversion
+@section SoundTracker 15 Instruments
+@cindex SoundTracker
+
+This format is pretty useless as SoundTracker is becoming really rare.
+Do not save SoundTracker modules unless you really need to, because
+most players/editors will stop supporting this format very soon.
+There is no way to identify a SoundTracker module, therefore
+@value{prgname} will ask you to confirm a SoundTracker module when
+the file being loaded matches no other known module format.
+
+
+
+@c ******************
+@c * StarTrekker *
+@c ******************
+
+@node StarTrekker, UNIC-Tracker, SoundTracker, Format Conversion
+@section StarTrekker
+@cindex StarTrekker
+
+StarTrekker is another NoiseTracker clone, which is capable of
+playing modules with 4 or 8 channels and supports MIDI devices.
+The format of 4 channels modules is the same of NoiseTracker, except
+for the @samp{FLT4} ID. Modules with 8 channels have the ID
+@samp{FLT8}, and keep the extra data in a second file, whose
+structure I don't know. If you want support for 8 channels
+StarTrekker modules, please send me some information on its
+format.
+
+
+
+@c ******************
+@c * UNIC-Tracker *
+@c ******************
+
+@node UNIC-Tracker, FastTracker, StarTrekker, Format Conversion
+@section UNIC-Tracker
+@cindex UNIC-Tracker
+
+Actually, I've never seen UnicTracker, but I sometimes ripp Unic modules
+with ExoticRipper, so I decided to give support for this weird Tracker
+clone too. The format is very similar to that of ProTracker modules,
+except for the @samp{EMW3} ID.
+
+
+
+@c ******************
+@c * FastTracker *
+@c ******************
+
+@node FastTracker, TakeTracker, UNIC-Tracker, Format Conversion
+@section FastTracker 1.0
+@cindex FastTracker
+@cindex 6CHN
+@cindex 8CHN
+
+The format of FastTracker 1.0 modules is exactly the same
+of @ref{TakeTracker}, but they can have only 6 or 8 channels.
+@value{prgname} does not support FastTracker 2.0 modules (those
+with extension @file{.xm}) yet.
+
+@c ******************
+@c * TakeTracker *
+@c ******************
+
+@node TakeTracker, Oktalyzer, FastTracker, Format Conversion
+@section TakeTracker
+@cindex TakeTracker
+@cindex 32 Channels ProTracker Modules
+
+These are Standard ProTracker modules, but they can have up to 32
+channels. Their ID is @dfn{5CHN}, @dfn{6CHN}, and so on, up to
+@dfn{32CH}. This format comes from some damn PeeCee tracker, which
+I've never seen. Anyway, adding this format was a matter of five
+minutes.
+
+
+@c ******************
+@c * Oktalyzer *
+@c ******************
+
+@node Oktalyzer, MED, TakeTracker, Format Conversion
+@section Oktalyzer 1.1
+@cindex Oktalyzer
+
+Oktalyzer is an old editor which can play up to 8 channels by mixing
+instruments in real time. Oktalyzer modules start with the ID
+@samp{OCTASONGGMOD}. Development of Oktalyzer has been
+discontinued, as the author seems to be programming on Windoze now.
+It's really a shame, as many musicians found that Oktalyzer was one
+of the most powerful trackers for the Amiga.
+
+
+
+@c ******************
+@c * MED *
+@c ******************
+
+@node MED, OctaMED, Oktalyzer, Format Conversion
+@section MED
+@cindex MED
+
+ Not all features of MED are supported by @value{prgname}.
+Synthetic and hybrid instruments are ignored.
+
+
+
+@c ******************
+@c * OctaMED *
+@c ******************
+
+@node OctaMED, ScreamTracker, MED, Format Conversion
+@section OctaMED
+@cindex OctaMED
+@cindex MMD Format
+
+ MMD2 modules are not supported. @value{prgname} will only
+load the first song in multisong modules.
+
+ @value{prgname} currently has an internal limit of only
+32 tracks, while OctaMED can do up to 128 tracks. However,
+OctaMED can only play the first 8 tracks, so what are
+the other tracks for? ;-)
+
+
+
+@c ******************
+@c * ScreamTracker *
+@c ******************
+
+@node ScreamTracker, MIDI File, OctaMED, Format Conversion
+@section ScreamTracker
+@cindex ScreamTracker
+@cindex S3M Format
+
+ ScreamTracker 3.01 is a damn PeeCee program made by those Future Crew
+guys who are trying to turn clones into Amigas. ScreamTracker has the
+amazing feature of playing 32 tracks, 16 of which are for 8-bit
+digital samples. The other 16 tracks play ADLib FM synths (not
+supported by @value{prgname}).
+
+
+
+@c ******************
+@c * MIDI File *
+@c ******************
+
+@node MIDI File, , ScreamTracker, Format Conversion
+@section MIDI File
+@cindex MIDI File
+
+ MIDI is the industry standard musical score format (the same way Windoze
+is the industry standard graphic interface :-). The MIDI concept is very
+different from tracker modules. There are no sequence and patterns, but a
+long stream of events (notes) that have to be sent to some MIDI compliant
+device, one after the other, at specific time intrvals, based on the
+duration of each note. There are no instruments inside MIDI files;
+instead, each note is associated with a preset number that matches a
+particular instrument in the device to which the MIDI events are sent.
+
+ @value{prgname}'s MIDI file saver is based on Andrew Scott's MidiMod
+program and I wish to thank him for granting me permission to use his
+code in @value{prgname}.
+
+
+
+@c ******************
+@c * Optimization *
+@c ******************
+
+@node Optimization, ARexx Interface, Format Conversion, Top
+@chapter Optimization
+@cindex Optimization
+
+ Module optimization is achieved by removing all unused data in such a way
+that the module will sound the same as before. @value{prgname} can process a
+module to reduce its size as much as possible. It is generally a good idea
+to optimize a module before distributing it, but never while you are still
+composing it, because you could easily loose some data that you didn't want
+to discard. Do not expect to gain a lot of bytes optimizing modules: saving
+20% of the total size is usually a very good result.
+
+@table @bullet{} @code
+@item Unused patterns
+ Some formats (e.g.: Sound/Noise/ProTracker) store empty patterns
+in a module if a pattern with a greater number is used. For instance,
+a song using patterns 1, 2 and 5 will contain patterns 3 and 4 as well.
+
+@strong{Warning}: If a song contains a part that has not been finished
+yet and has not already been inserted in the position table, you will
+lose it.
+
+@strong{Warning}: When a song is saved to Noise/ProTracker, patterns beyond
+the last used patterns are discarded regardless of this switch. This
+is due to a limitation of Noise/ProTracker module format.
+
+@item Pattern Cutting
+When a pattern is breaked with the Pattern Break effect, it is cut to the
+line containing the break. When saving to a format that does not support
+variable length patterns (eg.: @ref{SoundTracker}), the pattern is grown
+again, so this optimization won't help with such formats.
+
+@item Unused instruments
+ A composer may decide to use an instrument and then change his mind,
+but forget to clear the instrument. In this case the module will
+contain an instrument that is never played. These instruments
+are simply stripped away.
+
+@item Sample data after a loop
+ The part of an instrument following a loop is never played and is
+therefore discarded.
+
+@item Instruments zero tails
+ Long zero tails eat up memory and produce no sound. This optimization
+is performed only on loopless instruments, as a zero sequence inside a
+loop @emph{does} make difference. @value{prgname} leaves at least two zero
+bytes to avoid the nasty click produced by the speaker dropping its volume
+to zero too quickly.
+@end table
+
+
+@c *********************
+@c * ARexx Interface *
+@c *********************
+
+@node ARexx Interface, Known Bugs, Optimization, Top
+@chapter ARexx Interface
+
+WARNING: XModule's ARexx port is a little brain dead at the
+moment, sorry.
+
+
+List of ARexx commands:
+
+@menu
+* Activate: ARexx ACTIVATE. Not yet implemented
+* Clear: ARexx CLEAR. Not yet implemented
+* Close: ARexx CLOSE. Not yet implemented
+* Column: ARexx COLUMN. Not yet implemented
+* Copy: ARexx COPY. Not yet implemented
+* Cursor: ARexx CURSOR. Not yet implemented
+* Cut: ARexx CUT. Not yet implemented
+* Deactivate: ARexx DEACTIVATE. Not yet implemented
+* Erase: ARexx ERASE. Not yet implemented
+* GoToBookMark: ARexx GOTOBOOKMARK. Not yet implemented
+* Help: ARexx HELP. Displays help on a particular topic
+* Line: ARexx LINE. Not yet implemented
+* LockGUI: ARexx LOCKGUI. Blocks user input in @value{prgname} windows
+* New: ARexx NEW. Creates a new song
+* Open: ARexx OPEN. Opens a song over the current one
+* Optimize: ARexx OPTIMIZE. Performs an @ref{Optimization}
+* Paste: ARexx PASTE. Not yet implemented
+* Print: ARexx PRINT. Not yet implemented
+* Quit: ARexx QUIT. Exits @value{prgname}
+* RequestFile: ARexx REQUESTFILE. Not yet implemented
+* RequestResponse: ARexx REQUESTRESPONSE. Not yet implemented
+* RequestNotify: ARexx REQUESTNOTIFY. Not yet implemented
+* Save: ARexx SAVE. Saves the current song
+* SaveInstrument: ARexx SAVEINSTRUMENT. Saves the current instrument
+* ScreenToBack: ARexx SCREENTOBACK. Brings @value{prgname}'s screen to front
+* ScreenToFront: ARexx SCREENTOFRONT. Brings @value{prgname}'s screen to back
+* SelectInstrument: ARexx SELECTINSTRUMENT. Selects the current instrument
+* SetBookMark: ARexx SETBOOKMARK. Not yet implemented
+* ShowMessage: ARexx SHOWMESSAGE. Prints a message in the log window
+* UnLockGUI: ARexx UNLOCKGUI. Restores user input in the user interface
+* Version: ARexx VERSION. Returns version information about @value{prgname}
+@end menu
+
+
+
+
+@c **************
+@c * ACTIVATE *
+@c **************
+
+@node ARexx ACTIVATE, ARexx CLEAR, , ARexx Interface
+@section ACTIVATE
+
+@subsection Template
+@example
+ACTIVATE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+-
+
+
+
+@c ***********
+@c * CLEAR *
+@c ***********
+
+@node ARexx CLEAR, ARexx CLOSE, ARexx ACTIVATE, ARexx Interface
+@section CLEAR
+
+@subsection Template
+@example
+CLEAR
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+-
+
+@c ***********
+@c * CLOSE *
+@c ***********
+
+@node ARexx CLOSE, ARexx COLUMN, ARexx CLEAR, ARexx Interface
+@section CLOSE
+
+@subsection Template
+@example
+CLOSE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+-
+
+@c ************
+@c * COLUMN *
+@c ************
+
+@node ARexx COLUMN, ARexx COPY, ARexx CLOSE, ARexx Interface
+@section COLUMN
+
+@subsection Template
+@example
+COLUMN
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **********
+@c * COPY *
+@c **********
+
+@node ARexx COPY, ARexx CURSOR, ARexx COLUMN, ARexx Interface
+@section COPY
+
+@subsection Template
+@example
+COPY
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+@xref{ARexx CUT,CUT}, @xref{ARexx PASTE,PASTE}, @xref{ARexx ERASE,ERASE}.
+
+
+
+@c ************
+@c * CURSOR *
+@c ************
+
+@node ARexx CURSOR, ARexx CUT, ARexx COPY, ARexx Interface
+@section CURSOR
+
+@subsection Template
+@example
+CURSOR
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *********
+@c * CUT *
+@c *********
+
+@node ARexx CUT, ARexx DEACTIVATE, ARexx CURSOR, ARexx Interface
+@section CUT
+
+@subsection Template
+@example
+CUT
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ****************
+@c * DEACTIVATE *
+@c ****************
+
+@node ARexx DEACTIVATE, ARexx ERASE, ARexx CUT, ARexx Interface
+@section DEACTIVATE
+
+@subsection Template
+@example
+DEACTIVATE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ***********
+@c * ERASE *
+@c ***********
+
+@node ARexx ERASE, ARexx GOTOBOOKMARK, ARexx DEACTIVATE, ARexx Interface
+@section ERASE
+
+@subsection Template
+@example
+ERASE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ******************
+@c * GOTOBOOKMARK *
+@c ******************
+
+@node ARexx GOTOBOOKMARK, ARexx HELP, ARexx ERASE, ARexx Interface
+@section GOTOBOOKMARK
+
+@subsection Template
+@example
+GOTOBOOKMARK
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **********
+@c * HELP *
+@c **********
+
+@node ARexx HELP, ARexx LINE, ARexx GOTOBOOKMARK, ARexx Interface
+@section HELP
+
+@subsection Template
+@example
+HELP
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **********
+@c * LINE *
+@c **********
+
+@node ARexx LINE, ARexx LOCKGUI, ARexx HELP, ARexx Interface
+@section LINE
+
+@subsection Template
+@example
+LINE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *************
+@c * LOCKGUI *
+@c *************
+
+@node ARexx LOCKGUI, ARexx NEW, ARexx LINE, ARexx Interface
+@section LOCKGUI
+
+@subsection Template
+@example
+LOCKGUI
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *********
+@c * NEW *
+@c *********
+
+@node ARexx NEW, ARexx OPEN, ARexx LOCKGUI, ARexx Interface
+@section NEW
+
+@subsection Template
+@example
+NEW
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **********
+@c * OPEN *
+@c **********
+
+@node ARexx OPEN, ARexx OPTIMIZE, ARexx NEW, ARexx Interface
+@section OPEN
+
+@subsection Template
+@example
+OPEN
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **************
+@c * OPTIMIZE *
+@c **************
+
+@node ARexx OPTIMIZE, ARexx PASTE, ARexx OPEN, ARexx Interface
+@section OPTIMIZE
+
+@subsection Template
+@example
+OPTIMIZE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ***********
+@c * PASTE *
+@c ***********
+
+@node ARexx PASTE, ARexx PRINT, ARexx OPTIMIZE, ARexx Interface
+@section PASTE
+
+@subsection Template
+@example
+PASTE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ***********
+@c * PRINT *
+@c ***********
+
+@node ARexx PRINT, ARexx QUIT, ARexx PASTE, ARexx Interface
+@section PRINT
+
+@subsection Template
+@example
+PRINT
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **********
+@c * QUIT *
+@c **********
+
+@node ARexx QUIT, ARexx REQUESTFILE, ARexx PRINT, ARexx Interface
+@section QUIT
+
+@subsection Template
+@example
+QUIT
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *****************
+@c * REQUESTFILE *
+@c *****************
+
+@node ARexx REQUESTFILE, ARexx REQUESTRESPONSE, ARexx QUIT, ARexx Interface
+@section REQUESTFILE
+
+@subsection Template
+@example
+REQUESTFILE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *********************
+@c * REQUESTRESPONSE *
+@c *********************
+
+@node ARexx REQUESTRESPONSE, ARexx REQUESTNOTIFY, ARexx REQUESTFILE, ARexx Interface
+@section REQUESTRESPONSE
+
+@subsection Template
+@example
+REQUESTRESPONSE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *******************
+@c * REQUESTNOTIFY *
+@c *******************
+
+@node ARexx REQUESTNOTIFY, ARexx SAVE, ARexx REQUESTRESPONSE, ARexx Interface
+@section REQUESTNOTIFY
+
+@subsection Template
+@example
+REQUESTNOTIFY
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **********
+@c * SAVE *
+@c **********
+
+@node ARexx SAVE, ARexx SAVEINSTRUMENT, ARexx REQUESTNOTIFY, ARexx Interface
+@section SAVE
+
+@subsection Template
+@example
+SAVE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ********************
+@c * SAVEINSTRUMENT *
+@c ********************
+
+@node ARexx SAVEINSTRUMENT, ARexx SCREENTOBACK, ARexx SAVE, ARexx Interface
+@section SAVEINSTRUMENT
+
+@subsection Template
+@example
+SAVEINSTRUMENT
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ******************
+@c * SCREENTOBACK *
+@c ******************
+
+@node ARexx SCREENTOBACK, ARexx SCREENTOFRONT, ARexx SAVEINSTRUMENT, ARexx Interface
+@section SCREENTOBACK
+
+@subsection Template
+@example
+SCREENTOBACK
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *******************
+@c * SCREENTOFRONT *
+@c *******************
+
+@node ARexx SCREENTOFRONT, ARexx SELECTINSTRUMENT, ARexx SCREENTOBACK, ARexx Interface
+@section SCREENTOFRONT
+
+@subsection Template
+@example
+SCREENTOFRONT
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c **********************
+@c * SELECTINSTRUMENT *
+@c **********************
+
+@node ARexx SELECTINSTRUMENT, ARexx SETBOOKMARK, ARexx SCREENTOFRONT, ARexx Interface
+@section SELECTINSTRUMENT
+
+@subsection Template
+@example
+SELECTINSTRUMENT
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *****************
+@c * SETBOOKMARK *
+@c *****************
+
+@node ARexx SETBOOKMARK, ARexx SHOWMESSAGE, ARexx SELECTINSTRUMENT, ARexx Interface
+@section SETBOOKMARK
+
+@subsection Template
+@example
+SETBOOKMARK
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *****************
+@c * SHOWMESSAGE *
+@c *****************
+
+@node ARexx SHOWMESSAGE, ARexx UNLOCKGUI, ARexx SETBOOKMARK, ARexx Interface
+@section SHOWMESSAGE
+
+@subsection Template
+@example
+SHOWMESSAGE
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c ***************
+@c * UNLOCKGUI *
+@c ***************
+
+@node ARexx UNLOCKGUI, ARexx VERSION, ARexx SHOWMESSAGE, ARexx Interface
+@section UNLOCKGUI
+
+@subsection Template
+@example
+UNLOCKGUI
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+@c *************
+@c * VERSION *
+@c *************
+
+@node ARexx VERSION, , ARexx UNLOCKGUI, ARexx Interface
+@section VERSION
+
+@subsection Template
+@example
+VERSION
+@end example
+
+@subsection Description
+
+-
+
+@subsection See Also
+
+
+
+
+
+
+@c ******************
+@c * Known Bugs *
+@c ******************
+
+@node Known Bugs, Future Plans, ARexx Interface, Top
+@chapter Known Bugs
+@cindex Known Bugs
+@cindex Bugs, known ones
+@cindex Bug reports
+
+ Murphy's laws state that nothing can be perfect, not even computer
+programs. @value{prgname} is far from being perfect and I need
+your help to fix as many bugs as possible. If you discover a bug
+which isn't reported in the list below (note: there is no list at
+the moment; this is a beta release, remember?), please notify me.
+
+ When you report a bug, state exactly what happens and indicate
+how to reproduce the conditions that generated the problem.
+Reports like @cite{@value{prgname} sometimes crashes converting my
+favorite module} does not help at all. Include your name if you want
+to be credited in future @value{prgname} releases.
+
+ If the program refuses to convert a module, mail it to me on
+a disk or upload it somewhere I can reach it and I'll try to fix the
+problem as soon as possible.
+
+ @value{prgname} has been tested on several machines running Kickstart 2.0,
+3.0 and 3.1. It should work on any 68K family processor (was tested on
+68000, 68030 and 68040) and is Enforcer and Mungwall proof. I have also
+tested @value{prgname} with virtual memory programs such as VMM and found
+that everything seems to work correctly.
+
+
+
+@c ******************
+@c * Future Plans *
+@c ******************
+
+@node Future Plans, Programmers, Known Bugs, Top
+@chapter Future plans
+
+@table @bullet{} @code
+
+
+@item Other module formats
+Some formats I'd like to give support for in @value{prgname} as soon
+as possible:
+
+@itemize @minus
+@item
+FastTracker II (@file{.XM})
+@item
+MultiTracker (@file{.MTM})
+@item
+OctaMED MMD2
+@item
+Face The Music (@file{.FTM})
+@item
+Quadra Composer
+@end itemize
+
+If you have documentation regarding a format you would like to see in the
+next release, please send it to me together with a couple of modules and,
+if possible and legal, the program that created them. I'll try to
+implement the new format only if it is somewhat easy: it is very hard to
+convert, say, Future Composer to SoundTracker. Please, do not send a
+module without at least a text file describing its structure, as I am
+not willing to spend my nights hex-dumping modules :-).
+
+If you want to write a new loader/saver hook, please get the
+@value{prgame} developer package and read the included documentation.
+You will also find many examples and also the full source code of
+@value{prgname}.
+
+@item @TeX{}
+Another format I'd like to add is Amiga@TeX{}. If you know
+@TeX{}, you'll surely be wondering what a typesetting program can be
+used for in music. Well, Amiga@TeX{} is at the moment is the best
+implementation available on any computer of the @TeX{} typesetting
+environment, and its distribution includes @dfn{Music@TeX{}}, a powerful
+music language which can output notes in a great standard staff notation.
+I know no other program on the Amiga that can print notes of such fine
+quality (if you do, let me know!), so I think that many musicians would
+appreciate such an option.
+
+@end table
+
+
+
+@c ******************
+@c * Programmers *
+@c ******************
+
+@node Programmers, XModule Format, Future Plans, Top
+@chapter Programmer notes
+@cindex Programmers, notes for
+@cindex Modifying @value{prgname}
+@cindex Compiling @value{prgname}
+
+ @value{prgname} has been written in C and compiled with SAS C 6.51,
+Other compilers may require some changes in the source code.
+
+ The program is written in respect of the Amiga multitasking operative system
+and complies with Commodore programming guidelines. All allocated resources
+will be (hopefully) freed before program termination. I wrote @value{prgname}
+with the goal in mind of keeping the executable fast and small.
+
+ This manual has been typeset using MakeInfo, a GNU PD utility ported
+to the Amiga by Reinhard Spisser and Sebastiano Vigna.
+I found that the time I saved writing one file for three was nearly
+compensated by the time spent learning how to operate MakeInfo.
+
+ Besides, English is not my first language (as you might have guessed) and
+I bet this document is full of grammar mistakes.
+
+ If you intend to translate, improve or somewhat edit this document, please
+work on the supplied TexInfo file and then process it with the utilities
+supplied in the MakeGuide distribution. You can find MakeGuide in several
+PD libraries such as Aminet.
+
+ Do not hesitate sending criticisms to my work, and, even better,
+advices on how to improve the program and its documentation. @xref{Author Info}.
+
+
+
+@c ******************
+@c * XModule Format *
+@c ******************
+
+@node XModule Format, Credits, Programmers, Top
+@chapter XModule Module Format
+@cindex File Format used by XModule
+
+Before going into the details of the @value{prgname} module format, I'd like to
+explain why I felt the need to invent yet another module format. There were
+plenty of them, but none had the flexibility I was looking for. The only one
+exception is in my opinion OctaMED's MMD. But the MMD file format is very
+complicated, hard to read and poorly documented. When I designed it, I wanted
+XMOD to be as flexible, easy, efficient and expandable as possible. If you are
+developing a music program, please consider supporting XMOD. If you have troubles
+with the following format description, contact me and I'll be glad to help you.
+
+@value{prgname} modules are standard IFF files. They consist of a sequence of chunks
+(also called @dfn{proprieties}) each storing a particular type of information related
+to the module. Text chunks, such as NAME and AUTH chunks are variable sized and NOT
+null terminated. When you read such a chunk, you should take care to clip the string
+to your maximum buffer size. NAME and AUTH fields are optional and should be assumed
+empty when missing.
+
+As with every IFF file, you should not depend on the order and size of any chunk.
+However, as usual with most IFF formats, header chunks must always precede related
+BODY chunks. XModule requires this for PHDR chunks because it isn't possible to load
+a pattern without knowing its length and number of tracks. Some chunks may grow in
+size in future versions of XModule.
+
+@example
+FORM XMOD
+
+ [NAME]
+ The name chunk contains the original name used to save the module.
+
+ MHDR
+ Module header. Contains a ModuleHeader structure as defined in
+ <XModuleClass.h>.
+
+ [ANNO]
+ Some XModule copyright information.
+
+ FORM SONG
+ The song FORM contains all data relative to one song. Any
+ number of songs can be stored into one module.
+
+ [NAME]
+ Name of the song.
+
+ [AUTH]
+ Author of the song.
+
+ [ANNO]
+ Song description.
+
+ SHDR
+ Song Header. Contains a SongHeader structure as defined in
+ <XModuleClass.h>.
+
+ SEQN
+ Song Sequence. The contents of this chunk are unsigned words
+ representing the pattern to play at a particular position.
+ The size of this chunk should be twice the Length field of
+ the SongHeader.
+ When a song contains multiple sections, one SEQN chunk will
+ be stored for each of the song sections (not yet implemented).
+
+ FORM PATT
+ Each occurrence of this FORM contains one pattern. Patterns
+ are numbered starting from 0. Each pattern has its own
+ length and number of tracks.
+
+ [NAME]
+ Name of this Pattern.
+
+ PHDR
+ Pattern Header. Contains a PatternHeader structure as
+ defined in <XModuleClass.h>.
+
+ BODY
+ The BODY chunk is stored as an array of PatternNote
+ structures. Tracks are stored one after the other.
+ There are NumTracks*Length Note structures in the BODY
+ chunk.
+
+ END FORM PATT
+
+ FORM 8SVX
+ Each SONG contains one or more occurences of this FORM,
+ one for each sample. XModule currently saves and loads
+ only the NAME, VHDR and BODY chunks. Be prepared to
+ decode Fibonacci Delta Encoded BODY chunks. An additional
+ non-standard chunk is saved:
+
+ INST
+ XModule instrument info. Contains an InstrumentInfo
+ structure as defined in <XModuleClass.h>.
+
+ When the BODY chunk is missing, the sample must be
+ loaded from the instrument library using the contents
+ of the NAME chunk as the file name.
+
+ See the IFF 8SVX documentation for more details.
+
+ END FORM 8SVX
+
+ END FORM SONG
+
+END FORM XMOD
+@end example
+
+
+
+@c ******************
+@c * Credits *
+@c ******************
+
+@node Credits, Author Info, XModule Format, Top
+@chapter Credits
+@cindex Credits
+@cindex Acknowledgements
+@cindex Thanks
+
+ These people have helped me in developing @value{prgname}:
+
+@itemize @bullet
+@item
+Fabio Barzagli, for beta testing @value{prgname} and for giving me
+all his awesome modules!
+
+Keep on making good music, Fabio!
+
+@item
+Jarno Paanen, for sending me the obfuscated source of his 32 channel
+replay engine and for the e-mail support.
+
+Perhaps I would have understood more by disassembling the PS3M executable. :-))
+
+@item
+Peter Kunath, for his unvaluable help.
+
+No comment... :-)
+
+@item Stephen Cantini, Michael Reichenbach and Julien Wilk, respectively for
+writing the italian, german and french catalog.
+
+Thank you all! I'm sure @value{prgname} users in your countries will apreciate
+your efford.
+
+@item Pauli Porkka, for his kindness, for letting me win a free DASMP
+key, for the sources and for all the e-mails.
+
+Will you support my format in the next DASMP release?
+
+@item
+The Amiga, for being the best computer in the world. :^)
+
+Commodore: You've put really a lot of effort trying to kill the Amiga,
+but you didn't succeed!
+@end itemize
+
+
+
+@c ******************
+@c * Author Info *
+@c ******************
+
+@node Author Info, Concept Index, Credits, Top
+@chapter How to contact the author
+@cindex How to contact the author
+@cindex Author's address
+@cindex Contact the author, how to
+
+If you want to make me aware of your suggestions, bug reports, ideas
+or you want to send me a gift, a good module or, why not, some money :-),
+reach me in any of the following ways:
+
+@table @bullet{} @code
+@item E-Mail
+@example
+@group
+UseNet: xmodule@@shock.nervous.com (preferred)
+FidoNet: Bernardo Innocenti 2:332/125.1
+AmigaNet: Bernardo Innocenti 39:102/205.1
+@end group
+@end example
+
+@item SystemShock BBS
+@example
+@group
+Line 1: +39-55-499038 (28,800bps - V34)
+Line 2: +39-55-472514 (14,400bps - V32bis)
+Sysop: Bernardo Innocenti (me)
+Message area 300: XModule Support
+File Request magic name "XMODULE" to get the latest public version.
+@end group
+@end example
+
+@item Snail-Mail
+@example
+@group
+Bernardo Innocenti
+Via Ventiquattro Maggio, 14
+50129 - Firenze
+ITALY
+@end group
+@end example
+
+@item FAX
+@example
+@group
++39-55-8877771
+Attn: Bernardo Innocenti
+Subj: Amiga/@value{prgname}
+@end group
+@end example
+@end table
+
+
+I'll try to reply to all my e-mail, but I'm too lazy to reply
+snail-mail, unless it is very important. Your suggestions
+will be taken into account unless they are @cite{I want
+@value{prgname} to do ice tea}-style.
+
+
+@node Concept Index, , Author Info, Top
+@unnumbered Concept Index
+
+@printindex cp
+
+@contents
+@bye
--- /dev/null
+##base _XModuleBase
+##bias 30
+##public
+*--- functions in V1 or higher (Release 4.0) ---
+##private
+xmPrivate1()()
+xmPrivate2()()
+xmPrivate3()()
+xmPrivate4()()
+##public
+*
+* Public entries
+*
+xmCreateSongA(tagList)(a0)
+xmDeleteSong(si)(a0)
+xmAddSongA(si,position,tagList)(a0/a1/a2)
+xmRemSong(si)(a0)
+xmActivateSong(si)(a0)
+xmLockActiveSong(mode)(d0)
+xmAddHookA(tagList)(a0)
+xmRemHook(hook)(a0)
+xmIdentifyModule(fh,tagList)(d0,a0)
+xmLoadModuleA(fileName,tagList)(a0/a1)
+xmSaveModuleA(si,fileName,saver,tagList)(a0/a1/a2/a3)
+xmSetSongLen(si,length)(a0,d0)
+xmAddPatternA(si,tagList)(a0/a1)
+xmSetPatternA(si,pattNum,tagList)(a0,d0,a1)
+xmRemPattern(si,pattNum,replaceWith)(a0,d0,d1)
+xmAddInstrumentA(si,instrNum,tagList)(a0,d0,a1)
+xmSetInstrumentA(si,instrNum,tagList)(a0,d0,a1)
+xmRemInstrument(si,instrNum)(a0,d0)
+xmProcessSongA(si,reserved,tagList)(a0/a1/a2)
+xmDisplayMessageA(level,message,args)(d0,a0/a1)
+xmDisplayProgress(actual,total)(d0/d1)
+##end