From: Bernie Innocenti Date: Fri, 11 Mar 2011 04:45:00 +0000 (-0500) Subject: Initial commit. X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=amiga%2Fxmodule.git Initial commit. --- c4588370bd5da4dd123cb3046b5b0afd28d44464 diff --git a/App.c b/App.c new file mode 100644 index 0000000..bee18e7 --- /dev/null +++ b/App.c @@ -0,0 +1,184 @@ +/* +** App.c +** +** Copyright (C) 1994,95,96 by Bernardo Innocenti +** +** Handle AppIcons & AppWindows +*/ + +#include +#include + +#include +#include +#include +#include +#include + +#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; + } +} diff --git a/App.h b/App.h new file mode 100644 index 0000000..f6c7409 --- /dev/null +++ b/App.h @@ -0,0 +1,17 @@ +/* +** $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); diff --git a/Audio.c b/Audio.c new file mode 100644 index 0000000..fc65a8b --- /dev/null +++ b/Audio.c @@ -0,0 +1,532 @@ +/* +** 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 +#include +#include +#include + +#include +#include + +#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; + } +} diff --git a/Autodocs/songclass b/Autodocs/songclass new file mode 100644 index 0000000..8541f95 --- /dev/null +++ b/Autodocs/songclass @@ -0,0 +1,57 @@ +@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 diff --git a/Autodocs/songclass.doc b/Autodocs/songclass.doc new file mode 100644 index 0000000..7259095 --- /dev/null +++ b/Autodocs/songclass.doc @@ -0,0 +1,45 @@ +TABLE OF CONTENTS + +songclass/--background-- + songclass/--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. + + \ No newline at end of file diff --git a/Autodocs/xmodule b/Autodocs/xmodule new file mode 100644 index 0000000..b9a3e4c --- /dev/null +++ b/Autodocs/xmodule @@ -0,0 +1,1090 @@ +@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 diff --git a/Autodocs/xmodule.doc b/Autodocs/xmodule.doc new file mode 100644 index 0000000..c4bb47f --- /dev/null +++ b/Autodocs/xmodule.doc @@ -0,0 +1,1050 @@ +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 + xmodule/--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() + + xmodule/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 + + xmodule/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() + + xmodule/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 + + xmodule/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 + + xmodule/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() + + xmodule/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() + + xmodule/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() + + xmodule/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() + + xmodule/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. + + xmodule/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 + + xmodule/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() + + xmodule/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 + + xmodule/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. + + xmodule/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() + + xmodule/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 + + xmodule/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 + + xmodule/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() + + xmodule/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 + + xmodule/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 + + xmodule/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 + + xmodule/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. + + \ No newline at end of file diff --git a/BoopsiStubs.h b/BoopsiStubs.h new file mode 100644 index 0000000..a8202c7 --- /dev/null +++ b/BoopsiStubs.h @@ -0,0 +1,181 @@ +#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 + */ +#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 */ diff --git a/Catalogs/HowToTranslate.doc b/Catalogs/HowToTranslate.doc new file mode 100644 index 0000000..016bdce --- /dev/null +++ b/Catalogs/HowToTranslate.doc @@ -0,0 +1,61 @@ + +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 %$, where is the original +position of the % command, 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 .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/.ct + CATALOG=Catalogs//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. diff --git a/Catalogs/Makefile b/Catalogs/Makefile new file mode 100644 index 0000000..53b2951 --- /dev/null +++ b/Catalogs/Makefile @@ -0,0 +1,55 @@ +## +## 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" diff --git a/Catalogs/deutsch.ct b/Catalogs/deutsch.ct new file mode 100644 index 0000000..bc36eb7 --- /dev/null +++ b/Catalogs/deutsch.ct @@ -0,0 +1,1491 @@ +## version $VER: Deutsch.ct 1.0 (12.6.95) +## language deutsch +## codeset 0 +; +## chunk AUTH Deutsch catalog translation by Michael Reichenbach +;** +;** 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. diff --git a/Catalogs/empty.ct b/Catalogs/empty.ct new file mode 100644 index 0000000..31dd947 --- /dev/null +++ b/Catalogs/empty.ct @@ -0,0 +1,1473 @@ +## 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. diff --git "a/Catalogs/fran\347ais.ct" "b/Catalogs/fran\347ais.ct" new file mode 100644 index 0000000..5b6f2a6 --- /dev/null +++ "b/Catalogs/fran\347ais.ct" @@ -0,0 +1,1491 @@ +## version $VER: Français.ct 1.0 (10.9.95) +## language français +## codeset 0 +; +## chunk AUTH Français catalog translation by Julien Wilk +;** +;** 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. diff --git a/Catalogs/italiano.ct b/Catalogs/italiano.ct new file mode 100644 index 0000000..27bc5f1 --- /dev/null +++ b/Catalogs/italiano.ct @@ -0,0 +1,1488 @@ +## 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. diff --git a/Catalogs/nederlands.ct b/Catalogs/nederlands.ct new file mode 100644 index 0000000..08f4be2 --- /dev/null +++ b/Catalogs/nederlands.ct @@ -0,0 +1,1479 @@ +## 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. diff --git a/Catalogs/xmodule.cd b/Catalogs/xmodule.cd new file mode 100644 index 0000000..3cdbdd9 --- /dev/null +++ b/Catalogs/xmodule.cd @@ -0,0 +1,1160 @@ +#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. diff --git a/ClearWin.c b/ClearWin.c new file mode 100644 index 0000000..0d217cd --- /dev/null +++ b/ClearWin.c @@ -0,0 +1,125 @@ +/* +** ClearWin.c +** +** Copyright (C) 1994,95,96 Bernardo Innocenti +** +** Clear panel handling functions. +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#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); +} diff --git a/CompilerSpecific.h b/CompilerSpecific.h new file mode 100644 index 0000000..6041ece --- /dev/null +++ b/CompilerSpecific.h @@ -0,0 +1,304 @@ +#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 in 68K register +** +** min(a,b) +** Return the minimum between and +** +** max(a,b) +** Return the maximum between and +** +** abs(a) +** Return the absolute value of +** +** _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 +#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 + #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 +#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 + + #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 . + * 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 */ diff --git a/Compress.c b/Compress.c new file mode 100644 index 0000000..5ba53e6 --- /dev/null +++ b/Compress.c @@ -0,0 +1,269 @@ +/* +** Compress.c +** +** Copyright (C) 1994,96,97 Bernardo Innocenti +** +** Compression/Decompression handling functions. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/CustomClasses.c b/CustomClasses.c new file mode 100644 index 0000000..f70a1d5 --- /dev/null +++ b/CustomClasses.c @@ -0,0 +1,626 @@ +/* +** CustomClasses.c +** +** Copyright (C) 1995,96,97 Bernardo Innocenti +** +** Special custom BOOPSI classes. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/CustomClasses.h b/CustomClasses.h new file mode 100644 index 0000000..3dcfed8 --- /dev/null +++ b/CustomClasses.h @@ -0,0 +1,23 @@ +/* +** 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 diff --git a/Cx.c b/Cx.c new file mode 100644 index 0000000..4b841fd --- /dev/null +++ b/Cx.c @@ -0,0 +1,171 @@ +/* +** Cx.c +** +** Copyright (C) 1994,95,96,97,98 Bernardo Innocenti +** +** Commodity support functions +*/ + + +#include +#include + +#include +#include +#include +#include + +#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; + } +} diff --git a/Debug.h b/Debug.h new file mode 100644 index 0000000..ba7b825 --- /dev/null +++ b/Debug.h @@ -0,0 +1,103 @@ +#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 evalutates to a +** non-zero value, output a debug message otherwise. +** +** ASSERT_VALID(x) +** Checks if the expression 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 points to a valid +** memory location, and outputs a debug message +** otherwise. A NULL pointer is considered INVALID. +** +** DB(x) +** Compile the expression when making a debug +** executable, leave it out otherwise. +*/ + +#ifdef DEBUG + + /* Needed for TypeOfMem() */ + #ifndef PROTO_EXEC_H + #include + #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 */ diff --git a/FFT.c b/FFT.c new file mode 100644 index 0000000..cf85b57 --- /dev/null +++ b/FFT.c @@ -0,0 +1,279 @@ +#include +#include + + + + +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 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> 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; lNIL: + +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 $@ diff --git a/Gadgets/PattEditClass.c b/Gadgets/PattEditClass.c new file mode 100644 index 0000000..34c9ab2 --- /dev/null +++ b/Gadgets/PattEditClass.c @@ -0,0 +1,3049 @@ +/* +** PattEditClass.c +** +** Copyright (C) 1995,96,97,98 Bernardo Innocenti +** +** Pattern Editor gadget class +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#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 */ +#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 */ + + /* Routine used to convert a note into its ASCII representation + * This routine is passed the pointer to the note and a buffer + * with 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 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 to 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 with the ASCII representation of a + * Note structure. will be able to hold at + * least 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 with the ASCII representation of a + * Note structure. will be able to hold at + * least 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 , . + * + * 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); +} diff --git a/Gadgets/PattEditClassAsm.asm b/Gadgets/PattEditClassAsm.asm new file mode 100644 index 0000000..68dd865 --- /dev/null +++ b/Gadgets/PattEditClassAsm.asm @@ -0,0 +1,183 @@ +** +** 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 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 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' diff --git a/Gadgets/SCOPTIONS b/Gadgets/SCOPTIONS new file mode 100644 index 0000000..b074877 --- /dev/null +++ b/Gadgets/SCOPTIONS @@ -0,0 +1,29 @@ +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 diff --git a/Gadgets/SampEditClass.c b/Gadgets/SampEditClass.c new file mode 100644 index 0000000..7c140ff --- /dev/null +++ b/Gadgets/SampEditClass.c @@ -0,0 +1,844 @@ +/* +** SampEditClass.c +** +** Copyright (C) 1995,96 Bernardo Innocenti +** +** Sample editor class built on top of the "gadgetclass". +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +/* Some handy definitions missing in */ +#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 */ + + 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); +} diff --git a/Gadgets/pattedit.gadget b/Gadgets/pattedit.gadget new file mode 100644 index 0000000..772bb12 Binary files /dev/null and b/Gadgets/pattedit.gadget differ diff --git a/Gst.c b/Gst.c new file mode 100644 index 0000000..369972a --- /dev/null +++ b/Gst.c @@ -0,0 +1,81 @@ +/* +** 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LIBRARIES_PATTEDITCLASS_H +#include +#endif /* !LIBRARIES_PATTEDITCLASS_H */ + +#ifndef LIBRARIES_XMODULECLASS_H +#include +#endif /* !LIBRARIES_XMODULECLASS_H */ + +#ifndef LIBRARIES_SONGCLASS_H +#include +#endif /* !LIBRARIES_SONGCLASS_H */ + +#ifndef LIBRARIES_XMODULE_H +#include +#endif /* !LIBRARIES_XMODULE_H */ + +/* Can't include this one here because of the inline functions defined in + * + * #include "XModulePriv.h" + */ + +#include "Gui.h" diff --git a/Gui.c b/Gui.c new file mode 100644 index 0000000..36243f3 --- /dev/null +++ b/Gui.c @@ -0,0 +1,2934 @@ +/* +** Gui.c +** +** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti +** +** Graphic User Interface handling routines. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . 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 . + * 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; +} diff --git a/Gui.h b/Gui.h new file mode 100644 index 0000000..682e599 --- /dev/null +++ b/Gui.h @@ -0,0 +1,562 @@ +/* +** Gui.h +** +** Copyright (C) 1993,94,95,96,98 by Bernardo Innocenti +** +** Various definitions for the user interface. +*/ + +#ifndef INTUITION_INTUITION_H +#include +#endif + +#ifndef XMODULE_PRIV_H +#include +#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 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 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 */ + +#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); diff --git a/Guru.i b/Guru.i new file mode 100644 index 0000000..051443a --- /dev/null +++ b/Guru.i @@ -0,0 +1,91 @@ +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 diff --git a/Help.c b/Help.c new file mode 100644 index 0000000..362018f --- /dev/null +++ b/Help.c @@ -0,0 +1,124 @@ +/* +** Help.c +** +** Copyright (C) 1994,95,96 by Bernardo Innocenti +** +** Handle on-line, context sensitive, AmigaGuide help. +*/ + +#include + +#include +#include +#include + +#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; + } +} diff --git a/Hooks/FastTracker2.h b/Hooks/FastTracker2.h new file mode 100644 index 0000000..c778445 --- /dev/null +++ b/Hooks/FastTracker2.h @@ -0,0 +1,112 @@ +/* +** 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 */ + diff --git a/Hooks/GetFT2.c b/Hooks/GetFT2.c new file mode 100644 index 0000000..6acf333 --- /dev/null +++ b/Hooks/GetFT2.c @@ -0,0 +1,68 @@ +/* +** GetFT2.c +** +** Copyright (C) 1995 Bernardo Innocenti +** +** Load a FastTracker ][ module with any number of tracks. +*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/Hooks/MMDHook.c b/Hooks/MMDHook.c new file mode 100644 index 0000000..c493f71 --- /dev/null +++ b/Hooks/MMDHook.c @@ -0,0 +1,1025 @@ +/* +** 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 . +** +** +** Structure of an MMD0/1/2 module as saved by SaveMED(): +** +** MMD0 +** MMD0song +** MMD0exp +** InstrExt[] +** InstrInfo[] +** +** +** NotationInfo +** MMD0blockarr +** MMD0samparr +** +** /* Only for MMD1/MMD2 */ +** /* Only for MMD1/MMD2 */ +** +*/ + +#include + +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/Hooks/Makefile b/Hooks/Makefile new file mode 100644 index 0000000..52f10f9 --- /dev/null +++ b/Hooks/Makefile @@ -0,0 +1,47 @@ +## +## 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 diff --git a/Hooks/OctaMed.h b/Hooks/OctaMed.h new file mode 100644 index 0000000..2073f61 --- /dev/null +++ b/Hooks/OctaMed.h @@ -0,0 +1,298 @@ +/* +** 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 diff --git a/Hooks/OktalyzerHook.c b/Hooks/OktalyzerHook.c new file mode 100644 index 0000000..85e4f60 --- /dev/null +++ b/Hooks/OktalyzerHook.c @@ -0,0 +1,841 @@ +/* +** 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 +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/Hooks/SCOPTIONS b/Hooks/SCOPTIONS new file mode 100644 index 0000000..b074877 --- /dev/null +++ b/Hooks/SCOPTIONS @@ -0,0 +1,29 @@ +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 diff --git a/Hooks/SaveMIDI.c b/Hooks/SaveMIDI.c new file mode 100644 index 0000000..514accb --- /dev/null +++ b/Hooks/SaveMIDI.c @@ -0,0 +1,947 @@ +/* +** 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 + +#include + +#include + +#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 :( */ +} diff --git a/Hooks/ScreamTrackerHook.c b/Hooks/ScreamTrackerHook.c new file mode 100644 index 0000000..587445d --- /dev/null +++ b/Hooks/ScreamTrackerHook.c @@ -0,0 +1,606 @@ +/* +** 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 +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/Hooks/oktalyzer.xmhook b/Hooks/oktalyzer.xmhook new file mode 100644 index 0000000..d6af097 Binary files /dev/null and b/Hooks/oktalyzer.xmhook differ diff --git a/Hooks/screamtracker.xmhook b/Hooks/screamtracker.xmhook new file mode 100644 index 0000000..10a6a3b Binary files /dev/null and b/Hooks/screamtracker.xmhook differ diff --git a/Instr.c b/Instr.c new file mode 100644 index 0000000..5f07735 --- /dev/null +++ b/Instr.c @@ -0,0 +1,1057 @@ +/* +** Instr.c +** +** Copyright (C) 1994,95,96,97 Bernardo Innocenti +** +** Instrument loading/saving/handling routines. +*/ + +#define IFFPARSE_V37_NAMES_ONLY + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 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 . If 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 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; +} diff --git a/InstrumentsWin.c b/InstrumentsWin.c new file mode 100644 index 0000000..479d6e2 --- /dev/null +++ b/InstrumentsWin.c @@ -0,0 +1,657 @@ +/* +** InstrumentsWin.c +** +** Copyright (C) 1994,95,96 Bernardo Innocenti +** +** Instruments editor handling functions. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 */ +} diff --git a/Library.c b/Library.c new file mode 100644 index 0000000..16c1918 --- /dev/null +++ b/Library.c @@ -0,0 +1,2161 @@ +/* +** Library.c +** +** Copyright (C) 1995,96,97 Bernardo Innocenti +** +** xmodule.library functions +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/ListMacros.h b/ListMacros.h new file mode 100644 index 0000000..4b7d808 --- /dev/null +++ b/ListMacros.h @@ -0,0 +1,98 @@ +#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 */ diff --git a/Locale.c b/Locale.c new file mode 100644 index 0000000..1b48a82 --- /dev/null +++ b/Locale.c @@ -0,0 +1,402 @@ +/* +** 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 + +#include +#include + +#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; + } +} diff --git a/LocaleStrings.h b/LocaleStrings.h new file mode 100644 index 0000000..6f78d35 --- /dev/null +++ b/LocaleStrings.h @@ -0,0 +1,327 @@ +/* +** 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 */ diff --git a/LocaleStrings_h.sd b/LocaleStrings_h.sd new file mode 100644 index 0000000..902dcee --- /dev/null +++ b/LocaleStrings_h.sd @@ -0,0 +1,19 @@ +##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 */ diff --git a/Locale_c.sd b/Locale_c.sd new file mode 100644 index 0000000..f391506 --- /dev/null +++ b/Locale_c.sd @@ -0,0 +1,74 @@ +##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 + +#include +#include + +#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; + } +} diff --git a/Main.c b/Main.c new file mode 100644 index 0000000..bb71e6f --- /dev/null +++ b/Main.c @@ -0,0 +1,587 @@ +/* +** $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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#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) {} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7ccc92e --- /dev/null +++ b/Makefile @@ -0,0 +1,394 @@ +## +## $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 diff --git a/Misc.c b/Misc.c new file mode 100644 index 0000000..42f929d --- /dev/null +++ b/Misc.c @@ -0,0 +1,582 @@ +/* +** 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 +#include + +#include +#include +#include +#include + +#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 textattr structure over , allocating and copying + * the ta_Name field. ->ta_Name if FreeVec()ed before + * allocating the new one. + * + * Returns: 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 string + * using AllocVecPooled() and then copies the contents of + * there. If the buffer pointed by is not NULL, + * DupStringPooled() will first check to see if and + * are identical, in which case no new buffer will be + * allocated and the result will be FALSE. Otherwise, will + * be deallocated and a new buffer will be allocated. Both and <*dest> + * can be NULL. + * + * RESULT + * Non-zero if 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 icon to 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. + * is the template to build the backup name. + * Each occurence of the '#' character in the template is replaced with + * an ASCII decimal representation of . The '*' character is replaced + * with the original filename . + * + * RESULT + * The result is stored in . 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 --- */ diff --git a/Operators.c b/Operators.c new file mode 100644 index 0000000..0129a06 --- /dev/null +++ b/Operators.c @@ -0,0 +1,838 @@ +/* +** Operators.c +** +** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti +** +** General pourpose module handling/processing functions. +*/ + +#include + +#include +#include + +#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 and more than + * 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 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 + * pattern to the 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 with 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 with 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); +} diff --git a/OptimizationWin.c b/OptimizationWin.c new file mode 100644 index 0000000..ea4cd05 --- /dev/null +++ b/OptimizationWin.c @@ -0,0 +1,135 @@ +/* +** OptimizationWin.c +** +** Copyright (C) 1994,95,96 Bernardo Innocenti +** +** Song optimization panel. +*/ + + +#include +#include +#include + +#include +#include +#include +#include + +#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); +} diff --git a/OptimizationWin.s b/OptimizationWin.s new file mode 100644 index 0000000..e69de29 diff --git a/PattPrefsWin.c b/PattPrefsWin.c new file mode 100644 index 0000000..20c3e25 --- /dev/null +++ b/PattPrefsWin.c @@ -0,0 +1,790 @@ +/* +** PattPrefsWin.c +** +** Copyright (C) 1995,96 Bernardo Innocenti +** +** Pattern preferences panel and pattern size panel handling functions. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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); +} diff --git a/PatternWin.c b/PatternWin.c new file mode 100644 index 0000000..4a4d5e1 --- /dev/null +++ b/PatternWin.c @@ -0,0 +1,1075 @@ +/* +** 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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); +} diff --git a/PlayWin.c b/PlayWin.c new file mode 100644 index 0000000..78991c7 --- /dev/null +++ b/PlayWin.c @@ -0,0 +1,262 @@ +/* +** PlayWin.c +** +** Copyright (C) 1995,96 Bernardo Innocenti +** +** Play window handling functions. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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) +{ +} diff --git a/Player.asm b/Player.asm new file mode 100644 index 0000000..b8e78e4 --- /dev/null +++ b/Player.asm @@ -0,0 +1,3251 @@ +** +** 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 diff --git a/Player.h b/Player.h new file mode 100644 index 0000000..5aad2fe --- /dev/null +++ b/Player.h @@ -0,0 +1,24 @@ +/* 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, +}; diff --git a/Player.i b/Player.i new file mode 100644 index 0000000..1f9dde5 --- /dev/null +++ b/Player.i @@ -0,0 +1,103 @@ + 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 diff --git a/Players/32Channels.player b/Players/32Channels.player new file mode 100644 index 0000000..ad443d2 Binary files /dev/null and b/Players/32Channels.player differ diff --git a/Prefs.c b/Prefs.c new file mode 100644 index 0000000..809ce6f --- /dev/null +++ b/Prefs.c @@ -0,0 +1,645 @@ +/* +** Prefs.c +** +** Copyright (C) 1994,95,96,97 Bernardo Innocenti +** +** Save and load preferences. +*/ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/PrefsWin.c b/PrefsWin.c new file mode 100644 index 0000000..463d1eb --- /dev/null +++ b/PrefsWin.c @@ -0,0 +1,406 @@ +/* +** PrefsWin.c +** +** Copyright (C) 1994,95,96,97 by Bernardo Innocenti +** +** Preferences panel handling routines. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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); +} diff --git a/ProgressWin.c b/ProgressWin.c new file mode 100644 index 0000000..0e19fcc --- /dev/null +++ b/ProgressWin.c @@ -0,0 +1,361 @@ +/* +** 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/Requesters.c b/Requesters.c new file mode 100644 index 0000000..7e4fe8e --- /dev/null +++ b/Requesters.c @@ -0,0 +1,771 @@ +/* +** Requesters.c +** +** Copyright (C) 1994,95,96,97 Bernardo Innocenti +** +** Handle asyncronous file requester +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#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 and 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 + * 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 and + * 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 and 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; +} diff --git a/Requesters.h b/Requesters.h new file mode 100644 index 0000000..56d4ca8 --- /dev/null +++ b/Requesters.h @@ -0,0 +1,18 @@ +/* +** $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); diff --git a/Rexx.c b/Rexx.c new file mode 100644 index 0000000..b0ea25a --- /dev/null +++ b/Rexx.c @@ -0,0 +1,635 @@ +/* +** Rexx.c +** +** Copyright (C) 1994,95,96,97 by Bernardo Innocenti +** +** ARexx interface handling routines +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/RomTag.asm b/RomTag.asm new file mode 100644 index 0000000..4f808a8 --- /dev/null +++ b/RomTag.asm @@ -0,0 +1,301 @@ +****************************************************************************** +* +* $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 diff --git a/SCOPTIONS b/SCOPTIONS new file mode 100644 index 0000000..c7c88af --- /dev/null +++ b/SCOPTIONS @@ -0,0 +1,25 @@ +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 diff --git a/SampleWin.c b/SampleWin.c new file mode 100644 index 0000000..dda02db --- /dev/null +++ b/SampleWin.c @@ -0,0 +1,953 @@ +/* +** SampleWin.c +** +** Copyright (C) 1994,95,96 Bernardo Innocenti +** +** Sample editor handling functions. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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(); +} diff --git a/SaversWin.c b/SaversWin.c new file mode 100644 index 0000000..1d51d79 --- /dev/null +++ b/SaversWin.c @@ -0,0 +1,325 @@ +/* +** SaversWin.c +** +** Copyright (C) 1994,95,96 Bernardo Innocenti +** +** Savers panel handling functions. +*/ + + +#include +#include +#include + +#include +#include +#include +#include + +#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); +} diff --git a/SequenceWin.c b/SequenceWin.c new file mode 100644 index 0000000..c138def --- /dev/null +++ b/SequenceWin.c @@ -0,0 +1,504 @@ +/* +** SequenceWin.c +** +** Copyright (C) 1993,94,95,96 Bernardo Innocenti +** +** Sequence editor handling functions. +*/ + +#include +#include +#include + +#include +#include +#include +#include + + +#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 */ + } +} diff --git a/SongClass.c b/SongClass.c new file mode 100644 index 0000000..1278584 --- /dev/null +++ b/SongClass.c @@ -0,0 +1,1174 @@ +/* +** 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 . Patterns >= 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 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 >= will be moved + * back one slot. The position table is updated replacing each + * occurence of with pattern . If 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 is -1, it will load the instrument in the slot right after + * the last instrument. If 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; +} diff --git a/SongClass.i b/SongClass.i new file mode 100644 index 0000000..7a15d09 --- /dev/null +++ b/SongClass.i @@ -0,0 +1,151 @@ + 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 diff --git a/SongInfoWin.c b/SongInfoWin.c new file mode 100644 index 0000000..68f300a --- /dev/null +++ b/SongInfoWin.c @@ -0,0 +1,533 @@ +/* +** SongInfoWin.c +** +** Copyright (C) 1993,94,95,96 Bernardo Innocenti +** +** Handle Song Information panel. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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); +} diff --git a/Startup.asm b/Startup.asm new file mode 100644 index 0000000..a56c210 --- /dev/null +++ b/Startup.asm @@ -0,0 +1,532 @@ +;** +;** 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) + ENDM + +POP MACRO ; pop from stack + move.\0 (sp)+,\1 ; pop.(w|l) + 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 diff --git a/ToolBoxWin.c b/ToolBoxWin.c new file mode 100644 index 0000000..b01908d --- /dev/null +++ b/ToolBoxWin.c @@ -0,0 +1,490 @@ +/* +** ToolBoxWin.c +** +** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti +** +** Handle ToolBox panel +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/TrackerHook.c b/TrackerHook.c new file mode 100644 index 0000000..edc7fbf --- /dev/null +++ b/TrackerHook.c @@ -0,0 +1,1049 @@ +/* +** 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 +#include + +#include +#include +#include +#include +#include + +#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 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); +} diff --git a/XModule b/XModule new file mode 100644 index 0000000..b209ae9 Binary files /dev/null and b/XModule differ diff --git a/XModule.i b/XModule.i new file mode 100644 index 0000000..0fdebc0 --- /dev/null +++ b/XModule.i @@ -0,0 +1,121 @@ + 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 diff --git a/XModule.info b/XModule.info new file mode 100644 index 0000000..fb47624 Binary files /dev/null and b/XModule.info differ diff --git a/XModule.prefs b/XModule.prefs new file mode 100644 index 0000000..1f87e29 Binary files /dev/null and b/XModule.prefs differ diff --git a/XModuleHook.c b/XModuleHook.c new file mode 100644 index 0000000..d1e1d6c --- /dev/null +++ b/XModuleHook.c @@ -0,0 +1,716 @@ +/* +** XModuleHook.c +** +** Copyright (C) 1994,95,96,97 Bernardo Innocenti +** +** Internal loader/saver hook for the IFF XMOD module format +*/ + +#include +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/XModulePriv.h b/XModulePriv.h new file mode 100644 index 0000000..2a5c648 --- /dev/null +++ b/XModulePriv.h @@ -0,0 +1,290 @@ +#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 +#endif /* !EXEC_NODES_H */ + +#ifndef EXEC_LIBRARIES_H +#include +#endif /* !EXEC_LIBRARIES_H */ + +#ifndef DOS_DOS_H +#include +#endif /* !DOS_DOS_H */ + +#ifndef DOS_DOS_H +#include +#endif /* !DOS_DOS_H */ + +#ifndef LIBRARIES_IFFPARSE_H +#include "libraries/iffparse.h" +#endif /* LIBRARIES_IFFPARSE_H */ + +#ifndef LIBRARIES_XMODULE_H +#include +#endif /* !LIBRARIES_XMODULE_H */ + +#ifndef LIBRARIES_SONGCLASS_H +#include +#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 */ diff --git a/XModule_final.c b/XModule_final.c new file mode 100644 index 0000000..19be4a4 --- /dev/null +++ b/XModule_final.c @@ -0,0 +1,32 @@ +/* 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" diff --git a/XModule_rev.h b/XModule_rev.h new file mode 100644 index 0000000..68ef7c0 --- /dev/null +++ b/XModule_rev.h @@ -0,0 +1,12 @@ +/* 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)" diff --git a/XModule_rev.rev b/XModule_rev.rev new file mode 100644 index 0000000..62f9457 --- /dev/null +++ b/XModule_rev.rev @@ -0,0 +1 @@ +6 \ No newline at end of file diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..33d5557 --- /dev/null +++ b/config.mk @@ -0,0 +1,165 @@ +## +## $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: diff --git a/delta.asm b/delta.asm new file mode 100644 index 0000000..1aa1d4b --- /dev/null +++ b/delta.asm @@ -0,0 +1,56 @@ +** +** 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 diff --git a/include/clib/xmodule_protos.h b/include/clib/xmodule_protos.h new file mode 100644 index 0000000..c1f6120 --- /dev/null +++ b/include/clib/xmodule_protos.h @@ -0,0 +1,67 @@ +#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 +#endif +#ifndef UTILITY_TAGITEM_H +#include +#endif +#ifndef LIBRARIES_XMODULE_H +#include +#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 */ diff --git a/include/libraries/patteditclass.h b/include/libraries/patteditclass.h new file mode 100644 index 0000000..430e6c4 --- /dev/null +++ b/include/libraries/patteditclass.h @@ -0,0 +1,233 @@ +#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 +#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 */ diff --git a/include/libraries/sampeditclass.h b/include/libraries/sampeditclass.h new file mode 100644 index 0000000..402afb1 --- /dev/null +++ b/include/libraries/sampeditclass.h @@ -0,0 +1,123 @@ +#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 */ diff --git a/include/libraries/songclass.h b/include/libraries/songclass.h new file mode 100644 index 0000000..126f25a --- /dev/null +++ b/include/libraries/songclass.h @@ -0,0 +1,341 @@ +#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 +#endif /* EXEC_SEMAPHORES_H */ + +#ifndef INTUITION_CLASSES_H +#include +#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 */ diff --git a/include/libraries/songclass.i b/include/libraries/songclass.i new file mode 100644 index 0000000..7a15d09 --- /dev/null +++ b/include/libraries/songclass.i @@ -0,0 +1,151 @@ + 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 diff --git a/include/libraries/xmodule.h b/include/libraries/xmodule.h new file mode 100644 index 0000000..1b08f04 --- /dev/null +++ b/include/libraries/xmodule.h @@ -0,0 +1,240 @@ +#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 +#endif /* DOS_DOS_H */ + +#ifndef EXEC_LISTS_H +#include +#endif /* EXEC_LISTS_H */ + +#ifndef EXEC_LIBRARIES_H +#include +#endif /* EXEC_LIBRARIES_H */ + +#ifndef EXEC_SEMAPHORES_H +#include +#endif /* EXEC_SEMAPHORES_H */ + +#ifndef INTUITION_CLASSES_H +#include +#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 */ diff --git a/include/libraries/xmodule.i b/include/libraries/xmodule.i new file mode 100644 index 0000000..0fdebc0 --- /dev/null +++ b/include/libraries/xmodule.i @@ -0,0 +1,121 @@ + 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 diff --git a/include/libraries/xmodule_lib.i b/include/libraries/xmodule_lib.i new file mode 100644 index 0000000..b9ebac4 --- /dev/null +++ b/include/libraries/xmodule_lib.i @@ -0,0 +1,27 @@ + 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 diff --git a/include/libraries/xmoduleclass.h b/include/libraries/xmoduleclass.h new file mode 100644 index 0000000..6937813 --- /dev/null +++ b/include/libraries/xmoduleclass.h @@ -0,0 +1,164 @@ +#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 +#endif /* !IFF_IFFPARSE_H */ + +#ifndef DATATYPES_SOUNDCLASS_H +#include +#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 */ +#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 */ diff --git a/include/pragmas/xmodule_pragmas.h b/include/pragmas/xmodule_pragmas.h new file mode 100644 index 0000000..10ac355 --- /dev/null +++ b/include/pragmas/xmodule_pragmas.h @@ -0,0 +1,83 @@ +#ifndef _INCLUDE_PRAGMA_XMODULE_LIB_H +#define _INCLUDE_PRAGMA_XMODULE_LIB_H + +#ifndef CLIB_XMODULE_PROTOS_H +#include +#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 diff --git a/include/proto/xmodule.h b/include/proto/xmodule.h new file mode 100644 index 0000000..afb01e2 --- /dev/null +++ b/include/proto/xmodule.h @@ -0,0 +1,11 @@ +#ifndef _PROTO_XMODULE_H +#define _PROTO_XMODULE_H + +#include + +extern struct XModuleBase *XModuleBase; + +#include +#include + +#endif diff --git a/manuals/HISTORY b/manuals/HISTORY new file mode 100644 index 0000000..5f36b2d --- /dev/null +++ b/manuals/HISTORY @@ -0,0 +1,445 @@ +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 *** diff --git a/manuals/Makefile b/manuals/Makefile new file mode 100644 index 0000000..a6954cc --- /dev/null +++ b/manuals/Makefile @@ -0,0 +1,33 @@ +########################################################### +# 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 diff --git a/manuals/README b/manuals/README new file mode 100644 index 0000000..4b89897 --- /dev/null +++ b/manuals/README @@ -0,0 +1,66 @@ +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 diff --git a/manuals/TODO b/manuals/TODO new file mode 100644 index 0000000..a89d612 --- /dev/null +++ b/manuals/TODO @@ -0,0 +1,49 @@ + + 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 | | + | | | | + | | | | + | +---------------------------------------+ | + +-------------------------------------------+ diff --git a/manuals/XModule.texi b/manuals/XModule.texi new file mode 100644 index 0000000..7623675 --- /dev/null +++ b/manuals/XModule.texi @@ -0,0 +1,2403 @@ +\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 + . + + [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 + . + + 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 . + + 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 . + + 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 diff --git a/xmodule_lib.fd b/xmodule_lib.fd new file mode 100644 index 0000000..2132a77 --- /dev/null +++ b/xmodule_lib.fd @@ -0,0 +1,35 @@ +##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