From c4588370bd5da4dd123cb3046b5b0afd28d44464 Mon Sep 17 00:00:00 2001 From: Bernie Innocenti Date: Thu, 10 Mar 2011 23:45:00 -0500 Subject: [PATCH 1/1] Initial commit. --- App.c | 184 ++ App.h | 17 + Audio.c | 532 +++++ Autodocs/songclass | 57 + Autodocs/songclass.doc | 45 + Autodocs/xmodule | 1090 ++++++++++ Autodocs/xmodule.doc | 1050 ++++++++++ BoopsiStubs.h | 181 ++ Catalogs/HowToTranslate.doc | 61 + Catalogs/Makefile | 55 + Catalogs/deutsch.ct | 1491 +++++++++++++ Catalogs/empty.ct | 1473 +++++++++++++ "Catalogs/fran\347ais.ct" | 1491 +++++++++++++ Catalogs/italiano.ct | 1488 +++++++++++++ Catalogs/nederlands.ct | 1479 +++++++++++++ Catalogs/xmodule.cd | 1160 ++++++++++ ClearWin.c | 125 ++ CompilerSpecific.h | 304 +++ Compress.c | 269 +++ CustomClasses.c | 626 ++++++ CustomClasses.h | 23 + Cx.c | 171 ++ Debug.h | 103 + FFT.c | 279 +++ FakePlayer.asm | 20 + Gadgets/Makefile | 62 + Gadgets/PattEditClass.c | 3049 +++++++++++++++++++++++++++ Gadgets/PattEditClassAsm.asm | 183 ++ Gadgets/SCOPTIONS | 29 + Gadgets/SampEditClass.c | 844 ++++++++ Gadgets/pattedit.gadget | Bin 0 -> 15156 bytes Gst.c | 81 + Gui.c | 2934 ++++++++++++++++++++++++++ Gui.h | 562 +++++ Guru.i | 91 + Help.c | 124 ++ Hooks/FastTracker2.h | 112 + Hooks/GetFT2.c | 68 + Hooks/MMDHook.c | 1025 +++++++++ Hooks/Makefile | 47 + Hooks/OctaMed.h | 298 +++ Hooks/OktalyzerHook.c | 841 ++++++++ Hooks/SCOPTIONS | 29 + Hooks/SaveMIDI.c | 947 +++++++++ Hooks/ScreamTrackerHook.c | 606 ++++++ Hooks/oktalyzer.xmhook | Bin 0 -> 5628 bytes Hooks/screamtracker.xmhook | Bin 0 -> 3036 bytes Instr.c | 1057 ++++++++++ InstrumentsWin.c | 657 ++++++ Library.c | 2161 +++++++++++++++++++ ListMacros.h | 98 + Locale.c | 402 ++++ LocaleStrings.h | 327 +++ LocaleStrings_h.sd | 19 + Locale_c.sd | 74 + Main.c | 587 ++++++ Makefile | 394 ++++ Misc.c | 582 ++++++ Operators.c | 838 ++++++++ OptimizationWin.c | 135 ++ OptimizationWin.s | 0 PattPrefsWin.c | 790 +++++++ PatternWin.c | 1075 ++++++++++ PlayWin.c | 262 +++ Player.asm | 3251 +++++++++++++++++++++++++++++ Player.h | 24 + Player.i | 103 + Players/32Channels.player | Bin 0 -> 9412 bytes Prefs.c | 645 ++++++ PrefsWin.c | 406 ++++ ProgressWin.c | 361 ++++ Requesters.c | 771 +++++++ Requesters.h | 18 + Rexx.c | 635 ++++++ RomTag.asm | 301 +++ SCOPTIONS | 25 + SampleWin.c | 953 +++++++++ SaversWin.c | 325 +++ SequenceWin.c | 504 +++++ SongClass.c | 1174 +++++++++++ SongClass.i | 151 ++ SongInfoWin.c | 533 +++++ Startup.asm | 532 +++++ ToolBoxWin.c | 490 +++++ TrackerHook.c | 1049 ++++++++++ XModule | Bin 0 -> 106900 bytes XModule.i | 121 ++ XModule.info | Bin 0 -> 5381 bytes XModule.prefs | Bin 0 -> 2704 bytes XModuleHook.c | 716 +++++++ XModulePriv.h | 290 +++ XModule_final.c | 32 + XModule_rev.h | 12 + XModule_rev.rev | 1 + config.mk | 165 ++ delta.asm | 56 + include/clib/xmodule_protos.h | 67 + include/libraries/patteditclass.h | 233 +++ include/libraries/sampeditclass.h | 123 ++ include/libraries/songclass.h | 341 +++ include/libraries/songclass.i | 151 ++ include/libraries/xmodule.h | 240 +++ include/libraries/xmodule.i | 121 ++ include/libraries/xmodule_lib.i | 27 + include/libraries/xmoduleclass.h | 164 ++ include/pragmas/xmodule_pragmas.h | 83 + include/proto/xmodule.h | 11 + manuals/HISTORY | 445 ++++ manuals/Makefile | 33 + manuals/README | 66 + manuals/TODO | 49 + manuals/XModule.texi | 2403 +++++++++++++++++++++ xmodule_lib.fd | 35 + 113 files changed, 53400 insertions(+) create mode 100644 App.c create mode 100644 App.h create mode 100644 Audio.c create mode 100644 Autodocs/songclass create mode 100644 Autodocs/songclass.doc create mode 100644 Autodocs/xmodule create mode 100644 Autodocs/xmodule.doc create mode 100644 BoopsiStubs.h create mode 100644 Catalogs/HowToTranslate.doc create mode 100644 Catalogs/Makefile create mode 100644 Catalogs/deutsch.ct create mode 100644 Catalogs/empty.ct create mode 100644 "Catalogs/fran\347ais.ct" create mode 100644 Catalogs/italiano.ct create mode 100644 Catalogs/nederlands.ct create mode 100644 Catalogs/xmodule.cd create mode 100644 ClearWin.c create mode 100644 CompilerSpecific.h create mode 100644 Compress.c create mode 100644 CustomClasses.c create mode 100644 CustomClasses.h create mode 100644 Cx.c create mode 100644 Debug.h create mode 100644 FFT.c create mode 100644 FakePlayer.asm create mode 100644 Gadgets/Makefile create mode 100644 Gadgets/PattEditClass.c create mode 100644 Gadgets/PattEditClassAsm.asm create mode 100644 Gadgets/SCOPTIONS create mode 100644 Gadgets/SampEditClass.c create mode 100644 Gadgets/pattedit.gadget create mode 100644 Gst.c create mode 100644 Gui.c create mode 100644 Gui.h create mode 100644 Guru.i create mode 100644 Help.c create mode 100644 Hooks/FastTracker2.h create mode 100644 Hooks/GetFT2.c create mode 100644 Hooks/MMDHook.c create mode 100644 Hooks/Makefile create mode 100644 Hooks/OctaMed.h create mode 100644 Hooks/OktalyzerHook.c create mode 100644 Hooks/SCOPTIONS create mode 100644 Hooks/SaveMIDI.c create mode 100644 Hooks/ScreamTrackerHook.c create mode 100644 Hooks/oktalyzer.xmhook create mode 100644 Hooks/screamtracker.xmhook create mode 100644 Instr.c create mode 100644 InstrumentsWin.c create mode 100644 Library.c create mode 100644 ListMacros.h create mode 100644 Locale.c create mode 100644 LocaleStrings.h create mode 100644 LocaleStrings_h.sd create mode 100644 Locale_c.sd create mode 100644 Main.c create mode 100644 Makefile create mode 100644 Misc.c create mode 100644 Operators.c create mode 100644 OptimizationWin.c create mode 100644 OptimizationWin.s create mode 100644 PattPrefsWin.c create mode 100644 PatternWin.c create mode 100644 PlayWin.c create mode 100644 Player.asm create mode 100644 Player.h create mode 100644 Player.i create mode 100644 Players/32Channels.player create mode 100644 Prefs.c create mode 100644 PrefsWin.c create mode 100644 ProgressWin.c create mode 100644 Requesters.c create mode 100644 Requesters.h create mode 100644 Rexx.c create mode 100644 RomTag.asm create mode 100644 SCOPTIONS create mode 100644 SampleWin.c create mode 100644 SaversWin.c create mode 100644 SequenceWin.c create mode 100644 SongClass.c create mode 100644 SongClass.i create mode 100644 SongInfoWin.c create mode 100644 Startup.asm create mode 100644 ToolBoxWin.c create mode 100644 TrackerHook.c create mode 100644 XModule create mode 100644 XModule.i create mode 100644 XModule.info create mode 100644 XModule.prefs create mode 100644 XModuleHook.c create mode 100644 XModulePriv.h create mode 100644 XModule_final.c create mode 100644 XModule_rev.h create mode 100644 XModule_rev.rev create mode 100644 config.mk create mode 100644 delta.asm create mode 100644 include/clib/xmodule_protos.h create mode 100644 include/libraries/patteditclass.h create mode 100644 include/libraries/sampeditclass.h create mode 100644 include/libraries/songclass.h create mode 100644 include/libraries/songclass.i create mode 100644 include/libraries/xmodule.h create mode 100644 include/libraries/xmodule.i create mode 100644 include/libraries/xmodule_lib.i create mode 100644 include/libraries/xmoduleclass.h create mode 100644 include/pragmas/xmodule_pragmas.h create mode 100644 include/proto/xmodule.h create mode 100644 manuals/HISTORY create mode 100644 manuals/Makefile create mode 100644 manuals/README create mode 100644 manuals/TODO create mode 100644 manuals/XModule.texi create mode 100644 xmodule_lib.fd 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 0000000000000000000000000000000000000000..772bb12cf8e879b5e14da560a886b9209836dd92 GIT binary patch literal 15156 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d6af09765d329a9f0a1f0e319df37b15990fc4cb GIT binary patch literal 5628 zcmb_gZ%kX)6+hR$ei-vG#&IYmD7?OS%w>+BLs>>L1h$*BjvI_?v(!Z>bqLrbCE?A2 zh#*8BQ9`4xAELG&s%olGKhW%t3W}4*I3On~b z|6w5zJ`8mI?tAavdw%zK&pp5ME`a`P(EfDkD*-SFST6(6|BKb-m0)jR37=2^0t(4b zH-rbUvK@;PiwjEtOBa?Gu)K=pGL~Ba)=DfjEBH5J%m>AUPhlkgI~Xx@aM1?jVhMm! ziVtx-(X+!34$18F1b>gcOk&XaOh8HKg1txDWeE0$Pl8~46ZBvf%)#CW-UYD4O-2`c zme9p0Yh|2ecj}ueb(~ThLy%nH5QDO@A^-9QjvnjtZ{LaT_TH`z zSJ$z&~j71Cjh>Vln+AM0-v>@)3{z`;l)5VF(zoZo0Ny2lj#+u<663xE!&H(>D7v8 zkUL_@VhG%g!94(5Ra7lP-3V@pm)=8U(Lr)x4m@cK_N)crZVTLE;~aU!w_PaFc~+L4 z)3sxy9{n*U!{f-9ds|9LLiM(pp&qE&=6gfZNp)}_sF#R(HA=k;g@$Q5~<1;r+f3@$&aaW|%}|=*u}|hD#eEGd!Ojvp%~f zYkb8J=ey6WkiAFCac#>pYpY<$)7G|O;LI}Fy9sRD`7yT7R$)_Tv>t18Ha%wD`q=0- z6*uVxGS-i3_cQGO5xAXX}zfKD02rkN$UVA<6NyOd5qj~vFHJE0Eu<_%=tmYH2{ zSM@;*bOC{e*{+aq4LnezwL&Xl+&3nn4t2;K1O4lW7b7p(fCK#?a)41U$AP|!^;&Oz z^uhF)8+f(8N}YYVijVm=+o>ThQGGn*#a*f4Rrlms^l_WRlVL|UI{^J4&;}1w$AE+f zq4J=>;T_J0)YzW|%isQp2qdV(d7+}Lhf_Cdkw|IMK2F+f0Z5pVV1*uj<}B|?f^8^P ziQHDOXA&pnNmL>lBB2gFoT|8@h|3bNCQLHmNDqb;rbk;nK=7_i%=1aaR^b6>gBM(C z3!8WG+g7Ut&dY9qI9K5zd@LBoR=fl@V(a)2Eefn9I2kWcjnPa3*D2U7VF=J;R6~CJ zePFmJ5*ym%(K3$+s8u4{wtB(PV;U`Tr^Fd`a=)AOuz@0tv)b>pNsxN zTM6_gSbirw+f}<><3=rR)cx zAOXZIq_HOLcY7Xj+~4yE#i;c^o(Es0dwJGhS9o9e5g0{@)Lm9GdIXay;zLQ)!|$8z zSziLx|LrCR_E1|KKBZ46+{J2x@`&iGf%t(cPB3d!p~bQDX=Kv z;VEq0F^hg|QdNbbs-$PGddhJYrNif==a`yIqM9s!nAtxV8Qu>oi4tV>p$x0c!?YO6 z@|%IB8<;PsIaF$O)=<3@>eAqL8eKkE<)tCDyJS-chl%rR)5uV1Z2a73YR>zr$@i@*zjLl336Uf&O zGJNH{Fg=%C4z&g$cS;&SdX>%0h6@ake(GKPX7bTFct;kFAf?Q|9dZ-|KH!OmlKI%ZZlvtm4FQ zH9Q#YnLvdUC(Ch z(m$o`4y9uNSjz*6>aR=2fsM zuhP%UfSyyEJY$)D^1YR%0k&Um^fQle#w<+p-R>wt2wZV)YWpJ)0v9K$`mi5Q`q=m*~ze2hxJTg{Pxlsgc^ z2Xo*QoB#T*;}}@5{;|cD=u8%llXeGk@k7bL6gVM zwU%$CwDxBFkmuE&)F;6g7AKH|k!1^!A(lT>c;QP_awlN)_;+W8jJ`3e-6zc#F$2Ue zy7HJC-y1}@l^-GTssKDSorD)z&$(DRdy_0eM4WohEX~EZiY_p~9P)~aj2?7*{na5C zQQ29Zh>=)T_K(8nTah=$_g@%SA$*5xHpx_e0#)A4ltA4f9#!R8E>f9cY$W@=##A1o z@NFCiqadn0k1AinA|H!7ST|*KZ>{3P5~7(<^)o6+a7P_It;m(A$cai+Rh!TSyv5xK ze#Jk1Q+a@8yu(vtaHr!_RRwOx1Vr|*v^odUXR|R{iM@q_I7J`P)N1i|=$P6H5Mhyyu}Owb)E^sPfX{*1pZJHCXs z@Y;sgOL#rJe{t??E(7py!90MhEVFFDZ_VTd{$i}z)n^v0dDL1F){JV+?8j3Y!oEDC z{z6vQnbmrM#H-hQsg9kYsC7=wexiPZzkha7eL^uKVm7 zmuhdiK23StKLsxM9mcabMDaXtFSNrU@Ix)MK{GrCyYb^J4Evx44nqj@@M>s-Avo>igAq|N9Q#V8$nO14ZPu&SDMUvwjD?^C;#Qev}{J!@O89QqWTn zEijvAOqWa-Ob$~?{zCp#{%HQW{6qPIamhGi4CTF>H<(w$EpmV7M!9p`VQ#x&!7yWZ z-Jlp+47&{+{U^ObU!})sh}tRWQ~D8omENdZ&|THNs7vbHq{Lcht+JL|&8Cp9jF@qr zp#KWX=U6G}&YaD;`c8CqcRvgEL;J&fg}Wc)d7WEuRM)z8x%TXSSooE&YtNoXcJA3N Nw4E02b%>|h_%GoH`=S5< literal 0 HcmV?d00001 diff --git a/Hooks/screamtracker.xmhook b/Hooks/screamtracker.xmhook new file mode 100644 index 0000000000000000000000000000000000000000..10a6a3bb9fa4bfd94bb332a4880ddd09a8706ec4 GIT binary patch literal 3036 zcmbtWZEREL6@KoAudj`3TrY|OL(SzTioDGDI)r5lim2ms?Two_nVnVvMZx*-wMj|r z-NlhXQFoJt2u=He)ISJS8-E6@MIk~J(f$ZGZ zP9PhsKW6FpJ@=e*&wDUhC^GY_Rb9DwhgD+^ip<8y1F zcwv8+eyiCVLcQMpPvD1-RcG)BMQe-)C)UW~L)F=O4?Ak*39-n<=N?~9T87tiA=#Gb z#12DLUhqWn==ePUZf{q-0EZ|!x430^1tCvg<1;Cf%E2cKvW1QS-qtBfM6Xb3h~y-2Yvq_-2ZJ;zGk z$oEIGM!nph@jU%@coLPReUz5dfa9It(SE(O{{cM`jp&gDi)s5!>i46bKx88lm2CQ+ zWm6ZiBQiW{2fY!uqjr30VmEoz!~mx{Ydz(v^EQG4)g|5hlmWrdV(lY4klmK2^nJFT zU!Ft?{Plp8m|>O&R4C#`l@f$}k2Xn@8^#jZee}oK-FvdA6;oFrU#Q});9e?)uDQYZfG({PgXMiM0P#57#W?4XKiT6tHc#iC#3q@` zmZ=)f@1g9PxErs}M#r&7(4+Kg)mxjm#Wu>hZ30c)>@?Y(v+bxa;V?&dwr#O)8|D@x zWJ{A9<}BNGp4qlx&sX`4p49|R!*_@xnxr{2J6b^lTOE21cNxbko$Fmk5KHMRF91d zQ<~=nL)67xcF%53wb6+QLu;tQb2=}lZ0u}*j#dnc+N)S8;hLrukG9)WOg&ptJDx<& zYITU!YJ}qCD9+?-HLT{z*Hpc@w#Fn|sQNvObC=JN5j*}zMT);Rs9~uthhWC9zW&6s zBBj80P=pmsHu&^;z~-((krEc0);=3aB`d1$f6N`XLUY&n)pKXKOWDy3{Fcq%7c_r# zCahh1!^%9-y~iqYKYCU#63SZ8-O90SyIToYr5IRerYW-u8wVM1{dCf=6zQzsUWy%~ z=U~v0um0F7dAe+ks+46&lh*U385WoiHp77t_)^l%*?^h#{oyyV1CC6~ItK0Y*2Jsa z{8Xk&#rW1=PcK<-(3x^|`l@wvdS)syUA2K%+jd(79C?s3l}~%vHVF?z(m6=y%oI22 zBJ{N`pHO8F4u240bt`rm+hfR7jw|#0uv|Tft+<0C<6opwu`xsjPvI$Fsu#V3sSN}xc;CMmvlx`D>H*oNIPkGon{T96IcMtlW zx!WE-xbLf)-)4w07+)H|r_?#IW`UyXulYUw57zt*AJ95N>p5DD4V?7qUn8{Op?wV86xJsG|Fi^L5v&PUuZ@UU{LM(m9=)Od^vH151gb%2fz+N=uK +#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 0000000000000000000000000000000000000000..ad443d2f0542c71f425ec3d5c591282bdd22a2a2 GIT binary patch literal 9412 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 Nt>mSNTGztG{{e?t?PCA{ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b209ae9402f9095fc279724992a4cc84bba4af49 GIT binary patch literal 106900 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fb47624fc53462849ae98ad3fb7eb77ef992ba7e GIT binary patch literal 5381 zcmeH~-%C?r7{{OK(2!_@l4uup1fkj0V1g-gKavqjO>t8a3T84xFsIINNwOEAT2~3t zg;zo3jY=0?RM5>~e?W81d#~t}9&FY3N!I#Pmk}@Vr-rm#nnWA8;TYILz*W5j zc3?u)DY_Kf6=x-VJHRsh8|S|`@pnKnzp1F|dC9MHN(%z`CUdBGXnqho&om9{^h ziu>OHI*nF=!bj9zRc}x{UZ#zHT-7CtbChOfRPs=u=g;sj!oR`Q^#J)duyU<|{25re z{*L@3l7HBb=*SFTKQWKQRrOq6wTdaG743T|{G*axla2i-yJbh^A1Hr7`3K6sf;Rf` zvi<*1TK*l)T+2VpKkNNl{9H8sYp_25{-6IJG80yBzON(`YD~QDvra+ZPF7{YjLL*F zM9hS*1^FIQCKP>S68BRk%+egpgkm0MLY#ltOlV|99EgjFBhXwOe?{KekPPG&pIktF z_Q6uW2kEF=>eomwEIxdL3>fEQNcxG|gWJR$$?{B?5n1Y}nXpx4!!?_iG9PBb$D}b6 zO6C-q>|-W8M-TW{Gog_gABpGLhAcfUt|E1*&oD`c0A$s-jjW-KzVNMdzdB#Sc!3I3 z<$Vw|tvBHp2AK(4k(3GF5-}5graERq$($f%Lea-e*h=#-6FLPMg=N%f2nyQih+6T$ z?`b0|%I)PxT^5u-p!_O)TQjuLSN@exIh+ofZnOL|wZ)`p`DbEE1trVB3d}M&)$-58 zlnP3ge+Mw@6rR)Q?(M%A?d%JOBOShp+c(BWhqcu3R3b61jSt;Q_?u~t%Z(?5V&SgN zo=CLAHyDm;$tf+;BfFYA8ji)fBA2k0N=#3Wj^9jaBco%9k_no-x2G=_30(tK8Z+=Q?PO5@We!9M6Xo$bQvyXqUtG=h7qjQL7n5%xUbC9d6pF)JM zze}i(E7{gzYJ~U-;X9ZwK{Nz_Xaz6=@gd?|ASM`qWSKx1>H~-<7Ba%u%?~KN04BvA zqpich&yWP9U7&Of12=;cOdgGfyE7v-r!2K3GdWQ&EkCaWO%;$!NWLV$Ah8OEUXW=K zzQN8w3X91gS;_#R=!@_T2ZfOY5W_>5aRQ?;kk81H!0dn|&jNA@(^94sMkR(FSmZfC z@*u#-$gmegq3H+383g=i_zz^D%Y#xk14BN;Vn< nAOeIZ7*ZfAh93z`%cK4rM$tMNpTj6JfnFJn&(ZiCiXi|19 +#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 -- 2.25.1